CSM shadowmapping
parent
d219cb88a3
commit
7edd72015c
22
bind/v4k.lua
22
bind/v4k.lua
|
@ -1020,6 +1020,7 @@ typedef struct renderstate_t {
|
||||||
unsigned polygon_mode_draw;
|
unsigned polygon_mode_draw;
|
||||||
bool scissor_test_enabled;
|
bool scissor_test_enabled;
|
||||||
bool seamless_cubemap;
|
bool seamless_cubemap;
|
||||||
|
bool depth_clamp_enabled;
|
||||||
} renderstate_t;
|
} renderstate_t;
|
||||||
renderstate_t renderstate();
|
renderstate_t renderstate();
|
||||||
bool renderstate_compare(const renderstate_t *stateA, const renderstate_t *stateB);
|
bool renderstate_compare(const renderstate_t *stateA, const renderstate_t *stateB);
|
||||||
|
@ -1160,7 +1161,7 @@ typedef struct light_t {
|
||||||
unsigned shadow_technique;
|
unsigned shadow_technique;
|
||||||
float shadow_distance;
|
float shadow_distance;
|
||||||
float shadow_bias;
|
float shadow_bias;
|
||||||
mat44 shadow_matrix;
|
mat44 shadow_matrix[4];
|
||||||
bool cached;
|
bool cached;
|
||||||
} light_t;
|
} light_t;
|
||||||
light_t light();
|
light_t light();
|
||||||
|
@ -1178,25 +1179,33 @@ typedef struct light_t {
|
||||||
typedef struct shadowmap_t {
|
typedef struct shadowmap_t {
|
||||||
mat44 V;
|
mat44 V;
|
||||||
mat44 PV;
|
mat44 PV;
|
||||||
int texture_width;
|
int vsm_texture_width;
|
||||||
|
int pcf_texture_width;
|
||||||
int step;
|
int step;
|
||||||
int light_step;
|
int light_step;
|
||||||
|
int cascade_index;
|
||||||
unsigned shadow_technique;
|
unsigned shadow_technique;
|
||||||
|
float cascade_splits[4];
|
||||||
|
float cascade_distances[4];
|
||||||
|
bool blur_pcf;
|
||||||
|
float blur_scale;
|
||||||
bool skip_render;
|
bool skip_render;
|
||||||
int lights_pushed;
|
int lights_pushed;
|
||||||
struct {
|
struct {
|
||||||
|
unsigned shadow_technique;
|
||||||
handle fbos[6], texture, depth_texture;
|
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];
|
} 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 texture_width);
|
shadowmap_t shadowmap(int vsm_texture_width, int pcf_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);
|
||||||
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);
|
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(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);
|
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;
|
aabb bounds;
|
||||||
unsigned billboard;
|
unsigned billboard;
|
||||||
bool disable_frustum_check;
|
bool disable_frustum_check;
|
||||||
|
bool cast_shadows;
|
||||||
handle* old_texture_ids;
|
handle* old_texture_ids;
|
||||||
texture_t* old_textures;
|
texture_t* old_textures;
|
||||||
float distance;
|
float distance;
|
||||||
|
@ -1663,12 +1673,14 @@ enum SCENE_FLAGS {
|
||||||
SCENE_BACKGROUND = 4,
|
SCENE_BACKGROUND = 4,
|
||||||
SCENE_FOREGROUND = 8,
|
SCENE_FOREGROUND = 8,
|
||||||
SCENE_UPDATE_SH_COEF = 16,
|
SCENE_UPDATE_SH_COEF = 16,
|
||||||
|
SCENE_CAST_SHADOWS = 32,
|
||||||
};
|
};
|
||||||
typedef struct scene_t {
|
typedef struct scene_t {
|
||||||
object_t* objs;
|
object_t* objs;
|
||||||
light_t* lights;
|
light_t* lights;
|
||||||
skybox_t skybox;
|
skybox_t skybox;
|
||||||
int u_coefficients_sh;
|
int u_coefficients_sh;
|
||||||
|
shadowmap_t shadowmap;
|
||||||
} scene_t;
|
} scene_t;
|
||||||
scene_t* scene_push();
|
scene_t* scene_push();
|
||||||
void scene_pop();
|
void scene_pop();
|
||||||
|
|
|
@ -32,21 +32,26 @@ int main(int argc, char** argv) {
|
||||||
light_t lit = light(); {
|
light_t lit = light(); {
|
||||||
lit.type = LIGHT_POINT;
|
lit.type = LIGHT_POINT;
|
||||||
lit.cast_shadows = true;
|
lit.cast_shadows = true;
|
||||||
// lit.shadow_distance = 3.0f;
|
// lit.shadow_distance = 5.0f;
|
||||||
|
// lit.falloff.linear = 3.0f;
|
||||||
}
|
}
|
||||||
light_t lit2 = light(); {
|
light_t lit2 = light(); {
|
||||||
lit2.type = LIGHT_POINT;
|
lit2.type = LIGHT_POINT;
|
||||||
lit2.cast_shadows = true;
|
lit2.cast_shadows = true;
|
||||||
lit2.diffuse = vec3(1, 0.7, 0.8);
|
lit2.diffuse = vec3(1, 0.7, 0.8);
|
||||||
|
// lit2.shadow_distance = 5.0f;
|
||||||
|
// lit2.falloff.linear = 3.0f;
|
||||||
}
|
}
|
||||||
light_t lit3 = light(); {
|
light_t lit3 = light(); {
|
||||||
lit3.type = LIGHT_SPOT;
|
lit3.type = LIGHT_SPOT;
|
||||||
lit3.cast_shadows = true;
|
lit3.cast_shadows = true;
|
||||||
|
// lit3.shadow_distance = 5.0f;
|
||||||
lit3.diffuse = vec3(1, 0.7, 0.8);
|
lit3.diffuse = vec3(1, 0.7, 0.8);
|
||||||
}
|
}
|
||||||
light_t lit4 = light(); {
|
light_t lit4 = light(); {
|
||||||
lit4.type = LIGHT_DIRECTIONAL;
|
lit4.type = LIGHT_DIRECTIONAL;
|
||||||
lit4.cast_shadows = true;
|
lit4.cast_shadows = true;
|
||||||
|
lit4.shadow_distance = 2000.0f;
|
||||||
lit4.diffuse = vec3(1, 0.7, 0.8);
|
lit4.diffuse = vec3(1, 0.7, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +86,7 @@ int main(int argc, char** argv) {
|
||||||
if( !initialized ) {
|
if( !initialized ) {
|
||||||
initialized = 1;
|
initialized = 1;
|
||||||
sky = skybox(flag("--mie") ? 0 : SKY_DIRS[SKY_DIR], 0);
|
sky = skybox(flag("--mie") ? 0 : SKY_DIRS[SKY_DIR], 0);
|
||||||
sm = shadowmap(1024);
|
sm = shadowmap(512, 2048);
|
||||||
mdl = model(OBJ_MDLS[OBJ_MDL], 0);
|
mdl = model(OBJ_MDLS[OBJ_MDL], 0);
|
||||||
shader_bind(mdl.program);
|
shader_bind(mdl.program);
|
||||||
cubemap_sh_shader(&sky.cubemap);
|
cubemap_sh_shader(&sky.cubemap);
|
||||||
|
@ -100,7 +105,7 @@ int main(int argc, char** argv) {
|
||||||
enum {
|
enum {
|
||||||
POINT, SPOT, DIR, ALL
|
POINT, SPOT, DIR, ALL
|
||||||
};
|
};
|
||||||
static unsigned mode = POINT;
|
static unsigned mode = DIR;
|
||||||
|
|
||||||
if (input_down(KEY_1)) mode = POINT;
|
if (input_down(KEY_1)) mode = POINT;
|
||||||
if (input_down(KEY_2)) mode = SPOT;
|
if (input_down(KEY_2)) mode = SPOT;
|
||||||
|
@ -135,6 +140,7 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode == DIR) {
|
if (mode == DIR) {
|
||||||
|
lights[0].pos = cam.position;
|
||||||
lights[0].dir = vec3(1,-1,-1);
|
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++) {
|
for (int i = 0; i < array_count(lights); i++) {
|
||||||
while (shadowmap_step(&sm)) {
|
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);
|
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);
|
mat44 mvp; multiply44x2(mvp, cam.proj, cam.view);
|
||||||
{
|
{
|
||||||
skybox_render(&sky, cam.proj, cam.view);
|
skybox_render(&sky, cam.proj, cam.view);
|
||||||
|
|
||||||
shader_bind(mdl.program);
|
shader_bind(mdl.program);
|
||||||
shader_int("u_textured", false);
|
|
||||||
light_update(array_count(lights), lights);
|
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_shadow(&mdl, &sm);
|
||||||
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
|
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
uniform int u_num_lights;
|
uniform int u_num_lights;
|
||||||
|
|
||||||
|
#define NUM_SHADOW_CASCADES 4
|
||||||
|
|
||||||
struct light_t {
|
struct light_t {
|
||||||
int type;
|
int type;
|
||||||
vec3 diffuse;
|
vec3 diffuse;
|
||||||
|
@ -23,7 +25,7 @@ struct light_t {
|
||||||
float quadratic;
|
float quadratic;
|
||||||
|
|
||||||
// shadows
|
// shadows
|
||||||
mat4 shadow_matrix;
|
mat4 shadow_matrix[NUM_SHADOW_CASCADES];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_LIGHTS 16
|
#define MAX_LIGHTS 16
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
in vec4 vpeye;
|
in vec4 vpeye;
|
||||||
in vec4 vneye;
|
in vec4 vneye;
|
||||||
uniform bool u_shadow_receiver;
|
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 samplerCube shadowMap[MAX_LIGHTS];
|
||||||
uniform sampler2D shadowMap2D[MAX_LIGHTS];
|
uniform sampler2D shadowMap2D[MAX_LIGHTS * NUM_SHADOW_CASCADES];
|
||||||
|
|
||||||
//// 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) {
|
||||||
|
@ -22,44 +24,61 @@ float shadow_vsm(float distance, vec3 dir, int light_index) {
|
||||||
|
|
||||||
float d = distance - moments.x;
|
float d = distance - moments.x;
|
||||||
float p_max = variance / (variance + d*d);
|
float p_max = variance / (variance + d*d);
|
||||||
|
|
||||||
return p_max;
|
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
|
// Perform perspective divide
|
||||||
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||||
|
|
||||||
// Transform to [0,1] range
|
// Transform to [0,1] range
|
||||||
projCoords = projCoords * 0.5 + 0.5;
|
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;
|
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);
|
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
|
// PCF
|
||||||
float shadow = 0.0;
|
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 x = -1; x <= 1; ++x)
|
||||||
{
|
{
|
||||||
for(int y = -1; y <= 1; ++y)
|
for(int y = -1; y <= 1; ++y)
|
||||||
{
|
{
|
||||||
float pcfDepth = texture(shadowMap2D[light_index], projCoords.xy + vec2(x, y) * texelSize).r;
|
float pcfDepth = texture(shadowMap2D[index], projCoords.xy + vec2(x, y) * texelSize).r;
|
||||||
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
|
shadow += projCoords.z - bias > pcfDepth ? 1.0 : 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shadow /= 9.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;
|
return 1.0 - shadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,22 +93,14 @@ vec4 shadowmap(in vec4 peye, in vec4 neye) {
|
||||||
|
|
||||||
if (light.type == LIGHT_DIRECTIONAL) {
|
if (light.type == LIGHT_DIRECTIONAL) {
|
||||||
total_casters++;
|
total_casters++;
|
||||||
vec4 frag_pos = light.shadow_matrix * vec4(v_position_ws, 1.0);
|
factor += shadow_pcf(-peye.z, light.dir, i);
|
||||||
factor += shadow_pcf(frag_pos, light.dir, i);
|
} else if (light.type == LIGHT_POINT || light.type == LIGHT_SPOT) {
|
||||||
} 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) {
|
|
||||||
total_casters++;
|
total_casters++;
|
||||||
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;
|
||||||
vec4 sc = inv_view * vec4(dir, 0.0);
|
vec4 sc = inv_view * vec4(dir, 0.0);
|
||||||
factor += shadow_vsm(length(dir), -sc.xyz, i);
|
factor += shadow_vsm(length(dir), -sc.xyz, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowFactor += factor;
|
shadowFactor += factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
uniform sampler2D textureSource;
|
uniform sampler2D textureSource;
|
||||||
uniform vec2 ScaleU;
|
uniform vec2 ScaleU;
|
||||||
|
|
||||||
in vec2 Texcoord;
|
in vec2 uv;
|
||||||
out vec4 outColor;
|
out vec4 fragcolor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 color = vec4(0.0);
|
vec4 color = vec4(0.0);
|
||||||
color += texture( textureSource, Texcoord.st + vec2( -3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;
|
color += texture( textureSource, uv.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, uv.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, uv.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, uv.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, uv.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, uv.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;
|
color += texture( textureSource, uv.st + vec2( 3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;
|
||||||
outColor = vec4(color.xyz, 1.0);
|
fragcolor = vec4(color.xyz, 1.0);
|
||||||
}
|
}
|
|
@ -8,15 +8,15 @@ uniform int shadow_technique;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
if (shadow_technique == SHADOW_VSM) {
|
if (shadow_technique == SHADOW_VSM) {
|
||||||
float depth = length(v_position) / 20;
|
float depth = length(v_position) / 20;
|
||||||
|
|
||||||
float moment1 = depth;
|
float moment1 = depth;
|
||||||
float moment2 = depth * depth;
|
float moment2 = depth * depth;
|
||||||
|
|
||||||
float dx = dFdx(depth);
|
float dx = dFdx(depth);
|
||||||
float dy = dFdy(depth);
|
float dy = dFdy(depth);
|
||||||
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_PCF) {
|
||||||
fragcolor = vec4(vec3(gl_FragCoord.z), 1.0);
|
fragcolor = vec4(vec3(gl_FragCoord.z), 1.0);
|
||||||
|
|
|
@ -1,8 +1,26 @@
|
||||||
in vec3 position;
|
out vec2 uv;
|
||||||
in vec2 texcoord;
|
|
||||||
out vec2 Texcoord;
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(position, 1.0);
|
// Calculate vertex position based on gl_VertexID
|
||||||
Texcoord = texcoord;
|
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];
|
||||||
}
|
}
|
|
@ -17034,8 +17034,11 @@ typedef struct renderstate_t {
|
||||||
// Scissor test
|
// Scissor test
|
||||||
bool scissor_test_enabled;
|
bool scissor_test_enabled;
|
||||||
|
|
||||||
// bool Seamless Cubemap
|
// Seamless cubemap
|
||||||
bool seamless_cubemap;
|
bool seamless_cubemap;
|
||||||
|
|
||||||
|
// Depth clamp
|
||||||
|
bool depth_clamp_enabled;
|
||||||
} renderstate_t;
|
} renderstate_t;
|
||||||
|
|
||||||
API renderstate_t renderstate();
|
API renderstate_t renderstate();
|
||||||
|
@ -17264,6 +17267,8 @@ enum SHADOW_TECHNIQUE {
|
||||||
SHADOW_PCF,
|
SHADOW_PCF,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define NUM_SHADOW_CASCADES 4
|
||||||
|
|
||||||
typedef struct light_t {
|
typedef struct light_t {
|
||||||
char type;
|
char type;
|
||||||
vec3 diffuse, specular, ambient;
|
vec3 diffuse, specular, ambient;
|
||||||
|
@ -17281,7 +17286,7 @@ typedef struct light_t {
|
||||||
unsigned shadow_technique;
|
unsigned shadow_technique;
|
||||||
float shadow_distance;
|
float shadow_distance;
|
||||||
float shadow_bias;
|
float shadow_bias;
|
||||||
mat44 shadow_matrix;
|
mat44 shadow_matrix[NUM_SHADOW_CASCADES];
|
||||||
|
|
||||||
// internals
|
// internals
|
||||||
bool cached; //< used by scene to invalidate cached light data
|
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 {
|
typedef struct shadowmap_t {
|
||||||
mat44 V;
|
mat44 V;
|
||||||
mat44 PV;
|
mat44 PV;
|
||||||
int texture_width;
|
int vsm_texture_width;
|
||||||
|
int pcf_texture_width;
|
||||||
int step;
|
int step;
|
||||||
int light_step;
|
int light_step;
|
||||||
|
int cascade_index;
|
||||||
unsigned shadow_technique;
|
unsigned shadow_technique;
|
||||||
|
float cascade_splits[NUM_SHADOW_CASCADES];
|
||||||
|
float cascade_distances[NUM_SHADOW_CASCADES];
|
||||||
|
bool blur_pcf;
|
||||||
|
float blur_scale;
|
||||||
|
|
||||||
// signals
|
// signals
|
||||||
bool skip_render;
|
bool skip_render;
|
||||||
int lights_pushed;
|
int lights_pushed;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
unsigned shadow_technique;
|
||||||
handle fbos[6], texture, depth_texture;
|
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];
|
} maps[MAX_LIGHTS];
|
||||||
|
|
||||||
handle saved_fb;
|
handle saved_fb;
|
||||||
|
@ -17333,12 +17346,12 @@ typedef struct shadowmap_t {
|
||||||
int saved_vp[4];
|
int saved_vp[4];
|
||||||
} shadowmap_t;
|
} 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_destroy(shadowmap_t *s);
|
||||||
|
|
||||||
API void shadowmap_begin(shadowmap_t *s);
|
API void shadowmap_begin(shadowmap_t *s);
|
||||||
API bool shadowmap_step(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);
|
API void shadowmap_end(shadowmap_t *s);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -17993,6 +18006,7 @@ typedef struct object_t {
|
||||||
aabb bounds;
|
aabb bounds;
|
||||||
unsigned billboard; // [0..7] x(4),y(2),z(1) masks
|
unsigned billboard; // [0..7] x(4),y(2),z(1) masks
|
||||||
bool disable_frustum_check;
|
bool disable_frustum_check;
|
||||||
|
bool cast_shadows;
|
||||||
|
|
||||||
// internal states
|
// internal states
|
||||||
array(handle) old_texture_ids;
|
array(handle) old_texture_ids;
|
||||||
|
@ -18027,6 +18041,7 @@ enum SCENE_FLAGS {
|
||||||
SCENE_BACKGROUND = 4,
|
SCENE_BACKGROUND = 4,
|
||||||
SCENE_FOREGROUND = 8,
|
SCENE_FOREGROUND = 8,
|
||||||
SCENE_UPDATE_SH_COEF = 16,
|
SCENE_UPDATE_SH_COEF = 16,
|
||||||
|
SCENE_CAST_SHADOWS = 32,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct scene_t {
|
typedef struct scene_t {
|
||||||
|
@ -18036,6 +18051,7 @@ typedef struct scene_t {
|
||||||
// special objects below:
|
// special objects below:
|
||||||
skybox_t skybox;
|
skybox_t skybox;
|
||||||
int u_coefficients_sh;
|
int u_coefficients_sh;
|
||||||
|
shadowmap_t shadowmap;
|
||||||
} scene_t;
|
} scene_t;
|
||||||
|
|
||||||
API scene_t* scene_push();
|
API scene_t* scene_push();
|
||||||
|
@ -381836,6 +381852,9 @@ renderstate_t renderstate() {
|
||||||
// Enable seamless cubemap by default
|
// Enable seamless cubemap by default
|
||||||
state.seamless_cubemap = GL_TRUE;
|
state.seamless_cubemap = GL_TRUE;
|
||||||
|
|
||||||
|
// Disable depth clamp by default
|
||||||
|
state.depth_clamp_enabled = GL_FALSE;
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381949,6 +381968,13 @@ void renderstate_apply(const renderstate_t *state) {
|
||||||
} else {
|
} else {
|
||||||
glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
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.specularPower = 32.f;
|
||||||
l.innerCone = 0.85f;// 31 deg
|
l.innerCone = 0.85f;// 31 deg
|
||||||
l.outerCone = 0.9f; // 25 deg
|
l.outerCone = 0.9f; // 25 deg
|
||||||
|
l.cast_shadows = true;
|
||||||
l.shadow_distance = 100.0f;
|
l.shadow_distance = 100.0f;
|
||||||
l.shadow_bias = 0.5f;
|
l.shadow_bias = 0.01f;
|
||||||
return l;
|
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].quadratic", i), lv[i].falloff.quadratic);
|
||||||
shader_float(va("u_lights[%d].innerCone", i), lv[i].innerCone);
|
shader_float(va("u_lights[%d].innerCone", i), lv[i].innerCone);
|
||||||
shader_float(va("u_lights[%d].outerCone", i), lv[i].outerCone);
|
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
|
// shadowmaps
|
||||||
|
|
||||||
static inline
|
static inline void
|
||||||
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
|
shadowmap_init_caster_vsm(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) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create a cubemap color texture
|
// Create a cubemap color texture
|
||||||
glGenTextures(1, &s->maps[light_index].texture);
|
glGenTextures(1, &s->maps[light_index].texture);
|
||||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 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) {
|
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||||
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
||||||
}
|
}
|
||||||
// #if is(ems)
|
}
|
||||||
// GLenum nones[] = { GL_NONE };
|
}
|
||||||
// glDrawBuffers(1, nones);
|
|
||||||
// glReadBuffer(GL_NONE);
|
static inline void
|
||||||
// #else
|
shadowmap_init_caster_pcf(shadowmap_t *s, int light_index, int texture_width) {
|
||||||
// glDrawBuffer(GL_NONE);
|
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
|
||||||
// glReadBuffer(GL_NONE);
|
|
||||||
// #endif
|
if (s->maps[light_index].texture_2d[0]) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise shadow map 2D
|
// Initialise shadow map 2D
|
||||||
glGenTextures(1, &s->maps[light_index].texture_2d);
|
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d);
|
glGenTextures(1, &s->maps[light_index].texture_2d[i]);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
|
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
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);
|
glGenTextures(1, &s->maps[light_index].depth_texture_2d[i]);
|
||||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d);
|
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);
|
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_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 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);
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
||||||
|
}
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
glGenFramebuffers(1, &s->maps[light_index].fbo_2d);
|
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d);
|
glGenFramebuffers(1, &s->maps[light_index].fbo_2d[i]);
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]);
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d, 0);
|
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);
|
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||||
PANIC("ERROR: Framebuffer is not complete: %x\n", 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};
|
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.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);
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb);
|
||||||
|
|
||||||
|
#if 0
|
||||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
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);
|
glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb);
|
||||||
return s;
|
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) {
|
void shadowmap_destroy(shadowmap_t *s) {
|
||||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||||
glDeleteFramebuffers(6, s->maps[i].fbos);
|
shadowmap_destroy_light(s, i);
|
||||||
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_t z = {0};
|
shadowmap_t z = {0};
|
||||||
*s = z;
|
*s = z;
|
||||||
|
@ -383396,6 +383506,7 @@ void shadowmap_begin(shadowmap_t *s) {
|
||||||
s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
|
s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
|
||||||
s->step = 0;
|
s->step = 0;
|
||||||
s->light_step = 0;
|
s->light_step = 0;
|
||||||
|
s->cascade_index = 0;
|
||||||
active_shadowmap = s;
|
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;
|
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) {
|
if (dir != 0) {
|
||||||
s->skip_render = true;
|
s->skip_render = true;
|
||||||
return;
|
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 lightDir = norm3(l->dir);
|
||||||
vec3 up = vec3(0, 1, 0);
|
vec3 up = vec3(0, 1, 0);
|
||||||
|
|
||||||
// Ensure up vector is not parallel to light direction
|
lookat44(V, sub3(center, lightDir), center, up);
|
||||||
if (fabs(dot3(lightDir, up)) > 0.99f) {
|
|
||||||
up = vec3(0, 0, 1);
|
|
||||||
|
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);
|
float zMult = 10.0f;
|
||||||
vec3 lightPos = sub3(center, scale3(lightDir, l->shadow_distance*0.5f));
|
|
||||||
lookat44(V, lightPos, center, up);
|
// 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);
|
multiply44x2(PV, P, V);
|
||||||
|
|
||||||
copy44(s->V, V);
|
copy44(s->V, V);
|
||||||
copy44(s->PV, PV);
|
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;
|
s->shadow_technique = l->shadow_technique = SHADOW_PCF;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shadowmap_step(shadowmap_t *s) {
|
static inline
|
||||||
if (s->step >= 6) {
|
void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
|
||||||
s->step = 0;
|
if (!s->blur_pcf) {
|
||||||
s->light_step++;
|
return;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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->step++;
|
||||||
s->skip_render = false;
|
s->skip_render = false;
|
||||||
|
@ -383470,12 +383739,12 @@ bool shadowmap_step(shadowmap_t *s) {
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
void shadowmap_clear_fbo() {
|
void shadowmap_clear_fbo() {
|
||||||
glClearColor(0, 0, 0, 0);
|
glClearColor(1, 1, 1, 1);
|
||||||
glClearDepth(1.0f);
|
glClearDepth(1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
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) {
|
if (l->cast_shadows) {
|
||||||
int step = s->step - 1;
|
int step = s->step - 1;
|
||||||
|
|
||||||
|
@ -383484,18 +383753,29 @@ void shadowmap_light(shadowmap_t *s, light_t *l) {
|
||||||
} else if (l->type == LIGHT_SPOT) {
|
} 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);
|
shadowmap_light_directional(s, l, step, cam_fov, cam_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->skip_render) {
|
if (s->skip_render) {
|
||||||
return;
|
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);
|
ASSERT(s->lights_pushed == 0);
|
||||||
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);
|
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d[s->cascade_index]);
|
||||||
shadowmap_clear_fbo();
|
shadowmap_clear_fbo();
|
||||||
} else {
|
} else {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]);
|
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);
|
shader_bool("u_shadow_receiver", GL_TRUE);
|
||||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||||
shader_cubemap(va("shadowMap[%d]", i), m.shadow_map->maps[i].texture);
|
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 {
|
} else {
|
||||||
shader_bool("u_shadow_receiver", GL_FALSE);
|
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_enabled = 1;
|
||||||
shadow_rs->cull_face_mode = GL_BACK;
|
shadow_rs->cull_face_mode = GL_BACK;
|
||||||
shadow_rs->front_face = GL_CW;
|
shadow_rs->front_face = GL_CW;
|
||||||
|
shadow_rs->depth_clamp_enabled = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lightmap pass
|
// Lightmap pass
|
||||||
|
|
|
@ -122,6 +122,9 @@ renderstate_t renderstate() {
|
||||||
// Enable seamless cubemap by default
|
// Enable seamless cubemap by default
|
||||||
state.seamless_cubemap = GL_TRUE;
|
state.seamless_cubemap = GL_TRUE;
|
||||||
|
|
||||||
|
// Disable depth clamp by default
|
||||||
|
state.depth_clamp_enabled = GL_FALSE;
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,6 +238,13 @@ void renderstate_apply(const renderstate_t *state) {
|
||||||
} else {
|
} else {
|
||||||
glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
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.specularPower = 32.f;
|
||||||
l.innerCone = 0.85f;// 31 deg
|
l.innerCone = 0.85f;// 31 deg
|
||||||
l.outerCone = 0.9f; // 25 deg
|
l.outerCone = 0.9f; // 25 deg
|
||||||
|
l.cast_shadows = true;
|
||||||
l.shadow_distance = 100.0f;
|
l.shadow_distance = 100.0f;
|
||||||
l.shadow_bias = 0.5f;
|
l.shadow_bias = 0.01f;
|
||||||
return l;
|
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].quadratic", i), lv[i].falloff.quadratic);
|
||||||
shader_float(va("u_lights[%d].innerCone", i), lv[i].innerCone);
|
shader_float(va("u_lights[%d].innerCone", i), lv[i].innerCone);
|
||||||
shader_float(va("u_lights[%d].outerCone", i), lv[i].outerCone);
|
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
|
// shadowmaps
|
||||||
|
|
||||||
static inline
|
static inline void
|
||||||
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
|
shadowmap_init_caster_vsm(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) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create a cubemap color texture
|
// Create a cubemap color texture
|
||||||
glGenTextures(1, &s->maps[light_index].texture);
|
glGenTextures(1, &s->maps[light_index].texture);
|
||||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 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) {
|
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||||
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
||||||
}
|
}
|
||||||
// #if is(ems)
|
}
|
||||||
// GLenum nones[] = { GL_NONE };
|
}
|
||||||
// glDrawBuffers(1, nones);
|
|
||||||
// glReadBuffer(GL_NONE);
|
static inline void
|
||||||
// #else
|
shadowmap_init_caster_pcf(shadowmap_t *s, int light_index, int texture_width) {
|
||||||
// glDrawBuffer(GL_NONE);
|
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
|
||||||
// glReadBuffer(GL_NONE);
|
|
||||||
// #endif
|
if (s->maps[light_index].texture_2d[0]) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise shadow map 2D
|
// Initialise shadow map 2D
|
||||||
glGenTextures(1, &s->maps[light_index].texture_2d);
|
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d);
|
glGenTextures(1, &s->maps[light_index].texture_2d[i]);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
|
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
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);
|
glGenTextures(1, &s->maps[light_index].depth_texture_2d[i]);
|
||||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d);
|
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);
|
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_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 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);
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
||||||
|
}
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
glGenFramebuffers(1, &s->maps[light_index].fbo_2d);
|
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d);
|
glGenFramebuffers(1, &s->maps[light_index].fbo_2d[i]);
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]);
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d, 0);
|
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);
|
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||||
PANIC("ERROR: Framebuffer is not complete: %x\n", 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};
|
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.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);
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb);
|
||||||
|
|
||||||
|
#if 0
|
||||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
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);
|
glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb);
|
||||||
return s;
|
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) {
|
void shadowmap_destroy(shadowmap_t *s) {
|
||||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||||
glDeleteFramebuffers(6, s->maps[i].fbos);
|
shadowmap_destroy_light(s, i);
|
||||||
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_t z = {0};
|
shadowmap_t z = {0};
|
||||||
*s = z;
|
*s = z;
|
||||||
|
@ -1682,6 +1776,7 @@ void shadowmap_begin(shadowmap_t *s) {
|
||||||
s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
|
s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
|
||||||
s->step = 0;
|
s->step = 0;
|
||||||
s->light_step = 0;
|
s->light_step = 0;
|
||||||
|
s->cascade_index = 0;
|
||||||
active_shadowmap = s;
|
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;
|
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) {
|
if (dir != 0) {
|
||||||
s->skip_render = true;
|
s->skip_render = true;
|
||||||
return;
|
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 lightDir = norm3(l->dir);
|
||||||
vec3 up = vec3(0, 1, 0);
|
vec3 up = vec3(0, 1, 0);
|
||||||
|
|
||||||
// Ensure up vector is not parallel to light direction
|
lookat44(V, sub3(center, lightDir), center, up);
|
||||||
if (fabs(dot3(lightDir, up)) > 0.99f) {
|
|
||||||
up = vec3(0, 0, 1);
|
|
||||||
|
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);
|
float zMult = 10.0f;
|
||||||
vec3 lightPos = sub3(center, scale3(lightDir, l->shadow_distance*0.5f));
|
|
||||||
lookat44(V, lightPos, center, up);
|
// 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);
|
multiply44x2(PV, P, V);
|
||||||
|
|
||||||
copy44(s->V, V);
|
copy44(s->V, V);
|
||||||
copy44(s->PV, PV);
|
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;
|
s->shadow_technique = l->shadow_technique = SHADOW_PCF;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shadowmap_step(shadowmap_t *s) {
|
static inline
|
||||||
if (s->step >= 6) {
|
void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
|
||||||
s->step = 0;
|
if (!s->blur_pcf) {
|
||||||
s->light_step++;
|
return;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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->step++;
|
||||||
s->skip_render = false;
|
s->skip_render = false;
|
||||||
|
@ -1756,12 +2009,12 @@ bool shadowmap_step(shadowmap_t *s) {
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
void shadowmap_clear_fbo() {
|
void shadowmap_clear_fbo() {
|
||||||
glClearColor(0, 0, 0, 0);
|
glClearColor(1, 1, 1, 1);
|
||||||
glClearDepth(1.0f);
|
glClearDepth(1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
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) {
|
if (l->cast_shadows) {
|
||||||
int step = s->step - 1;
|
int step = s->step - 1;
|
||||||
|
|
||||||
|
@ -1770,18 +2023,29 @@ void shadowmap_light(shadowmap_t *s, light_t *l) {
|
||||||
} else if (l->type == LIGHT_SPOT) {
|
} 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);
|
shadowmap_light_directional(s, l, step, cam_fov, cam_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->skip_render) {
|
if (s->skip_render) {
|
||||||
return;
|
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);
|
ASSERT(s->lights_pushed == 0);
|
||||||
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);
|
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d[s->cascade_index]);
|
||||||
shadowmap_clear_fbo();
|
shadowmap_clear_fbo();
|
||||||
} else {
|
} else {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]);
|
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);
|
shader_bool("u_shadow_receiver", GL_TRUE);
|
||||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||||
shader_cubemap(va("shadowMap[%d]", i), m.shadow_map->maps[i].texture);
|
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 {
|
} else {
|
||||||
shader_bool("u_shadow_receiver", GL_FALSE);
|
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_enabled = 1;
|
||||||
shadow_rs->cull_face_mode = GL_BACK;
|
shadow_rs->cull_face_mode = GL_BACK;
|
||||||
shadow_rs->front_face = GL_CW;
|
shadow_rs->front_face = GL_CW;
|
||||||
|
shadow_rs->depth_clamp_enabled = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lightmap pass
|
// Lightmap pass
|
||||||
|
|
|
@ -66,8 +66,11 @@ typedef struct renderstate_t {
|
||||||
// Scissor test
|
// Scissor test
|
||||||
bool scissor_test_enabled;
|
bool scissor_test_enabled;
|
||||||
|
|
||||||
// bool Seamless Cubemap
|
// Seamless cubemap
|
||||||
bool seamless_cubemap;
|
bool seamless_cubemap;
|
||||||
|
|
||||||
|
// Depth clamp
|
||||||
|
bool depth_clamp_enabled;
|
||||||
} renderstate_t;
|
} renderstate_t;
|
||||||
|
|
||||||
API renderstate_t renderstate();
|
API renderstate_t renderstate();
|
||||||
|
@ -296,6 +299,8 @@ enum SHADOW_TECHNIQUE {
|
||||||
SHADOW_PCF,
|
SHADOW_PCF,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define NUM_SHADOW_CASCADES 4
|
||||||
|
|
||||||
typedef struct light_t {
|
typedef struct light_t {
|
||||||
char type;
|
char type;
|
||||||
vec3 diffuse, specular, ambient;
|
vec3 diffuse, specular, ambient;
|
||||||
|
@ -313,7 +318,7 @@ typedef struct light_t {
|
||||||
unsigned shadow_technique;
|
unsigned shadow_technique;
|
||||||
float shadow_distance;
|
float shadow_distance;
|
||||||
float shadow_bias;
|
float shadow_bias;
|
||||||
mat44 shadow_matrix;
|
mat44 shadow_matrix[NUM_SHADOW_CASCADES];
|
||||||
|
|
||||||
// internals
|
// internals
|
||||||
bool cached; //< used by scene to invalidate cached light data
|
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 {
|
typedef struct shadowmap_t {
|
||||||
mat44 V;
|
mat44 V;
|
||||||
mat44 PV;
|
mat44 PV;
|
||||||
int texture_width;
|
int vsm_texture_width;
|
||||||
|
int pcf_texture_width;
|
||||||
int step;
|
int step;
|
||||||
int light_step;
|
int light_step;
|
||||||
|
int cascade_index;
|
||||||
unsigned shadow_technique;
|
unsigned shadow_technique;
|
||||||
|
float cascade_splits[NUM_SHADOW_CASCADES];
|
||||||
|
float cascade_distances[NUM_SHADOW_CASCADES];
|
||||||
|
bool blur_pcf;
|
||||||
|
float blur_scale;
|
||||||
|
|
||||||
// signals
|
// signals
|
||||||
bool skip_render;
|
bool skip_render;
|
||||||
int lights_pushed;
|
int lights_pushed;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
unsigned shadow_technique;
|
||||||
handle fbos[6], texture, depth_texture;
|
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];
|
} maps[MAX_LIGHTS];
|
||||||
|
|
||||||
handle saved_fb;
|
handle saved_fb;
|
||||||
|
@ -365,12 +378,12 @@ typedef struct shadowmap_t {
|
||||||
int saved_vp[4];
|
int saved_vp[4];
|
||||||
} shadowmap_t;
|
} 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_destroy(shadowmap_t *s);
|
||||||
|
|
||||||
API void shadowmap_begin(shadowmap_t *s);
|
API void shadowmap_begin(shadowmap_t *s);
|
||||||
API bool shadowmap_step(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);
|
API void shadowmap_end(shadowmap_t *s);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -49,6 +49,7 @@ typedef struct object_t {
|
||||||
aabb bounds;
|
aabb bounds;
|
||||||
unsigned billboard; // [0..7] x(4),y(2),z(1) masks
|
unsigned billboard; // [0..7] x(4),y(2),z(1) masks
|
||||||
bool disable_frustum_check;
|
bool disable_frustum_check;
|
||||||
|
bool cast_shadows;
|
||||||
|
|
||||||
// internal states
|
// internal states
|
||||||
array(handle) old_texture_ids;
|
array(handle) old_texture_ids;
|
||||||
|
@ -83,6 +84,7 @@ enum SCENE_FLAGS {
|
||||||
SCENE_BACKGROUND = 4,
|
SCENE_BACKGROUND = 4,
|
||||||
SCENE_FOREGROUND = 8,
|
SCENE_FOREGROUND = 8,
|
||||||
SCENE_UPDATE_SH_COEF = 16,
|
SCENE_UPDATE_SH_COEF = 16,
|
||||||
|
SCENE_CAST_SHADOWS = 32,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct scene_t {
|
typedef struct scene_t {
|
||||||
|
@ -92,6 +94,7 @@ typedef struct scene_t {
|
||||||
// special objects below:
|
// special objects below:
|
||||||
skybox_t skybox;
|
skybox_t skybox;
|
||||||
int u_coefficients_sh;
|
int u_coefficients_sh;
|
||||||
|
shadowmap_t shadowmap;
|
||||||
} scene_t;
|
} scene_t;
|
||||||
|
|
||||||
API scene_t* scene_push();
|
API scene_t* scene_push();
|
||||||
|
|
409
engine/v4k.c
409
engine/v4k.c
|
@ -16921,6 +16921,9 @@ renderstate_t renderstate() {
|
||||||
// Enable seamless cubemap by default
|
// Enable seamless cubemap by default
|
||||||
state.seamless_cubemap = GL_TRUE;
|
state.seamless_cubemap = GL_TRUE;
|
||||||
|
|
||||||
|
// Disable depth clamp by default
|
||||||
|
state.depth_clamp_enabled = GL_FALSE;
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17034,6 +17037,13 @@ void renderstate_apply(const renderstate_t *state) {
|
||||||
} else {
|
} else {
|
||||||
glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
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.specularPower = 32.f;
|
||||||
l.innerCone = 0.85f;// 31 deg
|
l.innerCone = 0.85f;// 31 deg
|
||||||
l.outerCone = 0.9f; // 25 deg
|
l.outerCone = 0.9f; // 25 deg
|
||||||
|
l.cast_shadows = true;
|
||||||
l.shadow_distance = 100.0f;
|
l.shadow_distance = 100.0f;
|
||||||
l.shadow_bias = 0.5f;
|
l.shadow_bias = 0.01f;
|
||||||
return l;
|
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].quadratic", i), lv[i].falloff.quadratic);
|
||||||
shader_float(va("u_lights[%d].innerCone", i), lv[i].innerCone);
|
shader_float(va("u_lights[%d].innerCone", i), lv[i].innerCone);
|
||||||
shader_float(va("u_lights[%d].outerCone", i), lv[i].outerCone);
|
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
|
// shadowmaps
|
||||||
|
|
||||||
static inline
|
static inline void
|
||||||
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
|
shadowmap_init_caster_vsm(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) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create a cubemap color texture
|
// Create a cubemap color texture
|
||||||
glGenTextures(1, &s->maps[light_index].texture);
|
glGenTextures(1, &s->maps[light_index].texture);
|
||||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 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) {
|
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||||
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
||||||
}
|
}
|
||||||
// #if is(ems)
|
}
|
||||||
// GLenum nones[] = { GL_NONE };
|
}
|
||||||
// glDrawBuffers(1, nones);
|
|
||||||
// glReadBuffer(GL_NONE);
|
static inline void
|
||||||
// #else
|
shadowmap_init_caster_pcf(shadowmap_t *s, int light_index, int texture_width) {
|
||||||
// glDrawBuffer(GL_NONE);
|
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
|
||||||
// glReadBuffer(GL_NONE);
|
|
||||||
// #endif
|
if (s->maps[light_index].texture_2d[0]) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise shadow map 2D
|
// Initialise shadow map 2D
|
||||||
glGenTextures(1, &s->maps[light_index].texture_2d);
|
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d);
|
glGenTextures(1, &s->maps[light_index].texture_2d[i]);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
|
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
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);
|
glGenTextures(1, &s->maps[light_index].depth_texture_2d[i]);
|
||||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d);
|
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);
|
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_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 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);
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
||||||
|
}
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
glGenFramebuffers(1, &s->maps[light_index].fbo_2d);
|
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d);
|
glGenFramebuffers(1, &s->maps[light_index].fbo_2d[i]);
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]);
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d, 0);
|
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);
|
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||||
PANIC("ERROR: Framebuffer is not complete: %x\n", 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};
|
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.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);
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb);
|
||||||
|
|
||||||
|
#if 0
|
||||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
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);
|
glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb);
|
||||||
return s;
|
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) {
|
void shadowmap_destroy(shadowmap_t *s) {
|
||||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||||
glDeleteFramebuffers(6, s->maps[i].fbos);
|
shadowmap_destroy_light(s, i);
|
||||||
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_t z = {0};
|
shadowmap_t z = {0};
|
||||||
*s = z;
|
*s = z;
|
||||||
|
@ -18481,6 +18575,7 @@ void shadowmap_begin(shadowmap_t *s) {
|
||||||
s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
|
s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
|
||||||
s->step = 0;
|
s->step = 0;
|
||||||
s->light_step = 0;
|
s->light_step = 0;
|
||||||
|
s->cascade_index = 0;
|
||||||
active_shadowmap = s;
|
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;
|
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) {
|
if (dir != 0) {
|
||||||
s->skip_render = true;
|
s->skip_render = true;
|
||||||
return;
|
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 lightDir = norm3(l->dir);
|
||||||
vec3 up = vec3(0, 1, 0);
|
vec3 up = vec3(0, 1, 0);
|
||||||
|
|
||||||
// Ensure up vector is not parallel to light direction
|
lookat44(V, sub3(center, lightDir), center, up);
|
||||||
if (fabs(dot3(lightDir, up)) > 0.99f) {
|
|
||||||
up = vec3(0, 0, 1);
|
|
||||||
|
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);
|
float zMult = 10.0f;
|
||||||
vec3 lightPos = sub3(center, scale3(lightDir, l->shadow_distance*0.5f));
|
|
||||||
lookat44(V, lightPos, center, up);
|
// 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);
|
multiply44x2(PV, P, V);
|
||||||
|
|
||||||
copy44(s->V, V);
|
copy44(s->V, V);
|
||||||
copy44(s->PV, PV);
|
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;
|
s->shadow_technique = l->shadow_technique = SHADOW_PCF;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shadowmap_step(shadowmap_t *s) {
|
static inline
|
||||||
if (s->step >= 6) {
|
void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
|
||||||
s->step = 0;
|
if (!s->blur_pcf) {
|
||||||
s->light_step++;
|
return;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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->step++;
|
||||||
s->skip_render = false;
|
s->skip_render = false;
|
||||||
|
@ -18555,12 +18808,12 @@ bool shadowmap_step(shadowmap_t *s) {
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
void shadowmap_clear_fbo() {
|
void shadowmap_clear_fbo() {
|
||||||
glClearColor(0, 0, 0, 0);
|
glClearColor(1, 1, 1, 1);
|
||||||
glClearDepth(1.0f);
|
glClearDepth(1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
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) {
|
if (l->cast_shadows) {
|
||||||
int step = s->step - 1;
|
int step = s->step - 1;
|
||||||
|
|
||||||
|
@ -18569,18 +18822,29 @@ void shadowmap_light(shadowmap_t *s, light_t *l) {
|
||||||
} else if (l->type == LIGHT_SPOT) {
|
} 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);
|
shadowmap_light_directional(s, l, step, cam_fov, cam_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->skip_render) {
|
if (s->skip_render) {
|
||||||
return;
|
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);
|
ASSERT(s->lights_pushed == 0);
|
||||||
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);
|
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d[s->cascade_index]);
|
||||||
shadowmap_clear_fbo();
|
shadowmap_clear_fbo();
|
||||||
} else {
|
} else {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]);
|
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);
|
shader_bool("u_shadow_receiver", GL_TRUE);
|
||||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||||
shader_cubemap(va("shadowMap[%d]", i), m.shadow_map->maps[i].texture);
|
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 {
|
} else {
|
||||||
shader_bool("u_shadow_receiver", GL_FALSE);
|
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_enabled = 1;
|
||||||
shadow_rs->cull_face_mode = GL_BACK;
|
shadow_rs->cull_face_mode = GL_BACK;
|
||||||
shadow_rs->front_face = GL_CW;
|
shadow_rs->front_face = GL_CW;
|
||||||
|
shadow_rs->depth_clamp_enabled = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lightmap pass
|
// Lightmap pass
|
||||||
|
|
28
engine/v4k.h
28
engine/v4k.h
|
@ -3101,8 +3101,11 @@ typedef struct renderstate_t {
|
||||||
// Scissor test
|
// Scissor test
|
||||||
bool scissor_test_enabled;
|
bool scissor_test_enabled;
|
||||||
|
|
||||||
// bool Seamless Cubemap
|
// Seamless cubemap
|
||||||
bool seamless_cubemap;
|
bool seamless_cubemap;
|
||||||
|
|
||||||
|
// Depth clamp
|
||||||
|
bool depth_clamp_enabled;
|
||||||
} renderstate_t;
|
} renderstate_t;
|
||||||
|
|
||||||
API renderstate_t renderstate();
|
API renderstate_t renderstate();
|
||||||
|
@ -3331,6 +3334,8 @@ enum SHADOW_TECHNIQUE {
|
||||||
SHADOW_PCF,
|
SHADOW_PCF,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define NUM_SHADOW_CASCADES 4
|
||||||
|
|
||||||
typedef struct light_t {
|
typedef struct light_t {
|
||||||
char type;
|
char type;
|
||||||
vec3 diffuse, specular, ambient;
|
vec3 diffuse, specular, ambient;
|
||||||
|
@ -3348,7 +3353,7 @@ typedef struct light_t {
|
||||||
unsigned shadow_technique;
|
unsigned shadow_technique;
|
||||||
float shadow_distance;
|
float shadow_distance;
|
||||||
float shadow_bias;
|
float shadow_bias;
|
||||||
mat44 shadow_matrix;
|
mat44 shadow_matrix[NUM_SHADOW_CASCADES];
|
||||||
|
|
||||||
// internals
|
// internals
|
||||||
bool cached; //< used by scene to invalidate cached light data
|
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 {
|
typedef struct shadowmap_t {
|
||||||
mat44 V;
|
mat44 V;
|
||||||
mat44 PV;
|
mat44 PV;
|
||||||
int texture_width;
|
int vsm_texture_width;
|
||||||
|
int pcf_texture_width;
|
||||||
int step;
|
int step;
|
||||||
int light_step;
|
int light_step;
|
||||||
|
int cascade_index;
|
||||||
unsigned shadow_technique;
|
unsigned shadow_technique;
|
||||||
|
float cascade_splits[NUM_SHADOW_CASCADES];
|
||||||
|
float cascade_distances[NUM_SHADOW_CASCADES];
|
||||||
|
bool blur_pcf;
|
||||||
|
float blur_scale;
|
||||||
|
|
||||||
// signals
|
// signals
|
||||||
bool skip_render;
|
bool skip_render;
|
||||||
int lights_pushed;
|
int lights_pushed;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
unsigned shadow_technique;
|
||||||
handle fbos[6], texture, depth_texture;
|
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];
|
} maps[MAX_LIGHTS];
|
||||||
|
|
||||||
handle saved_fb;
|
handle saved_fb;
|
||||||
|
@ -3400,12 +3413,12 @@ typedef struct shadowmap_t {
|
||||||
int saved_vp[4];
|
int saved_vp[4];
|
||||||
} shadowmap_t;
|
} 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_destroy(shadowmap_t *s);
|
||||||
|
|
||||||
API void shadowmap_begin(shadowmap_t *s);
|
API void shadowmap_begin(shadowmap_t *s);
|
||||||
API bool shadowmap_step(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);
|
API void shadowmap_end(shadowmap_t *s);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -4060,6 +4073,7 @@ typedef struct object_t {
|
||||||
aabb bounds;
|
aabb bounds;
|
||||||
unsigned billboard; // [0..7] x(4),y(2),z(1) masks
|
unsigned billboard; // [0..7] x(4),y(2),z(1) masks
|
||||||
bool disable_frustum_check;
|
bool disable_frustum_check;
|
||||||
|
bool cast_shadows;
|
||||||
|
|
||||||
// internal states
|
// internal states
|
||||||
array(handle) old_texture_ids;
|
array(handle) old_texture_ids;
|
||||||
|
@ -4094,6 +4108,7 @@ enum SCENE_FLAGS {
|
||||||
SCENE_BACKGROUND = 4,
|
SCENE_BACKGROUND = 4,
|
||||||
SCENE_FOREGROUND = 8,
|
SCENE_FOREGROUND = 8,
|
||||||
SCENE_UPDATE_SH_COEF = 16,
|
SCENE_UPDATE_SH_COEF = 16,
|
||||||
|
SCENE_CAST_SHADOWS = 32,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct scene_t {
|
typedef struct scene_t {
|
||||||
|
@ -4103,6 +4118,7 @@ typedef struct scene_t {
|
||||||
// special objects below:
|
// special objects below:
|
||||||
skybox_t skybox;
|
skybox_t skybox;
|
||||||
int u_coefficients_sh;
|
int u_coefficients_sh;
|
||||||
|
shadowmap_t shadowmap;
|
||||||
} scene_t;
|
} scene_t;
|
||||||
|
|
||||||
API scene_t* scene_push();
|
API scene_t* scene_push();
|
||||||
|
|
Loading…
Reference in New Issue