pcf shadows for dir light

main
Dominik Madarász 2024-08-29 20:46:30 +02:00
parent 2744bbbc47
commit 6319b11df6
10 changed files with 546 additions and 153 deletions

View File

@ -1142,6 +1142,10 @@ enum LIGHT_TYPE {
LIGHT_POINT, LIGHT_POINT,
LIGHT_SPOT, LIGHT_SPOT,
}; };
enum SHADOW_TECHNIQUE {
SHADOW_VSM,
SHADOW_PCF,
};
typedef struct light_t { typedef struct light_t {
char type; char type;
vec3 diffuse, specular, ambient; vec3 diffuse, specular, ambient;
@ -1153,8 +1157,10 @@ typedef struct light_t {
float specularPower; float specularPower;
float innerCone, outerCone; float innerCone, outerCone;
bool cast_shadows; bool cast_shadows;
unsigned shadow_technique;
float shadow_distance; float shadow_distance;
float shadow_bias; float shadow_bias;
mat44 shadow_matrix;
bool cached; bool cached;
} light_t; } light_t;
light_t light(); light_t light();
@ -1175,8 +1181,12 @@ typedef struct shadowmap_t {
int texture_width; int texture_width;
int step; int step;
int light_step; int light_step;
unsigned shadow_technique;
bool skip_render;
int lights_pushed;
struct { struct {
handle fbos[6], texture, depth_texture; handle fbos[6], texture, depth_texture;
handle fbo_2d, texture_2d, depth_texture_2d;
} maps[MAX_LIGHTS]; } maps[MAX_LIGHTS];
handle saved_fb; handle saved_fb;
handle saved_pass; handle saved_pass;
@ -1184,7 +1194,6 @@ typedef struct shadowmap_t {
} shadowmap_t; } shadowmap_t;
shadowmap_t shadowmap(int texture_width); shadowmap_t shadowmap(int texture_width);
void shadowmap_destroy(shadowmap_t *s); void shadowmap_destroy(shadowmap_t *s);
void shadowmap_set_shadowmatrix(shadowmap_t *s, vec3 aLightPos, vec3 aLightAt, vec3 aLightUp, const mat44 projection);
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);

View File

@ -39,10 +39,26 @@ int main(int argc, char** argv) {
lit2.cast_shadows = true; lit2.cast_shadows = true;
lit2.diffuse = vec3(1, 0.7, 0.8); lit2.diffuse = vec3(1, 0.7, 0.8);
} }
light_t lit3 = light(); {
lit3.type = LIGHT_SPOT;
lit3.cast_shadows = true;
lit3.diffuse = vec3(1, 0.7, 0.8);
}
light_t lit4 = light(); {
lit4.type = LIGHT_DIRECTIONAL;
lit4.cast_shadows = true;
lit4.diffuse = vec3(1, 0.7, 0.8);
}
array(light_t) lights = 0; array(light_t) point_lights = 0;
array_push(lights, lit); array_push(point_lights, lit);
array_push(lights, lit2); array_push(point_lights, lit2);
array(light_t) spot_lights = 0;
array_push(spot_lights, lit3);
array(light_t) directional_lights = 0;
array_push(directional_lights, lit4);
bool initialized = 0; bool initialized = 0;
bool must_reload = 0; bool must_reload = 0;
@ -75,7 +91,24 @@ int main(int argc, char** argv) {
camera_moveby(&cam, wasdec); camera_moveby(&cam, wasdec);
camera_fps(&cam, mouse.x,mouse.y); camera_fps(&cam, mouse.x,mouse.y);
enum {
POINT, SPOT, DIR
};
static unsigned mode = POINT;
if (input_down(KEY_1)) mode = POINT;
if (input_down(KEY_2)) mode = SPOT;
if (input_down(KEY_3)) mode = DIR;
light_t *lights = 0;
switch (mode) {
case POINT: lights = point_lights; break;
case SPOT: lights = spot_lights; break;
case DIR: lights = directional_lights; break;
}
// Animate light // Animate light
if (mode == POINT) {
lights[0].pos = vec3(0, 5.5, 1); lights[0].pos = vec3(0, 5.5, 1);
lights[0].pos.x += sinf(window_time()*2)*4.5f; lights[0].pos.x += sinf(window_time()*2)*4.5f;
lights[0].pos.y += cosf(window_time()*2)*1.0; lights[0].pos.y += cosf(window_time()*2)*1.0;
@ -85,6 +118,18 @@ int main(int argc, char** argv) {
lights[1].pos.x += sinf(window_time()*4)*4.5f; lights[1].pos.x += sinf(window_time()*4)*4.5f;
lights[1].pos.y += cosf(window_time()*4)*1.0; lights[1].pos.y += cosf(window_time()*4)*1.0;
lights[1].pos.z += cosf(window_time()*4)*6.0; lights[1].pos.z += cosf(window_time()*4)*6.0;
}
static bool camera_spot = true;
if (input_down(KEY_SPACE)) camera_spot = !camera_spot;
if (mode == SPOT && camera_spot) {
lights[0].pos = cam.position;
lights[0].dir = cam.lookdir;
}
if (mode == DIR) {
lights[0].dir = vec3(1,-1,-1);
}
// Render shadowmap // Render shadowmap
shadowmap_begin(&sm); shadowmap_begin(&sm);
@ -106,9 +151,9 @@ int main(int argc, char** argv) {
shader_int("u_textured", false); 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[0].pos, 0.1f);
ddraw_sphere(lights[1].pos, 0.1f); // ddraw_sphere(lights[1].pos, 0.1f);
ddraw_flush(); // 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);

View File

@ -21,6 +21,9 @@ struct light_t {
float constant; float constant;
float linear; float linear;
float quadratic; float quadratic;
// shadows
mat4 shadow_matrix;
}; };
#define MAX_LIGHTS 16 #define MAX_LIGHTS 16

View File

@ -2,9 +2,10 @@ in vec4 vpeye;
in vec4 vneye; in vec4 vneye;
uniform bool u_shadow_receiver; uniform bool u_shadow_receiver;
uniform samplerCube shadowMap[MAX_LIGHTS]; uniform samplerCube shadowMap[MAX_LIGHTS];
uniform sampler2D shadowMap2D[MAX_LIGHTS];
//// From http://fabiensanglard.net/shadowmappingVSM/index.php //// From http://fabiensanglard.net/shadowmappingVSM/index.php
float chebyshevUpperBound(float distance, vec3 dir, int light_index) { float shadow_vsm(float distance, vec3 dir, int light_index) {
distance = distance/20; distance = distance/20;
vec2 moments = texture(shadowMap[light_index], dir).rg; vec2 moments = texture(shadowMap[light_index], dir).rg;
@ -25,6 +26,43 @@ float chebyshevUpperBound(float distance, vec3 dir, int light_index) {
return p_max; return p_max;
} }
float shadow_pcf(vec4 fragPosLightSpace, vec3 lightDir, int light_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)
vec3 normal = normalize(vneye.xyz);
float bias = max(0.05 * (1.0 - dot(normal, -lightDir)), 0.005);
// PCF
float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(shadowMap2D[light_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;
}
}
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;
}
vec4 shadowmap(in vec4 peye, in vec4 neye) { vec4 shadowmap(in vec4 peye, in vec4 neye) {
float shadowFactor = 0.0; float shadowFactor = 0.0;
vec3 fragment = vec3(peye); vec3 fragment = vec3(peye);
@ -35,21 +73,27 @@ vec4 shadowmap(in vec4 peye, in vec4 neye) {
float factor = 0.0; float factor = 0.0;
if (light.type == LIGHT_DIRECTIONAL) { if (light.type == LIGHT_DIRECTIONAL) {
// shadowFactor = chebyshevUpperBound(distance, light.dir); 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) { } else if (light.type == LIGHT_POINT) {
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 += chebyshevUpperBound(length(dir), -sc.xyz, i); factor += shadow_vsm(length(dir), -sc.xyz, i);
} else if (light.type == LIGHT_SPOT) { } else if (light.type == LIGHT_SPOT) {
// shadowFactor = chebyshevUpperBound(distance, light.pos); 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; shadowFactor += factor;
} }
if (u_num_lights == 0) { if (total_casters == 0) {
shadowFactor = 1.0; shadowFactor = 1.0;
} else { } else {
shadowFactor /= total_casters; shadowFactor /= total_casters;

View File

@ -1,7 +1,13 @@
in vec3 v_position; in vec3 v_position;
out vec4 fragcolor; out vec4 fragcolor;
const int SHADOW_VSM = 0;
const int SHADOW_PCF = 1;
uniform int shadow_technique;
void main() { void main() {
if (shadow_technique == SHADOW_VSM) {
float depth = length(v_position) / 20; float depth = length(v_position) / 20;
float moment1 = depth; float moment1 = depth;
@ -12,3 +18,7 @@ void main() {
moment2 += 0.25*(dx*dx+dy*dy); moment2 += 0.25*(dx*dx+dy*dy);
fragcolor = vec4( moment1, moment2, 0.0, 1.0); fragcolor = vec4( moment1, moment2, 0.0, 1.0);
} }
else if (shadow_technique == SHADOW_PCF) {
fragcolor = vec4(vec3(gl_FragCoord.z), 1.0);
}
}

View File

@ -17259,6 +17259,11 @@ enum LIGHT_TYPE {
LIGHT_SPOT, LIGHT_SPOT,
}; };
enum SHADOW_TECHNIQUE {
SHADOW_VSM,
SHADOW_PCF,
};
typedef struct light_t { typedef struct light_t {
char type; char type;
vec3 diffuse, specular, ambient; vec3 diffuse, specular, ambient;
@ -17273,8 +17278,10 @@ typedef struct light_t {
// Shadowmapping // Shadowmapping
bool cast_shadows; bool cast_shadows;
unsigned shadow_technique;
float shadow_distance; float shadow_distance;
float shadow_bias; float shadow_bias;
mat44 shadow_matrix;
// internals // internals
bool cached; //< used by scene to invalidate cached light data bool cached; //< used by scene to invalidate cached light data
@ -17310,9 +17317,15 @@ typedef struct shadowmap_t {
int texture_width; int texture_width;
int step; int step;
int light_step; int light_step;
unsigned shadow_technique;
// signals
bool skip_render;
int lights_pushed;
struct { struct {
handle fbos[6], texture, depth_texture; handle fbos[6], texture, depth_texture;
handle fbo_2d, texture_2d, depth_texture_2d;
} maps[MAX_LIGHTS]; } maps[MAX_LIGHTS];
handle saved_fb; handle saved_fb;
@ -17323,10 +17336,9 @@ typedef struct shadowmap_t {
API shadowmap_t shadowmap(int texture_width); // = 1024 API shadowmap_t shadowmap(int texture_width); // = 1024
API void shadowmap_destroy(shadowmap_t *s); API void shadowmap_destroy(shadowmap_t *s);
API void shadowmap_set_shadowmatrix(shadowmap_t *s, vec3 aLightPos, vec3 aLightAt, vec3 aLightUp, const mat44 projection);
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); API void shadowmap_light(shadowmap_t *s, light_t *l); //< can be called once per shadowmap_step
API void shadowmap_end(shadowmap_t *s); API void shadowmap_end(shadowmap_t *s);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -383254,6 +383266,7 @@ 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);
} }
} }
@ -383263,6 +383276,8 @@ void light_update(unsigned num_lights, light_t *lv) {
static inline static inline
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) { shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
// 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);
@ -383273,6 +383288,10 @@ shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor);
// Create a cubemap depth texture // Create a cubemap depth texture
glGenTextures(1, &s->maps[light_index].depth_texture); glGenTextures(1, &s->maps[light_index].depth_texture);
@ -383282,9 +383301,10 @@ shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
} }
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0); glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
@ -383298,7 +383318,46 @@ 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);
// #else
// glDrawBuffer(GL_NONE);
// glReadBuffer(GL_NONE);
// #endif
} }
// Initialise shadow map 2D
glGenTextures(1, &s->maps[light_index].texture_2d);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glGenTextures(1, &s->maps[light_index].depth_texture_2d);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
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);
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 shadowmap_t shadowmap(int texture_width) { // = 1024
@ -383320,25 +383379,14 @@ void shadowmap_destroy(shadowmap_t *s) {
glDeleteFramebuffers(6, s->maps[i].fbos); glDeleteFramebuffers(6, s->maps[i].fbos);
glDeleteTextures(1, &s->maps[i].texture); glDeleteTextures(1, &s->maps[i].texture);
glDeleteTextures(1, &s->maps[i].depth_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;
} }
void shadowmap_set_shadowmatrix(shadowmap_t *s, vec3 aLightPos, vec3 aLightAt, vec3 aLightUp, const mat44 projection) {
// copy44(s->proj, projection);
// lookat44(s->mv, aLightPos, aLightAt, aLightUp);
// mat44 bias = {
// 0.5, 0.0, 0.0, 0.0,
// 0.0, 0.5, 0.0, 0.0,
// 0.0, 0.0, 0.5, 0.0,
// 0.5, 0.5, 0.5, 1.0 };
// multiply44x3(s->shadowmatrix, bias, s->proj, s->mv);
// multiply44x2(s->mvp, projection, s->mv);
}
static shadowmap_t *active_shadowmap = NULL; static shadowmap_t *active_shadowmap = NULL;
void shadowmap_begin(shadowmap_t *s) { void shadowmap_begin(shadowmap_t *s) {
@ -383349,15 +383397,6 @@ void shadowmap_begin(shadowmap_t *s) {
s->step = 0; s->step = 0;
s->light_step = 0; s->light_step = 0;
active_shadowmap = s; active_shadowmap = s;
for (int i = 0; i < MAX_LIGHTS; i++) {
for (int j = 0; j < 6; j++) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[i].fbos[j]);
glClearColor(0, 0, 0, 0);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
}
} }
static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) { static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) {
@ -383376,6 +383415,42 @@ static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) {
copy44(s->V, V); copy44(s->V, V);
copy44(s->PV, PV); copy44(s->PV, PV);
s->shadow_technique = l->shadow_technique = SHADOW_VSM;
}
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir) {
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);
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);
}
vec3 center = vec3(0, 0, 0);
vec3 lightPos = sub3(center, scale3(lightDir, l->shadow_distance*0.5f));
lookat44(V, lightPos, center, up);
multiply44x2(PV, P, V);
copy44(s->V, V);
copy44(s->PV, PV);
copy44(l->shadow_matrix, PV);
s->shadow_technique = l->shadow_technique = SHADOW_PCF;
} }
bool shadowmap_step(shadowmap_t *s) { bool shadowmap_step(shadowmap_t *s) {
@ -383385,13 +383460,21 @@ bool shadowmap_step(shadowmap_t *s) {
return false; return false;
} }
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[s->step]);
glViewport(0, 0, s->texture_width, s->texture_width); glViewport(0, 0, s->texture_width, s->texture_width);
s->step++; s->step++;
s->skip_render = false;
s->lights_pushed = 0;
return true; return true;
} }
static inline
void shadowmap_clear_fbo() {
glClearColor(0, 0, 0, 0);
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) {
if (l->cast_shadows) { if (l->cast_shadows) {
int step = s->step - 1; int step = s->step - 1;
@ -383399,9 +383482,24 @@ void shadowmap_light(shadowmap_t *s, light_t *l) {
if (l->type == LIGHT_POINT) { if (l->type == LIGHT_POINT) {
shadowmap_light_point(s, l, step); shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_SPOT) { } else if (l->type == LIGHT_SPOT) {
// spot light shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_DIRECTIONAL) { } else if (l->type == LIGHT_DIRECTIONAL) {
// directional light shadowmap_light_directional(s, l, step);
}
if (s->skip_render) {
return;
}
ASSERT(s->lights_pushed == 0);
s->lights_pushed++;
if (l->type == LIGHT_DIRECTIONAL) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d);
shadowmap_clear_fbo();
} else {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]);
shadowmap_clear_fbo();
} }
} }
} }
@ -383413,16 +383511,6 @@ void shadowmap_end(shadowmap_t *s) {
active_shadowmap = NULL; active_shadowmap = NULL;
} }
// shadowmap utils
void shadowmatrix_proj(mat44 shm_proj, float aLightFov, float znear, float zfar) {
perspective44(shm_proj, aLightFov, 1.0f, znear, zfar);
}
void shadowmatrix_ortho(mat44 shm_proj, float left, float right, float bottom, float top, float znear, float zfar) {
ortho44(shm_proj, left, right, bottom, top, znear, zfar);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Occlusion queries // Occlusion queries
@ -385592,6 +385680,7 @@ void model_set_uniforms(model_t m, int shader, mat44 mv, mat44 proj, mat44 view,
ASSERT(sm); ASSERT(sm);
shader_mat44("cameraToShadowView", sm->V); shader_mat44("cameraToShadowView", sm->V);
shader_mat44("cameraToShadowProjector", sm->PV); shader_mat44("cameraToShadowProjector", sm->PV);
shader_int("shadow_technique", sm->shadow_technique);
} }
// shadow receiving // shadow receiving
@ -385600,6 +385689,7 @@ 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());
} }
} else { } else {
shader_bool("u_shadow_receiver", GL_FALSE); shader_bool("u_shadow_receiver", GL_FALSE);
@ -386750,6 +386840,10 @@ void model_render_instanced_pass(model_t m, mat44 proj, mat44 view, mat44* model
if(!m.iqm) return; if(!m.iqm) return;
iqm_t *q = m.iqm; iqm_t *q = m.iqm;
if (active_shadowmap && active_shadowmap->skip_render) {
return;
}
mat44 mv; multiply44x2(mv, view, models[0]); mat44 mv; multiply44x2(mv, view, models[0]);
if( count != m.num_instances ) { if( count != m.num_instances ) {

View File

@ -1552,6 +1552,7 @@ 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);
} }
} }
@ -1561,6 +1562,8 @@ void light_update(unsigned num_lights, light_t *lv) {
static inline static inline
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) { shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
// 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);
@ -1571,6 +1574,10 @@ shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor);
// Create a cubemap depth texture // Create a cubemap depth texture
glGenTextures(1, &s->maps[light_index].depth_texture); glGenTextures(1, &s->maps[light_index].depth_texture);
@ -1580,9 +1587,10 @@ shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
} }
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0); glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
@ -1596,7 +1604,46 @@ 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);
// #else
// glDrawBuffer(GL_NONE);
// glReadBuffer(GL_NONE);
// #endif
} }
// Initialise shadow map 2D
glGenTextures(1, &s->maps[light_index].texture_2d);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glGenTextures(1, &s->maps[light_index].depth_texture_2d);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
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);
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 shadowmap_t shadowmap(int texture_width) { // = 1024
@ -1618,25 +1665,14 @@ void shadowmap_destroy(shadowmap_t *s) {
glDeleteFramebuffers(6, s->maps[i].fbos); glDeleteFramebuffers(6, s->maps[i].fbos);
glDeleteTextures(1, &s->maps[i].texture); glDeleteTextures(1, &s->maps[i].texture);
glDeleteTextures(1, &s->maps[i].depth_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;
} }
void shadowmap_set_shadowmatrix(shadowmap_t *s, vec3 aLightPos, vec3 aLightAt, vec3 aLightUp, const mat44 projection) {
// copy44(s->proj, projection);
// lookat44(s->mv, aLightPos, aLightAt, aLightUp);
// mat44 bias = {
// 0.5, 0.0, 0.0, 0.0,
// 0.0, 0.5, 0.0, 0.0,
// 0.0, 0.0, 0.5, 0.0,
// 0.5, 0.5, 0.5, 1.0 };
// multiply44x3(s->shadowmatrix, bias, s->proj, s->mv);
// multiply44x2(s->mvp, projection, s->mv);
}
static shadowmap_t *active_shadowmap = NULL; static shadowmap_t *active_shadowmap = NULL;
void shadowmap_begin(shadowmap_t *s) { void shadowmap_begin(shadowmap_t *s) {
@ -1647,15 +1683,6 @@ void shadowmap_begin(shadowmap_t *s) {
s->step = 0; s->step = 0;
s->light_step = 0; s->light_step = 0;
active_shadowmap = s; active_shadowmap = s;
for (int i = 0; i < MAX_LIGHTS; i++) {
for (int j = 0; j < 6; j++) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[i].fbos[j]);
glClearColor(0, 0, 0, 0);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
}
} }
static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) { static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) {
@ -1674,6 +1701,42 @@ static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) {
copy44(s->V, V); copy44(s->V, V);
copy44(s->PV, PV); copy44(s->PV, PV);
s->shadow_technique = l->shadow_technique = SHADOW_VSM;
}
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir) {
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);
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);
}
vec3 center = vec3(0, 0, 0);
vec3 lightPos = sub3(center, scale3(lightDir, l->shadow_distance*0.5f));
lookat44(V, lightPos, center, up);
multiply44x2(PV, P, V);
copy44(s->V, V);
copy44(s->PV, PV);
copy44(l->shadow_matrix, PV);
s->shadow_technique = l->shadow_technique = SHADOW_PCF;
} }
bool shadowmap_step(shadowmap_t *s) { bool shadowmap_step(shadowmap_t *s) {
@ -1683,13 +1746,21 @@ bool shadowmap_step(shadowmap_t *s) {
return false; return false;
} }
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[s->step]);
glViewport(0, 0, s->texture_width, s->texture_width); glViewport(0, 0, s->texture_width, s->texture_width);
s->step++; s->step++;
s->skip_render = false;
s->lights_pushed = 0;
return true; return true;
} }
static inline
void shadowmap_clear_fbo() {
glClearColor(0, 0, 0, 0);
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) {
if (l->cast_shadows) { if (l->cast_shadows) {
int step = s->step - 1; int step = s->step - 1;
@ -1697,9 +1768,24 @@ void shadowmap_light(shadowmap_t *s, light_t *l) {
if (l->type == LIGHT_POINT) { if (l->type == LIGHT_POINT) {
shadowmap_light_point(s, l, step); shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_SPOT) { } else if (l->type == LIGHT_SPOT) {
// spot light shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_DIRECTIONAL) { } else if (l->type == LIGHT_DIRECTIONAL) {
// directional light shadowmap_light_directional(s, l, step);
}
if (s->skip_render) {
return;
}
ASSERT(s->lights_pushed == 0);
s->lights_pushed++;
if (l->type == LIGHT_DIRECTIONAL) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d);
shadowmap_clear_fbo();
} else {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]);
shadowmap_clear_fbo();
} }
} }
} }
@ -1711,16 +1797,6 @@ void shadowmap_end(shadowmap_t *s) {
active_shadowmap = NULL; active_shadowmap = NULL;
} }
// shadowmap utils
void shadowmatrix_proj(mat44 shm_proj, float aLightFov, float znear, float zfar) {
perspective44(shm_proj, aLightFov, 1.0f, znear, zfar);
}
void shadowmatrix_ortho(mat44 shm_proj, float left, float right, float bottom, float top, float znear, float zfar) {
ortho44(shm_proj, left, right, bottom, top, znear, zfar);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Occlusion queries // Occlusion queries
@ -3890,6 +3966,7 @@ void model_set_uniforms(model_t m, int shader, mat44 mv, mat44 proj, mat44 view,
ASSERT(sm); ASSERT(sm);
shader_mat44("cameraToShadowView", sm->V); shader_mat44("cameraToShadowView", sm->V);
shader_mat44("cameraToShadowProjector", sm->PV); shader_mat44("cameraToShadowProjector", sm->PV);
shader_int("shadow_technique", sm->shadow_technique);
} }
// shadow receiving // shadow receiving
@ -3898,6 +3975,7 @@ 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());
} }
} else { } else {
shader_bool("u_shadow_receiver", GL_FALSE); shader_bool("u_shadow_receiver", GL_FALSE);
@ -5048,6 +5126,10 @@ void model_render_instanced_pass(model_t m, mat44 proj, mat44 view, mat44* model
if(!m.iqm) return; if(!m.iqm) return;
iqm_t *q = m.iqm; iqm_t *q = m.iqm;
if (active_shadowmap && active_shadowmap->skip_render) {
return;
}
mat44 mv; multiply44x2(mv, view, models[0]); mat44 mv; multiply44x2(mv, view, models[0]);
if( count != m.num_instances ) { if( count != m.num_instances ) {

View File

@ -291,6 +291,11 @@ enum LIGHT_TYPE {
LIGHT_SPOT, LIGHT_SPOT,
}; };
enum SHADOW_TECHNIQUE {
SHADOW_VSM,
SHADOW_PCF,
};
typedef struct light_t { typedef struct light_t {
char type; char type;
vec3 diffuse, specular, ambient; vec3 diffuse, specular, ambient;
@ -305,8 +310,10 @@ typedef struct light_t {
// Shadowmapping // Shadowmapping
bool cast_shadows; bool cast_shadows;
unsigned shadow_technique;
float shadow_distance; float shadow_distance;
float shadow_bias; float shadow_bias;
mat44 shadow_matrix;
// internals // internals
bool cached; //< used by scene to invalidate cached light data bool cached; //< used by scene to invalidate cached light data
@ -342,9 +349,15 @@ typedef struct shadowmap_t {
int texture_width; int texture_width;
int step; int step;
int light_step; int light_step;
unsigned shadow_technique;
// signals
bool skip_render;
int lights_pushed;
struct { struct {
handle fbos[6], texture, depth_texture; handle fbos[6], texture, depth_texture;
handle fbo_2d, texture_2d, depth_texture_2d;
} maps[MAX_LIGHTS]; } maps[MAX_LIGHTS];
handle saved_fb; handle saved_fb;
@ -355,10 +368,9 @@ typedef struct shadowmap_t {
API shadowmap_t shadowmap(int texture_width); // = 1024 API shadowmap_t shadowmap(int texture_width); // = 1024
API void shadowmap_destroy(shadowmap_t *s); API void shadowmap_destroy(shadowmap_t *s);
API void shadowmap_set_shadowmatrix(shadowmap_t *s, vec3 aLightPos, vec3 aLightAt, vec3 aLightUp, const mat44 projection);
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); API void shadowmap_light(shadowmap_t *s, light_t *l); //< can be called once per shadowmap_step
API void shadowmap_end(shadowmap_t *s); API void shadowmap_end(shadowmap_t *s);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -18351,6 +18351,7 @@ 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);
} }
} }
@ -18360,6 +18361,8 @@ void light_update(unsigned num_lights, light_t *lv) {
static inline static inline
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) { shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
float borderColor[] = {1.0, 1.0, 1.0, 1.0};
// 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);
@ -18370,6 +18373,10 @@ shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor);
// Create a cubemap depth texture // Create a cubemap depth texture
glGenTextures(1, &s->maps[light_index].depth_texture); glGenTextures(1, &s->maps[light_index].depth_texture);
@ -18379,9 +18386,10 @@ shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
} }
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, borderColor);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0); glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
@ -18395,7 +18403,46 @@ 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);
// #else
// glDrawBuffer(GL_NONE);
// glReadBuffer(GL_NONE);
// #endif
} }
// Initialise shadow map 2D
glGenTextures(1, &s->maps[light_index].texture_2d);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].texture_2d);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glGenTextures(1, &s->maps[light_index].depth_texture_2d);
glBindTexture(GL_TEXTURE_2D, s->maps[light_index].depth_texture_2d);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
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);
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 shadowmap_t shadowmap(int texture_width) { // = 1024
@ -18417,25 +18464,14 @@ void shadowmap_destroy(shadowmap_t *s) {
glDeleteFramebuffers(6, s->maps[i].fbos); glDeleteFramebuffers(6, s->maps[i].fbos);
glDeleteTextures(1, &s->maps[i].texture); glDeleteTextures(1, &s->maps[i].texture);
glDeleteTextures(1, &s->maps[i].depth_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;
} }
void shadowmap_set_shadowmatrix(shadowmap_t *s, vec3 aLightPos, vec3 aLightAt, vec3 aLightUp, const mat44 projection) {
// copy44(s->proj, projection);
// lookat44(s->mv, aLightPos, aLightAt, aLightUp);
// mat44 bias = {
// 0.5, 0.0, 0.0, 0.0,
// 0.0, 0.5, 0.0, 0.0,
// 0.0, 0.0, 0.5, 0.0,
// 0.5, 0.5, 0.5, 1.0 };
// multiply44x3(s->shadowmatrix, bias, s->proj, s->mv);
// multiply44x2(s->mvp, projection, s->mv);
}
static shadowmap_t *active_shadowmap = NULL; static shadowmap_t *active_shadowmap = NULL;
void shadowmap_begin(shadowmap_t *s) { void shadowmap_begin(shadowmap_t *s) {
@ -18446,15 +18482,6 @@ void shadowmap_begin(shadowmap_t *s) {
s->step = 0; s->step = 0;
s->light_step = 0; s->light_step = 0;
active_shadowmap = s; active_shadowmap = s;
for (int i = 0; i < MAX_LIGHTS; i++) {
for (int j = 0; j < 6; j++) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[i].fbos[j]);
glClearColor(0, 0, 0, 0);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
}
} }
static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) { static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) {
@ -18473,6 +18500,42 @@ static void shadowmap_light_point(shadowmap_t *s, light_t *l, int dir) {
copy44(s->V, V); copy44(s->V, V);
copy44(s->PV, PV); copy44(s->PV, PV);
s->shadow_technique = l->shadow_technique = SHADOW_VSM;
}
static void shadowmap_light_directional(shadowmap_t *s, light_t *l, int dir) {
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);
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);
}
vec3 center = vec3(0, 0, 0);
vec3 lightPos = sub3(center, scale3(lightDir, l->shadow_distance*0.5f));
lookat44(V, lightPos, center, up);
multiply44x2(PV, P, V);
copy44(s->V, V);
copy44(s->PV, PV);
copy44(l->shadow_matrix, PV);
s->shadow_technique = l->shadow_technique = SHADOW_PCF;
} }
bool shadowmap_step(shadowmap_t *s) { bool shadowmap_step(shadowmap_t *s) {
@ -18482,13 +18545,21 @@ bool shadowmap_step(shadowmap_t *s) {
return false; return false;
} }
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[s->step]);
glViewport(0, 0, s->texture_width, s->texture_width); glViewport(0, 0, s->texture_width, s->texture_width);
s->step++; s->step++;
s->skip_render = false;
s->lights_pushed = 0;
return true; return true;
} }
static inline
void shadowmap_clear_fbo() {
glClearColor(0, 0, 0, 0);
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) {
if (l->cast_shadows) { if (l->cast_shadows) {
int step = s->step - 1; int step = s->step - 1;
@ -18496,9 +18567,24 @@ void shadowmap_light(shadowmap_t *s, light_t *l) {
if (l->type == LIGHT_POINT) { if (l->type == LIGHT_POINT) {
shadowmap_light_point(s, l, step); shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_SPOT) { } else if (l->type == LIGHT_SPOT) {
// spot light shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_DIRECTIONAL) { } else if (l->type == LIGHT_DIRECTIONAL) {
// directional light shadowmap_light_directional(s, l, step);
}
if (s->skip_render) {
return;
}
ASSERT(s->lights_pushed == 0);
s->lights_pushed++;
if (l->type == LIGHT_DIRECTIONAL) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbo_2d);
shadowmap_clear_fbo();
} else {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[s->light_step].fbos[step]);
shadowmap_clear_fbo();
} }
} }
} }
@ -18510,16 +18596,6 @@ void shadowmap_end(shadowmap_t *s) {
active_shadowmap = NULL; active_shadowmap = NULL;
} }
// shadowmap utils
void shadowmatrix_proj(mat44 shm_proj, float aLightFov, float znear, float zfar) {
perspective44(shm_proj, aLightFov, 1.0f, znear, zfar);
}
void shadowmatrix_ortho(mat44 shm_proj, float left, float right, float bottom, float top, float znear, float zfar) {
ortho44(shm_proj, left, right, bottom, top, znear, zfar);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Occlusion queries // Occlusion queries
@ -20689,6 +20765,7 @@ void model_set_uniforms(model_t m, int shader, mat44 mv, mat44 proj, mat44 view,
ASSERT(sm); ASSERT(sm);
shader_mat44("cameraToShadowView", sm->V); shader_mat44("cameraToShadowView", sm->V);
shader_mat44("cameraToShadowProjector", sm->PV); shader_mat44("cameraToShadowProjector", sm->PV);
shader_int("shadow_technique", sm->shadow_technique);
} }
// shadow receiving // shadow receiving
@ -20697,6 +20774,7 @@ 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());
} }
} else { } else {
shader_bool("u_shadow_receiver", GL_FALSE); shader_bool("u_shadow_receiver", GL_FALSE);
@ -21847,6 +21925,10 @@ void model_render_instanced_pass(model_t m, mat44 proj, mat44 view, mat44* model
if(!m.iqm) return; if(!m.iqm) return;
iqm_t *q = m.iqm; iqm_t *q = m.iqm;
if (active_shadowmap && active_shadowmap->skip_render) {
return;
}
mat44 mv; multiply44x2(mv, view, models[0]); mat44 mv; multiply44x2(mv, view, models[0]);
if( count != m.num_instances ) { if( count != m.num_instances ) {

View File

@ -3326,6 +3326,11 @@ enum LIGHT_TYPE {
LIGHT_SPOT, LIGHT_SPOT,
}; };
enum SHADOW_TECHNIQUE {
SHADOW_VSM,
SHADOW_PCF,
};
typedef struct light_t { typedef struct light_t {
char type; char type;
vec3 diffuse, specular, ambient; vec3 diffuse, specular, ambient;
@ -3340,8 +3345,10 @@ typedef struct light_t {
// Shadowmapping // Shadowmapping
bool cast_shadows; bool cast_shadows;
unsigned shadow_technique;
float shadow_distance; float shadow_distance;
float shadow_bias; float shadow_bias;
mat44 shadow_matrix;
// internals // internals
bool cached; //< used by scene to invalidate cached light data bool cached; //< used by scene to invalidate cached light data
@ -3377,9 +3384,15 @@ typedef struct shadowmap_t {
int texture_width; int texture_width;
int step; int step;
int light_step; int light_step;
unsigned shadow_technique;
// signals
bool skip_render;
int lights_pushed;
struct { struct {
handle fbos[6], texture, depth_texture; handle fbos[6], texture, depth_texture;
handle fbo_2d, texture_2d, depth_texture_2d;
} maps[MAX_LIGHTS]; } maps[MAX_LIGHTS];
handle saved_fb; handle saved_fb;
@ -3390,10 +3403,9 @@ typedef struct shadowmap_t {
API shadowmap_t shadowmap(int texture_width); // = 1024 API shadowmap_t shadowmap(int texture_width); // = 1024
API void shadowmap_destroy(shadowmap_t *s); API void shadowmap_destroy(shadowmap_t *s);
API void shadowmap_set_shadowmatrix(shadowmap_t *s, vec3 aLightPos, vec3 aLightAt, vec3 aLightUp, const mat44 projection);
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); API void shadowmap_light(shadowmap_t *s, light_t *l); //< can be called once per shadowmap_step
API void shadowmap_end(shadowmap_t *s); API void shadowmap_end(shadowmap_t *s);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------