From 7fd64941f3916f7378e242b34ebac5db5cb8d84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Madar=C3=A1sz?= Date: Fri, 30 Aug 2024 21:00:04 +0200 Subject: [PATCH] improved shadows again --- bind/v4k.lua | 21 +- demos/09-shadows.c | 5 + engine/art/shaderlib/shadowmap.glsl | 69 +++-- .../art/shaders/fs_shadow_blur_cubemap.glsl | 23 ++ engine/art/shaders/fs_shadow_vsm.glsl | 4 +- .../art/shaders/vs_shadow_blur_cubemap.glsl | 31 +++ engine/joint/v4k.h | 254 +++++++++++------- engine/split/v4k_render.c | 232 ++++++++++------ engine/split/v4k_render.h | 22 +- engine/v4k.c | 232 ++++++++++------ engine/v4k.h | 22 +- 11 files changed, 586 insertions(+), 329 deletions(-) create mode 100644 engine/art/shaders/fs_shadow_blur_cubemap.glsl create mode 100644 engine/art/shaders/vs_shadow_blur_cubemap.glsl diff --git a/bind/v4k.lua b/bind/v4k.lua index 218060f..74b1bc8 100644 --- a/bind/v4k.lua +++ b/bind/v4k.lua @@ -1145,7 +1145,7 @@ enum LIGHT_TYPE { }; enum SHADOW_TECHNIQUE { SHADOW_VSM, - SHADOW_PCF, + SHADOW_CSM, }; typedef struct light_t { char type; @@ -1181,28 +1181,31 @@ typedef struct shadowmap_t { mat44 V; mat44 PV; int vsm_texture_width; - int pcf_texture_width; + int csm_texture_width; int step; int light_step; int cascade_index; unsigned shadow_technique; float cascade_splits[6]; - bool blur_pcf; - float blur_scale; + bool blur_csm; + bool blur_vsm; + float csm_blur_scale; + float vsm_blur_scale; bool skip_render; int lights_pushed; + handle fbo; struct { unsigned shadow_technique; - handle fbos[6], texture, depth_texture; - handle fbo_2d[6], texture_2d[6], depth_texture_2d[6]; - handle blur_fbo_2d, blur_texture_2d; + handle texture, depth_texture; + handle texture_2d[6], depth_texture_2d[6]; + handle blur_texture, blur_texture_2d; float cascade_distances[6]; } maps[MAX_LIGHTS]; handle saved_fb; handle saved_pass; int saved_vp[4]; } shadowmap_t; - shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width); + shadowmap_t shadowmap(int vsm_texture_width, int csm_texture_width); void shadowmap_destroy(shadowmap_t *s); void shadowmap_begin(shadowmap_t *s); bool shadowmap_step(shadowmap_t *s); @@ -1410,7 +1413,7 @@ enum RENDER_PASS { RENDER_PASS_TRANSPARENT, RENDER_PASS_OVERRIDES_BEGIN, RENDER_PASS_SHADOW_BEGIN, - RENDER_PASS_SHADOW_PCF, + RENDER_PASS_SHADOW_CSM, RENDER_PASS_SHADOW_VSM, RENDER_PASS_SHADOW_END, RENDER_PASS_LIGHTMAP, diff --git a/demos/09-shadows.c b/demos/09-shadows.c index 5cc4f15..f27a496 100644 --- a/demos/09-shadows.c +++ b/demos/09-shadows.c @@ -203,6 +203,11 @@ int main(int argc, char** argv) { if( ui_list("Model", OBJ_MDLS, countof(OBJ_MDLS), &OBJ_MDL) ) { must_reload = 1; } + ui_separator(); + ui_bool("CSM Blur", &sm.blur_csm); + ui_slider("CSM Blur Scale", &sm.csm_blur_scale); + ui_bool("VSM Blur", &sm.blur_vsm); + ui_slider("VSM Blur Scale", &sm.vsm_blur_scale); ui_panel_end(); } } diff --git a/engine/art/shaderlib/shadowmap.glsl b/engine/art/shaderlib/shadowmap.glsl index 2ee5fe1..e3282b5 100644 --- a/engine/art/shaderlib/shadowmap.glsl +++ b/engine/art/shaderlib/shadowmap.glsl @@ -14,32 +14,47 @@ const float bias_modifier[NUM_SHADOW_CASCADES] = float[NUM_SHADOW_CASCADES](0.95 //// From http://fabiensanglard.net/shadowmappingVSM/index.php float shadow_vsm(float distance, vec3 dir, int light_index) { distance = distance/200; - vec2 moments = texture(shadowMap[light_index], dir).rg; + + // Define offsets for 2x2 PCF + vec3 offsets[4] = vec3[4]( + vec3(-1, -1, 0) * 0.01, + vec3( 1, -1, 0) * 0.01, + vec3(-1, 1, 0) * 0.01, + vec3( 1, 1, 0) * 0.01 + ); + + float shadow = 0.0; + + // Perform 2x2 PCF + for (int i = 0; i < 4; i++) { + vec3 sampleDir = dir + offsets[i] * (rand(vec2(v_position_ws.x + offsets[i].x, v_position_ws.y + offsets[i].y))*1.75f + 1.25f); + vec2 moments = texture(shadowMap[light_index], sampleDir).rg; - // If the shadow map is sampled outside of its bounds, return 1.0 - if (moments.x == 1.0 && moments.y == 1.0) { - return 1.0; + // If the shadow map is sampled outside of its bounds, add 1.0 + if (moments.x == 1.0 && moments.y == 1.0) { + shadow += 1.0; + continue; + } + + // Surface is fully lit if the current fragment is before the light occluder + if (distance <= moments.x) { + shadow += 1.0; + continue; + } + + // Calculate VSM for this sample + float variance = max(moments.y - (moments.x * moments.x), 0.0002); + float d = distance - moments.x; + float p_max = variance / (variance + d*d); + + shadow += p_max; } - // Surface is fully lit. as the current fragment is before the light occluder - if (distance <= moments.x) { - return 1.0; - } - - // The fragment is either in shadow or penumbra. We now use chebyshev's upperBound to check - // How likely this pixel is to be lit (p_max) - float variance = moments.y - (moments.x*moments.x); - //variance = max(variance, 0.000002); - // variance = max(variance, 0.00002); - variance = max(variance, 0.0002); - - float d = distance - moments.x; - float p_max = variance / (variance + d*d); - - return p_max; + // Average the results + return shadow / 4.0; } -float shadow_pcf(float distance, vec3 lightDir, int light_index) { +float shadow_csm(float distance, vec3 lightDir, int light_index) { // Determine which cascade to use int cascade_index = -1; int min_cascades_range = light_index * NUM_SHADOW_CASCADES; @@ -76,7 +91,7 @@ float shadow_pcf(float distance, vec3 lightDir, int light_index) { float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005); bias *= 1 / (u_cascade_distances[cascade_index] * bias_modifier[matrix_index]); - // PCF + // CSM float shadow = 0.0; vec2 texelSize = 1.0 / textureSize(shadowMap2D[cascade_index], 0); @@ -85,8 +100,8 @@ float shadow_pcf(float distance, vec3 lightDir, int light_index) { { for(int y = -3; y <= 3; ++y) { - float pcfDepth = texture(shadowMap2D[cascade_index], projCoords.xy + vec2(x, y) * texelSize * (rand(vec2(projCoords.x + x, projCoords.y + y))*0.75f + 0.25f)).r; - shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; + float csmDepth = texture(shadowMap2D[cascade_index], projCoords.xy + vec2(x, y) * texelSize * (rand(vec2(projCoords.x + x, projCoords.y + y))*0.75f + 0.25f)).r; + shadow += currentDepth - bias > csmDepth ? 1.0 : 0.0; } } shadow /= 36.0; @@ -95,8 +110,8 @@ float shadow_pcf(float distance, vec3 lightDir, int light_index) { { for(int y = -1; y <= 1; ++y) { - float pcfDepth = texture(shadowMap2D[cascade_index], projCoords.xy + vec2(x, y) * texelSize).r; - shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; + float csmDepth = texture(shadowMap2D[cascade_index], projCoords.xy + vec2(x, y) * texelSize).r; + shadow += currentDepth - bias > csmDepth ? 1.0 : 0.0; } } shadow /= 9.0; @@ -111,7 +126,7 @@ vec4 shadowmap(int idx, in vec4 peye, in vec4 neye) { if (light.processed_shadows) { if (light.type == LIGHT_DIRECTIONAL) { - shadowFactor = shadow_pcf(-peye.z, light.dir, idx); + shadowFactor = shadow_csm(-peye.z, light.dir, idx); } else if (light.type == LIGHT_POINT || light.type == LIGHT_SPOT) { vec3 light_pos = (view * vec4(light.pos, 1.0)).xyz; vec3 dir = light_pos - fragment; diff --git a/engine/art/shaders/fs_shadow_blur_cubemap.glsl b/engine/art/shaders/fs_shadow_blur_cubemap.glsl new file mode 100644 index 0000000..b7c4300 --- /dev/null +++ b/engine/art/shaders/fs_shadow_blur_cubemap.glsl @@ -0,0 +1,23 @@ +in vec3 TexCoords; +out vec4 FragColor; + +uniform samplerCube textureSource; +uniform vec2 ScaleU; + +void main() +{ + vec3 sampleDir = normalize(TexCoords); + vec3 right = normalize(cross(sampleDir, vec3(0.0, 1.0, 0.0))); + vec3 up = normalize(cross(right, sampleDir)); + + vec3 color = vec3(0.0); + float total = 0.0; + for (float x = -4.0; x <= 4.0; x += 1.0) { + for (float y = -4.0; y <= 4.0; y += 1.0) { + vec3 offset = right * ScaleU.x * x + up * ScaleU.y * y; + color += texture(textureSource, normalize(sampleDir + offset)).rgb; + total += 1.0; + } + } + FragColor = vec4(color / total, 1.0); +} \ No newline at end of file diff --git a/engine/art/shaders/fs_shadow_vsm.glsl b/engine/art/shaders/fs_shadow_vsm.glsl index 1214226..f32a5b3 100644 --- a/engine/art/shaders/fs_shadow_vsm.glsl +++ b/engine/art/shaders/fs_shadow_vsm.glsl @@ -2,7 +2,7 @@ in vec3 v_position; out vec4 fragcolor; const int SHADOW_VSM = 0; -const int SHADOW_PCF = 1; +const int SHADOW_CSM = 1; uniform int shadow_technique; @@ -18,7 +18,7 @@ void main() { moment2 += 0.25*(dx*dx+dy*dy); fragcolor = vec4( moment1, moment2, 0.0, 1.0); } - else if (shadow_technique == SHADOW_PCF) { + else if (shadow_technique == SHADOW_CSM) { fragcolor = vec4(vec3(gl_FragCoord.z), 1.0); } } diff --git a/engine/art/shaders/vs_shadow_blur_cubemap.glsl b/engine/art/shaders/vs_shadow_blur_cubemap.glsl new file mode 100644 index 0000000..ce541e9 --- /dev/null +++ b/engine/art/shaders/vs_shadow_blur_cubemap.glsl @@ -0,0 +1,31 @@ +out vec3 TexCoords; +uniform int face; + +vec3 cubemapCoord(vec2 uv, int face) { + vec2 UV = 2.0 * uv - 1.0; + if (face == 0) return vec3(1.0, -UV.y, -UV.x); // +X + else if (face == 1) return vec3(-1.0, -UV.y, UV.x); // -X + else if (face == 2) return vec3(UV.x, 1.0, -UV.y); // +Y + else if (face == 3) return vec3(UV.x, -1.0, UV.y); // -Y + else if (face == 4) return vec3(UV.x, -UV.y, 1.0); // +Z + else return vec3(-UV.x, -UV.y, -1.0); // -Z +} + +void main() +{ + // Generate a fullscreen quad + vec2 positions[6] = vec2[6]( + vec2(-1.0, -1.0), + vec2( 1.0, -1.0), + vec2(-1.0, 1.0), + vec2(-1.0, 1.0), + vec2( 1.0, -1.0), + vec2( 1.0, 1.0) + ); + + gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0); + + // Calculate texture coordinates for cubemap + vec2 texCoord = positions[gl_VertexID] * 0.5 + 0.5; + TexCoords = cubemapCoord(texCoord, face); +} \ No newline at end of file diff --git a/engine/joint/v4k.h b/engine/joint/v4k.h index 59cb8cf..4e49380 100644 --- a/engine/joint/v4k.h +++ b/engine/joint/v4k.h @@ -17264,7 +17264,7 @@ enum LIGHT_TYPE { enum SHADOW_TECHNIQUE { SHADOW_VSM, - SHADOW_PCF, + SHADOW_CSM, }; #define NUM_SHADOW_CASCADES 6 @@ -17314,24 +17314,28 @@ typedef struct shadowmap_t { mat44 V; mat44 PV; int vsm_texture_width; - int pcf_texture_width; + int csm_texture_width; int step; int light_step; int cascade_index; unsigned shadow_technique; float cascade_splits[NUM_SHADOW_CASCADES]; - bool blur_pcf; - float blur_scale; + bool blur_csm; + bool blur_vsm; + float csm_blur_scale; + float vsm_blur_scale; // signals bool skip_render; int lights_pushed; + handle fbo; struct { unsigned shadow_technique; - handle fbos[6], texture, depth_texture; - handle fbo_2d[NUM_SHADOW_CASCADES], texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES]; - handle blur_fbo_2d, blur_texture_2d; + handle texture, depth_texture; + handle texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES]; + handle blur_texture, blur_texture_2d; + // handle blur_fbo_cubemap, blur_texture_cubemap; float cascade_distances[NUM_SHADOW_CASCADES]; } maps[MAX_LIGHTS]; @@ -17340,7 +17344,7 @@ typedef struct shadowmap_t { int saved_vp[4]; } shadowmap_t; -API shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width); // = 512, 4096 +API shadowmap_t shadowmap(int vsm_texture_width, int csm_texture_width); // = 512, 4096 API void shadowmap_destroy(shadowmap_t *s); API void shadowmap_begin(shadowmap_t *s); @@ -17681,7 +17685,7 @@ enum RENDER_PASS { RENDER_PASS_OVERRIDES_BEGIN, RENDER_PASS_SHADOW_BEGIN, - RENDER_PASS_SHADOW_PCF, + RENDER_PASS_SHADOW_CSM, RENDER_PASS_SHADOW_VSM, RENDER_PASS_SHADOW_END, @@ -383342,22 +383346,10 @@ shadowmap_init_caster_vsm(shadowmap_t *s, int light_index, int texture_width) { glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - - // Create 6 framebuffers for each face of the cubemap - glGenFramebuffers(6, s->maps[light_index].fbos); - for (int i = 0; i < 6; i++) { - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbos[i]); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, s->maps[light_index].texture, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, s->maps[light_index].depth_texture, 0); - GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (GL_FRAMEBUFFER_COMPLETE != result) { - PANIC("ERROR: Framebuffer is not complete: %x\n", result); - } - } } static inline void -shadowmap_init_caster_pcf(shadowmap_t *s, int light_index, int texture_width) { +shadowmap_init_caster_csm(shadowmap_t *s, int light_index, int texture_width) { float borderColor[] = {1.0, 1.0, 1.0, 1.0}; if (s->maps[light_index].texture_2d[0]) { @@ -383386,56 +383378,30 @@ shadowmap_init_caster_pcf(shadowmap_t *s, int light_index, int texture_width) { } glBindTexture(GL_TEXTURE_2D, 0); - - for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { - glGenFramebuffers(1, &s->maps[light_index].fbo_2d[i]); - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d[i], 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d[i], 0); - GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (GL_FRAMEBUFFER_COMPLETE != result) { - PANIC("ERROR: Framebuffer is not complete: %x\n", result); - } - } - - // Blur texture - glGenTextures(1, &s->maps[light_index].blur_texture_2d); - glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - // Blur FBO - glGenFramebuffers(1, &s->maps[light_index].blur_fbo_2d); - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d, 0); - GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (GL_FRAMEBUFFER_COMPLETE != result) { - PANIC("ERROR: Framebuffer is not complete: %x\n", result); - } } static inline shadowmap_init_caster(shadowmap_t *s, int light_index) { shadowmap_init_caster_vsm(s, light_index, s->vsm_texture_width); - shadowmap_init_caster_pcf(s, light_index, s->pcf_texture_width); + shadowmap_init_caster_csm(s, light_index, s->csm_texture_width); } -shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width) { // = 512, 4096 +shadowmap_t shadowmap(int vsm_texture_width, int csm_texture_width) { // = 512, 4096 shadowmap_t s = {0}; s.vsm_texture_width = vsm_texture_width; - s.pcf_texture_width = pcf_texture_width; + s.csm_texture_width = csm_texture_width; s.saved_fb = 0; - s.blur_pcf = false; - s.blur_scale = 0.5f; + s.blur_csm = false; + s.blur_vsm = false; + s.csm_blur_scale = 0.5f; + s.vsm_blur_scale = 0.75f; s.cascade_splits[0] = 0.1f; s.cascade_splits[1] = 0.3f; s.cascade_splits[2] = 0.7f; s.cascade_splits[3] = 1.0f; s.cascade_splits[4] = 1.0f; s.cascade_splits[5] = 1.0f; /* sticks to camera far plane */ + glGenFramebuffers(1, &s.fbo); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb); @@ -383458,9 +383424,9 @@ shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width) { // = 512, static inline void shadowmap_destroy_light(shadowmap_t *s, int light_index) { - if (s->maps[light_index].fbos[0]) { - glDeleteFramebuffers(6, s->maps[light_index].fbos); - s->maps[light_index].fbos[0] = 0; + if (s->fbo) { + glDeleteFramebuffers(1, &s->fbo); + s->fbo = 0; } if (s->maps[light_index].texture) { glDeleteTextures(1, &s->maps[light_index].texture); @@ -383472,10 +383438,6 @@ void shadowmap_destroy_light(shadowmap_t *s, int light_index) { } for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { - if (s->maps[light_index].fbo_2d[i]) { - glDeleteFramebuffers(1, &s->maps[light_index].fbo_2d[i]); - s->maps[light_index].fbo_2d[i] = 0; - } if (s->maps[light_index].texture_2d[i]) { glDeleteTextures(1, &s->maps[light_index].texture_2d[i]); s->maps[light_index].texture_2d[i] = 0; @@ -383486,10 +383448,6 @@ void shadowmap_destroy_light(shadowmap_t *s, int light_index) { } } - if (s->maps[light_index].blur_fbo_2d) { - glDeleteFramebuffers(1, &s->maps[light_index].blur_fbo_2d); - s->maps[light_index].blur_fbo_2d = 0; - } if (s->maps[light_index].blur_texture_2d) { glDeleteTextures(1, &s->maps[light_index].blur_texture_2d); s->maps[light_index].blur_texture_2d = 0; @@ -383641,19 +383599,31 @@ static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir, flo copy44(l->shadow_matrix[s->cascade_index], PV); l->processed_shadows = true; - s->shadow_technique = l->shadow_technique = SHADOW_PCF; - model_setpass(RENDER_PASS_SHADOW_PCF); + s->shadow_technique = l->shadow_technique = SHADOW_CSM; + model_setpass(RENDER_PASS_SHADOW_CSM); } static inline -void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { - if (!s->blur_pcf) { +void shadowmap_blur_csm(shadowmap_t *s, int light_index) { + if (!s->blur_csm) { return; } - float blur_scale = 1.999 * (1 - s->blur_scale) + 0.001; + float blur_scale = 1.999 * (1 - s->csm_blur_scale) + 0.001; // blur_scale = 0.1f; + if (s->maps[light_index].blur_texture_2d == 0) { + // Blur texture + float borderColor[] = {1.0, 1.0, 1.0, 1.0}; + glGenTextures(1, &s->maps[light_index].blur_texture_2d); + glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, s->csm_texture_width, s->csm_texture_width, 0, GL_RGB, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + static renderstate_t rs; static int program = -1, vao = -1, u_scale = -1, u_source = -1; if (program < 0) { @@ -383671,8 +383641,8 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { glGenVertexArrays(1, &vao); } - renderstate_apply(&rs); - glViewport(0, 0, s->pcf_texture_width, s->pcf_texture_width); + // renderstate_apply(&rs); + glViewport(0, 0, s->csm_texture_width, s->csm_texture_width); unsigned oldprog = last_shader; glUseProgram(program); @@ -383682,8 +383652,9 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { // Horizontal pass for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d); - glUniform2f(u_scale, 1.0f / (s->pcf_texture_width * blur_scale), 0); + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d, 0); + glUniform2f(u_scale, 1.0f / (s->csm_texture_width * blur_scale), 0); glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]); glUniform1i(u_source, 0); //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -383693,8 +383664,9 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { profile_incstat("Render.num_triangles", +2); // Vertical pass - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]); - glUniform2f(u_scale, 0, 1.0f / (s->pcf_texture_width * blur_scale)); + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d[i], 0); + glUniform2f(u_scale, 0, 1.0f / (s->csm_texture_width * blur_scale)); glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d); glUniform1i(u_source, 0); //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -383708,15 +383680,97 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { glUseProgram(oldprog); } +static inline +void shadowmap_blur_vsm(shadowmap_t *s, int light_index) { + if (!s->blur_vsm) { + return; + } + + float blur_scale = 1.999 * (1 - s->vsm_blur_scale) + 0.001; + + if (s->maps[light_index].blur_texture == 0) { + // Blur texture + float borderColor[] = {1.0, 1.0, 1.0, 1.0}; + glGenTextures(1, &s->maps[light_index].blur_texture); + glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].blur_texture); + for (int i = 0; i < 6; i++) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB32F, s->vsm_texture_width, s->vsm_texture_width, 0, GL_RGB, GL_FLOAT, 0); + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor); + } + + static renderstate_t rs; + static int program = -1, vao = -1, u_scale = -1, u_source = -1, u_face = -1; + if (program < 0) { + rs = renderstate(); { + rs.depth_test_enabled = false; + rs.depth_write_enabled = false; + rs.blend_enabled = false; + } + const char* vs = vfs_read("shaders/vs_shadow_blur_cubemap.glsl"); + const char* fs = vfs_read("shaders/fs_shadow_blur_cubemap.glsl"); + + program = shader(vs, fs, "", "fragcolor", NULL); + u_scale = glGetUniformLocation(program, "ScaleU"); + u_source = glGetUniformLocation(program, "textureSource"); + u_face = glGetUniformLocation(program, "face"); + glGenVertexArrays(1, &vao); + } + + renderstate_apply(&rs); + // return; + glViewport(0, 0, s->vsm_texture_width, s->vsm_texture_width); + + unsigned oldprog = last_shader; + glUseProgram(program); + + glBindVertexArray(vao); + glActiveTexture(GL_TEXTURE0); + + for (int face = 0; face < 6; face++) { + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, s->maps[light_index].blur_texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, 0); + glUniform2f(u_scale, 1.0f / (s->vsm_texture_width * blur_scale), 0); + glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].texture); + glUniform1i(u_source, 0); + glUniform1i(u_face, face); + + glDrawArrays(GL_TRIANGLES, 0, 6); + profile_incstat("Render.num_drawcalls", +1); + profile_incstat("Render.num_triangles", +2); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, s->maps[light_index].texture, 0); + glUniform2f(u_scale, 0, 1.0f / (s->vsm_texture_width * blur_scale)); + glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].blur_texture); + glUniform1i(u_source, 0); + glUniform1i(u_face, face); + + glDrawArrays(GL_TRIANGLES, 0, 6); + profile_incstat("Render.num_drawcalls", +1); + profile_incstat("Render.num_triangles", +2); + } + + glBindVertexArray(0); + glUseProgram(oldprog); +} + static inline bool shadowmap_step_finish(shadowmap_t *s) { - if (s->shadow_technique == SHADOW_PCF) { + if (s->shadow_technique == SHADOW_CSM) { if (s->cascade_index < NUM_SHADOW_CASCADES-1) { s->cascade_index++; s->step = 0; return false; } - shadowmap_blur_pcf(s, s->light_step); + shadowmap_blur_csm(s, s->light_step); + } else { + shadowmap_blur_vsm(s, s->light_step); } s->step = 0; @@ -383727,7 +383781,7 @@ bool shadowmap_step_finish(shadowmap_t *s) { } bool shadowmap_step(shadowmap_t *s) { - int max_steps = s->shadow_technique == 0xffff ? 1 : s->shadow_technique == SHADOW_PCF ? 1 : 6; + int max_steps = s->shadow_technique == 0xffff ? 1 : s->shadow_technique == SHADOW_CSM ? 1 : 6; if (s->step >= max_steps) { if (shadowmap_step_finish(s)) { return false; @@ -383764,9 +383818,7 @@ void shadowmap_light(shadowmap_t *s, light_t *l, mat44 cam_proj, mat44 cam_view) cam_far *= 0.5f; } - if (l->type == LIGHT_POINT) { - shadowmap_light_point(s, l, step); - } else if (l->type == LIGHT_SPOT) { + if (l->type == LIGHT_POINT || l->type == LIGHT_SPOT) { shadowmap_light_point(s, l, step); } else if (l->type == LIGHT_DIRECTIONAL) { shadowmap_light_directional(s, l, step, cam_fov, cam_far, cam_view); @@ -383780,8 +383832,8 @@ void shadowmap_light(shadowmap_t *s, light_t *l, mat44 cam_proj, mat44 cam_view) // shadowmap_destroy_light(s, s->light_step); // @todo: we might wanna free the other set if (l->shadow_technique == SHADOW_VSM) { shadowmap_init_caster_vsm(s, s->light_step, s->vsm_texture_width); - } else if (l->shadow_technique == SHADOW_PCF) { - shadowmap_init_caster_pcf(s, s->light_step, s->pcf_texture_width); + } else if (l->shadow_technique == SHADOW_CSM) { + shadowmap_init_caster_csm(s, s->light_step, s->csm_texture_width); } } @@ -383791,14 +383843,18 @@ void shadowmap_light(shadowmap_t *s, light_t *l, mat44 cam_proj, mat44 cam_view) s->lights_pushed++; if (l->type == LIGHT_DIRECTIONAL) { - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d[s->cascade_index]); + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[s->light_step].texture_2d[s->cascade_index], 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[s->light_step].depth_texture_2d[s->cascade_index], 0); shadowmap_clear_fbo(); } else { - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]); + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + step, s->maps[s->light_step].texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + step, s->maps[s->light_step].depth_texture, 0); shadowmap_clear_fbo(); } - unsigned texture_width = s->shadow_technique == SHADOW_VSM ? s->vsm_texture_width : s->pcf_texture_width; + unsigned texture_width = s->shadow_technique == SHADOW_VSM ? s->vsm_texture_width : s->csm_texture_width; glViewport(0, 0, texture_width, texture_width); } } @@ -386620,15 +386676,17 @@ void model_set_renderstates(model_t *m) { } // Shadow pass - renderstate_t *pcf_shadow_rs = &m->rs[RENDER_PASS_SHADOW_PCF]; + renderstate_t *csm_shadow_rs = &m->rs[RENDER_PASS_SHADOW_CSM]; { - pcf_shadow_rs->blend_enabled = 1; - pcf_shadow_rs->blend_src = GL_SRC_ALPHA; - pcf_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; - pcf_shadow_rs->cull_face_enabled = 1; - pcf_shadow_rs->cull_face_mode = GL_BACK; - pcf_shadow_rs->front_face = GL_CW; - pcf_shadow_rs->depth_clamp_enabled = 1; + csm_shadow_rs->blend_enabled = 1; + csm_shadow_rs->blend_src = GL_SRC_ALPHA; + csm_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; + csm_shadow_rs->depth_test_enabled = true; + csm_shadow_rs->depth_write_enabled = true; + csm_shadow_rs->cull_face_enabled = 1; + csm_shadow_rs->cull_face_mode = GL_BACK; + csm_shadow_rs->front_face = GL_CW; + csm_shadow_rs->depth_clamp_enabled = 1; } renderstate_t *vsm_shadow_rs = &m->rs[RENDER_PASS_SHADOW_VSM]; @@ -386636,6 +386694,8 @@ void model_set_renderstates(model_t *m) { vsm_shadow_rs->blend_enabled = 1; vsm_shadow_rs->blend_src = GL_SRC_ALPHA; vsm_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; + vsm_shadow_rs->depth_test_enabled = true; + vsm_shadow_rs->depth_write_enabled = true; vsm_shadow_rs->cull_face_enabled = 1; vsm_shadow_rs->cull_face_mode = GL_BACK; vsm_shadow_rs->front_face = GL_CW; diff --git a/engine/split/v4k_render.c b/engine/split/v4k_render.c index acafb38..537c515 100644 --- a/engine/split/v4k_render.c +++ b/engine/split/v4k_render.c @@ -1612,22 +1612,10 @@ shadowmap_init_caster_vsm(shadowmap_t *s, int light_index, int texture_width) { glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - - // Create 6 framebuffers for each face of the cubemap - glGenFramebuffers(6, s->maps[light_index].fbos); - for (int i = 0; i < 6; i++) { - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbos[i]); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, s->maps[light_index].texture, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, s->maps[light_index].depth_texture, 0); - GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (GL_FRAMEBUFFER_COMPLETE != result) { - PANIC("ERROR: Framebuffer is not complete: %x\n", result); - } - } } static inline void -shadowmap_init_caster_pcf(shadowmap_t *s, int light_index, int texture_width) { +shadowmap_init_caster_csm(shadowmap_t *s, int light_index, int texture_width) { float borderColor[] = {1.0, 1.0, 1.0, 1.0}; if (s->maps[light_index].texture_2d[0]) { @@ -1656,56 +1644,30 @@ shadowmap_init_caster_pcf(shadowmap_t *s, int light_index, int texture_width) { } glBindTexture(GL_TEXTURE_2D, 0); - - for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { - glGenFramebuffers(1, &s->maps[light_index].fbo_2d[i]); - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d[i], 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d[i], 0); - GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (GL_FRAMEBUFFER_COMPLETE != result) { - PANIC("ERROR: Framebuffer is not complete: %x\n", result); - } - } - - // Blur texture - glGenTextures(1, &s->maps[light_index].blur_texture_2d); - glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - // Blur FBO - glGenFramebuffers(1, &s->maps[light_index].blur_fbo_2d); - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d, 0); - GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (GL_FRAMEBUFFER_COMPLETE != result) { - PANIC("ERROR: Framebuffer is not complete: %x\n", result); - } } static inline shadowmap_init_caster(shadowmap_t *s, int light_index) { shadowmap_init_caster_vsm(s, light_index, s->vsm_texture_width); - shadowmap_init_caster_pcf(s, light_index, s->pcf_texture_width); + shadowmap_init_caster_csm(s, light_index, s->csm_texture_width); } -shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width) { // = 512, 4096 +shadowmap_t shadowmap(int vsm_texture_width, int csm_texture_width) { // = 512, 4096 shadowmap_t s = {0}; s.vsm_texture_width = vsm_texture_width; - s.pcf_texture_width = pcf_texture_width; + s.csm_texture_width = csm_texture_width; s.saved_fb = 0; - s.blur_pcf = false; - s.blur_scale = 0.5f; + s.blur_csm = false; + s.blur_vsm = false; + s.csm_blur_scale = 0.5f; + s.vsm_blur_scale = 0.75f; s.cascade_splits[0] = 0.1f; s.cascade_splits[1] = 0.3f; s.cascade_splits[2] = 0.7f; s.cascade_splits[3] = 1.0f; s.cascade_splits[4] = 1.0f; s.cascade_splits[5] = 1.0f; /* sticks to camera far plane */ + glGenFramebuffers(1, &s.fbo); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb); @@ -1728,9 +1690,9 @@ shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width) { // = 512, static inline void shadowmap_destroy_light(shadowmap_t *s, int light_index) { - if (s->maps[light_index].fbos[0]) { - glDeleteFramebuffers(6, s->maps[light_index].fbos); - s->maps[light_index].fbos[0] = 0; + if (s->fbo) { + glDeleteFramebuffers(1, &s->fbo); + s->fbo = 0; } if (s->maps[light_index].texture) { glDeleteTextures(1, &s->maps[light_index].texture); @@ -1742,10 +1704,6 @@ void shadowmap_destroy_light(shadowmap_t *s, int light_index) { } for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { - if (s->maps[light_index].fbo_2d[i]) { - glDeleteFramebuffers(1, &s->maps[light_index].fbo_2d[i]); - s->maps[light_index].fbo_2d[i] = 0; - } if (s->maps[light_index].texture_2d[i]) { glDeleteTextures(1, &s->maps[light_index].texture_2d[i]); s->maps[light_index].texture_2d[i] = 0; @@ -1756,10 +1714,6 @@ void shadowmap_destroy_light(shadowmap_t *s, int light_index) { } } - if (s->maps[light_index].blur_fbo_2d) { - glDeleteFramebuffers(1, &s->maps[light_index].blur_fbo_2d); - s->maps[light_index].blur_fbo_2d = 0; - } if (s->maps[light_index].blur_texture_2d) { glDeleteTextures(1, &s->maps[light_index].blur_texture_2d); s->maps[light_index].blur_texture_2d = 0; @@ -1911,19 +1865,31 @@ static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir, flo copy44(l->shadow_matrix[s->cascade_index], PV); l->processed_shadows = true; - s->shadow_technique = l->shadow_technique = SHADOW_PCF; - model_setpass(RENDER_PASS_SHADOW_PCF); + s->shadow_technique = l->shadow_technique = SHADOW_CSM; + model_setpass(RENDER_PASS_SHADOW_CSM); } static inline -void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { - if (!s->blur_pcf) { +void shadowmap_blur_csm(shadowmap_t *s, int light_index) { + if (!s->blur_csm) { return; } - float blur_scale = 1.999 * (1 - s->blur_scale) + 0.001; + float blur_scale = 1.999 * (1 - s->csm_blur_scale) + 0.001; // blur_scale = 0.1f; + if (s->maps[light_index].blur_texture_2d == 0) { + // Blur texture + float borderColor[] = {1.0, 1.0, 1.0, 1.0}; + glGenTextures(1, &s->maps[light_index].blur_texture_2d); + glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, s->csm_texture_width, s->csm_texture_width, 0, GL_RGB, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + static renderstate_t rs; static int program = -1, vao = -1, u_scale = -1, u_source = -1; if (program < 0) { @@ -1941,8 +1907,8 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { glGenVertexArrays(1, &vao); } - renderstate_apply(&rs); - glViewport(0, 0, s->pcf_texture_width, s->pcf_texture_width); + // renderstate_apply(&rs); + glViewport(0, 0, s->csm_texture_width, s->csm_texture_width); unsigned oldprog = last_shader; glUseProgram(program); @@ -1952,8 +1918,9 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { // Horizontal pass for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d); - glUniform2f(u_scale, 1.0f / (s->pcf_texture_width * blur_scale), 0); + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d, 0); + glUniform2f(u_scale, 1.0f / (s->csm_texture_width * blur_scale), 0); glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]); glUniform1i(u_source, 0); //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -1963,8 +1930,9 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { profile_incstat("Render.num_triangles", +2); // Vertical pass - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]); - glUniform2f(u_scale, 0, 1.0f / (s->pcf_texture_width * blur_scale)); + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d[i], 0); + glUniform2f(u_scale, 0, 1.0f / (s->csm_texture_width * blur_scale)); glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d); glUniform1i(u_source, 0); //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -1978,15 +1946,97 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { glUseProgram(oldprog); } +static inline +void shadowmap_blur_vsm(shadowmap_t *s, int light_index) { + if (!s->blur_vsm) { + return; + } + + float blur_scale = 1.999 * (1 - s->vsm_blur_scale) + 0.001; + + if (s->maps[light_index].blur_texture == 0) { + // Blur texture + float borderColor[] = {1.0, 1.0, 1.0, 1.0}; + glGenTextures(1, &s->maps[light_index].blur_texture); + glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].blur_texture); + for (int i = 0; i < 6; i++) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB32F, s->vsm_texture_width, s->vsm_texture_width, 0, GL_RGB, GL_FLOAT, 0); + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor); + } + + static renderstate_t rs; + static int program = -1, vao = -1, u_scale = -1, u_source = -1, u_face = -1; + if (program < 0) { + rs = renderstate(); { + rs.depth_test_enabled = false; + rs.depth_write_enabled = false; + rs.blend_enabled = false; + } + const char* vs = vfs_read("shaders/vs_shadow_blur_cubemap.glsl"); + const char* fs = vfs_read("shaders/fs_shadow_blur_cubemap.glsl"); + + program = shader(vs, fs, "", "fragcolor", NULL); + u_scale = glGetUniformLocation(program, "ScaleU"); + u_source = glGetUniformLocation(program, "textureSource"); + u_face = glGetUniformLocation(program, "face"); + glGenVertexArrays(1, &vao); + } + + renderstate_apply(&rs); + // return; + glViewport(0, 0, s->vsm_texture_width, s->vsm_texture_width); + + unsigned oldprog = last_shader; + glUseProgram(program); + + glBindVertexArray(vao); + glActiveTexture(GL_TEXTURE0); + + for (int face = 0; face < 6; face++) { + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, s->maps[light_index].blur_texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, 0); + glUniform2f(u_scale, 1.0f / (s->vsm_texture_width * blur_scale), 0); + glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].texture); + glUniform1i(u_source, 0); + glUniform1i(u_face, face); + + glDrawArrays(GL_TRIANGLES, 0, 6); + profile_incstat("Render.num_drawcalls", +1); + profile_incstat("Render.num_triangles", +2); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, s->maps[light_index].texture, 0); + glUniform2f(u_scale, 0, 1.0f / (s->vsm_texture_width * blur_scale)); + glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].blur_texture); + glUniform1i(u_source, 0); + glUniform1i(u_face, face); + + glDrawArrays(GL_TRIANGLES, 0, 6); + profile_incstat("Render.num_drawcalls", +1); + profile_incstat("Render.num_triangles", +2); + } + + glBindVertexArray(0); + glUseProgram(oldprog); +} + static inline bool shadowmap_step_finish(shadowmap_t *s) { - if (s->shadow_technique == SHADOW_PCF) { + if (s->shadow_technique == SHADOW_CSM) { if (s->cascade_index < NUM_SHADOW_CASCADES-1) { s->cascade_index++; s->step = 0; return false; } - shadowmap_blur_pcf(s, s->light_step); + shadowmap_blur_csm(s, s->light_step); + } else { + shadowmap_blur_vsm(s, s->light_step); } s->step = 0; @@ -1997,7 +2047,7 @@ bool shadowmap_step_finish(shadowmap_t *s) { } bool shadowmap_step(shadowmap_t *s) { - int max_steps = s->shadow_technique == 0xffff ? 1 : s->shadow_technique == SHADOW_PCF ? 1 : 6; + int max_steps = s->shadow_technique == 0xffff ? 1 : s->shadow_technique == SHADOW_CSM ? 1 : 6; if (s->step >= max_steps) { if (shadowmap_step_finish(s)) { return false; @@ -2034,9 +2084,7 @@ void shadowmap_light(shadowmap_t *s, light_t *l, mat44 cam_proj, mat44 cam_view) cam_far *= 0.5f; } - if (l->type == LIGHT_POINT) { - shadowmap_light_point(s, l, step); - } else if (l->type == LIGHT_SPOT) { + if (l->type == LIGHT_POINT || l->type == LIGHT_SPOT) { shadowmap_light_point(s, l, step); } else if (l->type == LIGHT_DIRECTIONAL) { shadowmap_light_directional(s, l, step, cam_fov, cam_far, cam_view); @@ -2050,8 +2098,8 @@ void shadowmap_light(shadowmap_t *s, light_t *l, mat44 cam_proj, mat44 cam_view) // shadowmap_destroy_light(s, s->light_step); // @todo: we might wanna free the other set if (l->shadow_technique == SHADOW_VSM) { shadowmap_init_caster_vsm(s, s->light_step, s->vsm_texture_width); - } else if (l->shadow_technique == SHADOW_PCF) { - shadowmap_init_caster_pcf(s, s->light_step, s->pcf_texture_width); + } else if (l->shadow_technique == SHADOW_CSM) { + shadowmap_init_caster_csm(s, s->light_step, s->csm_texture_width); } } @@ -2061,14 +2109,18 @@ void shadowmap_light(shadowmap_t *s, light_t *l, mat44 cam_proj, mat44 cam_view) s->lights_pushed++; if (l->type == LIGHT_DIRECTIONAL) { - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d[s->cascade_index]); + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[s->light_step].texture_2d[s->cascade_index], 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[s->light_step].depth_texture_2d[s->cascade_index], 0); shadowmap_clear_fbo(); } else { - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]); + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + step, s->maps[s->light_step].texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + step, s->maps[s->light_step].depth_texture, 0); shadowmap_clear_fbo(); } - unsigned texture_width = s->shadow_technique == SHADOW_VSM ? s->vsm_texture_width : s->pcf_texture_width; + unsigned texture_width = s->shadow_technique == SHADOW_VSM ? s->vsm_texture_width : s->csm_texture_width; glViewport(0, 0, texture_width, texture_width); } } @@ -4890,15 +4942,17 @@ void model_set_renderstates(model_t *m) { } // Shadow pass - renderstate_t *pcf_shadow_rs = &m->rs[RENDER_PASS_SHADOW_PCF]; + renderstate_t *csm_shadow_rs = &m->rs[RENDER_PASS_SHADOW_CSM]; { - pcf_shadow_rs->blend_enabled = 1; - pcf_shadow_rs->blend_src = GL_SRC_ALPHA; - pcf_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; - pcf_shadow_rs->cull_face_enabled = 1; - pcf_shadow_rs->cull_face_mode = GL_BACK; - pcf_shadow_rs->front_face = GL_CW; - pcf_shadow_rs->depth_clamp_enabled = 1; + csm_shadow_rs->blend_enabled = 1; + csm_shadow_rs->blend_src = GL_SRC_ALPHA; + csm_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; + csm_shadow_rs->depth_test_enabled = true; + csm_shadow_rs->depth_write_enabled = true; + csm_shadow_rs->cull_face_enabled = 1; + csm_shadow_rs->cull_face_mode = GL_BACK; + csm_shadow_rs->front_face = GL_CW; + csm_shadow_rs->depth_clamp_enabled = 1; } renderstate_t *vsm_shadow_rs = &m->rs[RENDER_PASS_SHADOW_VSM]; @@ -4906,6 +4960,8 @@ void model_set_renderstates(model_t *m) { vsm_shadow_rs->blend_enabled = 1; vsm_shadow_rs->blend_src = GL_SRC_ALPHA; vsm_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; + vsm_shadow_rs->depth_test_enabled = true; + vsm_shadow_rs->depth_write_enabled = true; vsm_shadow_rs->cull_face_enabled = 1; vsm_shadow_rs->cull_face_mode = GL_BACK; vsm_shadow_rs->front_face = GL_CW; diff --git a/engine/split/v4k_render.h b/engine/split/v4k_render.h index 8265e3f..f33e6ef 100644 --- a/engine/split/v4k_render.h +++ b/engine/split/v4k_render.h @@ -296,7 +296,7 @@ enum LIGHT_TYPE { enum SHADOW_TECHNIQUE { SHADOW_VSM, - SHADOW_PCF, + SHADOW_CSM, }; #define NUM_SHADOW_CASCADES 6 @@ -346,24 +346,28 @@ typedef struct shadowmap_t { mat44 V; mat44 PV; int vsm_texture_width; - int pcf_texture_width; + int csm_texture_width; int step; int light_step; int cascade_index; unsigned shadow_technique; float cascade_splits[NUM_SHADOW_CASCADES]; - bool blur_pcf; - float blur_scale; + bool blur_csm; + bool blur_vsm; + float csm_blur_scale; + float vsm_blur_scale; // signals bool skip_render; int lights_pushed; + handle fbo; struct { unsigned shadow_technique; - handle fbos[6], texture, depth_texture; - handle fbo_2d[NUM_SHADOW_CASCADES], texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES]; - handle blur_fbo_2d, blur_texture_2d; + handle texture, depth_texture; + handle texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES]; + handle blur_texture, blur_texture_2d; + // handle blur_fbo_cubemap, blur_texture_cubemap; float cascade_distances[NUM_SHADOW_CASCADES]; } maps[MAX_LIGHTS]; @@ -372,7 +376,7 @@ typedef struct shadowmap_t { int saved_vp[4]; } shadowmap_t; -API shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width); // = 512, 4096 +API shadowmap_t shadowmap(int vsm_texture_width, int csm_texture_width); // = 512, 4096 API void shadowmap_destroy(shadowmap_t *s); API void shadowmap_begin(shadowmap_t *s); @@ -713,7 +717,7 @@ enum RENDER_PASS { RENDER_PASS_OVERRIDES_BEGIN, RENDER_PASS_SHADOW_BEGIN, - RENDER_PASS_SHADOW_PCF, + RENDER_PASS_SHADOW_CSM, RENDER_PASS_SHADOW_VSM, RENDER_PASS_SHADOW_END, diff --git a/engine/v4k.c b/engine/v4k.c index 7df4fc4..160a428 100644 --- a/engine/v4k.c +++ b/engine/v4k.c @@ -18411,22 +18411,10 @@ shadowmap_init_caster_vsm(shadowmap_t *s, int light_index, int texture_width) { glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - - // Create 6 framebuffers for each face of the cubemap - glGenFramebuffers(6, s->maps[light_index].fbos); - for (int i = 0; i < 6; i++) { - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbos[i]); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, s->maps[light_index].texture, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, s->maps[light_index].depth_texture, 0); - GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (GL_FRAMEBUFFER_COMPLETE != result) { - PANIC("ERROR: Framebuffer is not complete: %x\n", result); - } - } } static inline void -shadowmap_init_caster_pcf(shadowmap_t *s, int light_index, int texture_width) { +shadowmap_init_caster_csm(shadowmap_t *s, int light_index, int texture_width) { float borderColor[] = {1.0, 1.0, 1.0, 1.0}; if (s->maps[light_index].texture_2d[0]) { @@ -18455,56 +18443,30 @@ shadowmap_init_caster_pcf(shadowmap_t *s, int light_index, int texture_width) { } glBindTexture(GL_TEXTURE_2D, 0); - - for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { - glGenFramebuffers(1, &s->maps[light_index].fbo_2d[i]); - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d[i], 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d[i], 0); - GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (GL_FRAMEBUFFER_COMPLETE != result) { - PANIC("ERROR: Framebuffer is not complete: %x\n", result); - } - } - - // Blur texture - glGenTextures(1, &s->maps[light_index].blur_texture_2d); - glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - // Blur FBO - glGenFramebuffers(1, &s->maps[light_index].blur_fbo_2d); - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d, 0); - GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (GL_FRAMEBUFFER_COMPLETE != result) { - PANIC("ERROR: Framebuffer is not complete: %x\n", result); - } } static inline shadowmap_init_caster(shadowmap_t *s, int light_index) { shadowmap_init_caster_vsm(s, light_index, s->vsm_texture_width); - shadowmap_init_caster_pcf(s, light_index, s->pcf_texture_width); + shadowmap_init_caster_csm(s, light_index, s->csm_texture_width); } -shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width) { // = 512, 4096 +shadowmap_t shadowmap(int vsm_texture_width, int csm_texture_width) { // = 512, 4096 shadowmap_t s = {0}; s.vsm_texture_width = vsm_texture_width; - s.pcf_texture_width = pcf_texture_width; + s.csm_texture_width = csm_texture_width; s.saved_fb = 0; - s.blur_pcf = false; - s.blur_scale = 0.5f; + s.blur_csm = false; + s.blur_vsm = false; + s.csm_blur_scale = 0.5f; + s.vsm_blur_scale = 0.75f; s.cascade_splits[0] = 0.1f; s.cascade_splits[1] = 0.3f; s.cascade_splits[2] = 0.7f; s.cascade_splits[3] = 1.0f; s.cascade_splits[4] = 1.0f; s.cascade_splits[5] = 1.0f; /* sticks to camera far plane */ + glGenFramebuffers(1, &s.fbo); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb); @@ -18527,9 +18489,9 @@ shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width) { // = 512, static inline void shadowmap_destroy_light(shadowmap_t *s, int light_index) { - if (s->maps[light_index].fbos[0]) { - glDeleteFramebuffers(6, s->maps[light_index].fbos); - s->maps[light_index].fbos[0] = 0; + if (s->fbo) { + glDeleteFramebuffers(1, &s->fbo); + s->fbo = 0; } if (s->maps[light_index].texture) { glDeleteTextures(1, &s->maps[light_index].texture); @@ -18541,10 +18503,6 @@ void shadowmap_destroy_light(shadowmap_t *s, int light_index) { } for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { - if (s->maps[light_index].fbo_2d[i]) { - glDeleteFramebuffers(1, &s->maps[light_index].fbo_2d[i]); - s->maps[light_index].fbo_2d[i] = 0; - } if (s->maps[light_index].texture_2d[i]) { glDeleteTextures(1, &s->maps[light_index].texture_2d[i]); s->maps[light_index].texture_2d[i] = 0; @@ -18555,10 +18513,6 @@ void shadowmap_destroy_light(shadowmap_t *s, int light_index) { } } - if (s->maps[light_index].blur_fbo_2d) { - glDeleteFramebuffers(1, &s->maps[light_index].blur_fbo_2d); - s->maps[light_index].blur_fbo_2d = 0; - } if (s->maps[light_index].blur_texture_2d) { glDeleteTextures(1, &s->maps[light_index].blur_texture_2d); s->maps[light_index].blur_texture_2d = 0; @@ -18710,19 +18664,31 @@ static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir, flo copy44(l->shadow_matrix[s->cascade_index], PV); l->processed_shadows = true; - s->shadow_technique = l->shadow_technique = SHADOW_PCF; - model_setpass(RENDER_PASS_SHADOW_PCF); + s->shadow_technique = l->shadow_technique = SHADOW_CSM; + model_setpass(RENDER_PASS_SHADOW_CSM); } static inline -void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { - if (!s->blur_pcf) { +void shadowmap_blur_csm(shadowmap_t *s, int light_index) { + if (!s->blur_csm) { return; } - float blur_scale = 1.999 * (1 - s->blur_scale) + 0.001; + float blur_scale = 1.999 * (1 - s->csm_blur_scale) + 0.001; // blur_scale = 0.1f; + if (s->maps[light_index].blur_texture_2d == 0) { + // Blur texture + float borderColor[] = {1.0, 1.0, 1.0, 1.0}; + glGenTextures(1, &s->maps[light_index].blur_texture_2d); + glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, s->csm_texture_width, s->csm_texture_width, 0, GL_RGB, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + static renderstate_t rs; static int program = -1, vao = -1, u_scale = -1, u_source = -1; if (program < 0) { @@ -18740,8 +18706,8 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { glGenVertexArrays(1, &vao); } - renderstate_apply(&rs); - glViewport(0, 0, s->pcf_texture_width, s->pcf_texture_width); + // renderstate_apply(&rs); + glViewport(0, 0, s->csm_texture_width, s->csm_texture_width); unsigned oldprog = last_shader; glUseProgram(program); @@ -18751,8 +18717,9 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { // Horizontal pass for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d); - glUniform2f(u_scale, 1.0f / (s->pcf_texture_width * blur_scale), 0); + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d, 0); + glUniform2f(u_scale, 1.0f / (s->csm_texture_width * blur_scale), 0); glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]); glUniform1i(u_source, 0); //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -18762,8 +18729,9 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { profile_incstat("Render.num_triangles", +2); // Vertical pass - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]); - glUniform2f(u_scale, 0, 1.0f / (s->pcf_texture_width * blur_scale)); + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d[i], 0); + glUniform2f(u_scale, 0, 1.0f / (s->csm_texture_width * blur_scale)); glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d); glUniform1i(u_source, 0); //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -18777,15 +18745,97 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { glUseProgram(oldprog); } +static inline +void shadowmap_blur_vsm(shadowmap_t *s, int light_index) { + if (!s->blur_vsm) { + return; + } + + float blur_scale = 1.999 * (1 - s->vsm_blur_scale) + 0.001; + + if (s->maps[light_index].blur_texture == 0) { + // Blur texture + float borderColor[] = {1.0, 1.0, 1.0, 1.0}; + glGenTextures(1, &s->maps[light_index].blur_texture); + glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].blur_texture); + for (int i = 0; i < 6; i++) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB32F, s->vsm_texture_width, s->vsm_texture_width, 0, GL_RGB, GL_FLOAT, 0); + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor); + } + + static renderstate_t rs; + static int program = -1, vao = -1, u_scale = -1, u_source = -1, u_face = -1; + if (program < 0) { + rs = renderstate(); { + rs.depth_test_enabled = false; + rs.depth_write_enabled = false; + rs.blend_enabled = false; + } + const char* vs = vfs_read("shaders/vs_shadow_blur_cubemap.glsl"); + const char* fs = vfs_read("shaders/fs_shadow_blur_cubemap.glsl"); + + program = shader(vs, fs, "", "fragcolor", NULL); + u_scale = glGetUniformLocation(program, "ScaleU"); + u_source = glGetUniformLocation(program, "textureSource"); + u_face = glGetUniformLocation(program, "face"); + glGenVertexArrays(1, &vao); + } + + renderstate_apply(&rs); + // return; + glViewport(0, 0, s->vsm_texture_width, s->vsm_texture_width); + + unsigned oldprog = last_shader; + glUseProgram(program); + + glBindVertexArray(vao); + glActiveTexture(GL_TEXTURE0); + + for (int face = 0; face < 6; face++) { + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, s->maps[light_index].blur_texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, 0); + glUniform2f(u_scale, 1.0f / (s->vsm_texture_width * blur_scale), 0); + glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].texture); + glUniform1i(u_source, 0); + glUniform1i(u_face, face); + + glDrawArrays(GL_TRIANGLES, 0, 6); + profile_incstat("Render.num_drawcalls", +1); + profile_incstat("Render.num_triangles", +2); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, s->maps[light_index].texture, 0); + glUniform2f(u_scale, 0, 1.0f / (s->vsm_texture_width * blur_scale)); + glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].blur_texture); + glUniform1i(u_source, 0); + glUniform1i(u_face, face); + + glDrawArrays(GL_TRIANGLES, 0, 6); + profile_incstat("Render.num_drawcalls", +1); + profile_incstat("Render.num_triangles", +2); + } + + glBindVertexArray(0); + glUseProgram(oldprog); +} + static inline bool shadowmap_step_finish(shadowmap_t *s) { - if (s->shadow_technique == SHADOW_PCF) { + if (s->shadow_technique == SHADOW_CSM) { if (s->cascade_index < NUM_SHADOW_CASCADES-1) { s->cascade_index++; s->step = 0; return false; } - shadowmap_blur_pcf(s, s->light_step); + shadowmap_blur_csm(s, s->light_step); + } else { + shadowmap_blur_vsm(s, s->light_step); } s->step = 0; @@ -18796,7 +18846,7 @@ bool shadowmap_step_finish(shadowmap_t *s) { } bool shadowmap_step(shadowmap_t *s) { - int max_steps = s->shadow_technique == 0xffff ? 1 : s->shadow_technique == SHADOW_PCF ? 1 : 6; + int max_steps = s->shadow_technique == 0xffff ? 1 : s->shadow_technique == SHADOW_CSM ? 1 : 6; if (s->step >= max_steps) { if (shadowmap_step_finish(s)) { return false; @@ -18833,9 +18883,7 @@ void shadowmap_light(shadowmap_t *s, light_t *l, mat44 cam_proj, mat44 cam_view) cam_far *= 0.5f; } - if (l->type == LIGHT_POINT) { - shadowmap_light_point(s, l, step); - } else if (l->type == LIGHT_SPOT) { + if (l->type == LIGHT_POINT || l->type == LIGHT_SPOT) { shadowmap_light_point(s, l, step); } else if (l->type == LIGHT_DIRECTIONAL) { shadowmap_light_directional(s, l, step, cam_fov, cam_far, cam_view); @@ -18849,8 +18897,8 @@ void shadowmap_light(shadowmap_t *s, light_t *l, mat44 cam_proj, mat44 cam_view) // shadowmap_destroy_light(s, s->light_step); // @todo: we might wanna free the other set if (l->shadow_technique == SHADOW_VSM) { shadowmap_init_caster_vsm(s, s->light_step, s->vsm_texture_width); - } else if (l->shadow_technique == SHADOW_PCF) { - shadowmap_init_caster_pcf(s, s->light_step, s->pcf_texture_width); + } else if (l->shadow_technique == SHADOW_CSM) { + shadowmap_init_caster_csm(s, s->light_step, s->csm_texture_width); } } @@ -18860,14 +18908,18 @@ void shadowmap_light(shadowmap_t *s, light_t *l, mat44 cam_proj, mat44 cam_view) s->lights_pushed++; if (l->type == LIGHT_DIRECTIONAL) { - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d[s->cascade_index]); + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[s->light_step].texture_2d[s->cascade_index], 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[s->light_step].depth_texture_2d[s->cascade_index], 0); shadowmap_clear_fbo(); } else { - glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]); + glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + step, s->maps[s->light_step].texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + step, s->maps[s->light_step].depth_texture, 0); shadowmap_clear_fbo(); } - unsigned texture_width = s->shadow_technique == SHADOW_VSM ? s->vsm_texture_width : s->pcf_texture_width; + unsigned texture_width = s->shadow_technique == SHADOW_VSM ? s->vsm_texture_width : s->csm_texture_width; glViewport(0, 0, texture_width, texture_width); } } @@ -21689,15 +21741,17 @@ void model_set_renderstates(model_t *m) { } // Shadow pass - renderstate_t *pcf_shadow_rs = &m->rs[RENDER_PASS_SHADOW_PCF]; + renderstate_t *csm_shadow_rs = &m->rs[RENDER_PASS_SHADOW_CSM]; { - pcf_shadow_rs->blend_enabled = 1; - pcf_shadow_rs->blend_src = GL_SRC_ALPHA; - pcf_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; - pcf_shadow_rs->cull_face_enabled = 1; - pcf_shadow_rs->cull_face_mode = GL_BACK; - pcf_shadow_rs->front_face = GL_CW; - pcf_shadow_rs->depth_clamp_enabled = 1; + csm_shadow_rs->blend_enabled = 1; + csm_shadow_rs->blend_src = GL_SRC_ALPHA; + csm_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; + csm_shadow_rs->depth_test_enabled = true; + csm_shadow_rs->depth_write_enabled = true; + csm_shadow_rs->cull_face_enabled = 1; + csm_shadow_rs->cull_face_mode = GL_BACK; + csm_shadow_rs->front_face = GL_CW; + csm_shadow_rs->depth_clamp_enabled = 1; } renderstate_t *vsm_shadow_rs = &m->rs[RENDER_PASS_SHADOW_VSM]; @@ -21705,6 +21759,8 @@ void model_set_renderstates(model_t *m) { vsm_shadow_rs->blend_enabled = 1; vsm_shadow_rs->blend_src = GL_SRC_ALPHA; vsm_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; + vsm_shadow_rs->depth_test_enabled = true; + vsm_shadow_rs->depth_write_enabled = true; vsm_shadow_rs->cull_face_enabled = 1; vsm_shadow_rs->cull_face_mode = GL_BACK; vsm_shadow_rs->front_face = GL_CW; diff --git a/engine/v4k.h b/engine/v4k.h index 027625e..bac5a1e 100644 --- a/engine/v4k.h +++ b/engine/v4k.h @@ -3331,7 +3331,7 @@ enum LIGHT_TYPE { enum SHADOW_TECHNIQUE { SHADOW_VSM, - SHADOW_PCF, + SHADOW_CSM, }; #define NUM_SHADOW_CASCADES 6 @@ -3381,24 +3381,28 @@ typedef struct shadowmap_t { mat44 V; mat44 PV; int vsm_texture_width; - int pcf_texture_width; + int csm_texture_width; int step; int light_step; int cascade_index; unsigned shadow_technique; float cascade_splits[NUM_SHADOW_CASCADES]; - bool blur_pcf; - float blur_scale; + bool blur_csm; + bool blur_vsm; + float csm_blur_scale; + float vsm_blur_scale; // signals bool skip_render; int lights_pushed; + handle fbo; struct { unsigned shadow_technique; - handle fbos[6], texture, depth_texture; - handle fbo_2d[NUM_SHADOW_CASCADES], texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES]; - handle blur_fbo_2d, blur_texture_2d; + handle texture, depth_texture; + handle texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES]; + handle blur_texture, blur_texture_2d; + // handle blur_fbo_cubemap, blur_texture_cubemap; float cascade_distances[NUM_SHADOW_CASCADES]; } maps[MAX_LIGHTS]; @@ -3407,7 +3411,7 @@ typedef struct shadowmap_t { int saved_vp[4]; } shadowmap_t; -API shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width); // = 512, 4096 +API shadowmap_t shadowmap(int vsm_texture_width, int csm_texture_width); // = 512, 4096 API void shadowmap_destroy(shadowmap_t *s); API void shadowmap_begin(shadowmap_t *s); @@ -3748,7 +3752,7 @@ enum RENDER_PASS { RENDER_PASS_OVERRIDES_BEGIN, RENDER_PASS_SHADOW_BEGIN, - RENDER_PASS_SHADOW_PCF, + RENDER_PASS_SHADOW_CSM, RENDER_PASS_SHADOW_VSM, RENDER_PASS_SHADOW_END,