Skip to content

Commit

Permalink
Add bloom
Browse files Browse the repository at this point in the history
  • Loading branch information
zenith391 committed Feb 18, 2023
1 parent fe80fa3 commit 7dd71ad
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 20 deletions.
31 changes: 21 additions & 10 deletions src/renderer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,10 @@ const ShaderProgram = struct {
gl.uniform1i(location, int);
}

pub fn setUniformBool(self: ShaderProgram, uniform: [:0]const u8, boolean: bool) void {
self.setUniformInt(uniform, @boolToInt(boolean));
}

pub fn setUniformFloat(self: ShaderProgram, uniform: [:0]const u8, float: f32) void {
const location = gl.getUniformLocation(self.program, uniform);
gl.uniform1f(location, float);
Expand All @@ -423,7 +427,7 @@ const ShaderProgram = struct {

pub const Framebuffer = struct {
fbo: gl.GLuint,
colorTexture: gl.GLuint,
colorTextures: [2]gl.GLuint,
depthTexture: gl.GLuint,
width: c_int,
height: c_int,
Expand All @@ -434,13 +438,17 @@ pub const Framebuffer = struct {
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
defer gl.bindFramebuffer(gl.FRAMEBUFFER, 0);

var colorTexture: gl.GLuint = undefined;
gl.genTextures(1, &colorTexture);
gl.bindTexture(gl.TEXTURE_2D, colorTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB16F, width, height, 0, gl.RGB, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTexture, 0);
var colorTextures: [2]gl.GLuint = undefined;
gl.genTextures(2, &colorTextures);
for (colorTextures) |colorTexture, i| {
gl.bindTexture(gl.TEXTURE_2D, colorTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB16F, width, height, 0, gl.RGB, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + @intCast(c_uint, i), gl.TEXTURE_2D, colorTexture, 0);
}

var depthTexture: gl.GLuint = undefined;
gl.genTextures(1, &depthTexture);
Expand All @@ -452,13 +460,16 @@ pub const Framebuffer = struct {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);

const attachments: [2]gl.GLenum = .{ gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1 };
gl.drawBuffers(2, &attachments);

if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
return error.IncompleteFramebuffer;
}

return Framebuffer{
.fbo = fbo,
.colorTexture = colorTexture,
.colorTextures = colorTextures,
.depthTexture = depthTexture,
.width = width,
.height = height,
Expand All @@ -475,7 +486,7 @@ pub const Framebuffer = struct {

pub fn deinit(self: Framebuffer) void {
gl.deleteFramebuffers(1, &self.fbo);
gl.deleteTextures(1, &self.colorTexture);
gl.deleteTextures(2, &self.colorTextures);
gl.deleteTextures(1, &self.depthTexture);
}
};
80 changes: 78 additions & 2 deletions src/shaders/postprocess.fs
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
#version 330 core
#define M_PI 3.1415926535897932384626433832795

// Parameters
uniform vec3 viewPos;
uniform vec3 lightDir;
uniform float lightIntensity;
uniform float planetRadius;
uniform float atmosphereRadius;
uniform sampler2D screenTexture;
uniform sampler2D screenDepth;
uniform mat4 projMatrix;
uniform mat4 viewMatrix;
uniform bool enableAtmosphere;

// Samplers
uniform sampler2D screenTexture;
uniform sampler2D bloomTexture;
uniform sampler2D screenDepth;

// Operations
uniform bool doBrightTexture;
uniform bool doBlurring;
uniform bool horizontalBlurring;

in vec2 texCoords;

out vec4 fragColor;
Expand Down Expand Up @@ -97,8 +108,69 @@ float linearizeDepth(float d, float zNear, float zFar) {
return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear));
}

void drawBrightTexture() {
vec3 color = texture(screenTexture, texCoords).rgb;
float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
if (brightness > 1.0) {
fragColor = vec4(color.rgb, 1.0);
} else {
fragColor = vec4(0, 0, 0, 1.0);
}
}

void drawBlurredTexture() {
// TODO: just precompute it and paste it in the file!
float weight[16] = float[] (
0.11764705882353,
0.10382316500995,
0.071356548201486,
0.038194407924512,
0.015921798027837,
0.0051690510145185,
0.001306940769205,
0.00025735189625681,
3.9466191517943e-05,
4.7135643991707e-06,
4.3842978495043e-07,
3.1759747098388e-08,
1.7917623229074e-09,
7.8724542250503e-11,
2.6938057007595e-12,
7.1787490324768e-14
);
//for (int i = 0; i < 16; i++) {
// float w = cos(i * M_PI / 16.0) * 0.5 + 0.5;
// weight[i] = w / (7.5 * 2 + 1);
//}

float sampleDistance = 1.0;

vec2 tex_offset = 1.0 / textureSize(screenTexture, 0); // gets size of single texel
vec3 result = texture(screenTexture, texCoords).rgb * weight[0]; // current fragment's contribution
if (horizontalBlurring) {
for (int i = 1; i < 16; i++) {
result += texture(screenTexture, texCoords + vec2(tex_offset.x * i * sampleDistance, 0.0)).rgb * weight[i];
result += texture(screenTexture, texCoords - vec2(tex_offset.x * i * sampleDistance, 0.0)).rgb * weight[i];
}
} else {
for (int i = 1; i < 16; i++) {
result += texture(screenTexture, texCoords + vec2(0.0, tex_offset.y * i * sampleDistance)).rgb * weight[i];
result += texture(screenTexture, texCoords - vec2(0.0, tex_offset.y * i * sampleDistance)).rgb * weight[i];
}
}
fragColor = vec4(result, 1.0);
}

void main() {
// Post-process
if (doBrightTexture) {
drawBrightTexture();
return;
} else if (doBlurring) {
drawBlurredTexture();
return;
}

vec3 color = texture(screenTexture, texCoords).rgb;
float depth = texture(screenDepth, texCoords).r;

Expand Down Expand Up @@ -140,6 +212,10 @@ void main() {
}
//result = mix(result, vec3((dstToSurface - dstToAtmosphere - 500)), 0.9);

// Apply bloom
vec3 bloomColor = texture(bloomTexture, texCoords).rgb;
result += bloomColor;

// HDR
float gamma = 1.0; // 2.2
float exposure = 1.0;
Expand Down
3 changes: 2 additions & 1 deletion src/shaders/sun.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ out vec4 fragColor;

void main() {
// TODO: base on the sun's temperature?
vec3 objectColor = vec3(1.0f, 0.9f, 0.8f);
vec3 objectColor = vec3(10.0f, 9.0f, 8.0f);
vec3 result = objectColor;

fragColor = vec4(result, 1.0f);
}
2 changes: 1 addition & 1 deletion src/shaders/terrain.fs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ vec3 getNormal(vec3 viewDir) {
float waterBlend = 1 - exp(-waterElevation * 1.0);

float scale = 0.003;
float strength = 1.0;
float strength = 0.8;
vec2 uvX = parallaxMapping(worldPosition.zy * scale, viewDir);
vec2 uvY = parallaxMapping(worldPosition.xz * scale, viewDir);
vec2 uvZ = parallaxMapping(worldPosition.xy * scale, viewDir);
Expand Down
79 changes: 73 additions & 6 deletions src/states/play.zig
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ pub const PlayState = struct {
noiseCubemap: Texture,
skyboxCubemap: Texture,
framebuffer: Framebuffer,
blurFramebuffer: Framebuffer,

/// The position of the camera
/// This is already scaled by cameraDistance
Expand Down Expand Up @@ -254,7 +255,8 @@ pub const PlayState = struct {
// TODO: make a loading scene
const planetRadius = 5000; // a radius a bit smaller than Earth's (~6371km)
const seed = randomPrng.random().int(u64);
const planet = Planet.generate(game.allocator, 7, planetRadius, seed, .{}) catch unreachable;
const subdivisions = if (@import("builtin").mode == .Debug) 6 else 7;
const planet = Planet.generate(game.allocator, subdivisions, planetRadius, seed, .{}) catch unreachable;

if (false) {
// Load Earth
Expand All @@ -270,6 +272,7 @@ pub const PlayState = struct {
Lifeform.initMeshes(game.allocator) catch unreachable;

var framebuffer = Framebuffer.create(800, 600) catch unreachable;
var blurFramebuffer = Framebuffer.create(800, 600) catch unreachable;

const cursorPos = game.window.getCursorPos();
std.valgrind.callgrind.startInstrumentation();
Expand All @@ -279,6 +282,7 @@ pub const PlayState = struct {
.skyboxCubemap = skybox,
.planet = planet,
.framebuffer = framebuffer,
.blurFramebuffer = blurFramebuffer,
.cameraDistance = planetRadius * 10,
.targetCameraDistance = planetRadius * 2.5,
};
Expand Down Expand Up @@ -330,15 +334,73 @@ pub const PlayState = struct {
if (@floatToInt(c_int, size.x()) != self.framebuffer.width or @floatToInt(c_int, size.y()) != self.framebuffer.height) {
self.framebuffer.deinit();
self.framebuffer = Framebuffer.create(@floatToInt(c_int, size.x()), @floatToInt(c_int, size.y())) catch unreachable;

self.blurFramebuffer.deinit();
self.blurFramebuffer = Framebuffer.create(@floatToInt(c_int, size.x()), @floatToInt(c_int, size.y())) catch unreachable;
}

self.framebuffer.bind();
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);

self.renderScene(game, renderer);

self.framebuffer.unbind();

// Compute a texture from the HDR framebuffer with only bright parts
{
const program = renderer.postprocessProgram;
self.framebuffer.bind();
defer self.framebuffer.unbind();

const attachments: [1]gl.GLenum = .{gl.COLOR_ATTACHMENT1};
const ogAttachments: [1]gl.GLenum = .{gl.COLOR_ATTACHMENT0};
gl.drawBuffers(1, &attachments);
defer gl.drawBuffers(1, &ogAttachments);

program.use();
program.setUniformBool("doBrightTexture", true);

gl.bindVertexArray(QuadMesh.getVAO());
gl.disable(gl.DEPTH_TEST);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, self.framebuffer.colorTextures[0]);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}

// Blur the bright part to create bloom
{
const program = renderer.postprocessProgram;

// Horizontal blurring (framebuffer -> blurFramebuffer)
program.use();
program.setUniformBool("doBrightTexture", false);
program.setUniformBool("doBlurring", true);
program.setUniformBool("horizontalBlurring", true);
{
self.blurFramebuffer.bind();
defer self.blurFramebuffer.unbind();
gl.bindVertexArray(QuadMesh.getVAO());
gl.disable(gl.DEPTH_TEST);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, self.framebuffer.colorTextures[1]);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}

// Vertical blurring (blurFramebuffer -> framebuffer)
program.setUniformBool("horizontalBlurring", false);
{
self.framebuffer.bind();
defer self.framebuffer.unbind();
const attachments: [1]gl.GLenum = .{gl.COLOR_ATTACHMENT1};
const ogAttachments: [1]gl.GLenum = .{gl.COLOR_ATTACHMENT0};
gl.drawBuffers(1, &attachments);
defer gl.drawBuffers(1, &ogAttachments);

gl.bindTexture(gl.TEXTURE_2D, self.blurFramebuffer.colorTextures[0]);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
}

gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
{
const program = renderer.postprocessProgram;
Expand All @@ -355,22 +417,27 @@ pub const PlayState = struct {

program.use();
program.setUniformInt("screenTexture", 0);
program.setUniformInt("screenDepth", 1);
program.setUniformInt("bloomTexture", 1);
program.setUniformInt("screenDepth", 2);
program.setUniformMat4("projMatrix", Mat4.perspective(70, size.x() / size.y(), zNear, zFar));
program.setUniformMat4("viewMatrix", Mat4.lookAt(self.cameraPos, target, Vec3.new(0, 0, 1)));
program.setUniformVec3("viewPos", self.cameraPos);
program.setUniformVec3("lightDir", solarVector);
program.setUniformFloat("planetRadius", self.planet.radius);
program.setUniformFloat("atmosphereRadius", self.planet.radius + 50 * 10); // * HEIGHT_ELEVATION
program.setUniformFloat("lightIntensity", self.solarConstant / 1500);
program.setUniformInt("enableAtmosphere", @boolToInt(self.displayMode == .Normal));
program.setUniformBool("enableAtmosphere", self.displayMode == .Normal);
program.setUniformBool("doBrightTexture", false);
program.setUniformBool("doBlurring", false);

gl.bindVertexArray(QuadMesh.getVAO());
gl.disable(gl.DEPTH_TEST);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, self.framebuffer.colorTexture);
gl.bindTexture(gl.TEXTURE_2D, self.framebuffer.colorTextures[0]); // color
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, self.framebuffer.depthTexture);
gl.bindTexture(gl.TEXTURE_2D, self.framebuffer.colorTextures[1]); // blur
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, self.framebuffer.depthTexture); // depth
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
}
Expand Down
24 changes: 24 additions & 0 deletions sum.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
function gaussian(x, mean, stddev)
local a = (x - mean) / stddev
return math.exp(-0.5 * a * a)
end

local sumWeight = 0
local stddev = 2
for i = 0, 15 do
local w = gaussian(i+(16-1)/2, (16-1)/2.0, stddev)
sumWeight = sumWeight + math.cos(i * math.pi / 16.0) * 0.5 + 0.5
end
print(sumWeight)

local weights = {}
local sum = 0
print("{")
for i=0, 15 do
local w = gaussian(i+(16-1)/2, (16-1)/2.0, stddev) / sumWeight;
sum = sum + w
print("\t" .. w .. ",")
end
print("}")
print(sum)

0 comments on commit 7dd71ad

Please sign in to comment.