CSM shadowmapping

main
Dominik Madarász 2024-08-30 03:46:46 +02:00
parent d219cb88a3
commit 7edd72015c
13 changed files with 1196 additions and 296 deletions

View File

@ -1020,6 +1020,7 @@ typedef struct renderstate_t {
unsigned polygon_mode_draw;
bool scissor_test_enabled;
bool seamless_cubemap;
bool depth_clamp_enabled;
} renderstate_t;
renderstate_t renderstate();
bool renderstate_compare(const renderstate_t *stateA, const renderstate_t *stateB);
@ -1160,7 +1161,7 @@ typedef struct light_t {
unsigned shadow_technique;
float shadow_distance;
float shadow_bias;
mat44 shadow_matrix;
mat44 shadow_matrix[4];
bool cached;
} light_t;
light_t light();
@ -1178,25 +1179,33 @@ typedef struct light_t {
typedef struct shadowmap_t {
mat44 V;
mat44 PV;
int texture_width;
int vsm_texture_width;
int pcf_texture_width;
int step;
int light_step;
int cascade_index;
unsigned shadow_technique;
float cascade_splits[4];
float cascade_distances[4];
bool blur_pcf;
float blur_scale;
bool skip_render;
int lights_pushed;
struct {
unsigned shadow_technique;
handle fbos[6], texture, depth_texture;
handle fbo_2d, texture_2d, depth_texture_2d;
handle fbo_2d[4], texture_2d[4], depth_texture_2d[4];
handle blur_fbo_2d, blur_texture_2d;
} maps[MAX_LIGHTS];
handle saved_fb;
handle saved_pass;
int saved_vp[4];
} shadowmap_t;
shadowmap_t shadowmap(int texture_width);
shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width);
void shadowmap_destroy(shadowmap_t *s);
void shadowmap_begin(shadowmap_t *s);
bool shadowmap_step(shadowmap_t *s);
void shadowmap_light(shadowmap_t *s, light_t *l);
void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view);
void shadowmap_end(shadowmap_t *s);
unsigned shader(const char *vs, const char *fs, const char *attribs, const char *fragcolor, const char *defines);
unsigned shader_geom(const char *gs, const char *vs, const char *fs, const char *attribs, const char *fragcolor, const char *defines);
@ -1638,6 +1647,7 @@ typedef struct object_t {
aabb bounds;
unsigned billboard;
bool disable_frustum_check;
bool cast_shadows;
handle* old_texture_ids;
texture_t* old_textures;
float distance;
@ -1663,12 +1673,14 @@ enum SCENE_FLAGS {
SCENE_BACKGROUND = 4,
SCENE_FOREGROUND = 8,
SCENE_UPDATE_SH_COEF = 16,
SCENE_CAST_SHADOWS = 32,
};
typedef struct scene_t {
object_t* objs;
light_t* lights;
skybox_t skybox;
int u_coefficients_sh;
shadowmap_t shadowmap;
} scene_t;
scene_t* scene_push();
void scene_pop();

View File

@ -32,21 +32,26 @@ int main(int argc, char** argv) {
light_t lit = light(); {
lit.type = LIGHT_POINT;
lit.cast_shadows = true;
// lit.shadow_distance = 3.0f;
// lit.shadow_distance = 5.0f;
// lit.falloff.linear = 3.0f;
}
light_t lit2 = light(); {
lit2.type = LIGHT_POINT;
lit2.cast_shadows = true;
lit2.diffuse = vec3(1, 0.7, 0.8);
// lit2.shadow_distance = 5.0f;
// lit2.falloff.linear = 3.0f;
}
light_t lit3 = light(); {
lit3.type = LIGHT_SPOT;
lit3.cast_shadows = true;
// lit3.shadow_distance = 5.0f;
lit3.diffuse = vec3(1, 0.7, 0.8);
}
light_t lit4 = light(); {
lit4.type = LIGHT_DIRECTIONAL;
lit4.cast_shadows = true;
lit4.shadow_distance = 2000.0f;
lit4.diffuse = vec3(1, 0.7, 0.8);
}
@ -81,7 +86,7 @@ int main(int argc, char** argv) {
if( !initialized ) {
initialized = 1;
sky = skybox(flag("--mie") ? 0 : SKY_DIRS[SKY_DIR], 0);
sm = shadowmap(1024);
sm = shadowmap(512, 2048);
mdl = model(OBJ_MDLS[OBJ_MDL], 0);
shader_bind(mdl.program);
cubemap_sh_shader(&sky.cubemap);
@ -100,7 +105,7 @@ int main(int argc, char** argv) {
enum {
POINT, SPOT, DIR, ALL
};
static unsigned mode = POINT;
static unsigned mode = DIR;
if (input_down(KEY_1)) mode = POINT;
if (input_down(KEY_2)) mode = SPOT;
@ -135,6 +140,7 @@ int main(int argc, char** argv) {
}
if (mode == DIR) {
lights[0].pos = cam.position;
lights[0].dir = vec3(1,-1,-1);
}
@ -160,7 +166,7 @@ int main(int argc, char** argv) {
{
for (int i = 0; i < array_count(lights); i++) {
while (shadowmap_step(&sm)) {
shadowmap_light(&sm, &lights[i]);
shadowmap_light(&sm, &lights[i], cam.fov, cam.view);
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
}
}
@ -171,14 +177,10 @@ int main(int argc, char** argv) {
mat44 mvp; multiply44x2(mvp, cam.proj, cam.view);
{
skybox_render(&sky, cam.proj, cam.view);
shader_bind(mdl.program);
shader_int("u_textured", false);
light_update(array_count(lights), lights);
// ddraw_sphere(lights[0].pos, 0.1f);
// ddraw_sphere(lights[1].pos, 0.1f);
// ddraw_flush();
model_shadow(&mdl, &sm);
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
}

View File

@ -5,6 +5,8 @@
uniform int u_num_lights;
#define NUM_SHADOW_CASCADES 4
struct light_t {
int type;
vec3 diffuse;
@ -23,7 +25,7 @@ struct light_t {
float quadratic;
// shadows
mat4 shadow_matrix;
mat4 shadow_matrix[NUM_SHADOW_CASCADES];
};
#define MAX_LIGHTS 16

View File

@ -1,8 +1,10 @@
in vec4 vpeye;
in vec4 vneye;
uniform bool u_shadow_receiver;
uniform float u_cascade_splits[NUM_SHADOW_CASCADES];
uniform float u_cascade_distances[NUM_SHADOW_CASCADES];
uniform samplerCube shadowMap[MAX_LIGHTS];
uniform sampler2D shadowMap2D[MAX_LIGHTS];
uniform sampler2D shadowMap2D[MAX_LIGHTS * NUM_SHADOW_CASCADES];
//// From http://fabiensanglard.net/shadowmappingVSM/index.php
float shadow_vsm(float distance, vec3 dir, int light_index) {
@ -22,44 +24,61 @@ float shadow_vsm(float distance, vec3 dir, int light_index) {
float d = distance - moments.x;
float p_max = variance / (variance + d*d);
return p_max;
}
float shadow_pcf(vec4 fragPosLightSpace, vec3 lightDir, int light_index) {
float shadow_pcf(float distance, vec3 lightDir, int light_index) {
// Determine which cascade to use
int cascade_index = -1;
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
if (distance < u_cascade_distances[i]) {
cascade_index = i;
break;
}
}
if (cascade_index == -1) {
cascade_index = NUM_SHADOW_CASCADES - 1;
}
vec4 fragPosLightSpace = u_lights[light_index].shadow_matrix[cascade_index] * vec4(v_position_ws, 1.0);
int index = light_index * NUM_SHADOW_CASCADES + cascade_index;
// Perform perspective divide
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
// Transform to [0,1] range
projCoords = projCoords * 0.5 + 0.5;
// Get closest depth value from light's perspective (using [0,1] range fragPosLight as coords)
float closestDepth = texture(shadowMap2D[light_index], projCoords.xy).r;
// Get depth of current fragment from light's perspective
float currentDepth = projCoords.z;
// Calculate bias (based on depth map resolution and slope)
if (currentDepth > 1.0) {
return 1.0;
}
// Calculate bias
vec3 normal = normalize(vneye.xyz);
float bias = max(0.05 * (1.0 - dot(normal, -lightDir)), 0.005);
float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
const float bias_modifier = 0.5;
if (cascade_index == NUM_SHADOW_CASCADES-1) {
bias *= 1 / (u_cascade_distances[NUM_SHADOW_CASCADES-1] * bias_modifier);
} else {
bias *= 1 / (u_cascade_distances[cascade_index] * bias_modifier);
}
// PCF
float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(shadowMap2D[light_index], 0);
vec2 texelSize = 1.0 / textureSize(shadowMap2D[index], 0);
for(int x = -1; x <= 1; ++x)
{
for(int y = -1; y <= 1; ++y)
{
float pcfDepth = texture(shadowMap2D[light_index], projCoords.xy + vec2(x, y) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
float pcfDepth = texture(shadowMap2D[index], projCoords.xy + vec2(x, y) * texelSize).r;
shadow += projCoords.z - bias > pcfDepth ? 1.0 : 0.0;
}
}
shadow /= 9.0;
// Keep the shadow at 0.0 when outside the far_plane region of the light's frustum.
if(projCoords.z >= 0.9)
shadow = 0.0;
return 1.0 - shadow;
}
@ -74,22 +93,14 @@ vec4 shadowmap(in vec4 peye, in vec4 neye) {
if (light.type == LIGHT_DIRECTIONAL) {
total_casters++;
vec4 frag_pos = light.shadow_matrix * vec4(v_position_ws, 1.0);
factor += shadow_pcf(frag_pos, light.dir, i);
} else if (light.type == LIGHT_POINT) {
total_casters++;
vec3 light_pos = (view * vec4(light.pos, 1.0)).xyz;
vec3 dir = light_pos - fragment;
vec4 sc = inv_view * vec4(dir, 0.0);
factor += shadow_vsm(length(dir), -sc.xyz, i);
} else if (light.type == LIGHT_SPOT) {
factor += shadow_pcf(-peye.z, light.dir, i);
} else if (light.type == LIGHT_POINT || light.type == LIGHT_SPOT) {
total_casters++;
vec3 light_pos = (view * vec4(light.pos, 1.0)).xyz;
vec3 dir = light_pos - fragment;
vec4 sc = inv_view * vec4(dir, 0.0);
factor += shadow_vsm(length(dir), -sc.xyz, i);
}
shadowFactor += factor;
}

View File

@ -1,17 +1,17 @@
uniform sampler2D textureSource;
uniform vec2 ScaleU;
in vec2 Texcoord;
out vec4 outColor;
in vec2 uv;
out vec4 fragcolor;
void main() {
vec4 color = vec4(0.0);
color += texture( textureSource, Texcoord.st + vec2( -3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;
color += texture( textureSource, Texcoord.st + vec2( -2.0*ScaleU.x, -2.0*ScaleU.y ) )*0.09375;
color += texture( textureSource, Texcoord.st + vec2( -1.0*ScaleU.x, -1.0*ScaleU.y ) )*0.234375;
color += texture( textureSource, Texcoord.st + vec2( 0.0 , 0.0) )*0.3125;
color += texture( textureSource, Texcoord.st + vec2( 1.0*ScaleU.x, 1.0*ScaleU.y ) )*0.234375;
color += texture( textureSource, Texcoord.st + vec2( 2.0*ScaleU.x, 2.0*ScaleU.y ) )*0.09375;
color += texture( textureSource, Texcoord.st + vec2( 3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;
outColor = vec4(color.xyz, 1.0);
color += texture( textureSource, uv.st + vec2( -3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;
color += texture( textureSource, uv.st + vec2( -2.0*ScaleU.x, -2.0*ScaleU.y ) )*0.09375;
color += texture( textureSource, uv.st + vec2( -1.0*ScaleU.x, -1.0*ScaleU.y ) )*0.234375;
color += texture( textureSource, uv.st + vec2( 0.0 , 0.0) )*0.3125;
color += texture( textureSource, uv.st + vec2( 1.0*ScaleU.x, 1.0*ScaleU.y ) )*0.234375;
color += texture( textureSource, uv.st + vec2( 2.0*ScaleU.x, 2.0*ScaleU.y ) )*0.09375;
color += texture( textureSource, uv.st + vec2( 3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;
fragcolor = vec4(color.xyz, 1.0);
}

View File

@ -8,15 +8,15 @@ uniform int shadow_technique;
void main() {
if (shadow_technique == SHADOW_VSM) {
float depth = length(v_position) / 20;
float depth = length(v_position) / 20;
float moment1 = depth;
float moment2 = depth * depth;
float dx = dFdx(depth);
float dy = dFdy(depth);
moment2 += 0.25*(dx*dx+dy*dy);
fragcolor = vec4( moment1, moment2, 0.0, 1.0);
float moment1 = depth;
float moment2 = depth * depth;
float dx = dFdx(depth);
float dy = dFdy(depth);
moment2 += 0.25*(dx*dx+dy*dy);
fragcolor = vec4( moment1, moment2, 0.0, 1.0);
}
else if (shadow_technique == SHADOW_PCF) {
fragcolor = vec4(vec3(gl_FragCoord.z), 1.0);

View File

@ -1,8 +1,26 @@
in vec3 position;
in vec2 texcoord;
out vec2 Texcoord;
out vec2 uv;
void main() {
gl_Position = vec4(position, 1.0);
Texcoord = texcoord;
// Calculate vertex position based on gl_VertexID
vec2 positions[6] = vec2[6](
vec2(-1.0, 1.0), // Top-left
vec2( 1.0, 1.0), // Top-right
vec2( 1.0, -1.0), // Bottom-right
vec2( 1.0, -1.0), // Bottom-right (repeated)
vec2(-1.0, -1.0), // Bottom-left
vec2(-1.0, 1.0) // Top-left (repeated)
);
// Calculate UV coordinates based on gl_VertexID
vec2 texCoords[6] = vec2[6](
vec2(0.0, 1.0), // Top-left
vec2(1.0, 1.0), // Top-right
vec2(1.0, 0.0), // Bottom-right
vec2(1.0, 0.0), // Bottom-right (repeated)
vec2(0.0, 0.0), // Bottom-left
vec2(0.0, 1.0) // Top-left (repeated)
);
gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);
uv = texCoords[gl_VertexID];
}

View File

@ -17034,8 +17034,11 @@ typedef struct renderstate_t {
// Scissor test
bool scissor_test_enabled;
// bool Seamless Cubemap
// Seamless cubemap
bool seamless_cubemap;
// Depth clamp
bool depth_clamp_enabled;
} renderstate_t;
API renderstate_t renderstate();
@ -17264,6 +17267,8 @@ enum SHADOW_TECHNIQUE {
SHADOW_PCF,
};
#define NUM_SHADOW_CASCADES 4
typedef struct light_t {
char type;
vec3 diffuse, specular, ambient;
@ -17281,7 +17286,7 @@ typedef struct light_t {
unsigned shadow_technique;
float shadow_distance;
float shadow_bias;
mat44 shadow_matrix;
mat44 shadow_matrix[NUM_SHADOW_CASCADES];
// internals
bool cached; //< used by scene to invalidate cached light data
@ -17314,18 +17319,26 @@ API void light_update(unsigned num_lights, light_t *lv);
typedef struct shadowmap_t {
mat44 V;
mat44 PV;
int texture_width;
int vsm_texture_width;
int pcf_texture_width;
int step;
int light_step;
int cascade_index;
unsigned shadow_technique;
float cascade_splits[NUM_SHADOW_CASCADES];
float cascade_distances[NUM_SHADOW_CASCADES];
bool blur_pcf;
float blur_scale;
// signals
bool skip_render;
int lights_pushed;
struct {
unsigned shadow_technique;
handle fbos[6], texture, depth_texture;
handle fbo_2d, texture_2d, depth_texture_2d;
handle fbo_2d[NUM_SHADOW_CASCADES], texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES];
handle blur_fbo_2d, blur_texture_2d;
} maps[MAX_LIGHTS];
handle saved_fb;
@ -17333,12 +17346,12 @@ typedef struct shadowmap_t {
int saved_vp[4];
} shadowmap_t;
API shadowmap_t shadowmap(int texture_width); // = 1024
API shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width); // = 512, 4096
API void shadowmap_destroy(shadowmap_t *s);
API void shadowmap_begin(shadowmap_t *s);
API bool shadowmap_step(shadowmap_t *s);
API void shadowmap_light(shadowmap_t *s, light_t *l); //< can be called once per shadowmap_step
API void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view); //< can be called once per shadowmap_step
API void shadowmap_end(shadowmap_t *s);
// -----------------------------------------------------------------------------
@ -17993,6 +18006,7 @@ typedef struct object_t {
aabb bounds;
unsigned billboard; // [0..7] x(4),y(2),z(1) masks
bool disable_frustum_check;
bool cast_shadows;
// internal states
array(handle) old_texture_ids;
@ -18027,6 +18041,7 @@ enum SCENE_FLAGS {
SCENE_BACKGROUND = 4,
SCENE_FOREGROUND = 8,
SCENE_UPDATE_SH_COEF = 16,
SCENE_CAST_SHADOWS = 32,
};
typedef struct scene_t {
@ -18036,6 +18051,7 @@ typedef struct scene_t {
// special objects below:
skybox_t skybox;
int u_coefficients_sh;
shadowmap_t shadowmap;
} scene_t;
API scene_t* scene_push();
@ -381836,6 +381852,9 @@ renderstate_t renderstate() {
// Enable seamless cubemap by default
state.seamless_cubemap = GL_TRUE;
// Disable depth clamp by default
state.depth_clamp_enabled = GL_FALSE;
return state;
}
@ -381949,6 +381968,13 @@ void renderstate_apply(const renderstate_t *state) {
} else {
glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
// Apply depth clamp
if (state->depth_clamp_enabled) {
glEnable(GL_DEPTH_CLAMP);
} else {
glDisable(GL_DEPTH_CLAMP);
}
}
}
@ -383186,8 +383212,9 @@ light_t light() {
l.specularPower = 32.f;
l.innerCone = 0.85f;// 31 deg
l.outerCone = 0.9f; // 25 deg
l.cast_shadows = true;
l.shadow_distance = 100.0f;
l.shadow_bias = 0.5f;
l.shadow_bias = 0.01f;
return l;
}
@ -383266,7 +383293,9 @@ void light_update(unsigned num_lights, light_t *lv) {
shader_float(va("u_lights[%d].quadratic", i), lv[i].falloff.quadratic);
shader_float(va("u_lights[%d].innerCone", i), lv[i].innerCone);
shader_float(va("u_lights[%d].outerCone", i), lv[i].outerCone);
shader_mat44(va("u_lights[%d].shadow_matrix", i), lv[i].shadow_matrix);
for (int j = 0; j < NUM_SHADOW_CASCADES; j++) {
shader_mat44(va("u_lights[%d].shadow_matrix[%d]", i, j), lv[i].shadow_matrix[j]);
}
}
}
@ -383274,10 +383303,14 @@ void light_update(unsigned num_lights, light_t *lv) {
// -----------------------------------------------------------------------------
// shadowmaps
static inline
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
static inline void
shadowmap_init_caster_vsm(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) {
return;
}
// Create a cubemap color texture
glGenTextures(1, &s->maps[light_index].texture);
glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].texture);
@ -383318,70 +383351,147 @@ shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
if (GL_FRAMEBUFFER_COMPLETE != result) {
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
}
// #if is(ems)
// GLenum nones[] = { GL_NONE };
// glDrawBuffers(1, nones);
// glReadBuffer(GL_NONE);
// #else
// glDrawBuffer(GL_NONE);
// glReadBuffer(GL_NONE);
// #endif
}
}
static inline void
shadowmap_init_caster_pcf(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]) {
return;
}
// Initialise shadow map 2D
glGenTextures(1, &s->maps[light_index].texture_2d);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].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_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
glGenTextures(1, &s->maps[light_index].texture_2d[i]);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
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_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glGenTextures(1, &s->maps[light_index].depth_texture_2d);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glGenTextures(1, &s->maps[light_index].depth_texture_2d[i]);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 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_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
}
glBindTexture(GL_TEXTURE_2D, 0);
glGenFramebuffers(1, &s->maps[light_index].fbo_2d);
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_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);
}
}
shadowmap_t shadowmap(int texture_width) { // = 1024
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_t shadowmap(int vsm_texture_width, int pcf_texture_width) { // = 512, 4096
shadowmap_t s = {0};
s.texture_width = texture_width;
s.vsm_texture_width = vsm_texture_width;
s.pcf_texture_width = pcf_texture_width;
s.saved_fb = 0;
s.blur_pcf = true;
s.blur_scale = 0.5f;
s.cascade_splits[0] = 0.1f;
s.cascade_splits[1] = 0.3f;
s.cascade_splits[2] = 0.5f;
s.cascade_splits[3] = 1.0f;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb);
#if 0
for (int i = 0; i < MAX_LIGHTS; i++) {
shadowmap_init_caster(&s, i, texture_width);
shadowmap_init_caster(&s, i);
}
#else
for (int i = 0; i < MAX_LIGHTS; i++) {
s.maps[i].shadow_technique = 0xFFFF;
}
#endif
glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb);
return s;
}
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->maps[light_index].texture) {
glDeleteTextures(1, &s->maps[light_index].texture);
s->maps[light_index].texture = 0;
}
if (s->maps[light_index].depth_texture) {
glDeleteTextures(1, &s->maps[light_index].depth_texture);
s->maps[light_index].depth_texture = 0;
}
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;
}
if (s->maps[light_index].depth_texture_2d[i]) {
glDeleteTextures(1, &s->maps[light_index].depth_texture_2d[i]);
s->maps[light_index].depth_texture_2d[i] = 0;
}
}
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;
}
}
void shadowmap_destroy(shadowmap_t *s) {
for (int i = 0; i < MAX_LIGHTS; i++) {
glDeleteFramebuffers(6, s->maps[i].fbos);
glDeleteTextures(1, &s->maps[i].texture);
glDeleteTextures(1, &s->maps[i].depth_texture);
glDeleteFramebuffers(1, &s->maps[i].fbo_2d);
glDeleteTextures(1, &s->maps[i].texture_2d);
glDeleteTextures(1, &s->maps[i].depth_texture_2d);
shadowmap_destroy_light(s, i);
}
shadowmap_t z = {0};
*s = z;
@ -383396,6 +383506,7 @@ void shadowmap_begin(shadowmap_t *s) {
s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
s->step = 0;
s->light_step = 0;
s->cascade_index = 0;
active_shadowmap = s;
}
@ -383419,48 +383530,206 @@ static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) {
s->shadow_technique = l->shadow_technique = SHADOW_VSM;
}
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir) {
static array(vec3) frustum_corners = 0;
static inline
void shadowmap_light_directional_calc_frustum_corners(mat44 cam_proj, mat44 cam_view) {
mat44 PV; multiply44x2(PV, cam_proj, cam_view);
mat44 inverse_view_proj; invert44(inverse_view_proj, PV);
array_resize(frustum_corners, 0);
for (unsigned x = 0; x < 2; x++) {
for (unsigned y = 0; y < 2; y++) {
for (unsigned z = 0; z < 2; z++) {
vec4 corner = {
x * 2.0f - 1.0f,
y * 2.0f - 1.0f,
z * 2.0f - 1.0f,
1.0f
};
vec4 world_corner = transform444(inverse_view_proj, corner);
world_corner = scale4(world_corner, 1.0f / world_corner.w);
array_push(frustum_corners, vec3(world_corner.x, world_corner.y, world_corner.z));
}
}
}
}
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir, float cam_fov, mat44 cam_view) {
if (dir != 0) {
s->skip_render = true;
return;
}
mat44 P, V, PV;
l->shadow_distance = 25.0f;
ortho44(P,
-l->shadow_distance/2.0, l->shadow_distance/2.0,
-l->shadow_distance/2.0, l->shadow_distance/2.0,
l->shadow_bias, l->shadow_distance);
float far_plane = 0.0f;
float near_plane = 0.0f;
if (s->cascade_index == 0) {
near_plane = l->shadow_bias;
far_plane = l->shadow_distance * s->cascade_splits[0];
} else if (s->cascade_index < NUM_SHADOW_CASCADES - 1) {
near_plane = l->shadow_distance * s->cascade_splits[s->cascade_index-1];
far_plane = l->shadow_distance * s->cascade_splits[s->cascade_index];
} else {
near_plane = l->shadow_distance * s->cascade_splits[NUM_SHADOW_CASCADES-1];
far_plane = l->shadow_distance;
}
mat44 proj; perspective44(proj, cam_fov, window_width() / (float)window_height(), near_plane, far_plane);
shadowmap_light_directional_calc_frustum_corners(proj, cam_view);
vec3 center = {0,0,0};
for (unsigned i = 0; i < array_count(frustum_corners); i++) {
center = add3(center, frustum_corners[i]);
}
center = scale3(center, 1.0f / array_count(frustum_corners));
s->cascade_distances[s->cascade_index] = far_plane;
float minX = FLT_MAX, maxX = FLT_MIN;
float minY = FLT_MAX, maxY = FLT_MIN;
float minZ = FLT_MAX, maxZ = FLT_MIN;
mat44 V;
vec3 lightDir = norm3(l->dir);
vec3 up = vec3(0, 1, 0);
// Ensure up vector is not parallel to light direction
if (fabs(dot3(lightDir, up)) > 0.99f) {
up = vec3(0, 0, 1);
lookat44(V, sub3(center, lightDir), center, up);
for (unsigned i = 0; i < array_count(frustum_corners); i++) {
vec3 corner = frustum_corners[i];
corner = transform344(V, corner);
minX = min(minX, corner.x);
maxX = max(maxX, corner.x);
minY = min(minY, corner.y);
maxY = max(maxY, corner.y);
minZ = min(minZ, corner.z);
maxZ = max(maxZ, corner.z);
}
vec3 center = vec3(0, 0, 0);
vec3 lightPos = sub3(center, scale3(lightDir, l->shadow_distance*0.5f));
lookat44(V, lightPos, center, up);
float zMult = 10.0f;
// if (minZ < 0) {
// minZ *= zMult;
// } else {
// minZ /= zMult;
// }
// if (maxZ < 0) {
// maxZ /= zMult;
// } else {
// maxZ *= zMult;
// }
mat44 P, PV;
ortho44(P,
minX, maxX,
minY, maxY,
minZ, maxZ);
multiply44x2(PV, P, V);
copy44(s->V, V);
copy44(s->PV, PV);
copy44(l->shadow_matrix, PV);
copy44(l->shadow_matrix[s->cascade_index], PV);
s->shadow_technique = l->shadow_technique = SHADOW_PCF;
}
bool shadowmap_step(shadowmap_t *s) {
if (s->step >= 6) {
s->step = 0;
s->light_step++;
return false;
static inline
void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
if (!s->blur_pcf) {
return;
}
glViewport(0, 0, s->texture_width, s->texture_width);
float blur_scale = 1.999 * (1 - s->blur_scale) + 0.001;
// blur_scale = 0.1f;
static renderstate_t rs;
static int program = -1, vao = -1, u_scale = -1, u_source = -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.glsl");
const char* fs = vfs_read("shaders/fs_shadow_blur.glsl");
program = shader(vs, fs, "", "fragcolor", NULL);
u_scale = glGetUniformLocation(program, "ScaleU");
u_source = glGetUniformLocation(program, "textureSource");
glGenVertexArrays(1, &vao);
}
renderstate_apply(&rs);
glViewport(0, 0, s->pcf_texture_width, s->pcf_texture_width);
unsigned oldprog = last_shader;
glUseProgram(program);
glBindVertexArray(vao);
glActiveTexture(GL_TEXTURE0);
// 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);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
glUniform1i(u_source, 0);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 6);
profile_incstat("Render.num_drawcalls", +1);
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));
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d);
glUniform1i(u_source, 0);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
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->cascade_index < NUM_SHADOW_CASCADES - 1) {
s->cascade_index++;
s->step = 0;
return false;
}
shadowmap_blur_pcf(s, s->light_step);
}
s->step = 0;
s->light_step++;
s->cascade_index = 0;
return true;
}
bool shadowmap_step(shadowmap_t *s) {
int max_steps = s->shadow_technique == 0xffff ? 1 : s->shadow_technique == SHADOW_PCF ? 1 : 6;
if (s->step >= max_steps) {
if (shadowmap_step_finish(s)) {
return false;
} else {
return true;
}
}
unsigned texture_width = s->shadow_technique == SHADOW_VSM ? s->vsm_texture_width : s->pcf_texture_width;
glViewport(0, 0, texture_width, texture_width);
s->step++;
s->skip_render = false;
@ -383470,12 +383739,12 @@ bool shadowmap_step(shadowmap_t *s) {
static inline
void shadowmap_clear_fbo() {
glClearColor(0, 0, 0, 0);
glClearColor(1, 1, 1, 1);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void shadowmap_light(shadowmap_t *s, light_t *l) {
void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view) {
if (l->cast_shadows) {
int step = s->step - 1;
@ -383484,18 +383753,29 @@ void shadowmap_light(shadowmap_t *s, light_t *l) {
} else if (l->type == LIGHT_SPOT) {
shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_DIRECTIONAL) {
shadowmap_light_directional(s, l, step);
shadowmap_light_directional(s, l, step, cam_fov, cam_view);
}
if (s->skip_render) {
return;
}
if (s->maps[s->light_step].shadow_technique != l->shadow_technique) {
// 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);
}
}
s->maps[s->light_step].shadow_technique = l->shadow_technique;
ASSERT(s->lights_pushed == 0);
s->lights_pushed++;
if (l->type == LIGHT_DIRECTIONAL) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d);
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d[s->cascade_index]);
shadowmap_clear_fbo();
} else {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]);
@ -385689,7 +385969,11 @@ void model_set_uniforms(model_t m, int shader, mat44 mv, mat44 proj, mat44 view,
shader_bool("u_shadow_receiver", GL_TRUE);
for (int i = 0; i < MAX_LIGHTS; i++) {
shader_cubemap(va("shadowMap[%d]", i), m.shadow_map->maps[i].texture);
shader_texture_unit(va("shadowMap2D[%d]", i), m.shadow_map->maps[i].texture_2d, texture_unit());
for (int j = 0; j < NUM_SHADOW_CASCADES; j++) {
shader_texture_unit(va("shadowMap2D[%d]", i * NUM_SHADOW_CASCADES + j), m.shadow_map->maps[i].texture_2d[j], texture_unit());
shader_float(va("u_cascade_splits[%d]", j), m.shadow_map->cascade_splits[j]);
shader_float(va("u_cascade_distances[%d]", j), m.shadow_map->cascade_distances[j]);
}
}
} else {
shader_bool("u_shadow_receiver", GL_FALSE);
@ -386317,6 +386601,7 @@ void model_set_renderstates(model_t *m) {
shadow_rs->cull_face_enabled = 1;
shadow_rs->cull_face_mode = GL_BACK;
shadow_rs->front_face = GL_CW;
shadow_rs->depth_clamp_enabled = 1;
}
// Lightmap pass

View File

@ -122,6 +122,9 @@ renderstate_t renderstate() {
// Enable seamless cubemap by default
state.seamless_cubemap = GL_TRUE;
// Disable depth clamp by default
state.depth_clamp_enabled = GL_FALSE;
return state;
}
@ -235,6 +238,13 @@ void renderstate_apply(const renderstate_t *state) {
} else {
glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
// Apply depth clamp
if (state->depth_clamp_enabled) {
glEnable(GL_DEPTH_CLAMP);
} else {
glDisable(GL_DEPTH_CLAMP);
}
}
}
@ -1472,8 +1482,9 @@ light_t light() {
l.specularPower = 32.f;
l.innerCone = 0.85f;// 31 deg
l.outerCone = 0.9f; // 25 deg
l.cast_shadows = true;
l.shadow_distance = 100.0f;
l.shadow_bias = 0.5f;
l.shadow_bias = 0.01f;
return l;
}
@ -1552,7 +1563,9 @@ void light_update(unsigned num_lights, light_t *lv) {
shader_float(va("u_lights[%d].quadratic", i), lv[i].falloff.quadratic);
shader_float(va("u_lights[%d].innerCone", i), lv[i].innerCone);
shader_float(va("u_lights[%d].outerCone", i), lv[i].outerCone);
shader_mat44(va("u_lights[%d].shadow_matrix", i), lv[i].shadow_matrix);
for (int j = 0; j < NUM_SHADOW_CASCADES; j++) {
shader_mat44(va("u_lights[%d].shadow_matrix[%d]", i, j), lv[i].shadow_matrix[j]);
}
}
}
@ -1560,10 +1573,14 @@ void light_update(unsigned num_lights, light_t *lv) {
// -----------------------------------------------------------------------------
// shadowmaps
static inline
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
static inline void
shadowmap_init_caster_vsm(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) {
return;
}
// Create a cubemap color texture
glGenTextures(1, &s->maps[light_index].texture);
glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].texture);
@ -1604,70 +1621,147 @@ shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
if (GL_FRAMEBUFFER_COMPLETE != result) {
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
}
// #if is(ems)
// GLenum nones[] = { GL_NONE };
// glDrawBuffers(1, nones);
// glReadBuffer(GL_NONE);
// #else
// glDrawBuffer(GL_NONE);
// glReadBuffer(GL_NONE);
// #endif
}
}
static inline void
shadowmap_init_caster_pcf(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]) {
return;
}
// Initialise shadow map 2D
glGenTextures(1, &s->maps[light_index].texture_2d);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].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_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
glGenTextures(1, &s->maps[light_index].texture_2d[i]);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
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_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glGenTextures(1, &s->maps[light_index].depth_texture_2d);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glGenTextures(1, &s->maps[light_index].depth_texture_2d[i]);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 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_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
}
glBindTexture(GL_TEXTURE_2D, 0);
glGenFramebuffers(1, &s->maps[light_index].fbo_2d);
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_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);
}
}
shadowmap_t shadowmap(int texture_width) { // = 1024
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_t shadowmap(int vsm_texture_width, int pcf_texture_width) { // = 512, 4096
shadowmap_t s = {0};
s.texture_width = texture_width;
s.vsm_texture_width = vsm_texture_width;
s.pcf_texture_width = pcf_texture_width;
s.saved_fb = 0;
s.blur_pcf = true;
s.blur_scale = 0.5f;
s.cascade_splits[0] = 0.1f;
s.cascade_splits[1] = 0.3f;
s.cascade_splits[2] = 0.5f;
s.cascade_splits[3] = 1.0f;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb);
#if 0
for (int i = 0; i < MAX_LIGHTS; i++) {
shadowmap_init_caster(&s, i, texture_width);
shadowmap_init_caster(&s, i);
}
#else
for (int i = 0; i < MAX_LIGHTS; i++) {
s.maps[i].shadow_technique = 0xFFFF;
}
#endif
glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb);
return s;
}
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->maps[light_index].texture) {
glDeleteTextures(1, &s->maps[light_index].texture);
s->maps[light_index].texture = 0;
}
if (s->maps[light_index].depth_texture) {
glDeleteTextures(1, &s->maps[light_index].depth_texture);
s->maps[light_index].depth_texture = 0;
}
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;
}
if (s->maps[light_index].depth_texture_2d[i]) {
glDeleteTextures(1, &s->maps[light_index].depth_texture_2d[i]);
s->maps[light_index].depth_texture_2d[i] = 0;
}
}
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;
}
}
void shadowmap_destroy(shadowmap_t *s) {
for (int i = 0; i < MAX_LIGHTS; i++) {
glDeleteFramebuffers(6, s->maps[i].fbos);
glDeleteTextures(1, &s->maps[i].texture);
glDeleteTextures(1, &s->maps[i].depth_texture);
glDeleteFramebuffers(1, &s->maps[i].fbo_2d);
glDeleteTextures(1, &s->maps[i].texture_2d);
glDeleteTextures(1, &s->maps[i].depth_texture_2d);
shadowmap_destroy_light(s, i);
}
shadowmap_t z = {0};
*s = z;
@ -1682,6 +1776,7 @@ void shadowmap_begin(shadowmap_t *s) {
s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
s->step = 0;
s->light_step = 0;
s->cascade_index = 0;
active_shadowmap = s;
}
@ -1705,48 +1800,206 @@ static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) {
s->shadow_technique = l->shadow_technique = SHADOW_VSM;
}
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir) {
static array(vec3) frustum_corners = 0;
static inline
void shadowmap_light_directional_calc_frustum_corners(mat44 cam_proj, mat44 cam_view) {
mat44 PV; multiply44x2(PV, cam_proj, cam_view);
mat44 inverse_view_proj; invert44(inverse_view_proj, PV);
array_resize(frustum_corners, 0);
for (unsigned x = 0; x < 2; x++) {
for (unsigned y = 0; y < 2; y++) {
for (unsigned z = 0; z < 2; z++) {
vec4 corner = {
x * 2.0f - 1.0f,
y * 2.0f - 1.0f,
z * 2.0f - 1.0f,
1.0f
};
vec4 world_corner = transform444(inverse_view_proj, corner);
world_corner = scale4(world_corner, 1.0f / world_corner.w);
array_push(frustum_corners, vec3(world_corner.x, world_corner.y, world_corner.z));
}
}
}
}
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir, float cam_fov, mat44 cam_view) {
if (dir != 0) {
s->skip_render = true;
return;
}
mat44 P, V, PV;
l->shadow_distance = 25.0f;
ortho44(P,
-l->shadow_distance/2.0, l->shadow_distance/2.0,
-l->shadow_distance/2.0, l->shadow_distance/2.0,
l->shadow_bias, l->shadow_distance);
float far_plane = 0.0f;
float near_plane = 0.0f;
if (s->cascade_index == 0) {
near_plane = l->shadow_bias;
far_plane = l->shadow_distance * s->cascade_splits[0];
} else if (s->cascade_index < NUM_SHADOW_CASCADES - 1) {
near_plane = l->shadow_distance * s->cascade_splits[s->cascade_index-1];
far_plane = l->shadow_distance * s->cascade_splits[s->cascade_index];
} else {
near_plane = l->shadow_distance * s->cascade_splits[NUM_SHADOW_CASCADES-1];
far_plane = l->shadow_distance;
}
mat44 proj; perspective44(proj, cam_fov, window_width() / (float)window_height(), near_plane, far_plane);
shadowmap_light_directional_calc_frustum_corners(proj, cam_view);
vec3 center = {0,0,0};
for (unsigned i = 0; i < array_count(frustum_corners); i++) {
center = add3(center, frustum_corners[i]);
}
center = scale3(center, 1.0f / array_count(frustum_corners));
s->cascade_distances[s->cascade_index] = far_plane;
float minX = FLT_MAX, maxX = FLT_MIN;
float minY = FLT_MAX, maxY = FLT_MIN;
float minZ = FLT_MAX, maxZ = FLT_MIN;
mat44 V;
vec3 lightDir = norm3(l->dir);
vec3 up = vec3(0, 1, 0);
// Ensure up vector is not parallel to light direction
if (fabs(dot3(lightDir, up)) > 0.99f) {
up = vec3(0, 0, 1);
lookat44(V, sub3(center, lightDir), center, up);
for (unsigned i = 0; i < array_count(frustum_corners); i++) {
vec3 corner = frustum_corners[i];
corner = transform344(V, corner);
minX = min(minX, corner.x);
maxX = max(maxX, corner.x);
minY = min(minY, corner.y);
maxY = max(maxY, corner.y);
minZ = min(minZ, corner.z);
maxZ = max(maxZ, corner.z);
}
vec3 center = vec3(0, 0, 0);
vec3 lightPos = sub3(center, scale3(lightDir, l->shadow_distance*0.5f));
lookat44(V, lightPos, center, up);
float zMult = 10.0f;
// if (minZ < 0) {
// minZ *= zMult;
// } else {
// minZ /= zMult;
// }
// if (maxZ < 0) {
// maxZ /= zMult;
// } else {
// maxZ *= zMult;
// }
mat44 P, PV;
ortho44(P,
minX, maxX,
minY, maxY,
minZ, maxZ);
multiply44x2(PV, P, V);
copy44(s->V, V);
copy44(s->PV, PV);
copy44(l->shadow_matrix, PV);
copy44(l->shadow_matrix[s->cascade_index], PV);
s->shadow_technique = l->shadow_technique = SHADOW_PCF;
}
bool shadowmap_step(shadowmap_t *s) {
if (s->step >= 6) {
s->step = 0;
s->light_step++;
return false;
static inline
void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
if (!s->blur_pcf) {
return;
}
glViewport(0, 0, s->texture_width, s->texture_width);
float blur_scale = 1.999 * (1 - s->blur_scale) + 0.001;
// blur_scale = 0.1f;
static renderstate_t rs;
static int program = -1, vao = -1, u_scale = -1, u_source = -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.glsl");
const char* fs = vfs_read("shaders/fs_shadow_blur.glsl");
program = shader(vs, fs, "", "fragcolor", NULL);
u_scale = glGetUniformLocation(program, "ScaleU");
u_source = glGetUniformLocation(program, "textureSource");
glGenVertexArrays(1, &vao);
}
renderstate_apply(&rs);
glViewport(0, 0, s->pcf_texture_width, s->pcf_texture_width);
unsigned oldprog = last_shader;
glUseProgram(program);
glBindVertexArray(vao);
glActiveTexture(GL_TEXTURE0);
// 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);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
glUniform1i(u_source, 0);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 6);
profile_incstat("Render.num_drawcalls", +1);
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));
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d);
glUniform1i(u_source, 0);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
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->cascade_index < NUM_SHADOW_CASCADES - 1) {
s->cascade_index++;
s->step = 0;
return false;
}
shadowmap_blur_pcf(s, s->light_step);
}
s->step = 0;
s->light_step++;
s->cascade_index = 0;
return true;
}
bool shadowmap_step(shadowmap_t *s) {
int max_steps = s->shadow_technique == 0xffff ? 1 : s->shadow_technique == SHADOW_PCF ? 1 : 6;
if (s->step >= max_steps) {
if (shadowmap_step_finish(s)) {
return false;
} else {
return true;
}
}
unsigned texture_width = s->shadow_technique == SHADOW_VSM ? s->vsm_texture_width : s->pcf_texture_width;
glViewport(0, 0, texture_width, texture_width);
s->step++;
s->skip_render = false;
@ -1756,12 +2009,12 @@ bool shadowmap_step(shadowmap_t *s) {
static inline
void shadowmap_clear_fbo() {
glClearColor(0, 0, 0, 0);
glClearColor(1, 1, 1, 1);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void shadowmap_light(shadowmap_t *s, light_t *l) {
void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view) {
if (l->cast_shadows) {
int step = s->step - 1;
@ -1770,18 +2023,29 @@ void shadowmap_light(shadowmap_t *s, light_t *l) {
} else if (l->type == LIGHT_SPOT) {
shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_DIRECTIONAL) {
shadowmap_light_directional(s, l, step);
shadowmap_light_directional(s, l, step, cam_fov, cam_view);
}
if (s->skip_render) {
return;
}
if (s->maps[s->light_step].shadow_technique != l->shadow_technique) {
// 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);
}
}
s->maps[s->light_step].shadow_technique = l->shadow_technique;
ASSERT(s->lights_pushed == 0);
s->lights_pushed++;
if (l->type == LIGHT_DIRECTIONAL) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d);
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d[s->cascade_index]);
shadowmap_clear_fbo();
} else {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]);
@ -3975,7 +4239,11 @@ void model_set_uniforms(model_t m, int shader, mat44 mv, mat44 proj, mat44 view,
shader_bool("u_shadow_receiver", GL_TRUE);
for (int i = 0; i < MAX_LIGHTS; i++) {
shader_cubemap(va("shadowMap[%d]", i), m.shadow_map->maps[i].texture);
shader_texture_unit(va("shadowMap2D[%d]", i), m.shadow_map->maps[i].texture_2d, texture_unit());
for (int j = 0; j < NUM_SHADOW_CASCADES; j++) {
shader_texture_unit(va("shadowMap2D[%d]", i * NUM_SHADOW_CASCADES + j), m.shadow_map->maps[i].texture_2d[j], texture_unit());
shader_float(va("u_cascade_splits[%d]", j), m.shadow_map->cascade_splits[j]);
shader_float(va("u_cascade_distances[%d]", j), m.shadow_map->cascade_distances[j]);
}
}
} else {
shader_bool("u_shadow_receiver", GL_FALSE);
@ -4603,6 +4871,7 @@ void model_set_renderstates(model_t *m) {
shadow_rs->cull_face_enabled = 1;
shadow_rs->cull_face_mode = GL_BACK;
shadow_rs->front_face = GL_CW;
shadow_rs->depth_clamp_enabled = 1;
}
// Lightmap pass

View File

@ -66,8 +66,11 @@ typedef struct renderstate_t {
// Scissor test
bool scissor_test_enabled;
// bool Seamless Cubemap
// Seamless cubemap
bool seamless_cubemap;
// Depth clamp
bool depth_clamp_enabled;
} renderstate_t;
API renderstate_t renderstate();
@ -296,6 +299,8 @@ enum SHADOW_TECHNIQUE {
SHADOW_PCF,
};
#define NUM_SHADOW_CASCADES 4
typedef struct light_t {
char type;
vec3 diffuse, specular, ambient;
@ -313,7 +318,7 @@ typedef struct light_t {
unsigned shadow_technique;
float shadow_distance;
float shadow_bias;
mat44 shadow_matrix;
mat44 shadow_matrix[NUM_SHADOW_CASCADES];
// internals
bool cached; //< used by scene to invalidate cached light data
@ -346,18 +351,26 @@ API void light_update(unsigned num_lights, light_t *lv);
typedef struct shadowmap_t {
mat44 V;
mat44 PV;
int texture_width;
int vsm_texture_width;
int pcf_texture_width;
int step;
int light_step;
int cascade_index;
unsigned shadow_technique;
float cascade_splits[NUM_SHADOW_CASCADES];
float cascade_distances[NUM_SHADOW_CASCADES];
bool blur_pcf;
float blur_scale;
// signals
bool skip_render;
int lights_pushed;
struct {
unsigned shadow_technique;
handle fbos[6], texture, depth_texture;
handle fbo_2d, texture_2d, depth_texture_2d;
handle fbo_2d[NUM_SHADOW_CASCADES], texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES];
handle blur_fbo_2d, blur_texture_2d;
} maps[MAX_LIGHTS];
handle saved_fb;
@ -365,12 +378,12 @@ typedef struct shadowmap_t {
int saved_vp[4];
} shadowmap_t;
API shadowmap_t shadowmap(int texture_width); // = 1024
API shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width); // = 512, 4096
API void shadowmap_destroy(shadowmap_t *s);
API void shadowmap_begin(shadowmap_t *s);
API bool shadowmap_step(shadowmap_t *s);
API void shadowmap_light(shadowmap_t *s, light_t *l); //< can be called once per shadowmap_step
API void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view); //< can be called once per shadowmap_step
API void shadowmap_end(shadowmap_t *s);
// -----------------------------------------------------------------------------

View File

@ -49,6 +49,7 @@ typedef struct object_t {
aabb bounds;
unsigned billboard; // [0..7] x(4),y(2),z(1) masks
bool disable_frustum_check;
bool cast_shadows;
// internal states
array(handle) old_texture_ids;
@ -83,6 +84,7 @@ enum SCENE_FLAGS {
SCENE_BACKGROUND = 4,
SCENE_FOREGROUND = 8,
SCENE_UPDATE_SH_COEF = 16,
SCENE_CAST_SHADOWS = 32,
};
typedef struct scene_t {
@ -92,6 +94,7 @@ typedef struct scene_t {
// special objects below:
skybox_t skybox;
int u_coefficients_sh;
shadowmap_t shadowmap;
} scene_t;
API scene_t* scene_push();

View File

@ -16921,6 +16921,9 @@ renderstate_t renderstate() {
// Enable seamless cubemap by default
state.seamless_cubemap = GL_TRUE;
// Disable depth clamp by default
state.depth_clamp_enabled = GL_FALSE;
return state;
}
@ -17034,6 +17037,13 @@ void renderstate_apply(const renderstate_t *state) {
} else {
glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
// Apply depth clamp
if (state->depth_clamp_enabled) {
glEnable(GL_DEPTH_CLAMP);
} else {
glDisable(GL_DEPTH_CLAMP);
}
}
}
@ -18271,8 +18281,9 @@ light_t light() {
l.specularPower = 32.f;
l.innerCone = 0.85f;// 31 deg
l.outerCone = 0.9f; // 25 deg
l.cast_shadows = true;
l.shadow_distance = 100.0f;
l.shadow_bias = 0.5f;
l.shadow_bias = 0.01f;
return l;
}
@ -18351,7 +18362,9 @@ void light_update(unsigned num_lights, light_t *lv) {
shader_float(va("u_lights[%d].quadratic", i), lv[i].falloff.quadratic);
shader_float(va("u_lights[%d].innerCone", i), lv[i].innerCone);
shader_float(va("u_lights[%d].outerCone", i), lv[i].outerCone);
shader_mat44(va("u_lights[%d].shadow_matrix", i), lv[i].shadow_matrix);
for (int j = 0; j < NUM_SHADOW_CASCADES; j++) {
shader_mat44(va("u_lights[%d].shadow_matrix[%d]", i, j), lv[i].shadow_matrix[j]);
}
}
}
@ -18359,10 +18372,14 @@ void light_update(unsigned num_lights, light_t *lv) {
// -----------------------------------------------------------------------------
// shadowmaps
static inline
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
static inline void
shadowmap_init_caster_vsm(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) {
return;
}
// Create a cubemap color texture
glGenTextures(1, &s->maps[light_index].texture);
glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].texture);
@ -18403,70 +18420,147 @@ shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
if (GL_FRAMEBUFFER_COMPLETE != result) {
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
}
// #if is(ems)
// GLenum nones[] = { GL_NONE };
// glDrawBuffers(1, nones);
// glReadBuffer(GL_NONE);
// #else
// glDrawBuffer(GL_NONE);
// glReadBuffer(GL_NONE);
// #endif
}
}
static inline void
shadowmap_init_caster_pcf(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]) {
return;
}
// Initialise shadow map 2D
glGenTextures(1, &s->maps[light_index].texture_2d);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].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_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
glGenTextures(1, &s->maps[light_index].texture_2d[i]);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
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_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glGenTextures(1, &s->maps[light_index].depth_texture_2d);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glGenTextures(1, &s->maps[light_index].depth_texture_2d[i]);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 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_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
}
glBindTexture(GL_TEXTURE_2D, 0);
glGenFramebuffers(1, &s->maps[light_index].fbo_2d);
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_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);
}
}
shadowmap_t shadowmap(int texture_width) { // = 1024
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_t shadowmap(int vsm_texture_width, int pcf_texture_width) { // = 512, 4096
shadowmap_t s = {0};
s.texture_width = texture_width;
s.vsm_texture_width = vsm_texture_width;
s.pcf_texture_width = pcf_texture_width;
s.saved_fb = 0;
s.blur_pcf = true;
s.blur_scale = 0.5f;
s.cascade_splits[0] = 0.1f;
s.cascade_splits[1] = 0.3f;
s.cascade_splits[2] = 0.5f;
s.cascade_splits[3] = 1.0f;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb);
#if 0
for (int i = 0; i < MAX_LIGHTS; i++) {
shadowmap_init_caster(&s, i, texture_width);
shadowmap_init_caster(&s, i);
}
#else
for (int i = 0; i < MAX_LIGHTS; i++) {
s.maps[i].shadow_technique = 0xFFFF;
}
#endif
glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb);
return s;
}
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->maps[light_index].texture) {
glDeleteTextures(1, &s->maps[light_index].texture);
s->maps[light_index].texture = 0;
}
if (s->maps[light_index].depth_texture) {
glDeleteTextures(1, &s->maps[light_index].depth_texture);
s->maps[light_index].depth_texture = 0;
}
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;
}
if (s->maps[light_index].depth_texture_2d[i]) {
glDeleteTextures(1, &s->maps[light_index].depth_texture_2d[i]);
s->maps[light_index].depth_texture_2d[i] = 0;
}
}
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;
}
}
void shadowmap_destroy(shadowmap_t *s) {
for (int i = 0; i < MAX_LIGHTS; i++) {
glDeleteFramebuffers(6, s->maps[i].fbos);
glDeleteTextures(1, &s->maps[i].texture);
glDeleteTextures(1, &s->maps[i].depth_texture);
glDeleteFramebuffers(1, &s->maps[i].fbo_2d);
glDeleteTextures(1, &s->maps[i].texture_2d);
glDeleteTextures(1, &s->maps[i].depth_texture_2d);
shadowmap_destroy_light(s, i);
}
shadowmap_t z = {0};
*s = z;
@ -18481,6 +18575,7 @@ void shadowmap_begin(shadowmap_t *s) {
s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
s->step = 0;
s->light_step = 0;
s->cascade_index = 0;
active_shadowmap = s;
}
@ -18504,48 +18599,206 @@ static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) {
s->shadow_technique = l->shadow_technique = SHADOW_VSM;
}
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir) {
static array(vec3) frustum_corners = 0;
static inline
void shadowmap_light_directional_calc_frustum_corners(mat44 cam_proj, mat44 cam_view) {
mat44 PV; multiply44x2(PV, cam_proj, cam_view);
mat44 inverse_view_proj; invert44(inverse_view_proj, PV);
array_resize(frustum_corners, 0);
for (unsigned x = 0; x < 2; x++) {
for (unsigned y = 0; y < 2; y++) {
for (unsigned z = 0; z < 2; z++) {
vec4 corner = {
x * 2.0f - 1.0f,
y * 2.0f - 1.0f,
z * 2.0f - 1.0f,
1.0f
};
vec4 world_corner = transform444(inverse_view_proj, corner);
world_corner = scale4(world_corner, 1.0f / world_corner.w);
array_push(frustum_corners, vec3(world_corner.x, world_corner.y, world_corner.z));
}
}
}
}
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir, float cam_fov, mat44 cam_view) {
if (dir != 0) {
s->skip_render = true;
return;
}
mat44 P, V, PV;
l->shadow_distance = 25.0f;
ortho44(P,
-l->shadow_distance/2.0, l->shadow_distance/2.0,
-l->shadow_distance/2.0, l->shadow_distance/2.0,
l->shadow_bias, l->shadow_distance);
float far_plane = 0.0f;
float near_plane = 0.0f;
if (s->cascade_index == 0) {
near_plane = l->shadow_bias;
far_plane = l->shadow_distance * s->cascade_splits[0];
} else if (s->cascade_index < NUM_SHADOW_CASCADES - 1) {
near_plane = l->shadow_distance * s->cascade_splits[s->cascade_index-1];
far_plane = l->shadow_distance * s->cascade_splits[s->cascade_index];
} else {
near_plane = l->shadow_distance * s->cascade_splits[NUM_SHADOW_CASCADES-1];
far_plane = l->shadow_distance;
}
mat44 proj; perspective44(proj, cam_fov, window_width() / (float)window_height(), near_plane, far_plane);
shadowmap_light_directional_calc_frustum_corners(proj, cam_view);
vec3 center = {0,0,0};
for (unsigned i = 0; i < array_count(frustum_corners); i++) {
center = add3(center, frustum_corners[i]);
}
center = scale3(center, 1.0f / array_count(frustum_corners));
s->cascade_distances[s->cascade_index] = far_plane;
float minX = FLT_MAX, maxX = FLT_MIN;
float minY = FLT_MAX, maxY = FLT_MIN;
float minZ = FLT_MAX, maxZ = FLT_MIN;
mat44 V;
vec3 lightDir = norm3(l->dir);
vec3 up = vec3(0, 1, 0);
// Ensure up vector is not parallel to light direction
if (fabs(dot3(lightDir, up)) > 0.99f) {
up = vec3(0, 0, 1);
lookat44(V, sub3(center, lightDir), center, up);
for (unsigned i = 0; i < array_count(frustum_corners); i++) {
vec3 corner = frustum_corners[i];
corner = transform344(V, corner);
minX = min(minX, corner.x);
maxX = max(maxX, corner.x);
minY = min(minY, corner.y);
maxY = max(maxY, corner.y);
minZ = min(minZ, corner.z);
maxZ = max(maxZ, corner.z);
}
vec3 center = vec3(0, 0, 0);
vec3 lightPos = sub3(center, scale3(lightDir, l->shadow_distance*0.5f));
lookat44(V, lightPos, center, up);
float zMult = 10.0f;
// if (minZ < 0) {
// minZ *= zMult;
// } else {
// minZ /= zMult;
// }
// if (maxZ < 0) {
// maxZ /= zMult;
// } else {
// maxZ *= zMult;
// }
mat44 P, PV;
ortho44(P,
minX, maxX,
minY, maxY,
minZ, maxZ);
multiply44x2(PV, P, V);
copy44(s->V, V);
copy44(s->PV, PV);
copy44(l->shadow_matrix, PV);
copy44(l->shadow_matrix[s->cascade_index], PV);
s->shadow_technique = l->shadow_technique = SHADOW_PCF;
}
bool shadowmap_step(shadowmap_t *s) {
if (s->step >= 6) {
s->step = 0;
s->light_step++;
return false;
static inline
void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
if (!s->blur_pcf) {
return;
}
glViewport(0, 0, s->texture_width, s->texture_width);
float blur_scale = 1.999 * (1 - s->blur_scale) + 0.001;
// blur_scale = 0.1f;
static renderstate_t rs;
static int program = -1, vao = -1, u_scale = -1, u_source = -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.glsl");
const char* fs = vfs_read("shaders/fs_shadow_blur.glsl");
program = shader(vs, fs, "", "fragcolor", NULL);
u_scale = glGetUniformLocation(program, "ScaleU");
u_source = glGetUniformLocation(program, "textureSource");
glGenVertexArrays(1, &vao);
}
renderstate_apply(&rs);
glViewport(0, 0, s->pcf_texture_width, s->pcf_texture_width);
unsigned oldprog = last_shader;
glUseProgram(program);
glBindVertexArray(vao);
glActiveTexture(GL_TEXTURE0);
// 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);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
glUniform1i(u_source, 0);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 6);
profile_incstat("Render.num_drawcalls", +1);
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));
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d);
glUniform1i(u_source, 0);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
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->cascade_index < NUM_SHADOW_CASCADES - 1) {
s->cascade_index++;
s->step = 0;
return false;
}
shadowmap_blur_pcf(s, s->light_step);
}
s->step = 0;
s->light_step++;
s->cascade_index = 0;
return true;
}
bool shadowmap_step(shadowmap_t *s) {
int max_steps = s->shadow_technique == 0xffff ? 1 : s->shadow_technique == SHADOW_PCF ? 1 : 6;
if (s->step >= max_steps) {
if (shadowmap_step_finish(s)) {
return false;
} else {
return true;
}
}
unsigned texture_width = s->shadow_technique == SHADOW_VSM ? s->vsm_texture_width : s->pcf_texture_width;
glViewport(0, 0, texture_width, texture_width);
s->step++;
s->skip_render = false;
@ -18555,12 +18808,12 @@ bool shadowmap_step(shadowmap_t *s) {
static inline
void shadowmap_clear_fbo() {
glClearColor(0, 0, 0, 0);
glClearColor(1, 1, 1, 1);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void shadowmap_light(shadowmap_t *s, light_t *l) {
void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view) {
if (l->cast_shadows) {
int step = s->step - 1;
@ -18569,18 +18822,29 @@ void shadowmap_light(shadowmap_t *s, light_t *l) {
} else if (l->type == LIGHT_SPOT) {
shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_DIRECTIONAL) {
shadowmap_light_directional(s, l, step);
shadowmap_light_directional(s, l, step, cam_fov, cam_view);
}
if (s->skip_render) {
return;
}
if (s->maps[s->light_step].shadow_technique != l->shadow_technique) {
// 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);
}
}
s->maps[s->light_step].shadow_technique = l->shadow_technique;
ASSERT(s->lights_pushed == 0);
s->lights_pushed++;
if (l->type == LIGHT_DIRECTIONAL) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d);
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d[s->cascade_index]);
shadowmap_clear_fbo();
} else {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]);
@ -20774,7 +21038,11 @@ void model_set_uniforms(model_t m, int shader, mat44 mv, mat44 proj, mat44 view,
shader_bool("u_shadow_receiver", GL_TRUE);
for (int i = 0; i < MAX_LIGHTS; i++) {
shader_cubemap(va("shadowMap[%d]", i), m.shadow_map->maps[i].texture);
shader_texture_unit(va("shadowMap2D[%d]", i), m.shadow_map->maps[i].texture_2d, texture_unit());
for (int j = 0; j < NUM_SHADOW_CASCADES; j++) {
shader_texture_unit(va("shadowMap2D[%d]", i * NUM_SHADOW_CASCADES + j), m.shadow_map->maps[i].texture_2d[j], texture_unit());
shader_float(va("u_cascade_splits[%d]", j), m.shadow_map->cascade_splits[j]);
shader_float(va("u_cascade_distances[%d]", j), m.shadow_map->cascade_distances[j]);
}
}
} else {
shader_bool("u_shadow_receiver", GL_FALSE);
@ -21402,6 +21670,7 @@ void model_set_renderstates(model_t *m) {
shadow_rs->cull_face_enabled = 1;
shadow_rs->cull_face_mode = GL_BACK;
shadow_rs->front_face = GL_CW;
shadow_rs->depth_clamp_enabled = 1;
}
// Lightmap pass

View File

@ -3101,8 +3101,11 @@ typedef struct renderstate_t {
// Scissor test
bool scissor_test_enabled;
// bool Seamless Cubemap
// Seamless cubemap
bool seamless_cubemap;
// Depth clamp
bool depth_clamp_enabled;
} renderstate_t;
API renderstate_t renderstate();
@ -3331,6 +3334,8 @@ enum SHADOW_TECHNIQUE {
SHADOW_PCF,
};
#define NUM_SHADOW_CASCADES 4
typedef struct light_t {
char type;
vec3 diffuse, specular, ambient;
@ -3348,7 +3353,7 @@ typedef struct light_t {
unsigned shadow_technique;
float shadow_distance;
float shadow_bias;
mat44 shadow_matrix;
mat44 shadow_matrix[NUM_SHADOW_CASCADES];
// internals
bool cached; //< used by scene to invalidate cached light data
@ -3381,18 +3386,26 @@ API void light_update(unsigned num_lights, light_t *lv);
typedef struct shadowmap_t {
mat44 V;
mat44 PV;
int texture_width;
int vsm_texture_width;
int pcf_texture_width;
int step;
int light_step;
int cascade_index;
unsigned shadow_technique;
float cascade_splits[NUM_SHADOW_CASCADES];
float cascade_distances[NUM_SHADOW_CASCADES];
bool blur_pcf;
float blur_scale;
// signals
bool skip_render;
int lights_pushed;
struct {
unsigned shadow_technique;
handle fbos[6], texture, depth_texture;
handle fbo_2d, texture_2d, depth_texture_2d;
handle fbo_2d[NUM_SHADOW_CASCADES], texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES];
handle blur_fbo_2d, blur_texture_2d;
} maps[MAX_LIGHTS];
handle saved_fb;
@ -3400,12 +3413,12 @@ typedef struct shadowmap_t {
int saved_vp[4];
} shadowmap_t;
API shadowmap_t shadowmap(int texture_width); // = 1024
API shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width); // = 512, 4096
API void shadowmap_destroy(shadowmap_t *s);
API void shadowmap_begin(shadowmap_t *s);
API bool shadowmap_step(shadowmap_t *s);
API void shadowmap_light(shadowmap_t *s, light_t *l); //< can be called once per shadowmap_step
API void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view); //< can be called once per shadowmap_step
API void shadowmap_end(shadowmap_t *s);
// -----------------------------------------------------------------------------
@ -4060,6 +4073,7 @@ typedef struct object_t {
aabb bounds;
unsigned billboard; // [0..7] x(4),y(2),z(1) masks
bool disable_frustum_check;
bool cast_shadows;
// internal states
array(handle) old_texture_ids;
@ -4094,6 +4108,7 @@ enum SCENE_FLAGS {
SCENE_BACKGROUND = 4,
SCENE_FOREGROUND = 8,
SCENE_UPDATE_SH_COEF = 16,
SCENE_CAST_SHADOWS = 32,
};
typedef struct scene_t {
@ -4103,6 +4118,7 @@ typedef struct scene_t {
// special objects below:
skybox_t skybox;
int u_coefficients_sh;
shadowmap_t shadowmap;
} scene_t;
API scene_t* scene_push();