Skip to content

Commit

Permalink
Merge pull request #161 from erikstrand/ssao
Browse files Browse the repository at this point in the history
Add an SSAO example
  • Loading branch information
einarf committed Jul 2, 2022
2 parents 04112d5 + 94164e0 commit 22c135e
Show file tree
Hide file tree
Showing 8 changed files with 200,524 additions and 8 deletions.
32 changes: 31 additions & 1 deletion examples/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ def resize(self, width: int, height: int):


class OrbitCameraWindow(mglw.WindowConfig):
"""Base class with built in 3D orbit camera support"""
"""Base class with built in 3D orbit camera support
Move the mouse to orbit the camera around the view point.
"""

def __init__(self, **kwargs):
super().__init__(**kwargs)
Expand Down Expand Up @@ -61,3 +64,30 @@ def mouse_scroll_event(self, x_offset: float, y_offset: float):

def resize(self, width: int, height: int):
self.camera.projection.update(aspect_ratio=self.wnd.aspect_ratio)


class OrbitDragCameraWindow(mglw.WindowConfig):
"""Base class with drag-based 3D orbit support
Click and drag with the left mouse button to orbit the camera around the view point.
"""

def __init__(self, **kwargs):
super().__init__(**kwargs)
self.camera = OrbitCamera(aspect_ratio=self.wnd.aspect_ratio)

def key_event(self, key, action, modifiers):
keys = self.wnd.keys

if action == keys.ACTION_PRESS:
if key == keys.SPACE:
self.timer.toggle_pause()

def mouse_drag_event(self, x: int, y: int, dx, dy):
self.camera.rot_state(dx, dy)

def mouse_scroll_event(self, x_offset: float, y_offset: float):
self.camera.zoom_state(y_offset)

def resize(self, width: int, height: int):
self.camera.projection.update(aspect_ratio=self.wnd.aspect_ratio)
41 changes: 41 additions & 0 deletions examples/resources/programs/ssao/blur.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#version 330 core

#if defined VERTEX_SHADER

in vec3 in_position;
in vec2 in_texcoord_0;

out vec2 texcoord;

void main() {
gl_Position = vec4(in_position, 1.0);
texcoord = in_texcoord_0;
}

#elif defined FRAGMENT_SHADER

uniform sampler2D input_texture;

in vec2 texcoord;

layout(location=0) out float blurred_texture;

void main() {
vec2 texel_size = 1.0 / vec2(textureSize(input_texture, 0));
float result = 0.0;
const int h = 2;
uint n_samples = 0u;
for (int x = -h; x <= h; ++x) {
for (int y = -h; y <= h; ++y) {
vec2 offset = vec2(float(x), float(y)) * texel_size;
float sample = texture(input_texture, texcoord + offset).x;
if (sample != 0.0) {
result += sample;
++n_samples;
}
}
}
blurred_texture = result / float(n_samples);
}

#endif
35 changes: 35 additions & 0 deletions examples/resources/programs/ssao/geometry.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#version 330

#if defined VERTEX_SHADER

uniform mat4 mvp;

in vec3 in_position;
in vec3 in_normal;

out vec3 pos;
out vec3 normal;

void main() {
gl_Position = mvp * vec4(in_position, 1.0);;
pos = in_position;
normal = in_normal;
}

#elif defined FRAGMENT_SHADER

uniform mat4 m_camera;

in vec3 pos;
in vec3 normal;

layout(location=0) out float g_view_z;
layout(location=1) out vec3 g_normal;

void main() {
// Rotate into view space, and record the z component.
g_view_z = -(m_camera * vec4(pos, 1.0)).z;
g_normal = normalize(normal);
}

#endif
83 changes: 83 additions & 0 deletions examples/resources/programs/ssao/shading.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#version 330

#if defined VERTEX_SHADER

uniform mat4 m_camera_inverse;
uniform mat4 m_projection_inverse;
uniform vec3 v_camera_pos;

in vec3 in_position;
in vec2 in_texcoord_0;

out vec3 view_ray;
out vec2 texcoord;

void main() {
gl_Position = vec4(in_position, 1.0);

// Convert in_position from clip space to view space.
vec4 pos = m_projection_inverse * vec4(in_position, 1.0);
// Normalize its z value.
pos.xy /= -pos.z;
pos.z = -1.0;
pos.w = 1.0;
// Convert to world space.
pos = m_camera_inverse * pos;
view_ray = pos.xyz - v_camera_pos;

texcoord = in_texcoord_0;
}

#elif defined FRAGMENT_SHADER

uniform vec3 light_pos;
uniform vec3 camera_pos;
uniform vec3 base_color;
uniform vec4 material_properties;
uniform int render_mode;

uniform sampler2D g_view_z;
uniform sampler2D g_normal;
uniform sampler2D ssao_occlusion;

in vec3 view_ray;
in vec2 texcoord;

layout(location=0) out vec4 frag_color;

void main() {
// Ignore background fragments.
float view_z = texture(g_view_z, texcoord).x;
if (view_z == 0.0) {
discard;
}

// Load/compute the position and normal vectors (in world space).
vec3 position = camera_pos + view_z * view_ray;
vec3 normal = texture(g_normal, texcoord).xyz;

// Compute lighting.
float ambient_magnitude = material_properties.x;
float diffuse_magnitude = material_properties.y;
float specular_magnitude = material_properties.z;
float specular_exponent = material_properties.w;
float occlusion;
if (render_mode != 1) {
occlusion = texture(ssao_occlusion, texcoord).x;
} else {
occlusion = 1.0;
}
vec3 light_dir = normalize(light_pos - position);
vec3 reflection_dir = reflect(-light_dir, normal);
float ambient = ambient_magnitude * occlusion;
float diffuse = diffuse_magnitude * max(dot(light_dir, normal), 0.0);
float specular = specular_magnitude * pow(max(dot(light_dir, normal), 0.0), specular_exponent);
float luminosity = ambient + diffuse + specular;
vec3 color = luminosity * base_color;
if (render_mode == 2) {
color = vec3(occlusion);
}
frag_color = vec4(color, 1.0);
}

#endif
94 changes: 94 additions & 0 deletions examples/resources/programs/ssao/ssao.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#version 330

#if defined VERTEX_SHADER

uniform mat4 m_camera_inverse;
uniform mat4 m_projection_inverse;
uniform vec3 v_camera_pos;

in vec3 in_position;
in vec2 in_texcoord_0;

out vec3 view_ray;
out vec2 texcoord;

void main() {
gl_Position = vec4(in_position, 1.0);

// Convert in_position from clip space to view space.
vec4 pos = m_projection_inverse * vec4(in_position, 1.0);
// Normalize its z value.
pos.xy /= -pos.z;
pos.z = -1.0;
pos.w = 1.0;
// Convert to world space.
pos = m_camera_inverse * pos;
view_ray = pos.xyz - v_camera_pos;

texcoord = in_texcoord_0;
}

#elif defined FRAGMENT_SHADER

const int n_samples = 64;

uniform vec3 f_camera_pos;
uniform mat4 mvp;
uniform vec3 samples[n_samples];
uniform float z_offset;

uniform sampler2D g_view_z;
uniform sampler2D g_norm;
uniform sampler2D noise;

in vec3 view_ray;
in vec2 texcoord;

layout(location=0) out float occlusion;

void main() {
// Ignore background fragments.
float f_view_z = texture(g_view_z, texcoord).x;
if (f_view_z == 0.0) {
discard;
}

// Load/compute the position and normal vectors (in world coordinates).
vec3 f_pos = f_camera_pos + f_view_z * view_ray;
vec3 f_norm = texture(g_norm, texcoord).xyz;

// Compute the rotation matrix that takes us from tangent space to world space.
// Note that the x and y axes in tangent space aren't aligned with the texture coordinates or
// anything -- they are intentionally randomized to decorrelate our samples in nearby pixels.
const int noise_size = 32;
vec2 noise_pos = (1.0 / float(noise_size)) * vec2(
float(mod(gl_FragCoord.x, noise_size)),
float(mod(gl_FragCoord.y, noise_size))
);
vec3 random_vec = normalize(texture(noise, noise_pos).xyz);
vec3 tangent_x = normalize(random_vec - f_norm * dot(random_vec, f_norm));
vec3 tangent_y = cross(f_norm, tangent_x);
mat3 tan_to_world = mat3(tangent_x, tangent_y, f_norm);

// Measure occlusion.
occlusion = 0.0;
for (int i = 0; i < n_samples; ++i) {
// Compute the sample position in world coordinates.
vec3 sample_offset = tan_to_world * samples[i];
vec4 sample_pos = vec4(f_pos + sample_offset, 1.0);

// Convert to clip space, then scale the relevant coordinates to the range [0, 1].
sample_pos = mvp * sample_pos;
sample_pos.xyz /= sample_pos.w;
sample_pos.xy = 0.5 * sample_pos.xy + 0.5;

// Read the actual depth at the sample point.
float actual_view_z = texture(g_view_z, sample_pos.xy).x;

// If the actual depth is less than the depth of the sample point, the sample is occluded.
occlusion += (actual_view_z != 0.0 && actual_view_z + z_offset < f_view_z) ? 1.0 : 0.0;
}
occlusion = 1.0 - (1.0 / float(n_samples)) * occlusion;
}

#endif
Loading

0 comments on commit 22c135e

Please sign in to comment.