Add basis for variance shadow mapping

This commit is contained in:
karl 2021-05-21 22:38:40 +02:00
parent 88611815e1
commit 3dcfc89b52
4 changed files with 39 additions and 27 deletions

View File

@ -60,9 +60,6 @@ vec2 get_parallax_offset_uv(vec2 uv, vec3 view_direction) {
} }
float get_shadow(vec4 fragPosLightSpace, vec3 normal) { float get_shadow(vec4 fragPosLightSpace, vec3 normal) {
// The bias varies depending on the angle to the light (the steeper the angle, the bigger the bias needs to be)
mediump float bias = max(0.005 * (1.0 - dot(normal, fs_in.TangentLightDir)), 0.0005);
// perform perspective divide // perform perspective divide
mediump vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; mediump vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
// transform to [0,1] range // transform to [0,1] range
@ -71,14 +68,25 @@ float get_shadow(vec4 fragPosLightSpace, vec3 normal) {
// If we're outside of [0.0, 1.0] in the coordinates, return 0 // If we're outside of [0.0, 1.0] in the coordinates, return 0
if (projCoords.x < 0.0 || projCoords.y < 0.0 || projCoords.x > 1.0 || projCoords.y > 1.0) return 0.0; if (projCoords.x < 0.0 || projCoords.y < 0.0 || projCoords.x > 1.0 || projCoords.y > 1.0) return 0.0;
// Variance Shadow Map Calculation
vec2 moments = texture(shadowMap, projCoords.xy).rg;
// get closest depth value from light's perspective (using [0,1] range fragPosLight as coords) // get closest depth value from light's perspective (using [0,1] range fragPosLight as coords)
mediump float closestDepth = texture(shadowMap, projCoords.xy).r; mediump float closestDepth = texture(shadowMap, projCoords.xy).r;
// get depth of current fragment from light's perspective // get depth of current fragment from light's perspective
mediump float currentDepth = projCoords.z; mediump float currentDepth = projCoords.z;
// check whether current frag pos is in shadow // check whether current frag pos is in shadow
mediump float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; float p = step(projCoords.z, moments.x);
// We divide by this later, so make sure it's not exactly 0
// It seems like it should always be 0.0, but due to interpolation it's not -- it increases with the deviation!
float variance = max(moments.y - moments.x * moments.x, 0.00002);
return shadow; float d = projCoords.z - moments.x * 1.0; // bias should be "compare", what is that?
float p_max = variance / (variance + d * d);
// If this pixel is exactly in the light, p is 1, so make sure we return that in that case
// min() to make sure that it doesn't get greater than 1.0
return 1.0 - min(max(p, p_max), 1.0);
} }
void main() { void main() {

View File

@ -2,6 +2,6 @@
void main() void main()
{ {
// This happens implicitly anyways // The mean and the mean squared, which we need to calculate the variance
// gl_FragDepth = gl_FragCoord.z; gl_FragColor = vec4(gl_FragCoord.z, gl_FragCoord.z * gl_FragCoord.z, 0.0, 0.0);
} }

View File

@ -17,16 +17,16 @@ class DirectionalLight {
// Create depth texture // Create depth texture
glGenTextures(1, &depth_map); glGenTextures(1, &depth_map);
glBindTexture(GL_TEXTURE_2D, depth_map); glBindTexture(GL_TEXTURE_2D, depth_map);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, // R and G with 32 bit floats: stores mean and variance
NULL); glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// Attach depth texture as FBO's depth buffer // Attach depth texture as FBO's depth buffer
glBindFramebuffer(GL_FRAMEBUFFER, depth_map_fbo); glBindFramebuffer(GL_FRAMEBUFFER, depth_map_fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_map, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, depth_map, 0);
glDrawBuffer(GL_NONE); glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE); glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
@ -43,8 +43,8 @@ class DirectionalLight {
glBindTexture(GL_TEXTURE_2D, depth_map); glBindTexture(GL_TEXTURE_2D, depth_map);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
NULL); NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
@ -96,8 +96,8 @@ class DirectionalLight {
unsigned int depth_map; unsigned int depth_map;
unsigned int depth_map_fbo; unsigned int depth_map_fbo;
const unsigned int SHADOW_WIDTH = 4096; const unsigned int SHADOW_WIDTH = 2048;
const unsigned int SHADOW_HEIGHT = 4096; const unsigned int SHADOW_HEIGHT = 2048;
Gedeng::Shader shadow_shader; Gedeng::Shader shadow_shader;
}; };

View File

@ -37,10 +37,10 @@ void renderQuad() {
glBindVertexArray(0); glBindVertexArray(0);
} }
class ShadowApp : public Gedeng::Application { class mesh3 : public Gedeng::Application {
public: public:
ShadowApp(unsigned long ms_per_update, unsigned int window_size_x, unsigned int window_size_y, mesh3(unsigned long ms_per_update, unsigned int window_size_x, unsigned int window_size_y,
Gedeng::String window_name) Gedeng::String window_name)
: Application(ms_per_update, window_size_x, window_size_y, window_name), particle_interval(0.2), : Application(ms_per_update, window_size_x, window_size_y, window_name), particle_interval(0.2),
number_of_steps(10.0), number_of_refinement_steps(10.0), bump_depth(0.1), number_of_steps(10.0), number_of_refinement_steps(10.0), bump_depth(0.1),
render_shader(Gedeng::Shader("Shader/bump.vs", "Shader/bump.fs")), render_shader(Gedeng::Shader("Shader/bump.vs", "Shader/bump.fs")),
@ -53,7 +53,9 @@ class ShadowApp : public Gedeng::Application {
particle_tex2("Resources/Textures/Particles/magic.png", Gedeng::Texture::Settings()), particle_tex2("Resources/Textures/Particles/magic.png", Gedeng::Texture::Settings()),
particle_tex3("Resources/Textures/Particles/smoke.png", Gedeng::Texture::Settings()), particle_tex3("Resources/Textures/Particles/smoke.png", Gedeng::Texture::Settings()),
quad_mesh(Gedeng::QuadMesh(10.0)), light(glm::vec3(0.57735, -0.57735, 0.57735)), quad_mesh(Gedeng::QuadMesh(10.0)), light(glm::vec3(0.57735, -0.57735, 0.57735)),
monkey_mesh("Resources/Meshes/Monkey.obj", Gedeng::ObjMesh::Settings()) { mesh1("Resources/Meshes/Monkey.obj", Gedeng::ObjMesh::Settings()),
mesh2("Resources/Meshes/Monkey.obj", Gedeng::ObjMesh::Settings()),
mesh3("Resources/Meshes/Monkey.obj", Gedeng::ObjMesh::Settings()) {
particles.set_position(glm::vec3(0.0f, 0.0f, -10.0f)); particles.set_position(glm::vec3(0.0f, 0.0f, -10.0f));
particles.set_velocity(glm::vec3(-2, 4, -2), glm::vec3(2, 6, 2)); particles.set_velocity(glm::vec3(-2, 4, -2), glm::vec3(2, 6, 2));
particles.set_gravity(glm::vec3(0, -4, 0)); particles.set_gravity(glm::vec3(0, -4, 0));
@ -67,10 +69,10 @@ class ShadowApp : public Gedeng::Application {
camera.translate(glm::vec3(0.0, 2.0, 1.0)); camera.translate(glm::vec3(0.0, 2.0, 1.0));
// camera.rotate(30, glm::vec3(1.0, 0.0, 0.0)); // camera.rotate(30, glm::vec3(1.0, 0.0, 0.0));
monkey_mesh.translate(glm::vec3(2.0, 3.0, -2.0)); mesh1.translate(glm::vec3(2.0, 3.0, -2.0));
} }
~ShadowApp() = default; ~mesh3() = default;
void fixed_update(double delta) override { void fixed_update(double delta) override {
camera.update(delta); camera.update(delta);
@ -87,7 +89,7 @@ class ShadowApp : public Gedeng::Application {
void dynamic_update(double delta) override { void dynamic_update(double delta) override {
// Shadows // Shadows
light.clear_shadows(); light.clear_shadows();
light.render_shadow(monkey_mesh); light.render_shadow(mesh1);
light.render_shadow(quad_mesh); light.render_shadow(quad_mesh);
glViewport(0, 0, 1920, 1080); glViewport(0, 0, 1920, 1080);
@ -124,7 +126,7 @@ class ShadowApp : public Gedeng::Application {
// Props // Props
render_shader.use(); render_shader.use();
monkey_mesh.render(render_shader); mesh1.render(render_shader);
/* // Render the light's depth map to a quad for debugging /* // Render the light's depth map to a quad for debugging
debug_shader.use(); debug_shader.use();
@ -159,10 +161,12 @@ class ShadowApp : public Gedeng::Application {
Gedeng::ParticleSystem particles; Gedeng::ParticleSystem particles;
Gedeng::DirectionalLight light; Gedeng::DirectionalLight light;
Gedeng::ObjMesh monkey_mesh; Gedeng::ObjMesh mesh1;
Gedeng::ObjMesh mesh2;
Gedeng::ObjMesh mesh3;
}; };
Gedeng::Application *Gedeng::create_application() { Gedeng::Application *Gedeng::create_application() {
GG_CLIENT_INFO("Creating Application"); GG_CLIENT_INFO("Creating Application");
return new ShadowApp(20, 1920, 1080, String("Parallax Demo")); return new mesh3(20, 1920, 1080, String("Parallax Demo"));
} }