improved shadows again

main
Dominik Madarász 2024-08-30 21:00:04 +02:00
parent aa64db6e7f
commit 7fd64941f3
11 changed files with 586 additions and 329 deletions

View File

@ -1145,7 +1145,7 @@ enum LIGHT_TYPE {
}; };
enum SHADOW_TECHNIQUE { enum SHADOW_TECHNIQUE {
SHADOW_VSM, SHADOW_VSM,
SHADOW_PCF, SHADOW_CSM,
}; };
typedef struct light_t { typedef struct light_t {
char type; char type;
@ -1181,28 +1181,31 @@ typedef struct shadowmap_t {
mat44 V; mat44 V;
mat44 PV; mat44 PV;
int vsm_texture_width; int vsm_texture_width;
int pcf_texture_width; int csm_texture_width;
int step; int step;
int light_step; int light_step;
int cascade_index; int cascade_index;
unsigned shadow_technique; unsigned shadow_technique;
float cascade_splits[6]; float cascade_splits[6];
bool blur_pcf; bool blur_csm;
float blur_scale; bool blur_vsm;
float csm_blur_scale;
float vsm_blur_scale;
bool skip_render; bool skip_render;
int lights_pushed; int lights_pushed;
handle fbo;
struct { struct {
unsigned shadow_technique; unsigned shadow_technique;
handle fbos[6], texture, depth_texture; handle texture, depth_texture;
handle fbo_2d[6], texture_2d[6], depth_texture_2d[6]; handle texture_2d[6], depth_texture_2d[6];
handle blur_fbo_2d, blur_texture_2d; handle blur_texture, blur_texture_2d;
float cascade_distances[6]; float cascade_distances[6];
} maps[MAX_LIGHTS]; } maps[MAX_LIGHTS];
handle saved_fb; handle saved_fb;
handle saved_pass; handle saved_pass;
int saved_vp[4]; int saved_vp[4];
} shadowmap_t; } 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_destroy(shadowmap_t *s);
void shadowmap_begin(shadowmap_t *s); void shadowmap_begin(shadowmap_t *s);
bool shadowmap_step(shadowmap_t *s); bool shadowmap_step(shadowmap_t *s);
@ -1410,7 +1413,7 @@ enum RENDER_PASS {
RENDER_PASS_TRANSPARENT, RENDER_PASS_TRANSPARENT,
RENDER_PASS_OVERRIDES_BEGIN, RENDER_PASS_OVERRIDES_BEGIN,
RENDER_PASS_SHADOW_BEGIN, RENDER_PASS_SHADOW_BEGIN,
RENDER_PASS_SHADOW_PCF, RENDER_PASS_SHADOW_CSM,
RENDER_PASS_SHADOW_VSM, RENDER_PASS_SHADOW_VSM,
RENDER_PASS_SHADOW_END, RENDER_PASS_SHADOW_END,
RENDER_PASS_LIGHTMAP, RENDER_PASS_LIGHTMAP,

View File

@ -203,6 +203,11 @@ int main(int argc, char** argv) {
if( ui_list("Model", OBJ_MDLS, countof(OBJ_MDLS), &OBJ_MDL) ) { if( ui_list("Model", OBJ_MDLS, countof(OBJ_MDLS), &OBJ_MDL) ) {
must_reload = 1; 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(); ui_panel_end();
} }
} }

View File

@ -14,32 +14,47 @@ const float bias_modifier[NUM_SHADOW_CASCADES] = float[NUM_SHADOW_CASCADES](0.95
//// From http://fabiensanglard.net/shadowmappingVSM/index.php //// From http://fabiensanglard.net/shadowmappingVSM/index.php
float shadow_vsm(float distance, vec3 dir, int light_index) { float shadow_vsm(float distance, vec3 dir, int light_index) {
distance = distance/200; 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 the shadow map is sampled outside of its bounds, add 1.0
if (moments.x == 1.0 && moments.y == 1.0) { if (moments.x == 1.0 && moments.y == 1.0) {
return 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 // Average the results
if (distance <= moments.x) { return shadow / 4.0;
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;
} }
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 // Determine which cascade to use
int cascade_index = -1; int cascade_index = -1;
int min_cascades_range = light_index * NUM_SHADOW_CASCADES; 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); float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
bias *= 1 / (u_cascade_distances[cascade_index] * bias_modifier[matrix_index]); bias *= 1 / (u_cascade_distances[cascade_index] * bias_modifier[matrix_index]);
// PCF // CSM
float shadow = 0.0; float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(shadowMap2D[cascade_index], 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) 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; 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 > pcfDepth ? 1.0 : 0.0; shadow += currentDepth - bias > csmDepth ? 1.0 : 0.0;
} }
} }
shadow /= 36.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) for(int y = -1; y <= 1; ++y)
{ {
float pcfDepth = texture(shadowMap2D[cascade_index], projCoords.xy + vec2(x, y) * texelSize).r; float csmDepth = texture(shadowMap2D[cascade_index], projCoords.xy + vec2(x, y) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; shadow += currentDepth - bias > csmDepth ? 1.0 : 0.0;
} }
} }
shadow /= 9.0; shadow /= 9.0;
@ -111,7 +126,7 @@ vec4 shadowmap(int idx, in vec4 peye, in vec4 neye) {
if (light.processed_shadows) { if (light.processed_shadows) {
if (light.type == LIGHT_DIRECTIONAL) { 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) { } else if (light.type == LIGHT_POINT || light.type == LIGHT_SPOT) {
vec3 light_pos = (view * vec4(light.pos, 1.0)).xyz; vec3 light_pos = (view * vec4(light.pos, 1.0)).xyz;
vec3 dir = light_pos - fragment; vec3 dir = light_pos - fragment;

View File

@ -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);
}

View File

@ -2,7 +2,7 @@ in vec3 v_position;
out vec4 fragcolor; out vec4 fragcolor;
const int SHADOW_VSM = 0; const int SHADOW_VSM = 0;
const int SHADOW_PCF = 1; const int SHADOW_CSM = 1;
uniform int shadow_technique; uniform int shadow_technique;
@ -18,7 +18,7 @@ void main() {
moment2 += 0.25*(dx*dx+dy*dy); moment2 += 0.25*(dx*dx+dy*dy);
fragcolor = vec4( moment1, moment2, 0.0, 1.0); 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); fragcolor = vec4(vec3(gl_FragCoord.z), 1.0);
} }
} }

View File

@ -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);
}

View File

@ -17264,7 +17264,7 @@ enum LIGHT_TYPE {
enum SHADOW_TECHNIQUE { enum SHADOW_TECHNIQUE {
SHADOW_VSM, SHADOW_VSM,
SHADOW_PCF, SHADOW_CSM,
}; };
#define NUM_SHADOW_CASCADES 6 #define NUM_SHADOW_CASCADES 6
@ -17314,24 +17314,28 @@ typedef struct shadowmap_t {
mat44 V; mat44 V;
mat44 PV; mat44 PV;
int vsm_texture_width; int vsm_texture_width;
int pcf_texture_width; int csm_texture_width;
int step; int step;
int light_step; int light_step;
int cascade_index; int cascade_index;
unsigned shadow_technique; unsigned shadow_technique;
float cascade_splits[NUM_SHADOW_CASCADES]; float cascade_splits[NUM_SHADOW_CASCADES];
bool blur_pcf; bool blur_csm;
float blur_scale; bool blur_vsm;
float csm_blur_scale;
float vsm_blur_scale;
// signals // signals
bool skip_render; bool skip_render;
int lights_pushed; int lights_pushed;
handle fbo;
struct { struct {
unsigned shadow_technique; unsigned shadow_technique;
handle fbos[6], texture, depth_texture; handle texture, depth_texture;
handle fbo_2d[NUM_SHADOW_CASCADES], texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES]; handle texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES];
handle blur_fbo_2d, blur_texture_2d; handle blur_texture, blur_texture_2d;
// handle blur_fbo_cubemap, blur_texture_cubemap;
float cascade_distances[NUM_SHADOW_CASCADES]; float cascade_distances[NUM_SHADOW_CASCADES];
} maps[MAX_LIGHTS]; } maps[MAX_LIGHTS];
@ -17340,7 +17344,7 @@ typedef struct shadowmap_t {
int saved_vp[4]; int saved_vp[4];
} shadowmap_t; } 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_destroy(shadowmap_t *s);
API void shadowmap_begin(shadowmap_t *s); API void shadowmap_begin(shadowmap_t *s);
@ -17681,7 +17685,7 @@ enum RENDER_PASS {
RENDER_PASS_OVERRIDES_BEGIN, RENDER_PASS_OVERRIDES_BEGIN,
RENDER_PASS_SHADOW_BEGIN, RENDER_PASS_SHADOW_BEGIN,
RENDER_PASS_SHADOW_PCF, RENDER_PASS_SHADOW_CSM,
RENDER_PASS_SHADOW_VSM, RENDER_PASS_SHADOW_VSM,
RENDER_PASS_SHADOW_END, 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); glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0); 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 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}; float borderColor[] = {1.0, 1.0, 1.0, 1.0};
if (s->maps[light_index].texture_2d[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); 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 static inline
shadowmap_init_caster(shadowmap_t *s, int light_index) { shadowmap_init_caster(shadowmap_t *s, int light_index) {
shadowmap_init_caster_vsm(s, light_index, s->vsm_texture_width); 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}; shadowmap_t s = {0};
s.vsm_texture_width = vsm_texture_width; 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.saved_fb = 0;
s.blur_pcf = false; s.blur_csm = false;
s.blur_scale = 0.5f; 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[0] = 0.1f;
s.cascade_splits[1] = 0.3f; s.cascade_splits[1] = 0.3f;
s.cascade_splits[2] = 0.7f; s.cascade_splits[2] = 0.7f;
s.cascade_splits[3] = 1.0f; s.cascade_splits[3] = 1.0f;
s.cascade_splits[4] = 1.0f; s.cascade_splits[4] = 1.0f;
s.cascade_splits[5] = 1.0f; /* sticks to camera far plane */ s.cascade_splits[5] = 1.0f; /* sticks to camera far plane */
glGenFramebuffers(1, &s.fbo);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb); 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 static inline
void shadowmap_destroy_light(shadowmap_t *s, int light_index) { void shadowmap_destroy_light(shadowmap_t *s, int light_index) {
if (s->maps[light_index].fbos[0]) { if (s->fbo) {
glDeleteFramebuffers(6, s->maps[light_index].fbos); glDeleteFramebuffers(1, &s->fbo);
s->maps[light_index].fbos[0] = 0; s->fbo = 0;
} }
if (s->maps[light_index].texture) { if (s->maps[light_index].texture) {
glDeleteTextures(1, &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++) { 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]) { if (s->maps[light_index].texture_2d[i]) {
glDeleteTextures(1, &s->maps[light_index].texture_2d[i]); glDeleteTextures(1, &s->maps[light_index].texture_2d[i]);
s->maps[light_index].texture_2d[i] = 0; 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) { if (s->maps[light_index].blur_texture_2d) {
glDeleteTextures(1, &s->maps[light_index].blur_texture_2d); glDeleteTextures(1, &s->maps[light_index].blur_texture_2d);
s->maps[light_index].blur_texture_2d = 0; 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); copy44(l->shadow_matrix[s->cascade_index], PV);
l->processed_shadows = true; l->processed_shadows = true;
s->shadow_technique = l->shadow_technique = SHADOW_PCF; s->shadow_technique = l->shadow_technique = SHADOW_CSM;
model_setpass(RENDER_PASS_SHADOW_PCF); model_setpass(RENDER_PASS_SHADOW_CSM);
} }
static inline static inline
void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { void shadowmap_blur_csm(shadowmap_t *s, int light_index) {
if (!s->blur_pcf) { if (!s->blur_csm) {
return; 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; // 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 renderstate_t rs;
static int program = -1, vao = -1, u_scale = -1, u_source = -1; static int program = -1, vao = -1, u_scale = -1, u_source = -1;
if (program < 0) { if (program < 0) {
@ -383671,8 +383641,8 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
glGenVertexArrays(1, &vao); glGenVertexArrays(1, &vao);
} }
renderstate_apply(&rs); // renderstate_apply(&rs);
glViewport(0, 0, s->pcf_texture_width, s->pcf_texture_width); glViewport(0, 0, s->csm_texture_width, s->csm_texture_width);
unsigned oldprog = last_shader; unsigned oldprog = last_shader;
glUseProgram(program); glUseProgram(program);
@ -383682,8 +383652,9 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
// Horizontal pass // Horizontal pass
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d); glBindFramebuffer(GL_FRAMEBUFFER, s->fbo);
glUniform2f(u_scale, 1.0f / (s->pcf_texture_width * blur_scale), 0); 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]); glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
glUniform1i(u_source, 0); glUniform1i(u_source, 0);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //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); profile_incstat("Render.num_triangles", +2);
// Vertical pass // Vertical pass
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]); glBindFramebuffer(GL_FRAMEBUFFER, s->fbo);
glUniform2f(u_scale, 0, 1.0f / (s->pcf_texture_width * blur_scale)); 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); glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d);
glUniform1i(u_source, 0); glUniform1i(u_source, 0);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //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); 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 static inline
bool shadowmap_step_finish(shadowmap_t *s) { 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) { if (s->cascade_index < NUM_SHADOW_CASCADES-1) {
s->cascade_index++; s->cascade_index++;
s->step = 0; s->step = 0;
return false; 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; s->step = 0;
@ -383727,7 +383781,7 @@ bool shadowmap_step_finish(shadowmap_t *s) {
} }
bool shadowmap_step(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 (s->step >= max_steps) {
if (shadowmap_step_finish(s)) { if (shadowmap_step_finish(s)) {
return false; 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; cam_far *= 0.5f;
} }
if (l->type == LIGHT_POINT) { if (l->type == LIGHT_POINT || l->type == LIGHT_SPOT) {
shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_SPOT) {
shadowmap_light_point(s, l, step); shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_DIRECTIONAL) { } else if (l->type == LIGHT_DIRECTIONAL) {
shadowmap_light_directional(s, l, step, cam_fov, cam_far, cam_view); 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 // shadowmap_destroy_light(s, s->light_step); // @todo: we might wanna free the other set
if (l->shadow_technique == SHADOW_VSM) { if (l->shadow_technique == SHADOW_VSM) {
shadowmap_init_caster_vsm(s, s->light_step, s->vsm_texture_width); shadowmap_init_caster_vsm(s, s->light_step, s->vsm_texture_width);
} else if (l->shadow_technique == SHADOW_PCF) { } else if (l->shadow_technique == SHADOW_CSM) {
shadowmap_init_caster_pcf(s, s->light_step, s->pcf_texture_width); 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++; s->lights_pushed++;
if (l->type == LIGHT_DIRECTIONAL) { 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(); shadowmap_clear_fbo();
} else { } 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(); 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); glViewport(0, 0, texture_width, texture_width);
} }
} }
@ -386620,15 +386676,17 @@ void model_set_renderstates(model_t *m) {
} }
// Shadow pass // 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; csm_shadow_rs->blend_enabled = 1;
pcf_shadow_rs->blend_src = GL_SRC_ALPHA; csm_shadow_rs->blend_src = GL_SRC_ALPHA;
pcf_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; csm_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA;
pcf_shadow_rs->cull_face_enabled = 1; csm_shadow_rs->depth_test_enabled = true;
pcf_shadow_rs->cull_face_mode = GL_BACK; csm_shadow_rs->depth_write_enabled = true;
pcf_shadow_rs->front_face = GL_CW; csm_shadow_rs->cull_face_enabled = 1;
pcf_shadow_rs->depth_clamp_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]; 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_enabled = 1;
vsm_shadow_rs->blend_src = GL_SRC_ALPHA; vsm_shadow_rs->blend_src = GL_SRC_ALPHA;
vsm_shadow_rs->blend_dst = GL_ONE_MINUS_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_enabled = 1;
vsm_shadow_rs->cull_face_mode = GL_BACK; vsm_shadow_rs->cull_face_mode = GL_BACK;
vsm_shadow_rs->front_face = GL_CW; vsm_shadow_rs->front_face = GL_CW;

View File

@ -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); glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0); 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 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}; float borderColor[] = {1.0, 1.0, 1.0, 1.0};
if (s->maps[light_index].texture_2d[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); 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 static inline
shadowmap_init_caster(shadowmap_t *s, int light_index) { shadowmap_init_caster(shadowmap_t *s, int light_index) {
shadowmap_init_caster_vsm(s, light_index, s->vsm_texture_width); 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}; shadowmap_t s = {0};
s.vsm_texture_width = vsm_texture_width; 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.saved_fb = 0;
s.blur_pcf = false; s.blur_csm = false;
s.blur_scale = 0.5f; 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[0] = 0.1f;
s.cascade_splits[1] = 0.3f; s.cascade_splits[1] = 0.3f;
s.cascade_splits[2] = 0.7f; s.cascade_splits[2] = 0.7f;
s.cascade_splits[3] = 1.0f; s.cascade_splits[3] = 1.0f;
s.cascade_splits[4] = 1.0f; s.cascade_splits[4] = 1.0f;
s.cascade_splits[5] = 1.0f; /* sticks to camera far plane */ s.cascade_splits[5] = 1.0f; /* sticks to camera far plane */
glGenFramebuffers(1, &s.fbo);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb); 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 static inline
void shadowmap_destroy_light(shadowmap_t *s, int light_index) { void shadowmap_destroy_light(shadowmap_t *s, int light_index) {
if (s->maps[light_index].fbos[0]) { if (s->fbo) {
glDeleteFramebuffers(6, s->maps[light_index].fbos); glDeleteFramebuffers(1, &s->fbo);
s->maps[light_index].fbos[0] = 0; s->fbo = 0;
} }
if (s->maps[light_index].texture) { if (s->maps[light_index].texture) {
glDeleteTextures(1, &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++) { 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]) { if (s->maps[light_index].texture_2d[i]) {
glDeleteTextures(1, &s->maps[light_index].texture_2d[i]); glDeleteTextures(1, &s->maps[light_index].texture_2d[i]);
s->maps[light_index].texture_2d[i] = 0; 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) { if (s->maps[light_index].blur_texture_2d) {
glDeleteTextures(1, &s->maps[light_index].blur_texture_2d); glDeleteTextures(1, &s->maps[light_index].blur_texture_2d);
s->maps[light_index].blur_texture_2d = 0; 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); copy44(l->shadow_matrix[s->cascade_index], PV);
l->processed_shadows = true; l->processed_shadows = true;
s->shadow_technique = l->shadow_technique = SHADOW_PCF; s->shadow_technique = l->shadow_technique = SHADOW_CSM;
model_setpass(RENDER_PASS_SHADOW_PCF); model_setpass(RENDER_PASS_SHADOW_CSM);
} }
static inline static inline
void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { void shadowmap_blur_csm(shadowmap_t *s, int light_index) {
if (!s->blur_pcf) { if (!s->blur_csm) {
return; 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; // 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 renderstate_t rs;
static int program = -1, vao = -1, u_scale = -1, u_source = -1; static int program = -1, vao = -1, u_scale = -1, u_source = -1;
if (program < 0) { if (program < 0) {
@ -1941,8 +1907,8 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
glGenVertexArrays(1, &vao); glGenVertexArrays(1, &vao);
} }
renderstate_apply(&rs); // renderstate_apply(&rs);
glViewport(0, 0, s->pcf_texture_width, s->pcf_texture_width); glViewport(0, 0, s->csm_texture_width, s->csm_texture_width);
unsigned oldprog = last_shader; unsigned oldprog = last_shader;
glUseProgram(program); glUseProgram(program);
@ -1952,8 +1918,9 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
// Horizontal pass // Horizontal pass
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d); glBindFramebuffer(GL_FRAMEBUFFER, s->fbo);
glUniform2f(u_scale, 1.0f / (s->pcf_texture_width * blur_scale), 0); 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]); glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
glUniform1i(u_source, 0); glUniform1i(u_source, 0);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //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); profile_incstat("Render.num_triangles", +2);
// Vertical pass // Vertical pass
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]); glBindFramebuffer(GL_FRAMEBUFFER, s->fbo);
glUniform2f(u_scale, 0, 1.0f / (s->pcf_texture_width * blur_scale)); 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); glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d);
glUniform1i(u_source, 0); glUniform1i(u_source, 0);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //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); 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 static inline
bool shadowmap_step_finish(shadowmap_t *s) { 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) { if (s->cascade_index < NUM_SHADOW_CASCADES-1) {
s->cascade_index++; s->cascade_index++;
s->step = 0; s->step = 0;
return false; 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; s->step = 0;
@ -1997,7 +2047,7 @@ bool shadowmap_step_finish(shadowmap_t *s) {
} }
bool shadowmap_step(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 (s->step >= max_steps) {
if (shadowmap_step_finish(s)) { if (shadowmap_step_finish(s)) {
return false; 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; cam_far *= 0.5f;
} }
if (l->type == LIGHT_POINT) { if (l->type == LIGHT_POINT || l->type == LIGHT_SPOT) {
shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_SPOT) {
shadowmap_light_point(s, l, step); shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_DIRECTIONAL) { } else if (l->type == LIGHT_DIRECTIONAL) {
shadowmap_light_directional(s, l, step, cam_fov, cam_far, cam_view); 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 // shadowmap_destroy_light(s, s->light_step); // @todo: we might wanna free the other set
if (l->shadow_technique == SHADOW_VSM) { if (l->shadow_technique == SHADOW_VSM) {
shadowmap_init_caster_vsm(s, s->light_step, s->vsm_texture_width); shadowmap_init_caster_vsm(s, s->light_step, s->vsm_texture_width);
} else if (l->shadow_technique == SHADOW_PCF) { } else if (l->shadow_technique == SHADOW_CSM) {
shadowmap_init_caster_pcf(s, s->light_step, s->pcf_texture_width); 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++; s->lights_pushed++;
if (l->type == LIGHT_DIRECTIONAL) { 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(); shadowmap_clear_fbo();
} else { } 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(); 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); glViewport(0, 0, texture_width, texture_width);
} }
} }
@ -4890,15 +4942,17 @@ void model_set_renderstates(model_t *m) {
} }
// Shadow pass // 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; csm_shadow_rs->blend_enabled = 1;
pcf_shadow_rs->blend_src = GL_SRC_ALPHA; csm_shadow_rs->blend_src = GL_SRC_ALPHA;
pcf_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; csm_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA;
pcf_shadow_rs->cull_face_enabled = 1; csm_shadow_rs->depth_test_enabled = true;
pcf_shadow_rs->cull_face_mode = GL_BACK; csm_shadow_rs->depth_write_enabled = true;
pcf_shadow_rs->front_face = GL_CW; csm_shadow_rs->cull_face_enabled = 1;
pcf_shadow_rs->depth_clamp_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]; 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_enabled = 1;
vsm_shadow_rs->blend_src = GL_SRC_ALPHA; vsm_shadow_rs->blend_src = GL_SRC_ALPHA;
vsm_shadow_rs->blend_dst = GL_ONE_MINUS_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_enabled = 1;
vsm_shadow_rs->cull_face_mode = GL_BACK; vsm_shadow_rs->cull_face_mode = GL_BACK;
vsm_shadow_rs->front_face = GL_CW; vsm_shadow_rs->front_face = GL_CW;

View File

@ -296,7 +296,7 @@ enum LIGHT_TYPE {
enum SHADOW_TECHNIQUE { enum SHADOW_TECHNIQUE {
SHADOW_VSM, SHADOW_VSM,
SHADOW_PCF, SHADOW_CSM,
}; };
#define NUM_SHADOW_CASCADES 6 #define NUM_SHADOW_CASCADES 6
@ -346,24 +346,28 @@ typedef struct shadowmap_t {
mat44 V; mat44 V;
mat44 PV; mat44 PV;
int vsm_texture_width; int vsm_texture_width;
int pcf_texture_width; int csm_texture_width;
int step; int step;
int light_step; int light_step;
int cascade_index; int cascade_index;
unsigned shadow_technique; unsigned shadow_technique;
float cascade_splits[NUM_SHADOW_CASCADES]; float cascade_splits[NUM_SHADOW_CASCADES];
bool blur_pcf; bool blur_csm;
float blur_scale; bool blur_vsm;
float csm_blur_scale;
float vsm_blur_scale;
// signals // signals
bool skip_render; bool skip_render;
int lights_pushed; int lights_pushed;
handle fbo;
struct { struct {
unsigned shadow_technique; unsigned shadow_technique;
handle fbos[6], texture, depth_texture; handle texture, depth_texture;
handle fbo_2d[NUM_SHADOW_CASCADES], texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES]; handle texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES];
handle blur_fbo_2d, blur_texture_2d; handle blur_texture, blur_texture_2d;
// handle blur_fbo_cubemap, blur_texture_cubemap;
float cascade_distances[NUM_SHADOW_CASCADES]; float cascade_distances[NUM_SHADOW_CASCADES];
} maps[MAX_LIGHTS]; } maps[MAX_LIGHTS];
@ -372,7 +376,7 @@ typedef struct shadowmap_t {
int saved_vp[4]; int saved_vp[4];
} shadowmap_t; } 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_destroy(shadowmap_t *s);
API void shadowmap_begin(shadowmap_t *s); API void shadowmap_begin(shadowmap_t *s);
@ -713,7 +717,7 @@ enum RENDER_PASS {
RENDER_PASS_OVERRIDES_BEGIN, RENDER_PASS_OVERRIDES_BEGIN,
RENDER_PASS_SHADOW_BEGIN, RENDER_PASS_SHADOW_BEGIN,
RENDER_PASS_SHADOW_PCF, RENDER_PASS_SHADOW_CSM,
RENDER_PASS_SHADOW_VSM, RENDER_PASS_SHADOW_VSM,
RENDER_PASS_SHADOW_END, RENDER_PASS_SHADOW_END,

View File

@ -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); glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0); 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 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}; float borderColor[] = {1.0, 1.0, 1.0, 1.0};
if (s->maps[light_index].texture_2d[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); 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 static inline
shadowmap_init_caster(shadowmap_t *s, int light_index) { shadowmap_init_caster(shadowmap_t *s, int light_index) {
shadowmap_init_caster_vsm(s, light_index, s->vsm_texture_width); 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}; shadowmap_t s = {0};
s.vsm_texture_width = vsm_texture_width; 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.saved_fb = 0;
s.blur_pcf = false; s.blur_csm = false;
s.blur_scale = 0.5f; 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[0] = 0.1f;
s.cascade_splits[1] = 0.3f; s.cascade_splits[1] = 0.3f;
s.cascade_splits[2] = 0.7f; s.cascade_splits[2] = 0.7f;
s.cascade_splits[3] = 1.0f; s.cascade_splits[3] = 1.0f;
s.cascade_splits[4] = 1.0f; s.cascade_splits[4] = 1.0f;
s.cascade_splits[5] = 1.0f; /* sticks to camera far plane */ s.cascade_splits[5] = 1.0f; /* sticks to camera far plane */
glGenFramebuffers(1, &s.fbo);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb); 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 static inline
void shadowmap_destroy_light(shadowmap_t *s, int light_index) { void shadowmap_destroy_light(shadowmap_t *s, int light_index) {
if (s->maps[light_index].fbos[0]) { if (s->fbo) {
glDeleteFramebuffers(6, s->maps[light_index].fbos); glDeleteFramebuffers(1, &s->fbo);
s->maps[light_index].fbos[0] = 0; s->fbo = 0;
} }
if (s->maps[light_index].texture) { if (s->maps[light_index].texture) {
glDeleteTextures(1, &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++) { 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]) { if (s->maps[light_index].texture_2d[i]) {
glDeleteTextures(1, &s->maps[light_index].texture_2d[i]); glDeleteTextures(1, &s->maps[light_index].texture_2d[i]);
s->maps[light_index].texture_2d[i] = 0; 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) { if (s->maps[light_index].blur_texture_2d) {
glDeleteTextures(1, &s->maps[light_index].blur_texture_2d); glDeleteTextures(1, &s->maps[light_index].blur_texture_2d);
s->maps[light_index].blur_texture_2d = 0; 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); copy44(l->shadow_matrix[s->cascade_index], PV);
l->processed_shadows = true; l->processed_shadows = true;
s->shadow_technique = l->shadow_technique = SHADOW_PCF; s->shadow_technique = l->shadow_technique = SHADOW_CSM;
model_setpass(RENDER_PASS_SHADOW_PCF); model_setpass(RENDER_PASS_SHADOW_CSM);
} }
static inline static inline
void shadowmap_blur_pcf(shadowmap_t *s, int light_index) { void shadowmap_blur_csm(shadowmap_t *s, int light_index) {
if (!s->blur_pcf) { if (!s->blur_csm) {
return; 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; // 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 renderstate_t rs;
static int program = -1, vao = -1, u_scale = -1, u_source = -1; static int program = -1, vao = -1, u_scale = -1, u_source = -1;
if (program < 0) { if (program < 0) {
@ -18740,8 +18706,8 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
glGenVertexArrays(1, &vao); glGenVertexArrays(1, &vao);
} }
renderstate_apply(&rs); // renderstate_apply(&rs);
glViewport(0, 0, s->pcf_texture_width, s->pcf_texture_width); glViewport(0, 0, s->csm_texture_width, s->csm_texture_width);
unsigned oldprog = last_shader; unsigned oldprog = last_shader;
glUseProgram(program); glUseProgram(program);
@ -18751,8 +18717,9 @@ void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
// Horizontal pass // Horizontal pass
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d); glBindFramebuffer(GL_FRAMEBUFFER, s->fbo);
glUniform2f(u_scale, 1.0f / (s->pcf_texture_width * blur_scale), 0); 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]); glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
glUniform1i(u_source, 0); glUniform1i(u_source, 0);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //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); profile_incstat("Render.num_triangles", +2);
// Vertical pass // Vertical pass
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]); glBindFramebuffer(GL_FRAMEBUFFER, s->fbo);
glUniform2f(u_scale, 0, 1.0f / (s->pcf_texture_width * blur_scale)); 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); glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d);
glUniform1i(u_source, 0); glUniform1i(u_source, 0);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //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); 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 static inline
bool shadowmap_step_finish(shadowmap_t *s) { 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) { if (s->cascade_index < NUM_SHADOW_CASCADES-1) {
s->cascade_index++; s->cascade_index++;
s->step = 0; s->step = 0;
return false; 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; s->step = 0;
@ -18796,7 +18846,7 @@ bool shadowmap_step_finish(shadowmap_t *s) {
} }
bool shadowmap_step(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 (s->step >= max_steps) {
if (shadowmap_step_finish(s)) { if (shadowmap_step_finish(s)) {
return false; 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; cam_far *= 0.5f;
} }
if (l->type == LIGHT_POINT) { if (l->type == LIGHT_POINT || l->type == LIGHT_SPOT) {
shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_SPOT) {
shadowmap_light_point(s, l, step); shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_DIRECTIONAL) { } else if (l->type == LIGHT_DIRECTIONAL) {
shadowmap_light_directional(s, l, step, cam_fov, cam_far, cam_view); 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 // shadowmap_destroy_light(s, s->light_step); // @todo: we might wanna free the other set
if (l->shadow_technique == SHADOW_VSM) { if (l->shadow_technique == SHADOW_VSM) {
shadowmap_init_caster_vsm(s, s->light_step, s->vsm_texture_width); shadowmap_init_caster_vsm(s, s->light_step, s->vsm_texture_width);
} else if (l->shadow_technique == SHADOW_PCF) { } else if (l->shadow_technique == SHADOW_CSM) {
shadowmap_init_caster_pcf(s, s->light_step, s->pcf_texture_width); 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++; s->lights_pushed++;
if (l->type == LIGHT_DIRECTIONAL) { 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(); shadowmap_clear_fbo();
} else { } 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(); 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); glViewport(0, 0, texture_width, texture_width);
} }
} }
@ -21689,15 +21741,17 @@ void model_set_renderstates(model_t *m) {
} }
// Shadow pass // 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; csm_shadow_rs->blend_enabled = 1;
pcf_shadow_rs->blend_src = GL_SRC_ALPHA; csm_shadow_rs->blend_src = GL_SRC_ALPHA;
pcf_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; csm_shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA;
pcf_shadow_rs->cull_face_enabled = 1; csm_shadow_rs->depth_test_enabled = true;
pcf_shadow_rs->cull_face_mode = GL_BACK; csm_shadow_rs->depth_write_enabled = true;
pcf_shadow_rs->front_face = GL_CW; csm_shadow_rs->cull_face_enabled = 1;
pcf_shadow_rs->depth_clamp_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]; 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_enabled = 1;
vsm_shadow_rs->blend_src = GL_SRC_ALPHA; vsm_shadow_rs->blend_src = GL_SRC_ALPHA;
vsm_shadow_rs->blend_dst = GL_ONE_MINUS_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_enabled = 1;
vsm_shadow_rs->cull_face_mode = GL_BACK; vsm_shadow_rs->cull_face_mode = GL_BACK;
vsm_shadow_rs->front_face = GL_CW; vsm_shadow_rs->front_face = GL_CW;

View File

@ -3331,7 +3331,7 @@ enum LIGHT_TYPE {
enum SHADOW_TECHNIQUE { enum SHADOW_TECHNIQUE {
SHADOW_VSM, SHADOW_VSM,
SHADOW_PCF, SHADOW_CSM,
}; };
#define NUM_SHADOW_CASCADES 6 #define NUM_SHADOW_CASCADES 6
@ -3381,24 +3381,28 @@ typedef struct shadowmap_t {
mat44 V; mat44 V;
mat44 PV; mat44 PV;
int vsm_texture_width; int vsm_texture_width;
int pcf_texture_width; int csm_texture_width;
int step; int step;
int light_step; int light_step;
int cascade_index; int cascade_index;
unsigned shadow_technique; unsigned shadow_technique;
float cascade_splits[NUM_SHADOW_CASCADES]; float cascade_splits[NUM_SHADOW_CASCADES];
bool blur_pcf; bool blur_csm;
float blur_scale; bool blur_vsm;
float csm_blur_scale;
float vsm_blur_scale;
// signals // signals
bool skip_render; bool skip_render;
int lights_pushed; int lights_pushed;
handle fbo;
struct { struct {
unsigned shadow_technique; unsigned shadow_technique;
handle fbos[6], texture, depth_texture; handle texture, depth_texture;
handle fbo_2d[NUM_SHADOW_CASCADES], texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES]; handle texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES];
handle blur_fbo_2d, blur_texture_2d; handle blur_texture, blur_texture_2d;
// handle blur_fbo_cubemap, blur_texture_cubemap;
float cascade_distances[NUM_SHADOW_CASCADES]; float cascade_distances[NUM_SHADOW_CASCADES];
} maps[MAX_LIGHTS]; } maps[MAX_LIGHTS];
@ -3407,7 +3411,7 @@ typedef struct shadowmap_t {
int saved_vp[4]; int saved_vp[4];
} shadowmap_t; } 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_destroy(shadowmap_t *s);
API void shadowmap_begin(shadowmap_t *s); API void shadowmap_begin(shadowmap_t *s);
@ -3748,7 +3752,7 @@ enum RENDER_PASS {
RENDER_PASS_OVERRIDES_BEGIN, RENDER_PASS_OVERRIDES_BEGIN,
RENDER_PASS_SHADOW_BEGIN, RENDER_PASS_SHADOW_BEGIN,
RENDER_PASS_SHADOW_PCF, RENDER_PASS_SHADOW_CSM,
RENDER_PASS_SHADOW_VSM, RENDER_PASS_SHADOW_VSM,
RENDER_PASS_SHADOW_END, RENDER_PASS_SHADOW_END,