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;
|
||||
bool scissor_test_enabled;
|
||||
bool seamless_cubemap;
|
||||
bool depth_clamp_enabled;
|
||||
} renderstate_t;
|
||||
renderstate_t renderstate();
|
||||
bool renderstate_compare(const renderstate_t *stateA, const renderstate_t *stateB);
|
||||
|
@ -1160,7 +1161,7 @@ typedef struct light_t {
|
|||
unsigned shadow_technique;
|
||||
float shadow_distance;
|
||||
float shadow_bias;
|
||||
mat44 shadow_matrix;
|
||||
mat44 shadow_matrix[4];
|
||||
bool cached;
|
||||
} light_t;
|
||||
light_t light();
|
||||
|
@ -1178,25 +1179,33 @@ typedef struct light_t {
|
|||
typedef struct shadowmap_t {
|
||||
mat44 V;
|
||||
mat44 PV;
|
||||
int texture_width;
|
||||
int vsm_texture_width;
|
||||
int pcf_texture_width;
|
||||
int step;
|
||||
int light_step;
|
||||
int cascade_index;
|
||||
unsigned shadow_technique;
|
||||
float cascade_splits[4];
|
||||
float cascade_distances[4];
|
||||
bool blur_pcf;
|
||||
float blur_scale;
|
||||
bool skip_render;
|
||||
int lights_pushed;
|
||||
struct {
|
||||
unsigned shadow_technique;
|
||||
handle fbos[6], texture, depth_texture;
|
||||
handle fbo_2d, texture_2d, depth_texture_2d;
|
||||
handle fbo_2d[4], texture_2d[4], depth_texture_2d[4];
|
||||
handle blur_fbo_2d, blur_texture_2d;
|
||||
} maps[MAX_LIGHTS];
|
||||
handle saved_fb;
|
||||
handle saved_pass;
|
||||
int saved_vp[4];
|
||||
} shadowmap_t;
|
||||
shadowmap_t shadowmap(int texture_width);
|
||||
shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width);
|
||||
void shadowmap_destroy(shadowmap_t *s);
|
||||
void shadowmap_begin(shadowmap_t *s);
|
||||
bool shadowmap_step(shadowmap_t *s);
|
||||
void shadowmap_light(shadowmap_t *s, light_t *l);
|
||||
void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view);
|
||||
void shadowmap_end(shadowmap_t *s);
|
||||
unsigned shader(const char *vs, const char *fs, const char *attribs, const char *fragcolor, const char *defines);
|
||||
unsigned shader_geom(const char *gs, const char *vs, const char *fs, const char *attribs, const char *fragcolor, const char *defines);
|
||||
|
@ -1638,6 +1647,7 @@ typedef struct object_t {
|
|||
aabb bounds;
|
||||
unsigned billboard;
|
||||
bool disable_frustum_check;
|
||||
bool cast_shadows;
|
||||
handle* old_texture_ids;
|
||||
texture_t* old_textures;
|
||||
float distance;
|
||||
|
@ -1663,12 +1673,14 @@ enum SCENE_FLAGS {
|
|||
SCENE_BACKGROUND = 4,
|
||||
SCENE_FOREGROUND = 8,
|
||||
SCENE_UPDATE_SH_COEF = 16,
|
||||
SCENE_CAST_SHADOWS = 32,
|
||||
};
|
||||
typedef struct scene_t {
|
||||
object_t* objs;
|
||||
light_t* lights;
|
||||
skybox_t skybox;
|
||||
int u_coefficients_sh;
|
||||
shadowmap_t shadowmap;
|
||||
} scene_t;
|
||||
scene_t* scene_push();
|
||||
void scene_pop();
|
||||
|
|
|
@ -32,21 +32,26 @@ int main(int argc, char** argv) {
|
|||
light_t lit = light(); {
|
||||
lit.type = LIGHT_POINT;
|
||||
lit.cast_shadows = true;
|
||||
// lit.shadow_distance = 3.0f;
|
||||
// lit.shadow_distance = 5.0f;
|
||||
// lit.falloff.linear = 3.0f;
|
||||
}
|
||||
light_t lit2 = light(); {
|
||||
lit2.type = LIGHT_POINT;
|
||||
lit2.cast_shadows = true;
|
||||
lit2.diffuse = vec3(1, 0.7, 0.8);
|
||||
// lit2.shadow_distance = 5.0f;
|
||||
// lit2.falloff.linear = 3.0f;
|
||||
}
|
||||
light_t lit3 = light(); {
|
||||
lit3.type = LIGHT_SPOT;
|
||||
lit3.cast_shadows = true;
|
||||
// lit3.shadow_distance = 5.0f;
|
||||
lit3.diffuse = vec3(1, 0.7, 0.8);
|
||||
}
|
||||
light_t lit4 = light(); {
|
||||
lit4.type = LIGHT_DIRECTIONAL;
|
||||
lit4.cast_shadows = true;
|
||||
lit4.shadow_distance = 2000.0f;
|
||||
lit4.diffuse = vec3(1, 0.7, 0.8);
|
||||
}
|
||||
|
||||
|
@ -81,7 +86,7 @@ int main(int argc, char** argv) {
|
|||
if( !initialized ) {
|
||||
initialized = 1;
|
||||
sky = skybox(flag("--mie") ? 0 : SKY_DIRS[SKY_DIR], 0);
|
||||
sm = shadowmap(1024);
|
||||
sm = shadowmap(512, 2048);
|
||||
mdl = model(OBJ_MDLS[OBJ_MDL], 0);
|
||||
shader_bind(mdl.program);
|
||||
cubemap_sh_shader(&sky.cubemap);
|
||||
|
@ -100,7 +105,7 @@ int main(int argc, char** argv) {
|
|||
enum {
|
||||
POINT, SPOT, DIR, ALL
|
||||
};
|
||||
static unsigned mode = POINT;
|
||||
static unsigned mode = DIR;
|
||||
|
||||
if (input_down(KEY_1)) mode = POINT;
|
||||
if (input_down(KEY_2)) mode = SPOT;
|
||||
|
@ -135,6 +140,7 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
if (mode == DIR) {
|
||||
lights[0].pos = cam.position;
|
||||
lights[0].dir = vec3(1,-1,-1);
|
||||
}
|
||||
|
||||
|
@ -160,7 +166,7 @@ int main(int argc, char** argv) {
|
|||
{
|
||||
for (int i = 0; i < array_count(lights); i++) {
|
||||
while (shadowmap_step(&sm)) {
|
||||
shadowmap_light(&sm, &lights[i]);
|
||||
shadowmap_light(&sm, &lights[i], cam.fov, cam.view);
|
||||
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
|
||||
}
|
||||
}
|
||||
|
@ -171,13 +177,9 @@ int main(int argc, char** argv) {
|
|||
mat44 mvp; multiply44x2(mvp, cam.proj, cam.view);
|
||||
{
|
||||
skybox_render(&sky, cam.proj, cam.view);
|
||||
shader_bind(mdl.program);
|
||||
shader_int("u_textured", false);
|
||||
light_update(array_count(lights), lights);
|
||||
|
||||
// ddraw_sphere(lights[0].pos, 0.1f);
|
||||
// ddraw_sphere(lights[1].pos, 0.1f);
|
||||
// ddraw_flush();
|
||||
shader_bind(mdl.program);
|
||||
light_update(array_count(lights), lights);
|
||||
|
||||
model_shadow(&mdl, &sm);
|
||||
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
uniform int u_num_lights;
|
||||
|
||||
#define NUM_SHADOW_CASCADES 4
|
||||
|
||||
struct light_t {
|
||||
int type;
|
||||
vec3 diffuse;
|
||||
|
@ -23,7 +25,7 @@ struct light_t {
|
|||
float quadratic;
|
||||
|
||||
// shadows
|
||||
mat4 shadow_matrix;
|
||||
mat4 shadow_matrix[NUM_SHADOW_CASCADES];
|
||||
};
|
||||
|
||||
#define MAX_LIGHTS 16
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
in vec4 vpeye;
|
||||
in vec4 vneye;
|
||||
uniform bool u_shadow_receiver;
|
||||
uniform float u_cascade_splits[NUM_SHADOW_CASCADES];
|
||||
uniform float u_cascade_distances[NUM_SHADOW_CASCADES];
|
||||
uniform samplerCube shadowMap[MAX_LIGHTS];
|
||||
uniform sampler2D shadowMap2D[MAX_LIGHTS];
|
||||
uniform sampler2D shadowMap2D[MAX_LIGHTS * NUM_SHADOW_CASCADES];
|
||||
|
||||
//// From http://fabiensanglard.net/shadowmappingVSM/index.php
|
||||
float shadow_vsm(float distance, vec3 dir, int light_index) {
|
||||
|
@ -26,40 +28,57 @@ float shadow_vsm(float distance, vec3 dir, int light_index) {
|
|||
return p_max;
|
||||
}
|
||||
|
||||
float shadow_pcf(vec4 fragPosLightSpace, vec3 lightDir, int light_index) {
|
||||
float shadow_pcf(float distance, vec3 lightDir, int light_index) {
|
||||
// Determine which cascade to use
|
||||
int cascade_index = -1;
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||
if (distance < u_cascade_distances[i]) {
|
||||
cascade_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cascade_index == -1) {
|
||||
cascade_index = NUM_SHADOW_CASCADES - 1;
|
||||
}
|
||||
|
||||
vec4 fragPosLightSpace = u_lights[light_index].shadow_matrix[cascade_index] * vec4(v_position_ws, 1.0);
|
||||
int index = light_index * NUM_SHADOW_CASCADES + cascade_index;
|
||||
|
||||
// Perform perspective divide
|
||||
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||
|
||||
// Transform to [0,1] range
|
||||
projCoords = projCoords * 0.5 + 0.5;
|
||||
|
||||
// Get closest depth value from light's perspective (using [0,1] range fragPosLight as coords)
|
||||
float closestDepth = texture(shadowMap2D[light_index], projCoords.xy).r;
|
||||
|
||||
// Get depth of current fragment from light's perspective
|
||||
float currentDepth = projCoords.z;
|
||||
|
||||
// Calculate bias (based on depth map resolution and slope)
|
||||
if (currentDepth > 1.0) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
// Calculate bias
|
||||
vec3 normal = normalize(vneye.xyz);
|
||||
float bias = max(0.05 * (1.0 - dot(normal, -lightDir)), 0.005);
|
||||
float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
|
||||
const float bias_modifier = 0.5;
|
||||
if (cascade_index == NUM_SHADOW_CASCADES-1) {
|
||||
bias *= 1 / (u_cascade_distances[NUM_SHADOW_CASCADES-1] * bias_modifier);
|
||||
} else {
|
||||
bias *= 1 / (u_cascade_distances[cascade_index] * bias_modifier);
|
||||
}
|
||||
|
||||
// PCF
|
||||
float shadow = 0.0;
|
||||
vec2 texelSize = 1.0 / textureSize(shadowMap2D[light_index], 0);
|
||||
vec2 texelSize = 1.0 / textureSize(shadowMap2D[index], 0);
|
||||
for(int x = -1; x <= 1; ++x)
|
||||
{
|
||||
for(int y = -1; y <= 1; ++y)
|
||||
{
|
||||
float pcfDepth = texture(shadowMap2D[light_index], projCoords.xy + vec2(x, y) * texelSize).r;
|
||||
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
|
||||
float pcfDepth = texture(shadowMap2D[index], projCoords.xy + vec2(x, y) * texelSize).r;
|
||||
shadow += projCoords.z - bias > pcfDepth ? 1.0 : 0.0;
|
||||
}
|
||||
}
|
||||
shadow /= 9.0;
|
||||
|
||||
// Keep the shadow at 0.0 when outside the far_plane region of the light's frustum.
|
||||
if(projCoords.z >= 0.9)
|
||||
shadow = 0.0;
|
||||
|
||||
return 1.0 - shadow;
|
||||
}
|
||||
|
||||
|
@ -74,22 +93,14 @@ vec4 shadowmap(in vec4 peye, in vec4 neye) {
|
|||
|
||||
if (light.type == LIGHT_DIRECTIONAL) {
|
||||
total_casters++;
|
||||
vec4 frag_pos = light.shadow_matrix * vec4(v_position_ws, 1.0);
|
||||
factor += shadow_pcf(frag_pos, light.dir, i);
|
||||
} else if (light.type == LIGHT_POINT) {
|
||||
total_casters++;
|
||||
vec3 light_pos = (view * vec4(light.pos, 1.0)).xyz;
|
||||
vec3 dir = light_pos - fragment;
|
||||
vec4 sc = inv_view * vec4(dir, 0.0);
|
||||
factor += shadow_vsm(length(dir), -sc.xyz, i);
|
||||
} else if (light.type == LIGHT_SPOT) {
|
||||
factor += shadow_pcf(-peye.z, light.dir, i);
|
||||
} else if (light.type == LIGHT_POINT || light.type == LIGHT_SPOT) {
|
||||
total_casters++;
|
||||
vec3 light_pos = (view * vec4(light.pos, 1.0)).xyz;
|
||||
vec3 dir = light_pos - fragment;
|
||||
vec4 sc = inv_view * vec4(dir, 0.0);
|
||||
factor += shadow_vsm(length(dir), -sc.xyz, i);
|
||||
}
|
||||
|
||||
shadowFactor += factor;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
uniform sampler2D textureSource;
|
||||
uniform vec2 ScaleU;
|
||||
|
||||
in vec2 Texcoord;
|
||||
out vec4 outColor;
|
||||
in vec2 uv;
|
||||
out vec4 fragcolor;
|
||||
|
||||
void main() {
|
||||
vec4 color = vec4(0.0);
|
||||
color += texture( textureSource, Texcoord.st + vec2( -3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;
|
||||
color += texture( textureSource, Texcoord.st + vec2( -2.0*ScaleU.x, -2.0*ScaleU.y ) )*0.09375;
|
||||
color += texture( textureSource, Texcoord.st + vec2( -1.0*ScaleU.x, -1.0*ScaleU.y ) )*0.234375;
|
||||
color += texture( textureSource, Texcoord.st + vec2( 0.0 , 0.0) )*0.3125;
|
||||
color += texture( textureSource, Texcoord.st + vec2( 1.0*ScaleU.x, 1.0*ScaleU.y ) )*0.234375;
|
||||
color += texture( textureSource, Texcoord.st + vec2( 2.0*ScaleU.x, 2.0*ScaleU.y ) )*0.09375;
|
||||
color += texture( textureSource, Texcoord.st + vec2( 3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;
|
||||
outColor = vec4(color.xyz, 1.0);
|
||||
color += texture( textureSource, uv.st + vec2( -3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;
|
||||
color += texture( textureSource, uv.st + vec2( -2.0*ScaleU.x, -2.0*ScaleU.y ) )*0.09375;
|
||||
color += texture( textureSource, uv.st + vec2( -1.0*ScaleU.x, -1.0*ScaleU.y ) )*0.234375;
|
||||
color += texture( textureSource, uv.st + vec2( 0.0 , 0.0) )*0.3125;
|
||||
color += texture( textureSource, uv.st + vec2( 1.0*ScaleU.x, 1.0*ScaleU.y ) )*0.234375;
|
||||
color += texture( textureSource, uv.st + vec2( 2.0*ScaleU.x, 2.0*ScaleU.y ) )*0.09375;
|
||||
color += texture( textureSource, uv.st + vec2( 3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;
|
||||
fragcolor = vec4(color.xyz, 1.0);
|
||||
}
|
|
@ -1,8 +1,26 @@
|
|||
in vec3 position;
|
||||
in vec2 texcoord;
|
||||
out vec2 Texcoord;
|
||||
out vec2 uv;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(position, 1.0);
|
||||
Texcoord = texcoord;
|
||||
// Calculate vertex position based on gl_VertexID
|
||||
vec2 positions[6] = vec2[6](
|
||||
vec2(-1.0, 1.0), // Top-left
|
||||
vec2( 1.0, 1.0), // Top-right
|
||||
vec2( 1.0, -1.0), // Bottom-right
|
||||
vec2( 1.0, -1.0), // Bottom-right (repeated)
|
||||
vec2(-1.0, -1.0), // Bottom-left
|
||||
vec2(-1.0, 1.0) // Top-left (repeated)
|
||||
);
|
||||
|
||||
// Calculate UV coordinates based on gl_VertexID
|
||||
vec2 texCoords[6] = vec2[6](
|
||||
vec2(0.0, 1.0), // Top-left
|
||||
vec2(1.0, 1.0), // Top-right
|
||||
vec2(1.0, 0.0), // Bottom-right
|
||||
vec2(1.0, 0.0), // Bottom-right (repeated)
|
||||
vec2(0.0, 0.0), // Bottom-left
|
||||
vec2(0.0, 1.0) // Top-left (repeated)
|
||||
);
|
||||
|
||||
gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);
|
||||
uv = texCoords[gl_VertexID];
|
||||
}
|
|
@ -17034,8 +17034,11 @@ typedef struct renderstate_t {
|
|||
// Scissor test
|
||||
bool scissor_test_enabled;
|
||||
|
||||
// bool Seamless Cubemap
|
||||
// Seamless cubemap
|
||||
bool seamless_cubemap;
|
||||
|
||||
// Depth clamp
|
||||
bool depth_clamp_enabled;
|
||||
} renderstate_t;
|
||||
|
||||
API renderstate_t renderstate();
|
||||
|
@ -17264,6 +17267,8 @@ enum SHADOW_TECHNIQUE {
|
|||
SHADOW_PCF,
|
||||
};
|
||||
|
||||
#define NUM_SHADOW_CASCADES 4
|
||||
|
||||
typedef struct light_t {
|
||||
char type;
|
||||
vec3 diffuse, specular, ambient;
|
||||
|
@ -17281,7 +17286,7 @@ typedef struct light_t {
|
|||
unsigned shadow_technique;
|
||||
float shadow_distance;
|
||||
float shadow_bias;
|
||||
mat44 shadow_matrix;
|
||||
mat44 shadow_matrix[NUM_SHADOW_CASCADES];
|
||||
|
||||
// internals
|
||||
bool cached; //< used by scene to invalidate cached light data
|
||||
|
@ -17314,18 +17319,26 @@ API void light_update(unsigned num_lights, light_t *lv);
|
|||
typedef struct shadowmap_t {
|
||||
mat44 V;
|
||||
mat44 PV;
|
||||
int texture_width;
|
||||
int vsm_texture_width;
|
||||
int pcf_texture_width;
|
||||
int step;
|
||||
int light_step;
|
||||
int cascade_index;
|
||||
unsigned shadow_technique;
|
||||
float cascade_splits[NUM_SHADOW_CASCADES];
|
||||
float cascade_distances[NUM_SHADOW_CASCADES];
|
||||
bool blur_pcf;
|
||||
float blur_scale;
|
||||
|
||||
// signals
|
||||
bool skip_render;
|
||||
int lights_pushed;
|
||||
|
||||
struct {
|
||||
unsigned shadow_technique;
|
||||
handle fbos[6], texture, depth_texture;
|
||||
handle fbo_2d, texture_2d, depth_texture_2d;
|
||||
handle fbo_2d[NUM_SHADOW_CASCADES], texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES];
|
||||
handle blur_fbo_2d, blur_texture_2d;
|
||||
} maps[MAX_LIGHTS];
|
||||
|
||||
handle saved_fb;
|
||||
|
@ -17333,12 +17346,12 @@ typedef struct shadowmap_t {
|
|||
int saved_vp[4];
|
||||
} shadowmap_t;
|
||||
|
||||
API shadowmap_t shadowmap(int texture_width); // = 1024
|
||||
API shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width); // = 512, 4096
|
||||
API void shadowmap_destroy(shadowmap_t *s);
|
||||
|
||||
API void shadowmap_begin(shadowmap_t *s);
|
||||
API bool shadowmap_step(shadowmap_t *s);
|
||||
API void shadowmap_light(shadowmap_t *s, light_t *l); //< can be called once per shadowmap_step
|
||||
API void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view); //< can be called once per shadowmap_step
|
||||
API void shadowmap_end(shadowmap_t *s);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -17993,6 +18006,7 @@ typedef struct object_t {
|
|||
aabb bounds;
|
||||
unsigned billboard; // [0..7] x(4),y(2),z(1) masks
|
||||
bool disable_frustum_check;
|
||||
bool cast_shadows;
|
||||
|
||||
// internal states
|
||||
array(handle) old_texture_ids;
|
||||
|
@ -18027,6 +18041,7 @@ enum SCENE_FLAGS {
|
|||
SCENE_BACKGROUND = 4,
|
||||
SCENE_FOREGROUND = 8,
|
||||
SCENE_UPDATE_SH_COEF = 16,
|
||||
SCENE_CAST_SHADOWS = 32,
|
||||
};
|
||||
|
||||
typedef struct scene_t {
|
||||
|
@ -18036,6 +18051,7 @@ typedef struct scene_t {
|
|||
// special objects below:
|
||||
skybox_t skybox;
|
||||
int u_coefficients_sh;
|
||||
shadowmap_t shadowmap;
|
||||
} scene_t;
|
||||
|
||||
API scene_t* scene_push();
|
||||
|
@ -381836,6 +381852,9 @@ renderstate_t renderstate() {
|
|||
// Enable seamless cubemap by default
|
||||
state.seamless_cubemap = GL_TRUE;
|
||||
|
||||
// Disable depth clamp by default
|
||||
state.depth_clamp_enabled = GL_FALSE;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -381949,6 +381968,13 @@ void renderstate_apply(const renderstate_t *state) {
|
|||
} else {
|
||||
glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
||||
}
|
||||
|
||||
// Apply depth clamp
|
||||
if (state->depth_clamp_enabled) {
|
||||
glEnable(GL_DEPTH_CLAMP);
|
||||
} else {
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383186,8 +383212,9 @@ light_t light() {
|
|||
l.specularPower = 32.f;
|
||||
l.innerCone = 0.85f;// 31 deg
|
||||
l.outerCone = 0.9f; // 25 deg
|
||||
l.cast_shadows = true;
|
||||
l.shadow_distance = 100.0f;
|
||||
l.shadow_bias = 0.5f;
|
||||
l.shadow_bias = 0.01f;
|
||||
return l;
|
||||
}
|
||||
|
||||
|
@ -383266,7 +383293,9 @@ void light_update(unsigned num_lights, light_t *lv) {
|
|||
shader_float(va("u_lights[%d].quadratic", i), lv[i].falloff.quadratic);
|
||||
shader_float(va("u_lights[%d].innerCone", i), lv[i].innerCone);
|
||||
shader_float(va("u_lights[%d].outerCone", i), lv[i].outerCone);
|
||||
shader_mat44(va("u_lights[%d].shadow_matrix", i), lv[i].shadow_matrix);
|
||||
for (int j = 0; j < NUM_SHADOW_CASCADES; j++) {
|
||||
shader_mat44(va("u_lights[%d].shadow_matrix[%d]", i, j), lv[i].shadow_matrix[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383274,10 +383303,14 @@ void light_update(unsigned num_lights, light_t *lv) {
|
|||
// -----------------------------------------------------------------------------
|
||||
// shadowmaps
|
||||
|
||||
static inline
|
||||
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
|
||||
static inline void
|
||||
shadowmap_init_caster_vsm(shadowmap_t *s, int light_index, int texture_width) {
|
||||
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
if (s->maps[light_index].texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a cubemap color texture
|
||||
glGenTextures(1, &s->maps[light_index].texture);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].texture);
|
||||
|
@ -383318,70 +383351,147 @@ shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
|
|||
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
||||
}
|
||||
// #if is(ems)
|
||||
// GLenum nones[] = { GL_NONE };
|
||||
// glDrawBuffers(1, nones);
|
||||
// glReadBuffer(GL_NONE);
|
||||
// #else
|
||||
// glDrawBuffer(GL_NONE);
|
||||
// glReadBuffer(GL_NONE);
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
shadowmap_init_caster_pcf(shadowmap_t *s, int light_index, int texture_width) {
|
||||
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
if (s->maps[light_index].texture_2d[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialise shadow map 2D
|
||||
glGenTextures(1, &s->maps[light_index].texture_2d);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d);
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||
glGenTextures(1, &s->maps[light_index].texture_2d[i]);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
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_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
||||
|
||||
glGenTextures(1, &s->maps[light_index].depth_texture_2d);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d);
|
||||
glGenTextures(1, &s->maps[light_index].depth_texture_2d[i]);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d[i]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
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_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glGenFramebuffers(1, &s->maps[light_index].fbo_2d);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d, 0);
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||
glGenFramebuffers(1, &s->maps[light_index].fbo_2d[i]);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d[i], 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d[i], 0);
|
||||
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
shadowmap_t shadowmap(int texture_width) { // = 1024
|
||||
// Blur texture
|
||||
glGenTextures(1, &s->maps[light_index].blur_texture_2d);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Blur FBO
|
||||
glGenFramebuffers(1, &s->maps[light_index].blur_fbo_2d);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d, 0);
|
||||
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
shadowmap_init_caster(shadowmap_t *s, int light_index) {
|
||||
shadowmap_init_caster_vsm(s, light_index, s->vsm_texture_width);
|
||||
shadowmap_init_caster_pcf(s, light_index, s->pcf_texture_width);
|
||||
}
|
||||
|
||||
shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width) { // = 512, 4096
|
||||
shadowmap_t s = {0};
|
||||
s.texture_width = texture_width;
|
||||
s.vsm_texture_width = vsm_texture_width;
|
||||
s.pcf_texture_width = pcf_texture_width;
|
||||
s.saved_fb = 0;
|
||||
s.blur_pcf = true;
|
||||
s.blur_scale = 0.5f;
|
||||
s.cascade_splits[0] = 0.1f;
|
||||
s.cascade_splits[1] = 0.3f;
|
||||
s.cascade_splits[2] = 0.5f;
|
||||
s.cascade_splits[3] = 1.0f;
|
||||
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb);
|
||||
|
||||
#if 0
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||
shadowmap_init_caster(&s, i, texture_width);
|
||||
shadowmap_init_caster(&s, i);
|
||||
}
|
||||
#else
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||
s.maps[i].shadow_technique = 0xFFFF;
|
||||
}
|
||||
#endif
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb);
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline
|
||||
void shadowmap_destroy_light(shadowmap_t *s, int light_index) {
|
||||
if (s->maps[light_index].fbos[0]) {
|
||||
glDeleteFramebuffers(6, s->maps[light_index].fbos);
|
||||
s->maps[light_index].fbos[0] = 0;
|
||||
}
|
||||
if (s->maps[light_index].texture) {
|
||||
glDeleteTextures(1, &s->maps[light_index].texture);
|
||||
s->maps[light_index].texture = 0;
|
||||
}
|
||||
if (s->maps[light_index].depth_texture) {
|
||||
glDeleteTextures(1, &s->maps[light_index].depth_texture);
|
||||
s->maps[light_index].depth_texture = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||
if (s->maps[light_index].fbo_2d[i]) {
|
||||
glDeleteFramebuffers(1, &s->maps[light_index].fbo_2d[i]);
|
||||
s->maps[light_index].fbo_2d[i] = 0;
|
||||
}
|
||||
if (s->maps[light_index].texture_2d[i]) {
|
||||
glDeleteTextures(1, &s->maps[light_index].texture_2d[i]);
|
||||
s->maps[light_index].texture_2d[i] = 0;
|
||||
}
|
||||
if (s->maps[light_index].depth_texture_2d[i]) {
|
||||
glDeleteTextures(1, &s->maps[light_index].depth_texture_2d[i]);
|
||||
s->maps[light_index].depth_texture_2d[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->maps[light_index].blur_fbo_2d) {
|
||||
glDeleteFramebuffers(1, &s->maps[light_index].blur_fbo_2d);
|
||||
s->maps[light_index].blur_fbo_2d = 0;
|
||||
}
|
||||
if (s->maps[light_index].blur_texture_2d) {
|
||||
glDeleteTextures(1, &s->maps[light_index].blur_texture_2d);
|
||||
s->maps[light_index].blur_texture_2d = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void shadowmap_destroy(shadowmap_t *s) {
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||
glDeleteFramebuffers(6, s->maps[i].fbos);
|
||||
glDeleteTextures(1, &s->maps[i].texture);
|
||||
glDeleteTextures(1, &s->maps[i].depth_texture);
|
||||
glDeleteFramebuffers(1, &s->maps[i].fbo_2d);
|
||||
glDeleteTextures(1, &s->maps[i].texture_2d);
|
||||
glDeleteTextures(1, &s->maps[i].depth_texture_2d);
|
||||
shadowmap_destroy_light(s, i);
|
||||
}
|
||||
shadowmap_t z = {0};
|
||||
*s = z;
|
||||
|
@ -383396,6 +383506,7 @@ void shadowmap_begin(shadowmap_t *s) {
|
|||
s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
|
||||
s->step = 0;
|
||||
s->light_step = 0;
|
||||
s->cascade_index = 0;
|
||||
active_shadowmap = s;
|
||||
}
|
||||
|
||||
|
@ -383419,48 +383530,206 @@ static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) {
|
|||
s->shadow_technique = l->shadow_technique = SHADOW_VSM;
|
||||
}
|
||||
|
||||
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir) {
|
||||
static array(vec3) frustum_corners = 0;
|
||||
|
||||
static inline
|
||||
void shadowmap_light_directional_calc_frustum_corners(mat44 cam_proj, mat44 cam_view) {
|
||||
mat44 PV; multiply44x2(PV, cam_proj, cam_view);
|
||||
mat44 inverse_view_proj; invert44(inverse_view_proj, PV);
|
||||
array_resize(frustum_corners, 0);
|
||||
for (unsigned x = 0; x < 2; x++) {
|
||||
for (unsigned y = 0; y < 2; y++) {
|
||||
for (unsigned z = 0; z < 2; z++) {
|
||||
vec4 corner = {
|
||||
x * 2.0f - 1.0f,
|
||||
y * 2.0f - 1.0f,
|
||||
z * 2.0f - 1.0f,
|
||||
1.0f
|
||||
};
|
||||
vec4 world_corner = transform444(inverse_view_proj, corner);
|
||||
world_corner = scale4(world_corner, 1.0f / world_corner.w);
|
||||
array_push(frustum_corners, vec3(world_corner.x, world_corner.y, world_corner.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir, float cam_fov, mat44 cam_view) {
|
||||
if (dir != 0) {
|
||||
s->skip_render = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mat44 P, V, PV;
|
||||
l->shadow_distance = 25.0f;
|
||||
ortho44(P,
|
||||
-l->shadow_distance/2.0, l->shadow_distance/2.0,
|
||||
-l->shadow_distance/2.0, l->shadow_distance/2.0,
|
||||
l->shadow_bias, l->shadow_distance);
|
||||
float far_plane = 0.0f;
|
||||
float near_plane = 0.0f;
|
||||
|
||||
if (s->cascade_index == 0) {
|
||||
near_plane = l->shadow_bias;
|
||||
far_plane = l->shadow_distance * s->cascade_splits[0];
|
||||
} else if (s->cascade_index < NUM_SHADOW_CASCADES - 1) {
|
||||
near_plane = l->shadow_distance * s->cascade_splits[s->cascade_index-1];
|
||||
far_plane = l->shadow_distance * s->cascade_splits[s->cascade_index];
|
||||
} else {
|
||||
near_plane = l->shadow_distance * s->cascade_splits[NUM_SHADOW_CASCADES-1];
|
||||
far_plane = l->shadow_distance;
|
||||
}
|
||||
|
||||
mat44 proj; perspective44(proj, cam_fov, window_width() / (float)window_height(), near_plane, far_plane);
|
||||
shadowmap_light_directional_calc_frustum_corners(proj, cam_view);
|
||||
|
||||
vec3 center = {0,0,0};
|
||||
for (unsigned i = 0; i < array_count(frustum_corners); i++) {
|
||||
center = add3(center, frustum_corners[i]);
|
||||
}
|
||||
center = scale3(center, 1.0f / array_count(frustum_corners));
|
||||
|
||||
|
||||
s->cascade_distances[s->cascade_index] = far_plane;
|
||||
|
||||
float minX = FLT_MAX, maxX = FLT_MIN;
|
||||
float minY = FLT_MAX, maxY = FLT_MIN;
|
||||
float minZ = FLT_MAX, maxZ = FLT_MIN;
|
||||
|
||||
mat44 V;
|
||||
vec3 lightDir = norm3(l->dir);
|
||||
vec3 up = vec3(0, 1, 0);
|
||||
|
||||
// Ensure up vector is not parallel to light direction
|
||||
if (fabs(dot3(lightDir, up)) > 0.99f) {
|
||||
up = vec3(0, 0, 1);
|
||||
lookat44(V, sub3(center, lightDir), center, up);
|
||||
|
||||
|
||||
for (unsigned i = 0; i < array_count(frustum_corners); i++) {
|
||||
vec3 corner = frustum_corners[i];
|
||||
corner = transform344(V, corner);
|
||||
minX = min(minX, corner.x);
|
||||
maxX = max(maxX, corner.x);
|
||||
minY = min(minY, corner.y);
|
||||
maxY = max(maxY, corner.y);
|
||||
minZ = min(minZ, corner.z);
|
||||
maxZ = max(maxZ, corner.z);
|
||||
}
|
||||
|
||||
vec3 center = vec3(0, 0, 0);
|
||||
vec3 lightPos = sub3(center, scale3(lightDir, l->shadow_distance*0.5f));
|
||||
lookat44(V, lightPos, center, up);
|
||||
float zMult = 10.0f;
|
||||
|
||||
// if (minZ < 0) {
|
||||
// minZ *= zMult;
|
||||
// } else {
|
||||
// minZ /= zMult;
|
||||
// }
|
||||
// if (maxZ < 0) {
|
||||
// maxZ /= zMult;
|
||||
// } else {
|
||||
// maxZ *= zMult;
|
||||
// }
|
||||
|
||||
|
||||
mat44 P, PV;
|
||||
ortho44(P,
|
||||
minX, maxX,
|
||||
minY, maxY,
|
||||
minZ, maxZ);
|
||||
|
||||
multiply44x2(PV, P, V);
|
||||
|
||||
copy44(s->V, V);
|
||||
copy44(s->PV, PV);
|
||||
copy44(l->shadow_matrix, PV);
|
||||
copy44(l->shadow_matrix[s->cascade_index], PV);
|
||||
|
||||
s->shadow_technique = l->shadow_technique = SHADOW_PCF;
|
||||
}
|
||||
|
||||
bool shadowmap_step(shadowmap_t *s) {
|
||||
if (s->step >= 6) {
|
||||
s->step = 0;
|
||||
s->light_step++;
|
||||
return false;
|
||||
static inline
|
||||
void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
|
||||
if (!s->blur_pcf) {
|
||||
return;
|
||||
}
|
||||
|
||||
glViewport(0, 0, s->texture_width, s->texture_width);
|
||||
float blur_scale = 1.999 * (1 - s->blur_scale) + 0.001;
|
||||
// blur_scale = 0.1f;
|
||||
|
||||
static renderstate_t rs;
|
||||
static int program = -1, vao = -1, u_scale = -1, u_source = -1;
|
||||
if (program < 0) {
|
||||
rs = renderstate(); {
|
||||
rs.depth_test_enabled = false;
|
||||
rs.depth_write_enabled = false;
|
||||
rs.blend_enabled = false;
|
||||
}
|
||||
const char* vs = vfs_read("shaders/vs_shadow_blur.glsl");
|
||||
const char* fs = vfs_read("shaders/fs_shadow_blur.glsl");
|
||||
|
||||
program = shader(vs, fs, "", "fragcolor", NULL);
|
||||
u_scale = glGetUniformLocation(program, "ScaleU");
|
||||
u_source = glGetUniformLocation(program, "textureSource");
|
||||
glGenVertexArrays(1, &vao);
|
||||
}
|
||||
|
||||
renderstate_apply(&rs);
|
||||
glViewport(0, 0, s->pcf_texture_width, s->pcf_texture_width);
|
||||
|
||||
unsigned oldprog = last_shader;
|
||||
glUseProgram(program);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
// Horizontal pass
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d);
|
||||
glUniform2f(u_scale, 1.0f / (s->pcf_texture_width * blur_scale), 0);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
|
||||
glUniform1i(u_source, 0);
|
||||
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
profile_incstat("Render.num_drawcalls", +1);
|
||||
profile_incstat("Render.num_triangles", +2);
|
||||
|
||||
// Vertical pass
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]);
|
||||
glUniform2f(u_scale, 0, 1.0f / (s->pcf_texture_width * blur_scale));
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d);
|
||||
glUniform1i(u_source, 0);
|
||||
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
profile_incstat("Render.num_drawcalls", +1);
|
||||
profile_incstat("Render.num_triangles", +2);
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
glUseProgram(oldprog);
|
||||
}
|
||||
|
||||
static inline
|
||||
bool shadowmap_step_finish(shadowmap_t *s) {
|
||||
if (s->shadow_technique == SHADOW_PCF) {
|
||||
if (s->cascade_index < NUM_SHADOW_CASCADES - 1) {
|
||||
s->cascade_index++;
|
||||
s->step = 0;
|
||||
return false;
|
||||
}
|
||||
shadowmap_blur_pcf(s, s->light_step);
|
||||
}
|
||||
|
||||
s->step = 0;
|
||||
s->light_step++;
|
||||
s->cascade_index = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shadowmap_step(shadowmap_t *s) {
|
||||
int max_steps = s->shadow_technique == 0xffff ? 1 : s->shadow_technique == SHADOW_PCF ? 1 : 6;
|
||||
if (s->step >= max_steps) {
|
||||
if (shadowmap_step_finish(s)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned texture_width = s->shadow_technique == SHADOW_VSM ? s->vsm_texture_width : s->pcf_texture_width;
|
||||
|
||||
glViewport(0, 0, texture_width, texture_width);
|
||||
|
||||
s->step++;
|
||||
s->skip_render = false;
|
||||
|
@ -383470,12 +383739,12 @@ bool shadowmap_step(shadowmap_t *s) {
|
|||
|
||||
static inline
|
||||
void shadowmap_clear_fbo() {
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClearColor(1, 1, 1, 1);
|
||||
glClearDepth(1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void shadowmap_light(shadowmap_t *s, light_t *l) {
|
||||
void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view) {
|
||||
if (l->cast_shadows) {
|
||||
int step = s->step - 1;
|
||||
|
||||
|
@ -383484,18 +383753,29 @@ void shadowmap_light(shadowmap_t *s, light_t *l) {
|
|||
} else if (l->type == LIGHT_SPOT) {
|
||||
shadowmap_light_point(s, l, step);
|
||||
} else if (l->type == LIGHT_DIRECTIONAL) {
|
||||
shadowmap_light_directional(s, l, step);
|
||||
shadowmap_light_directional(s, l, step, cam_fov, cam_view);
|
||||
}
|
||||
|
||||
if (s->skip_render) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->maps[s->light_step].shadow_technique != l->shadow_technique) {
|
||||
// shadowmap_destroy_light(s, s->light_step); // @todo: we might wanna free the other set
|
||||
if (l->shadow_technique == SHADOW_VSM) {
|
||||
shadowmap_init_caster_vsm(s, s->light_step, s->vsm_texture_width);
|
||||
} else if (l->shadow_technique == SHADOW_PCF) {
|
||||
shadowmap_init_caster_pcf(s, s->light_step, s->pcf_texture_width);
|
||||
}
|
||||
}
|
||||
|
||||
s->maps[s->light_step].shadow_technique = l->shadow_technique;
|
||||
|
||||
ASSERT(s->lights_pushed == 0);
|
||||
s->lights_pushed++;
|
||||
|
||||
if (l->type == LIGHT_DIRECTIONAL) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d[s->cascade_index]);
|
||||
shadowmap_clear_fbo();
|
||||
} else {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]);
|
||||
|
@ -385689,7 +385969,11 @@ void model_set_uniforms(model_t m, int shader, mat44 mv, mat44 proj, mat44 view,
|
|||
shader_bool("u_shadow_receiver", GL_TRUE);
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||
shader_cubemap(va("shadowMap[%d]", i), m.shadow_map->maps[i].texture);
|
||||
shader_texture_unit(va("shadowMap2D[%d]", i), m.shadow_map->maps[i].texture_2d, texture_unit());
|
||||
for (int j = 0; j < NUM_SHADOW_CASCADES; j++) {
|
||||
shader_texture_unit(va("shadowMap2D[%d]", i * NUM_SHADOW_CASCADES + j), m.shadow_map->maps[i].texture_2d[j], texture_unit());
|
||||
shader_float(va("u_cascade_splits[%d]", j), m.shadow_map->cascade_splits[j]);
|
||||
shader_float(va("u_cascade_distances[%d]", j), m.shadow_map->cascade_distances[j]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
shader_bool("u_shadow_receiver", GL_FALSE);
|
||||
|
@ -386317,6 +386601,7 @@ void model_set_renderstates(model_t *m) {
|
|||
shadow_rs->cull_face_enabled = 1;
|
||||
shadow_rs->cull_face_mode = GL_BACK;
|
||||
shadow_rs->front_face = GL_CW;
|
||||
shadow_rs->depth_clamp_enabled = 1;
|
||||
}
|
||||
|
||||
// Lightmap pass
|
||||
|
|
|
@ -122,6 +122,9 @@ renderstate_t renderstate() {
|
|||
// Enable seamless cubemap by default
|
||||
state.seamless_cubemap = GL_TRUE;
|
||||
|
||||
// Disable depth clamp by default
|
||||
state.depth_clamp_enabled = GL_FALSE;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -235,6 +238,13 @@ void renderstate_apply(const renderstate_t *state) {
|
|||
} else {
|
||||
glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
||||
}
|
||||
|
||||
// Apply depth clamp
|
||||
if (state->depth_clamp_enabled) {
|
||||
glEnable(GL_DEPTH_CLAMP);
|
||||
} else {
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1472,8 +1482,9 @@ light_t light() {
|
|||
l.specularPower = 32.f;
|
||||
l.innerCone = 0.85f;// 31 deg
|
||||
l.outerCone = 0.9f; // 25 deg
|
||||
l.cast_shadows = true;
|
||||
l.shadow_distance = 100.0f;
|
||||
l.shadow_bias = 0.5f;
|
||||
l.shadow_bias = 0.01f;
|
||||
return l;
|
||||
}
|
||||
|
||||
|
@ -1552,7 +1563,9 @@ void light_update(unsigned num_lights, light_t *lv) {
|
|||
shader_float(va("u_lights[%d].quadratic", i), lv[i].falloff.quadratic);
|
||||
shader_float(va("u_lights[%d].innerCone", i), lv[i].innerCone);
|
||||
shader_float(va("u_lights[%d].outerCone", i), lv[i].outerCone);
|
||||
shader_mat44(va("u_lights[%d].shadow_matrix", i), lv[i].shadow_matrix);
|
||||
for (int j = 0; j < NUM_SHADOW_CASCADES; j++) {
|
||||
shader_mat44(va("u_lights[%d].shadow_matrix[%d]", i, j), lv[i].shadow_matrix[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1560,10 +1573,14 @@ void light_update(unsigned num_lights, light_t *lv) {
|
|||
// -----------------------------------------------------------------------------
|
||||
// shadowmaps
|
||||
|
||||
static inline
|
||||
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
|
||||
static inline void
|
||||
shadowmap_init_caster_vsm(shadowmap_t *s, int light_index, int texture_width) {
|
||||
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
if (s->maps[light_index].texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a cubemap color texture
|
||||
glGenTextures(1, &s->maps[light_index].texture);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].texture);
|
||||
|
@ -1604,70 +1621,147 @@ shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
|
|||
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
||||
}
|
||||
// #if is(ems)
|
||||
// GLenum nones[] = { GL_NONE };
|
||||
// glDrawBuffers(1, nones);
|
||||
// glReadBuffer(GL_NONE);
|
||||
// #else
|
||||
// glDrawBuffer(GL_NONE);
|
||||
// glReadBuffer(GL_NONE);
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
shadowmap_init_caster_pcf(shadowmap_t *s, int light_index, int texture_width) {
|
||||
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
if (s->maps[light_index].texture_2d[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialise shadow map 2D
|
||||
glGenTextures(1, &s->maps[light_index].texture_2d);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d);
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||
glGenTextures(1, &s->maps[light_index].texture_2d[i]);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
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_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
||||
|
||||
glGenTextures(1, &s->maps[light_index].depth_texture_2d);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d);
|
||||
glGenTextures(1, &s->maps[light_index].depth_texture_2d[i]);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d[i]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
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_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glGenFramebuffers(1, &s->maps[light_index].fbo_2d);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d, 0);
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||
glGenFramebuffers(1, &s->maps[light_index].fbo_2d[i]);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d[i], 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d[i], 0);
|
||||
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
shadowmap_t shadowmap(int texture_width) { // = 1024
|
||||
// Blur texture
|
||||
glGenTextures(1, &s->maps[light_index].blur_texture_2d);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Blur FBO
|
||||
glGenFramebuffers(1, &s->maps[light_index].blur_fbo_2d);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d, 0);
|
||||
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
shadowmap_init_caster(shadowmap_t *s, int light_index) {
|
||||
shadowmap_init_caster_vsm(s, light_index, s->vsm_texture_width);
|
||||
shadowmap_init_caster_pcf(s, light_index, s->pcf_texture_width);
|
||||
}
|
||||
|
||||
shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width) { // = 512, 4096
|
||||
shadowmap_t s = {0};
|
||||
s.texture_width = texture_width;
|
||||
s.vsm_texture_width = vsm_texture_width;
|
||||
s.pcf_texture_width = pcf_texture_width;
|
||||
s.saved_fb = 0;
|
||||
s.blur_pcf = true;
|
||||
s.blur_scale = 0.5f;
|
||||
s.cascade_splits[0] = 0.1f;
|
||||
s.cascade_splits[1] = 0.3f;
|
||||
s.cascade_splits[2] = 0.5f;
|
||||
s.cascade_splits[3] = 1.0f;
|
||||
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb);
|
||||
|
||||
#if 0
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||
shadowmap_init_caster(&s, i, texture_width);
|
||||
shadowmap_init_caster(&s, i);
|
||||
}
|
||||
#else
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||
s.maps[i].shadow_technique = 0xFFFF;
|
||||
}
|
||||
#endif
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb);
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline
|
||||
void shadowmap_destroy_light(shadowmap_t *s, int light_index) {
|
||||
if (s->maps[light_index].fbos[0]) {
|
||||
glDeleteFramebuffers(6, s->maps[light_index].fbos);
|
||||
s->maps[light_index].fbos[0] = 0;
|
||||
}
|
||||
if (s->maps[light_index].texture) {
|
||||
glDeleteTextures(1, &s->maps[light_index].texture);
|
||||
s->maps[light_index].texture = 0;
|
||||
}
|
||||
if (s->maps[light_index].depth_texture) {
|
||||
glDeleteTextures(1, &s->maps[light_index].depth_texture);
|
||||
s->maps[light_index].depth_texture = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||
if (s->maps[light_index].fbo_2d[i]) {
|
||||
glDeleteFramebuffers(1, &s->maps[light_index].fbo_2d[i]);
|
||||
s->maps[light_index].fbo_2d[i] = 0;
|
||||
}
|
||||
if (s->maps[light_index].texture_2d[i]) {
|
||||
glDeleteTextures(1, &s->maps[light_index].texture_2d[i]);
|
||||
s->maps[light_index].texture_2d[i] = 0;
|
||||
}
|
||||
if (s->maps[light_index].depth_texture_2d[i]) {
|
||||
glDeleteTextures(1, &s->maps[light_index].depth_texture_2d[i]);
|
||||
s->maps[light_index].depth_texture_2d[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->maps[light_index].blur_fbo_2d) {
|
||||
glDeleteFramebuffers(1, &s->maps[light_index].blur_fbo_2d);
|
||||
s->maps[light_index].blur_fbo_2d = 0;
|
||||
}
|
||||
if (s->maps[light_index].blur_texture_2d) {
|
||||
glDeleteTextures(1, &s->maps[light_index].blur_texture_2d);
|
||||
s->maps[light_index].blur_texture_2d = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void shadowmap_destroy(shadowmap_t *s) {
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||
glDeleteFramebuffers(6, s->maps[i].fbos);
|
||||
glDeleteTextures(1, &s->maps[i].texture);
|
||||
glDeleteTextures(1, &s->maps[i].depth_texture);
|
||||
glDeleteFramebuffers(1, &s->maps[i].fbo_2d);
|
||||
glDeleteTextures(1, &s->maps[i].texture_2d);
|
||||
glDeleteTextures(1, &s->maps[i].depth_texture_2d);
|
||||
shadowmap_destroy_light(s, i);
|
||||
}
|
||||
shadowmap_t z = {0};
|
||||
*s = z;
|
||||
|
@ -1682,6 +1776,7 @@ void shadowmap_begin(shadowmap_t *s) {
|
|||
s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
|
||||
s->step = 0;
|
||||
s->light_step = 0;
|
||||
s->cascade_index = 0;
|
||||
active_shadowmap = s;
|
||||
}
|
||||
|
||||
|
@ -1705,48 +1800,206 @@ static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) {
|
|||
s->shadow_technique = l->shadow_technique = SHADOW_VSM;
|
||||
}
|
||||
|
||||
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir) {
|
||||
static array(vec3) frustum_corners = 0;
|
||||
|
||||
static inline
|
||||
void shadowmap_light_directional_calc_frustum_corners(mat44 cam_proj, mat44 cam_view) {
|
||||
mat44 PV; multiply44x2(PV, cam_proj, cam_view);
|
||||
mat44 inverse_view_proj; invert44(inverse_view_proj, PV);
|
||||
array_resize(frustum_corners, 0);
|
||||
for (unsigned x = 0; x < 2; x++) {
|
||||
for (unsigned y = 0; y < 2; y++) {
|
||||
for (unsigned z = 0; z < 2; z++) {
|
||||
vec4 corner = {
|
||||
x * 2.0f - 1.0f,
|
||||
y * 2.0f - 1.0f,
|
||||
z * 2.0f - 1.0f,
|
||||
1.0f
|
||||
};
|
||||
vec4 world_corner = transform444(inverse_view_proj, corner);
|
||||
world_corner = scale4(world_corner, 1.0f / world_corner.w);
|
||||
array_push(frustum_corners, vec3(world_corner.x, world_corner.y, world_corner.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir, float cam_fov, mat44 cam_view) {
|
||||
if (dir != 0) {
|
||||
s->skip_render = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mat44 P, V, PV;
|
||||
l->shadow_distance = 25.0f;
|
||||
ortho44(P,
|
||||
-l->shadow_distance/2.0, l->shadow_distance/2.0,
|
||||
-l->shadow_distance/2.0, l->shadow_distance/2.0,
|
||||
l->shadow_bias, l->shadow_distance);
|
||||
float far_plane = 0.0f;
|
||||
float near_plane = 0.0f;
|
||||
|
||||
if (s->cascade_index == 0) {
|
||||
near_plane = l->shadow_bias;
|
||||
far_plane = l->shadow_distance * s->cascade_splits[0];
|
||||
} else if (s->cascade_index < NUM_SHADOW_CASCADES - 1) {
|
||||
near_plane = l->shadow_distance * s->cascade_splits[s->cascade_index-1];
|
||||
far_plane = l->shadow_distance * s->cascade_splits[s->cascade_index];
|
||||
} else {
|
||||
near_plane = l->shadow_distance * s->cascade_splits[NUM_SHADOW_CASCADES-1];
|
||||
far_plane = l->shadow_distance;
|
||||
}
|
||||
|
||||
mat44 proj; perspective44(proj, cam_fov, window_width() / (float)window_height(), near_plane, far_plane);
|
||||
shadowmap_light_directional_calc_frustum_corners(proj, cam_view);
|
||||
|
||||
vec3 center = {0,0,0};
|
||||
for (unsigned i = 0; i < array_count(frustum_corners); i++) {
|
||||
center = add3(center, frustum_corners[i]);
|
||||
}
|
||||
center = scale3(center, 1.0f / array_count(frustum_corners));
|
||||
|
||||
|
||||
s->cascade_distances[s->cascade_index] = far_plane;
|
||||
|
||||
float minX = FLT_MAX, maxX = FLT_MIN;
|
||||
float minY = FLT_MAX, maxY = FLT_MIN;
|
||||
float minZ = FLT_MAX, maxZ = FLT_MIN;
|
||||
|
||||
mat44 V;
|
||||
vec3 lightDir = norm3(l->dir);
|
||||
vec3 up = vec3(0, 1, 0);
|
||||
|
||||
// Ensure up vector is not parallel to light direction
|
||||
if (fabs(dot3(lightDir, up)) > 0.99f) {
|
||||
up = vec3(0, 0, 1);
|
||||
lookat44(V, sub3(center, lightDir), center, up);
|
||||
|
||||
|
||||
for (unsigned i = 0; i < array_count(frustum_corners); i++) {
|
||||
vec3 corner = frustum_corners[i];
|
||||
corner = transform344(V, corner);
|
||||
minX = min(minX, corner.x);
|
||||
maxX = max(maxX, corner.x);
|
||||
minY = min(minY, corner.y);
|
||||
maxY = max(maxY, corner.y);
|
||||
minZ = min(minZ, corner.z);
|
||||
maxZ = max(maxZ, corner.z);
|
||||
}
|
||||
|
||||
vec3 center = vec3(0, 0, 0);
|
||||
vec3 lightPos = sub3(center, scale3(lightDir, l->shadow_distance*0.5f));
|
||||
lookat44(V, lightPos, center, up);
|
||||
float zMult = 10.0f;
|
||||
|
||||
// if (minZ < 0) {
|
||||
// minZ *= zMult;
|
||||
// } else {
|
||||
// minZ /= zMult;
|
||||
// }
|
||||
// if (maxZ < 0) {
|
||||
// maxZ /= zMult;
|
||||
// } else {
|
||||
// maxZ *= zMult;
|
||||
// }
|
||||
|
||||
|
||||
mat44 P, PV;
|
||||
ortho44(P,
|
||||
minX, maxX,
|
||||
minY, maxY,
|
||||
minZ, maxZ);
|
||||
|
||||
multiply44x2(PV, P, V);
|
||||
|
||||
copy44(s->V, V);
|
||||
copy44(s->PV, PV);
|
||||
copy44(l->shadow_matrix, PV);
|
||||
copy44(l->shadow_matrix[s->cascade_index], PV);
|
||||
|
||||
s->shadow_technique = l->shadow_technique = SHADOW_PCF;
|
||||
}
|
||||
|
||||
bool shadowmap_step(shadowmap_t *s) {
|
||||
if (s->step >= 6) {
|
||||
s->step = 0;
|
||||
s->light_step++;
|
||||
return false;
|
||||
static inline
|
||||
void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
|
||||
if (!s->blur_pcf) {
|
||||
return;
|
||||
}
|
||||
|
||||
glViewport(0, 0, s->texture_width, s->texture_width);
|
||||
float blur_scale = 1.999 * (1 - s->blur_scale) + 0.001;
|
||||
// blur_scale = 0.1f;
|
||||
|
||||
static renderstate_t rs;
|
||||
static int program = -1, vao = -1, u_scale = -1, u_source = -1;
|
||||
if (program < 0) {
|
||||
rs = renderstate(); {
|
||||
rs.depth_test_enabled = false;
|
||||
rs.depth_write_enabled = false;
|
||||
rs.blend_enabled = false;
|
||||
}
|
||||
const char* vs = vfs_read("shaders/vs_shadow_blur.glsl");
|
||||
const char* fs = vfs_read("shaders/fs_shadow_blur.glsl");
|
||||
|
||||
program = shader(vs, fs, "", "fragcolor", NULL);
|
||||
u_scale = glGetUniformLocation(program, "ScaleU");
|
||||
u_source = glGetUniformLocation(program, "textureSource");
|
||||
glGenVertexArrays(1, &vao);
|
||||
}
|
||||
|
||||
renderstate_apply(&rs);
|
||||
glViewport(0, 0, s->pcf_texture_width, s->pcf_texture_width);
|
||||
|
||||
unsigned oldprog = last_shader;
|
||||
glUseProgram(program);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
// Horizontal pass
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d);
|
||||
glUniform2f(u_scale, 1.0f / (s->pcf_texture_width * blur_scale), 0);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
|
||||
glUniform1i(u_source, 0);
|
||||
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
profile_incstat("Render.num_drawcalls", +1);
|
||||
profile_incstat("Render.num_triangles", +2);
|
||||
|
||||
// Vertical pass
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]);
|
||||
glUniform2f(u_scale, 0, 1.0f / (s->pcf_texture_width * blur_scale));
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d);
|
||||
glUniform1i(u_source, 0);
|
||||
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
profile_incstat("Render.num_drawcalls", +1);
|
||||
profile_incstat("Render.num_triangles", +2);
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
glUseProgram(oldprog);
|
||||
}
|
||||
|
||||
static inline
|
||||
bool shadowmap_step_finish(shadowmap_t *s) {
|
||||
if (s->shadow_technique == SHADOW_PCF) {
|
||||
if (s->cascade_index < NUM_SHADOW_CASCADES - 1) {
|
||||
s->cascade_index++;
|
||||
s->step = 0;
|
||||
return false;
|
||||
}
|
||||
shadowmap_blur_pcf(s, s->light_step);
|
||||
}
|
||||
|
||||
s->step = 0;
|
||||
s->light_step++;
|
||||
s->cascade_index = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shadowmap_step(shadowmap_t *s) {
|
||||
int max_steps = s->shadow_technique == 0xffff ? 1 : s->shadow_technique == SHADOW_PCF ? 1 : 6;
|
||||
if (s->step >= max_steps) {
|
||||
if (shadowmap_step_finish(s)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned texture_width = s->shadow_technique == SHADOW_VSM ? s->vsm_texture_width : s->pcf_texture_width;
|
||||
|
||||
glViewport(0, 0, texture_width, texture_width);
|
||||
|
||||
s->step++;
|
||||
s->skip_render = false;
|
||||
|
@ -1756,12 +2009,12 @@ bool shadowmap_step(shadowmap_t *s) {
|
|||
|
||||
static inline
|
||||
void shadowmap_clear_fbo() {
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClearColor(1, 1, 1, 1);
|
||||
glClearDepth(1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void shadowmap_light(shadowmap_t *s, light_t *l) {
|
||||
void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view) {
|
||||
if (l->cast_shadows) {
|
||||
int step = s->step - 1;
|
||||
|
||||
|
@ -1770,18 +2023,29 @@ void shadowmap_light(shadowmap_t *s, light_t *l) {
|
|||
} else if (l->type == LIGHT_SPOT) {
|
||||
shadowmap_light_point(s, l, step);
|
||||
} else if (l->type == LIGHT_DIRECTIONAL) {
|
||||
shadowmap_light_directional(s, l, step);
|
||||
shadowmap_light_directional(s, l, step, cam_fov, cam_view);
|
||||
}
|
||||
|
||||
if (s->skip_render) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->maps[s->light_step].shadow_technique != l->shadow_technique) {
|
||||
// shadowmap_destroy_light(s, s->light_step); // @todo: we might wanna free the other set
|
||||
if (l->shadow_technique == SHADOW_VSM) {
|
||||
shadowmap_init_caster_vsm(s, s->light_step, s->vsm_texture_width);
|
||||
} else if (l->shadow_technique == SHADOW_PCF) {
|
||||
shadowmap_init_caster_pcf(s, s->light_step, s->pcf_texture_width);
|
||||
}
|
||||
}
|
||||
|
||||
s->maps[s->light_step].shadow_technique = l->shadow_technique;
|
||||
|
||||
ASSERT(s->lights_pushed == 0);
|
||||
s->lights_pushed++;
|
||||
|
||||
if (l->type == LIGHT_DIRECTIONAL) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d[s->cascade_index]);
|
||||
shadowmap_clear_fbo();
|
||||
} else {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]);
|
||||
|
@ -3975,7 +4239,11 @@ void model_set_uniforms(model_t m, int shader, mat44 mv, mat44 proj, mat44 view,
|
|||
shader_bool("u_shadow_receiver", GL_TRUE);
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||
shader_cubemap(va("shadowMap[%d]", i), m.shadow_map->maps[i].texture);
|
||||
shader_texture_unit(va("shadowMap2D[%d]", i), m.shadow_map->maps[i].texture_2d, texture_unit());
|
||||
for (int j = 0; j < NUM_SHADOW_CASCADES; j++) {
|
||||
shader_texture_unit(va("shadowMap2D[%d]", i * NUM_SHADOW_CASCADES + j), m.shadow_map->maps[i].texture_2d[j], texture_unit());
|
||||
shader_float(va("u_cascade_splits[%d]", j), m.shadow_map->cascade_splits[j]);
|
||||
shader_float(va("u_cascade_distances[%d]", j), m.shadow_map->cascade_distances[j]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
shader_bool("u_shadow_receiver", GL_FALSE);
|
||||
|
@ -4603,6 +4871,7 @@ void model_set_renderstates(model_t *m) {
|
|||
shadow_rs->cull_face_enabled = 1;
|
||||
shadow_rs->cull_face_mode = GL_BACK;
|
||||
shadow_rs->front_face = GL_CW;
|
||||
shadow_rs->depth_clamp_enabled = 1;
|
||||
}
|
||||
|
||||
// Lightmap pass
|
||||
|
|
|
@ -66,8 +66,11 @@ typedef struct renderstate_t {
|
|||
// Scissor test
|
||||
bool scissor_test_enabled;
|
||||
|
||||
// bool Seamless Cubemap
|
||||
// Seamless cubemap
|
||||
bool seamless_cubemap;
|
||||
|
||||
// Depth clamp
|
||||
bool depth_clamp_enabled;
|
||||
} renderstate_t;
|
||||
|
||||
API renderstate_t renderstate();
|
||||
|
@ -296,6 +299,8 @@ enum SHADOW_TECHNIQUE {
|
|||
SHADOW_PCF,
|
||||
};
|
||||
|
||||
#define NUM_SHADOW_CASCADES 4
|
||||
|
||||
typedef struct light_t {
|
||||
char type;
|
||||
vec3 diffuse, specular, ambient;
|
||||
|
@ -313,7 +318,7 @@ typedef struct light_t {
|
|||
unsigned shadow_technique;
|
||||
float shadow_distance;
|
||||
float shadow_bias;
|
||||
mat44 shadow_matrix;
|
||||
mat44 shadow_matrix[NUM_SHADOW_CASCADES];
|
||||
|
||||
// internals
|
||||
bool cached; //< used by scene to invalidate cached light data
|
||||
|
@ -346,18 +351,26 @@ API void light_update(unsigned num_lights, light_t *lv);
|
|||
typedef struct shadowmap_t {
|
||||
mat44 V;
|
||||
mat44 PV;
|
||||
int texture_width;
|
||||
int vsm_texture_width;
|
||||
int pcf_texture_width;
|
||||
int step;
|
||||
int light_step;
|
||||
int cascade_index;
|
||||
unsigned shadow_technique;
|
||||
float cascade_splits[NUM_SHADOW_CASCADES];
|
||||
float cascade_distances[NUM_SHADOW_CASCADES];
|
||||
bool blur_pcf;
|
||||
float blur_scale;
|
||||
|
||||
// signals
|
||||
bool skip_render;
|
||||
int lights_pushed;
|
||||
|
||||
struct {
|
||||
unsigned shadow_technique;
|
||||
handle fbos[6], texture, depth_texture;
|
||||
handle fbo_2d, texture_2d, depth_texture_2d;
|
||||
handle fbo_2d[NUM_SHADOW_CASCADES], texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES];
|
||||
handle blur_fbo_2d, blur_texture_2d;
|
||||
} maps[MAX_LIGHTS];
|
||||
|
||||
handle saved_fb;
|
||||
|
@ -365,12 +378,12 @@ typedef struct shadowmap_t {
|
|||
int saved_vp[4];
|
||||
} shadowmap_t;
|
||||
|
||||
API shadowmap_t shadowmap(int texture_width); // = 1024
|
||||
API shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width); // = 512, 4096
|
||||
API void shadowmap_destroy(shadowmap_t *s);
|
||||
|
||||
API void shadowmap_begin(shadowmap_t *s);
|
||||
API bool shadowmap_step(shadowmap_t *s);
|
||||
API void shadowmap_light(shadowmap_t *s, light_t *l); //< can be called once per shadowmap_step
|
||||
API void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view); //< can be called once per shadowmap_step
|
||||
API void shadowmap_end(shadowmap_t *s);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -49,6 +49,7 @@ typedef struct object_t {
|
|||
aabb bounds;
|
||||
unsigned billboard; // [0..7] x(4),y(2),z(1) masks
|
||||
bool disable_frustum_check;
|
||||
bool cast_shadows;
|
||||
|
||||
// internal states
|
||||
array(handle) old_texture_ids;
|
||||
|
@ -83,6 +84,7 @@ enum SCENE_FLAGS {
|
|||
SCENE_BACKGROUND = 4,
|
||||
SCENE_FOREGROUND = 8,
|
||||
SCENE_UPDATE_SH_COEF = 16,
|
||||
SCENE_CAST_SHADOWS = 32,
|
||||
};
|
||||
|
||||
typedef struct scene_t {
|
||||
|
@ -92,6 +94,7 @@ typedef struct scene_t {
|
|||
// special objects below:
|
||||
skybox_t skybox;
|
||||
int u_coefficients_sh;
|
||||
shadowmap_t shadowmap;
|
||||
} scene_t;
|
||||
|
||||
API scene_t* scene_push();
|
||||
|
|
387
engine/v4k.c
387
engine/v4k.c
|
@ -16921,6 +16921,9 @@ renderstate_t renderstate() {
|
|||
// Enable seamless cubemap by default
|
||||
state.seamless_cubemap = GL_TRUE;
|
||||
|
||||
// Disable depth clamp by default
|
||||
state.depth_clamp_enabled = GL_FALSE;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -17034,6 +17037,13 @@ void renderstate_apply(const renderstate_t *state) {
|
|||
} else {
|
||||
glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
||||
}
|
||||
|
||||
// Apply depth clamp
|
||||
if (state->depth_clamp_enabled) {
|
||||
glEnable(GL_DEPTH_CLAMP);
|
||||
} else {
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18271,8 +18281,9 @@ light_t light() {
|
|||
l.specularPower = 32.f;
|
||||
l.innerCone = 0.85f;// 31 deg
|
||||
l.outerCone = 0.9f; // 25 deg
|
||||
l.cast_shadows = true;
|
||||
l.shadow_distance = 100.0f;
|
||||
l.shadow_bias = 0.5f;
|
||||
l.shadow_bias = 0.01f;
|
||||
return l;
|
||||
}
|
||||
|
||||
|
@ -18351,7 +18362,9 @@ void light_update(unsigned num_lights, light_t *lv) {
|
|||
shader_float(va("u_lights[%d].quadratic", i), lv[i].falloff.quadratic);
|
||||
shader_float(va("u_lights[%d].innerCone", i), lv[i].innerCone);
|
||||
shader_float(va("u_lights[%d].outerCone", i), lv[i].outerCone);
|
||||
shader_mat44(va("u_lights[%d].shadow_matrix", i), lv[i].shadow_matrix);
|
||||
for (int j = 0; j < NUM_SHADOW_CASCADES; j++) {
|
||||
shader_mat44(va("u_lights[%d].shadow_matrix[%d]", i, j), lv[i].shadow_matrix[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18359,10 +18372,14 @@ void light_update(unsigned num_lights, light_t *lv) {
|
|||
// -----------------------------------------------------------------------------
|
||||
// shadowmaps
|
||||
|
||||
static inline
|
||||
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
|
||||
static inline void
|
||||
shadowmap_init_caster_vsm(shadowmap_t *s, int light_index, int texture_width) {
|
||||
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
if (s->maps[light_index].texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a cubemap color texture
|
||||
glGenTextures(1, &s->maps[light_index].texture);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].texture);
|
||||
|
@ -18403,70 +18420,147 @@ shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
|
|||
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
||||
}
|
||||
// #if is(ems)
|
||||
// GLenum nones[] = { GL_NONE };
|
||||
// glDrawBuffers(1, nones);
|
||||
// glReadBuffer(GL_NONE);
|
||||
// #else
|
||||
// glDrawBuffer(GL_NONE);
|
||||
// glReadBuffer(GL_NONE);
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
shadowmap_init_caster_pcf(shadowmap_t *s, int light_index, int texture_width) {
|
||||
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
if (s->maps[light_index].texture_2d[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialise shadow map 2D
|
||||
glGenTextures(1, &s->maps[light_index].texture_2d);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d);
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||
glGenTextures(1, &s->maps[light_index].texture_2d[i]);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
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_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
||||
|
||||
glGenTextures(1, &s->maps[light_index].depth_texture_2d);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d);
|
||||
glGenTextures(1, &s->maps[light_index].depth_texture_2d[i]);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d[i]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
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_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glGenFramebuffers(1, &s->maps[light_index].fbo_2d);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d, 0);
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||
glGenFramebuffers(1, &s->maps[light_index].fbo_2d[i]);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].texture_2d[i], 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d[i], 0);
|
||||
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
shadowmap_t shadowmap(int texture_width) { // = 1024
|
||||
// Blur texture
|
||||
glGenTextures(1, &s->maps[light_index].blur_texture_2d);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Blur FBO
|
||||
glGenFramebuffers(1, &s->maps[light_index].blur_fbo_2d);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d, 0);
|
||||
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (GL_FRAMEBUFFER_COMPLETE != result) {
|
||||
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
shadowmap_init_caster(shadowmap_t *s, int light_index) {
|
||||
shadowmap_init_caster_vsm(s, light_index, s->vsm_texture_width);
|
||||
shadowmap_init_caster_pcf(s, light_index, s->pcf_texture_width);
|
||||
}
|
||||
|
||||
shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width) { // = 512, 4096
|
||||
shadowmap_t s = {0};
|
||||
s.texture_width = texture_width;
|
||||
s.vsm_texture_width = vsm_texture_width;
|
||||
s.pcf_texture_width = pcf_texture_width;
|
||||
s.saved_fb = 0;
|
||||
s.blur_pcf = true;
|
||||
s.blur_scale = 0.5f;
|
||||
s.cascade_splits[0] = 0.1f;
|
||||
s.cascade_splits[1] = 0.3f;
|
||||
s.cascade_splits[2] = 0.5f;
|
||||
s.cascade_splits[3] = 1.0f;
|
||||
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb);
|
||||
|
||||
#if 0
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||
shadowmap_init_caster(&s, i, texture_width);
|
||||
shadowmap_init_caster(&s, i);
|
||||
}
|
||||
#else
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||
s.maps[i].shadow_technique = 0xFFFF;
|
||||
}
|
||||
#endif
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb);
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline
|
||||
void shadowmap_destroy_light(shadowmap_t *s, int light_index) {
|
||||
if (s->maps[light_index].fbos[0]) {
|
||||
glDeleteFramebuffers(6, s->maps[light_index].fbos);
|
||||
s->maps[light_index].fbos[0] = 0;
|
||||
}
|
||||
if (s->maps[light_index].texture) {
|
||||
glDeleteTextures(1, &s->maps[light_index].texture);
|
||||
s->maps[light_index].texture = 0;
|
||||
}
|
||||
if (s->maps[light_index].depth_texture) {
|
||||
glDeleteTextures(1, &s->maps[light_index].depth_texture);
|
||||
s->maps[light_index].depth_texture = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||
if (s->maps[light_index].fbo_2d[i]) {
|
||||
glDeleteFramebuffers(1, &s->maps[light_index].fbo_2d[i]);
|
||||
s->maps[light_index].fbo_2d[i] = 0;
|
||||
}
|
||||
if (s->maps[light_index].texture_2d[i]) {
|
||||
glDeleteTextures(1, &s->maps[light_index].texture_2d[i]);
|
||||
s->maps[light_index].texture_2d[i] = 0;
|
||||
}
|
||||
if (s->maps[light_index].depth_texture_2d[i]) {
|
||||
glDeleteTextures(1, &s->maps[light_index].depth_texture_2d[i]);
|
||||
s->maps[light_index].depth_texture_2d[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->maps[light_index].blur_fbo_2d) {
|
||||
glDeleteFramebuffers(1, &s->maps[light_index].blur_fbo_2d);
|
||||
s->maps[light_index].blur_fbo_2d = 0;
|
||||
}
|
||||
if (s->maps[light_index].blur_texture_2d) {
|
||||
glDeleteTextures(1, &s->maps[light_index].blur_texture_2d);
|
||||
s->maps[light_index].blur_texture_2d = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void shadowmap_destroy(shadowmap_t *s) {
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||
glDeleteFramebuffers(6, s->maps[i].fbos);
|
||||
glDeleteTextures(1, &s->maps[i].texture);
|
||||
glDeleteTextures(1, &s->maps[i].depth_texture);
|
||||
glDeleteFramebuffers(1, &s->maps[i].fbo_2d);
|
||||
glDeleteTextures(1, &s->maps[i].texture_2d);
|
||||
glDeleteTextures(1, &s->maps[i].depth_texture_2d);
|
||||
shadowmap_destroy_light(s, i);
|
||||
}
|
||||
shadowmap_t z = {0};
|
||||
*s = z;
|
||||
|
@ -18481,6 +18575,7 @@ void shadowmap_begin(shadowmap_t *s) {
|
|||
s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
|
||||
s->step = 0;
|
||||
s->light_step = 0;
|
||||
s->cascade_index = 0;
|
||||
active_shadowmap = s;
|
||||
}
|
||||
|
||||
|
@ -18504,48 +18599,206 @@ static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) {
|
|||
s->shadow_technique = l->shadow_technique = SHADOW_VSM;
|
||||
}
|
||||
|
||||
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir) {
|
||||
static array(vec3) frustum_corners = 0;
|
||||
|
||||
static inline
|
||||
void shadowmap_light_directional_calc_frustum_corners(mat44 cam_proj, mat44 cam_view) {
|
||||
mat44 PV; multiply44x2(PV, cam_proj, cam_view);
|
||||
mat44 inverse_view_proj; invert44(inverse_view_proj, PV);
|
||||
array_resize(frustum_corners, 0);
|
||||
for (unsigned x = 0; x < 2; x++) {
|
||||
for (unsigned y = 0; y < 2; y++) {
|
||||
for (unsigned z = 0; z < 2; z++) {
|
||||
vec4 corner = {
|
||||
x * 2.0f - 1.0f,
|
||||
y * 2.0f - 1.0f,
|
||||
z * 2.0f - 1.0f,
|
||||
1.0f
|
||||
};
|
||||
vec4 world_corner = transform444(inverse_view_proj, corner);
|
||||
world_corner = scale4(world_corner, 1.0f / world_corner.w);
|
||||
array_push(frustum_corners, vec3(world_corner.x, world_corner.y, world_corner.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir, float cam_fov, mat44 cam_view) {
|
||||
if (dir != 0) {
|
||||
s->skip_render = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mat44 P, V, PV;
|
||||
l->shadow_distance = 25.0f;
|
||||
ortho44(P,
|
||||
-l->shadow_distance/2.0, l->shadow_distance/2.0,
|
||||
-l->shadow_distance/2.0, l->shadow_distance/2.0,
|
||||
l->shadow_bias, l->shadow_distance);
|
||||
float far_plane = 0.0f;
|
||||
float near_plane = 0.0f;
|
||||
|
||||
if (s->cascade_index == 0) {
|
||||
near_plane = l->shadow_bias;
|
||||
far_plane = l->shadow_distance * s->cascade_splits[0];
|
||||
} else if (s->cascade_index < NUM_SHADOW_CASCADES - 1) {
|
||||
near_plane = l->shadow_distance * s->cascade_splits[s->cascade_index-1];
|
||||
far_plane = l->shadow_distance * s->cascade_splits[s->cascade_index];
|
||||
} else {
|
||||
near_plane = l->shadow_distance * s->cascade_splits[NUM_SHADOW_CASCADES-1];
|
||||
far_plane = l->shadow_distance;
|
||||
}
|
||||
|
||||
mat44 proj; perspective44(proj, cam_fov, window_width() / (float)window_height(), near_plane, far_plane);
|
||||
shadowmap_light_directional_calc_frustum_corners(proj, cam_view);
|
||||
|
||||
vec3 center = {0,0,0};
|
||||
for (unsigned i = 0; i < array_count(frustum_corners); i++) {
|
||||
center = add3(center, frustum_corners[i]);
|
||||
}
|
||||
center = scale3(center, 1.0f / array_count(frustum_corners));
|
||||
|
||||
|
||||
s->cascade_distances[s->cascade_index] = far_plane;
|
||||
|
||||
float minX = FLT_MAX, maxX = FLT_MIN;
|
||||
float minY = FLT_MAX, maxY = FLT_MIN;
|
||||
float minZ = FLT_MAX, maxZ = FLT_MIN;
|
||||
|
||||
mat44 V;
|
||||
vec3 lightDir = norm3(l->dir);
|
||||
vec3 up = vec3(0, 1, 0);
|
||||
|
||||
// Ensure up vector is not parallel to light direction
|
||||
if (fabs(dot3(lightDir, up)) > 0.99f) {
|
||||
up = vec3(0, 0, 1);
|
||||
lookat44(V, sub3(center, lightDir), center, up);
|
||||
|
||||
|
||||
for (unsigned i = 0; i < array_count(frustum_corners); i++) {
|
||||
vec3 corner = frustum_corners[i];
|
||||
corner = transform344(V, corner);
|
||||
minX = min(minX, corner.x);
|
||||
maxX = max(maxX, corner.x);
|
||||
minY = min(minY, corner.y);
|
||||
maxY = max(maxY, corner.y);
|
||||
minZ = min(minZ, corner.z);
|
||||
maxZ = max(maxZ, corner.z);
|
||||
}
|
||||
|
||||
vec3 center = vec3(0, 0, 0);
|
||||
vec3 lightPos = sub3(center, scale3(lightDir, l->shadow_distance*0.5f));
|
||||
lookat44(V, lightPos, center, up);
|
||||
float zMult = 10.0f;
|
||||
|
||||
// if (minZ < 0) {
|
||||
// minZ *= zMult;
|
||||
// } else {
|
||||
// minZ /= zMult;
|
||||
// }
|
||||
// if (maxZ < 0) {
|
||||
// maxZ /= zMult;
|
||||
// } else {
|
||||
// maxZ *= zMult;
|
||||
// }
|
||||
|
||||
|
||||
mat44 P, PV;
|
||||
ortho44(P,
|
||||
minX, maxX,
|
||||
minY, maxY,
|
||||
minZ, maxZ);
|
||||
|
||||
multiply44x2(PV, P, V);
|
||||
|
||||
copy44(s->V, V);
|
||||
copy44(s->PV, PV);
|
||||
copy44(l->shadow_matrix, PV);
|
||||
copy44(l->shadow_matrix[s->cascade_index], PV);
|
||||
|
||||
s->shadow_technique = l->shadow_technique = SHADOW_PCF;
|
||||
}
|
||||
|
||||
bool shadowmap_step(shadowmap_t *s) {
|
||||
if (s->step >= 6) {
|
||||
s->step = 0;
|
||||
s->light_step++;
|
||||
return false;
|
||||
static inline
|
||||
void shadowmap_blur_pcf(shadowmap_t *s, int light_index) {
|
||||
if (!s->blur_pcf) {
|
||||
return;
|
||||
}
|
||||
|
||||
glViewport(0, 0, s->texture_width, s->texture_width);
|
||||
float blur_scale = 1.999 * (1 - s->blur_scale) + 0.001;
|
||||
// blur_scale = 0.1f;
|
||||
|
||||
static renderstate_t rs;
|
||||
static int program = -1, vao = -1, u_scale = -1, u_source = -1;
|
||||
if (program < 0) {
|
||||
rs = renderstate(); {
|
||||
rs.depth_test_enabled = false;
|
||||
rs.depth_write_enabled = false;
|
||||
rs.blend_enabled = false;
|
||||
}
|
||||
const char* vs = vfs_read("shaders/vs_shadow_blur.glsl");
|
||||
const char* fs = vfs_read("shaders/fs_shadow_blur.glsl");
|
||||
|
||||
program = shader(vs, fs, "", "fragcolor", NULL);
|
||||
u_scale = glGetUniformLocation(program, "ScaleU");
|
||||
u_source = glGetUniformLocation(program, "textureSource");
|
||||
glGenVertexArrays(1, &vao);
|
||||
}
|
||||
|
||||
renderstate_apply(&rs);
|
||||
glViewport(0, 0, s->pcf_texture_width, s->pcf_texture_width);
|
||||
|
||||
unsigned oldprog = last_shader;
|
||||
glUseProgram(program);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
// Horizontal pass
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].blur_fbo_2d);
|
||||
glUniform2f(u_scale, 1.0f / (s->pcf_texture_width * blur_scale), 0);
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d[i]);
|
||||
glUniform1i(u_source, 0);
|
||||
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
profile_incstat("Render.num_drawcalls", +1);
|
||||
profile_incstat("Render.num_triangles", +2);
|
||||
|
||||
// Vertical pass
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbo_2d[i]);
|
||||
glUniform2f(u_scale, 0, 1.0f / (s->pcf_texture_width * blur_scale));
|
||||
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].blur_texture_2d);
|
||||
glUniform1i(u_source, 0);
|
||||
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
profile_incstat("Render.num_drawcalls", +1);
|
||||
profile_incstat("Render.num_triangles", +2);
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
glUseProgram(oldprog);
|
||||
}
|
||||
|
||||
static inline
|
||||
bool shadowmap_step_finish(shadowmap_t *s) {
|
||||
if (s->shadow_technique == SHADOW_PCF) {
|
||||
if (s->cascade_index < NUM_SHADOW_CASCADES - 1) {
|
||||
s->cascade_index++;
|
||||
s->step = 0;
|
||||
return false;
|
||||
}
|
||||
shadowmap_blur_pcf(s, s->light_step);
|
||||
}
|
||||
|
||||
s->step = 0;
|
||||
s->light_step++;
|
||||
s->cascade_index = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shadowmap_step(shadowmap_t *s) {
|
||||
int max_steps = s->shadow_technique == 0xffff ? 1 : s->shadow_technique == SHADOW_PCF ? 1 : 6;
|
||||
if (s->step >= max_steps) {
|
||||
if (shadowmap_step_finish(s)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned texture_width = s->shadow_technique == SHADOW_VSM ? s->vsm_texture_width : s->pcf_texture_width;
|
||||
|
||||
glViewport(0, 0, texture_width, texture_width);
|
||||
|
||||
s->step++;
|
||||
s->skip_render = false;
|
||||
|
@ -18555,12 +18808,12 @@ bool shadowmap_step(shadowmap_t *s) {
|
|||
|
||||
static inline
|
||||
void shadowmap_clear_fbo() {
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClearColor(1, 1, 1, 1);
|
||||
glClearDepth(1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void shadowmap_light(shadowmap_t *s, light_t *l) {
|
||||
void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view) {
|
||||
if (l->cast_shadows) {
|
||||
int step = s->step - 1;
|
||||
|
||||
|
@ -18569,18 +18822,29 @@ void shadowmap_light(shadowmap_t *s, light_t *l) {
|
|||
} else if (l->type == LIGHT_SPOT) {
|
||||
shadowmap_light_point(s, l, step);
|
||||
} else if (l->type == LIGHT_DIRECTIONAL) {
|
||||
shadowmap_light_directional(s, l, step);
|
||||
shadowmap_light_directional(s, l, step, cam_fov, cam_view);
|
||||
}
|
||||
|
||||
if (s->skip_render) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->maps[s->light_step].shadow_technique != l->shadow_technique) {
|
||||
// shadowmap_destroy_light(s, s->light_step); // @todo: we might wanna free the other set
|
||||
if (l->shadow_technique == SHADOW_VSM) {
|
||||
shadowmap_init_caster_vsm(s, s->light_step, s->vsm_texture_width);
|
||||
} else if (l->shadow_technique == SHADOW_PCF) {
|
||||
shadowmap_init_caster_pcf(s, s->light_step, s->pcf_texture_width);
|
||||
}
|
||||
}
|
||||
|
||||
s->maps[s->light_step].shadow_technique = l->shadow_technique;
|
||||
|
||||
ASSERT(s->lights_pushed == 0);
|
||||
s->lights_pushed++;
|
||||
|
||||
if (l->type == LIGHT_DIRECTIONAL) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d[s->cascade_index]);
|
||||
shadowmap_clear_fbo();
|
||||
} else {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]);
|
||||
|
@ -20774,7 +21038,11 @@ void model_set_uniforms(model_t m, int shader, mat44 mv, mat44 proj, mat44 view,
|
|||
shader_bool("u_shadow_receiver", GL_TRUE);
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) {
|
||||
shader_cubemap(va("shadowMap[%d]", i), m.shadow_map->maps[i].texture);
|
||||
shader_texture_unit(va("shadowMap2D[%d]", i), m.shadow_map->maps[i].texture_2d, texture_unit());
|
||||
for (int j = 0; j < NUM_SHADOW_CASCADES; j++) {
|
||||
shader_texture_unit(va("shadowMap2D[%d]", i * NUM_SHADOW_CASCADES + j), m.shadow_map->maps[i].texture_2d[j], texture_unit());
|
||||
shader_float(va("u_cascade_splits[%d]", j), m.shadow_map->cascade_splits[j]);
|
||||
shader_float(va("u_cascade_distances[%d]", j), m.shadow_map->cascade_distances[j]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
shader_bool("u_shadow_receiver", GL_FALSE);
|
||||
|
@ -21402,6 +21670,7 @@ void model_set_renderstates(model_t *m) {
|
|||
shadow_rs->cull_face_enabled = 1;
|
||||
shadow_rs->cull_face_mode = GL_BACK;
|
||||
shadow_rs->front_face = GL_CW;
|
||||
shadow_rs->depth_clamp_enabled = 1;
|
||||
}
|
||||
|
||||
// Lightmap pass
|
||||
|
|
28
engine/v4k.h
28
engine/v4k.h
|
@ -3101,8 +3101,11 @@ typedef struct renderstate_t {
|
|||
// Scissor test
|
||||
bool scissor_test_enabled;
|
||||
|
||||
// bool Seamless Cubemap
|
||||
// Seamless cubemap
|
||||
bool seamless_cubemap;
|
||||
|
||||
// Depth clamp
|
||||
bool depth_clamp_enabled;
|
||||
} renderstate_t;
|
||||
|
||||
API renderstate_t renderstate();
|
||||
|
@ -3331,6 +3334,8 @@ enum SHADOW_TECHNIQUE {
|
|||
SHADOW_PCF,
|
||||
};
|
||||
|
||||
#define NUM_SHADOW_CASCADES 4
|
||||
|
||||
typedef struct light_t {
|
||||
char type;
|
||||
vec3 diffuse, specular, ambient;
|
||||
|
@ -3348,7 +3353,7 @@ typedef struct light_t {
|
|||
unsigned shadow_technique;
|
||||
float shadow_distance;
|
||||
float shadow_bias;
|
||||
mat44 shadow_matrix;
|
||||
mat44 shadow_matrix[NUM_SHADOW_CASCADES];
|
||||
|
||||
// internals
|
||||
bool cached; //< used by scene to invalidate cached light data
|
||||
|
@ -3381,18 +3386,26 @@ API void light_update(unsigned num_lights, light_t *lv);
|
|||
typedef struct shadowmap_t {
|
||||
mat44 V;
|
||||
mat44 PV;
|
||||
int texture_width;
|
||||
int vsm_texture_width;
|
||||
int pcf_texture_width;
|
||||
int step;
|
||||
int light_step;
|
||||
int cascade_index;
|
||||
unsigned shadow_technique;
|
||||
float cascade_splits[NUM_SHADOW_CASCADES];
|
||||
float cascade_distances[NUM_SHADOW_CASCADES];
|
||||
bool blur_pcf;
|
||||
float blur_scale;
|
||||
|
||||
// signals
|
||||
bool skip_render;
|
||||
int lights_pushed;
|
||||
|
||||
struct {
|
||||
unsigned shadow_technique;
|
||||
handle fbos[6], texture, depth_texture;
|
||||
handle fbo_2d, texture_2d, depth_texture_2d;
|
||||
handle fbo_2d[NUM_SHADOW_CASCADES], texture_2d[NUM_SHADOW_CASCADES], depth_texture_2d[NUM_SHADOW_CASCADES];
|
||||
handle blur_fbo_2d, blur_texture_2d;
|
||||
} maps[MAX_LIGHTS];
|
||||
|
||||
handle saved_fb;
|
||||
|
@ -3400,12 +3413,12 @@ typedef struct shadowmap_t {
|
|||
int saved_vp[4];
|
||||
} shadowmap_t;
|
||||
|
||||
API shadowmap_t shadowmap(int texture_width); // = 1024
|
||||
API shadowmap_t shadowmap(int vsm_texture_width, int pcf_texture_width); // = 512, 4096
|
||||
API void shadowmap_destroy(shadowmap_t *s);
|
||||
|
||||
API void shadowmap_begin(shadowmap_t *s);
|
||||
API bool shadowmap_step(shadowmap_t *s);
|
||||
API void shadowmap_light(shadowmap_t *s, light_t *l); //< can be called once per shadowmap_step
|
||||
API void shadowmap_light(shadowmap_t *s, light_t *l, float cam_fov, mat44 cam_view); //< can be called once per shadowmap_step
|
||||
API void shadowmap_end(shadowmap_t *s);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -4060,6 +4073,7 @@ typedef struct object_t {
|
|||
aabb bounds;
|
||||
unsigned billboard; // [0..7] x(4),y(2),z(1) masks
|
||||
bool disable_frustum_check;
|
||||
bool cast_shadows;
|
||||
|
||||
// internal states
|
||||
array(handle) old_texture_ids;
|
||||
|
@ -4094,6 +4108,7 @@ enum SCENE_FLAGS {
|
|||
SCENE_BACKGROUND = 4,
|
||||
SCENE_FOREGROUND = 8,
|
||||
SCENE_UPDATE_SH_COEF = 16,
|
||||
SCENE_CAST_SHADOWS = 32,
|
||||
};
|
||||
|
||||
typedef struct scene_t {
|
||||
|
@ -4103,6 +4118,7 @@ typedef struct scene_t {
|
|||
// special objects below:
|
||||
skybox_t skybox;
|
||||
int u_coefficients_sh;
|
||||
shadowmap_t shadowmap;
|
||||
} scene_t;
|
||||
|
||||
API scene_t* scene_push();
|
||||
|
|
Loading…
Reference in New Issue