wip: shadowmaps

main
Dominik Madarász 2024-08-29 17:32:34 +02:00
parent ac0dd11cc1
commit 2744bbbc47
21 changed files with 6366 additions and 895 deletions

View File

@ -1019,6 +1019,7 @@ typedef struct renderstate_t {
unsigned polygon_mode_face; unsigned polygon_mode_face;
unsigned polygon_mode_draw; unsigned polygon_mode_draw;
bool scissor_test_enabled; bool scissor_test_enabled;
bool seamless_cubemap;
} renderstate_t; } renderstate_t;
renderstate_t renderstate(); renderstate_t renderstate();
bool renderstate_compare(const renderstate_t *stateA, const renderstate_t *stateB); bool renderstate_compare(const renderstate_t *stateA, const renderstate_t *stateB);
@ -1133,24 +1134,61 @@ typedef struct cubemap_t {
void fbo_bind(unsigned id); void fbo_bind(unsigned id);
void fbo_unbind(); void fbo_unbind();
void fbo_destroy(unsigned id); void fbo_destroy(unsigned id);
enum {
MAX_LIGHTS = 16,
};
enum LIGHT_TYPE {
LIGHT_DIRECTIONAL,
LIGHT_POINT,
LIGHT_SPOT,
};
typedef struct light_t {
char type;
vec3 diffuse, specular, ambient;
vec3 pos, dir;
struct {
float constant, linear, quadratic;
} falloff;
float radius;
float specularPower;
float innerCone, outerCone;
bool cast_shadows;
float shadow_distance;
float shadow_bias;
bool cached;
} light_t;
light_t light();
void light_type(light_t* l, char type);
void light_diffuse(light_t* l, vec3 color);
void light_specular(light_t* l, vec3 color);
void light_ambient(light_t* l, vec3 color);
void light_teleport(light_t* l, vec3 pos);
void light_dir(light_t* l, vec3 dir);
void light_power(light_t* l, float power);
void light_radius(light_t* l, float radius);
void light_falloff(light_t* l, float constant, float linear, float quadratic);
void light_cone(light_t* l, float innerCone, float outerCone);
void light_update(unsigned num_lights, light_t *lv);
typedef struct shadowmap_t { typedef struct shadowmap_t {
mat44 shadowmatrix; mat44 V;
mat44 mvp; mat44 PV;
mat44 mv;
mat44 proj;
vec4 light_position;
int saved_fb;
int saved_viewport[4];
handle fbo, texture;
int texture_width; int texture_width;
int step;
int light_step;
struct {
handle fbos[6], texture, depth_texture;
} maps[MAX_LIGHTS];
handle saved_fb;
handle saved_pass;
int saved_vp[4];
} 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_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);
void shadowmap_light(shadowmap_t *s, light_t *l);
void shadowmap_end(shadowmap_t *s); void shadowmap_end(shadowmap_t *s);
void shadowmatrix_proj(mat44 shm_proj, float aLightFov, float znear, float zfar);
void shadowmatrix_ortho(mat44 shm_proj, float left, float right, float bottom, float top, float znear, float zfar);
unsigned shader(const char *vs, const char *fs, const char *attribs, const char *fragcolor, const char *defines); unsigned shader(const char *vs, const char *fs, const char *attribs, const char *fragcolor, const char *defines);
unsigned shader_geom(const char *gs, const char *vs, const char *fs, const char *attribs, const char *fragcolor, const char *defines); unsigned shader_geom(const char *gs, const char *vs, const char *fs, const char *attribs, const char *fragcolor, const char *defines);
unsigned shader_bind(unsigned program); unsigned shader_bind(unsigned program);
@ -1400,6 +1438,9 @@ typedef struct model_t {
unsigned num_anims; unsigned num_anims;
unsigned num_frames; unsigned num_frames;
handle program; handle program;
handle shadow_program;
shadowmap_t *shadow_map;
bool shadow_receiver;
float curframe; float curframe;
mat44 pivot; mat44 pivot;
int stride; int stride;
@ -1442,6 +1483,7 @@ enum BILLBOARD_MODE {
void model_shading(model_t*, int shading); void model_shading(model_t*, int shading);
void model_shading_custom(model_t*, int shading, const char *vs, const char *fs, const char *defines); void model_shading_custom(model_t*, int shading, const char *vs, const char *fs, const char *defines);
void model_skybox(model_t*, skybox_t sky, bool load_sh); void model_skybox(model_t*, skybox_t sky, bool load_sh);
void model_shadow(model_t*, shadowmap_t *sm);
void model_fog(model_t*, unsigned mode, vec3 color, float start, float end, float density); void model_fog(model_t*, unsigned mode, vec3 color, float start, float end, float density);
void model_render(model_t, mat44 proj, mat44 view, mat44 model, int shader); void model_render(model_t, mat44 proj, mat44 view, mat44 model, int shader);
void model_render_skeleton(model_t, mat44 model); void model_render_skeleton(model_t, mat44 model);
@ -1606,38 +1648,6 @@ typedef struct object_t {
void object_diffuse_push(object_t *obj, texture_t tex); void object_diffuse_push(object_t *obj, texture_t tex);
void object_diffuse_pop(object_t *obj); void object_diffuse_pop(object_t *obj);
void object_billboard(object_t *obj, unsigned mode); void object_billboard(object_t *obj, unsigned mode);
enum LIGHT_TYPE {
LIGHT_DIRECTIONAL,
LIGHT_POINT,
LIGHT_SPOT,
};
enum LIGHT_FLAGS {
LIGHT_CAST_SHADOWS = 1,
};
typedef struct light_t {
char type;
vec3 diffuse, specular, ambient;
vec3 pos, dir;
struct {
float constant, linear, quadratic;
} falloff;
float radius;
float specularPower;
float innerCone, outerCone;
bool cached;
} light_t;
light_t light();
void light_type(light_t* l, char type);
void light_diffuse(light_t* l, vec3 color);
void light_specular(light_t* l, vec3 color);
void light_ambient(light_t* l, vec3 color);
void light_teleport(light_t* l, vec3 pos);
void light_dir(light_t* l, vec3 dir);
void light_power(light_t* l, float power);
void light_radius(light_t* l, float radius);
void light_falloff(light_t* l, float constant, float linear, float quadratic);
void light_cone(light_t* l, float innerCone, float outerCone);
void light_update(unsigned num_lights, light_t *lv);
enum SCENE_FLAGS { enum SCENE_FLAGS {
SCENE_WIREFRAME = 1, SCENE_WIREFRAME = 1,
SCENE_CULLFACE = 2, SCENE_CULLFACE = 2,

View File

@ -18,6 +18,7 @@ const char *OBJ_MDLS[] = {
int main(int argc, char** argv) { int main(int argc, char** argv) {
window_create(85, WINDOW_MSAA8); window_create(85, WINDOW_MSAA8);
window_fps_unlock();
camera_t cam = camera(); { camera_t cam = camera(); {
cam.position = vec3(0, 7.5, 15); cam.position = vec3(0, 7.5, 15);
@ -63,8 +64,9 @@ int main(int argc, char** argv) {
camera_fps(&cam, mouse.x,mouse.y); camera_fps(&cam, mouse.x,mouse.y);
static bool first_time = true; static bool first_time = true;
static bool animate_probe_pos = false; static bool animate_probe_pos = true;
static bool follow_cam = false; static bool follow_cam = false;
static bool show_env_probe = false;
static vec3 probe_pos; static vec3 probe_pos;
if (input_down(KEY_T)) { if (input_down(KEY_T)) {
animate_probe_pos = !animate_probe_pos; animate_probe_pos = !animate_probe_pos;
@ -72,9 +74,13 @@ int main(int argc, char** argv) {
if (input_down(KEY_F)) { if (input_down(KEY_F)) {
follow_cam = !follow_cam; follow_cam = !follow_cam;
} }
if (input_down(KEY_P)) {
show_env_probe = !show_env_probe;
}
if (animate_probe_pos) { if (animate_probe_pos) {
probe_pos = vec3(0, 5, 0); probe_pos = vec3(0, 5.5, 1);
probe_pos.x = sinf(window_time()*2)*2.0f; probe_pos.x = sinf(window_time()*2)*3.9f;
probe_pos.y += cosf(window_time()*2)*1.0;
} }
if (input_down(KEY_SPACE) || first_time || animate_probe_pos || follow_cam) { if (input_down(KEY_SPACE) || first_time || animate_probe_pos || follow_cam) {
first_time = false; first_time = false;
@ -85,7 +91,7 @@ int main(int argc, char** argv) {
if (follow_cam) { if (follow_cam) {
probe_pos = cam.position; probe_pos = cam.position;
} }
unsigned tex_size = 256; unsigned tex_size = 64;
cubemap_bake_begin(&env_probe.cubemap, probe_pos, tex_size, tex_size); cubemap_bake_begin(&env_probe.cubemap, probe_pos, tex_size, tex_size);
while (cubemap_bake_step(&env_probe.cubemap, probe_proj, probe_view)) { while (cubemap_bake_step(&env_probe.cubemap, probe_proj, probe_view)) {
skybox_render(&sky, probe_proj, probe_view); skybox_render(&sky, probe_proj, probe_view);
@ -102,14 +108,16 @@ int main(int argc, char** argv) {
// render // render
mat44 mvp; multiply44x2(mvp, cam.proj, cam.view); mat44 mvp; multiply44x2(mvp, cam.proj, cam.view);
{ {
// skybox_render(&env_probe, cam.proj, cam.view); if (show_env_probe) {
skybox_render(&sky, cam.proj, cam.view); skybox_render(&env_probe, cam.proj, cam.view);
} else {
skybox_render(&sky, cam.proj, cam.view);
shader_bind(mdl.program);
cubemap_sh_blend(vec3(0,0,0), 10.0f, 1, &env_probe.cubemap);
shader_int("u_textured", false);
shader_bind(mdl.program); model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
cubemap_sh_blend(vec3(0,0,0), 10.0f, 1, &env_probe.cubemap); }
shader_int("u_textured", false);
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
} }
if( ui_panel("Scene", 0)) { if( ui_panel("Scene", 0)) {
@ -121,7 +129,7 @@ int main(int argc, char** argv) {
} }
ui_separator(); ui_separator();
for (int i = 0; i < 9; i++) { for (int i = 0; i < 9; i++) {
ui_color3f(va("SH Coefficient [%d]", i), &sky.cubemap.sh[i].x); ui_color3f(va("SH Coefficient [%d]", i), &env_probe.cubemap.sh[i].x);
} }
ui_panel_end(); ui_panel_end();
} }

127
demos/09-shadows.c 100644
View File

@ -0,0 +1,127 @@
#include "v4k.h"
int SKY_DIR = 0;
const char *SKY_DIRS[] = {
"cubemaps/bridge3/",
"cubemaps/colors/",
"cubemaps/colors2/",
"hdr/Tokyo_BigSight_1k.hdr",
};
int OBJ_MDL = 0;
const char *OBJ_MDLS[] = {
"meshes/ShadowsTest.obj",
"meshes/sphere.obj",
"meshes/suzanne.obj",
"meshes/gazebo.obj",
};
int main(int argc, char** argv) {
window_create(85, WINDOW_MSAA8);
// window_fps_unlock();
camera_t cam = camera(); {
cam.position = vec3(0, 7.5, 15);
cam.pitch = -15;
cam.yaw = -90;
camera_fps(&cam, 0, 0);
}
skybox_t sky = {0};
model_t mdl = {0};
shadowmap_t sm = {0};
light_t lit = light(); {
lit.type = LIGHT_POINT;
lit.cast_shadows = true;
// lit.shadow_distance = 3.0f;
}
light_t lit2 = light(); {
lit2.type = LIGHT_POINT;
lit2.cast_shadows = true;
lit2.diffuse = vec3(1, 0.7, 0.8);
}
array(light_t) lights = 0;
array_push(lights, lit);
array_push(lights, lit2);
bool initialized = 0;
bool must_reload = 0;
while( window_swap()) {
if (input_down(KEY_ESC)) break;
// reloading
if( must_reload ) {
must_reload = 0;
skybox_destroy(&sky);
model_destroy(mdl);
initialized = 0;
}
if( !initialized ) {
initialized = 1;
sky = skybox(flag("--mie") ? 0 : SKY_DIRS[SKY_DIR], 0);
sm = shadowmap(1024);
mdl = model(OBJ_MDLS[OBJ_MDL], 0);
shader_bind(mdl.program);
cubemap_sh_shader(&sky.cubemap);
}
// fps camera
bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
window_cursor( !active );
if( active ) cam.speed = clampf(cam.speed + input_diff(MOUSE_W) / 10, 0.05f, 5.0f);
vec2 mouse = scale2(vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
vec3 wasdec = scale3(vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-input(KEY_C),input(KEY_W)-input(KEY_S)), cam.speed);
camera_moveby(&cam, wasdec);
camera_fps(&cam, mouse.x,mouse.y);
// Animate light
lights[0].pos = vec3(0, 5.5, 1);
lights[0].pos.x += sinf(window_time()*2)*4.5f;
lights[0].pos.y += cosf(window_time()*2)*1.0;
lights[0].pos.z += cosf(window_time()*2)*6.0;
lights[1].pos = vec3(0, 7.5, 1);
lights[1].pos.x += sinf(window_time()*4)*4.5f;
lights[1].pos.y += cosf(window_time()*4)*1.0;
lights[1].pos.z += cosf(window_time()*4)*6.0;
// Render shadowmap
shadowmap_begin(&sm);
{
for (int i = 0; i < array_count(lights); i++) {
while (shadowmap_step(&sm)) {
shadowmap_light(&sm, &lights[i]);
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
}
}
}
shadowmap_end(&sm);
// render
mat44 mvp; multiply44x2(mvp, cam.proj, cam.view);
{
skybox_render(&sky, cam.proj, cam.view);
shader_bind(mdl.program);
shader_int("u_textured", false);
light_update(array_count(lights), lights);
ddraw_sphere(lights[0].pos, 0.1f);
ddraw_sphere(lights[1].pos, 0.1f);
ddraw_flush();
model_shadow(&mdl, &sm);
model_render(mdl, cam.proj, cam.view, mdl.pivot, 0);
}
if( ui_panel("Scene", 0)) {
if( ui_list("Skybox", SKY_DIRS, countof(SKY_DIRS), &SKY_DIR) ) {
must_reload = 1;
}
if( ui_list("Model", OBJ_MDLS, countof(OBJ_MDLS), &OBJ_MDL) ) {
must_reload = 1;
}
ui_panel_end();
}
}
}

633
demos/99-spot.c 100644
View File

@ -0,0 +1,633 @@
// [ref] http://fabiensanglard.net/shadowmappingVSM/index.php
// [ref] http://www.opengl-tutorial.org/es/intermediate-tutorials/tutorial-16-shadow-mapping/
// [ref] https://github.com/cforfang/opengl-shadowmapping
// [ref] https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping
// @todo: spotlight (cone light)
// @todo: pointlight (cubemap light)
// @todo: area light (rect or circle light)
// @todo: directional light (sunlight)
// further reading (in order):
// DPSM [3] Brabec, Annen: Shadow mapping for hemispherical and omnidirectional light sources (2002)
// DPSM* [4] Osman, Bukowski: Practical implementation of dual paraboloid shadow maps (2006)
// IPSM [5] Vanek, Herout: High-quality Shadows with Improved Paraboloid Mapping (2011)
// LiSPSMs
// CSMs
// status: CUBE(0)+BLUR(0): ok
// status: CUBE(0)+BLUR(1): ok
// status: CUBE(1)+BLUR(1): ok
// status: CUBE(1)+BLUR(0): ok
// status: CUBE(?)+BLUR(?): no {
// 003.470s|!cannot find uniform 'shadowMap' in shader program 21 |shader_uniform|fwk_render.c:772
// 001: 00007FF7AF6A3FDA callstack (C:\prj\thread\FWK\fwk_system.c:250)
// 002: 00007FF7AF8E7CBC shader_uniform (C:\prj\thread\FWK\fwk_render.c:772)
// 003: 00007FF7AF691C27 shader_int (C:\prj\thread\FWK\fwk_render.c:777)
// 004: 00007FF7AF8F54EF color_begin (C:\prj\thread\FWK\spot.c:525)
// 005: 00007FF7AF8F5BF7 main (C:\prj\thread\FWK\spot.c:607)
// }
#ifndef VSMCUBE
#define VSMCUBE 0
#endif
#ifndef VSMBLUR
#define VSMBLUR 1
#endif
#include "v4k.h"
model_t sponza;
typedef struct Mesh
{
GLuint vao;
GLuint vbo;
} Mesh;
static float quadVertices[] = {
// Front-face
// Pos // Color //Tex // Norm
-1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Top-left
1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Top-right
1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // Bottom-right
1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // Bottom-right
-1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, //Bottom-left
-1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Top-left
};
static Mesh create_mesh(float* verts, int size) {
Mesh mesh;
// Create VAO
glGenVertexArrays(1, &mesh.vao);
glBindVertexArray(mesh.vao);
// Create VBO and copy the vertex data to it
glGenBuffers(1, &mesh.vbo);
glBindBuffer(GL_ARRAY_BUFFER, mesh.vbo);
glBufferData(GL_ARRAY_BUFFER, size, verts, GL_STATIC_DRAW);
// Enable attribs
// Position
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(float), 0);
// Color
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void*)(3 * sizeof(float)));
// Texcoords
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void*)(6 * sizeof(float)));
// Normal
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 11 * sizeof(float), (void*)(8 * sizeof(float)));
return mesh;
}
Mesh create_quad() {
return create_mesh(quadVertices, sizeof(quadVertices));
}
/*
#define Mesh model_t
model_t create_cube() {
model_t m = model("#meshes/cube.obj", 0); //MESH_TRIANGLE_STRIP);
return m;
}
*/
static const char* const vs_shadow_blur = "//" FILELINE "\n"
"in vec3 position;\n"
"in vec2 texcoord;\n"
"out vec2 Texcoord;\n"
"void main() {\n"
" gl_Position = vec4(position, 1.0);\n"
" Texcoord = texcoord;\n"
"}\n";
static const char* const fs_shadow_blur = "//" FILELINE "\n"
"uniform sampler2D textureSource;\n"
"uniform vec2 ScaleU;\n"
"in vec2 Texcoord;\n"
"out vec4 outColor;\n"
"void main() {\n"
" vec4 color = vec4(0.0);\n"
" color += texture( textureSource, Texcoord.st + vec2( -3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;\n"
" color += texture( textureSource, Texcoord.st + vec2( -2.0*ScaleU.x, -2.0*ScaleU.y ) )*0.09375;\n"
" color += texture( textureSource, Texcoord.st + vec2( -1.0*ScaleU.x, -1.0*ScaleU.y ) )*0.234375;\n"
" color += texture( textureSource, Texcoord.st + vec2( 0.0 , 0.0) )*0.3125;\n"
" color += texture( textureSource, Texcoord.st + vec2( 1.0*ScaleU.x, 1.0*ScaleU.y ) )*0.234375;\n"
" color += texture( textureSource, Texcoord.st + vec2( 2.0*ScaleU.x, 2.0*ScaleU.y ) )*0.09375;\n"
" color += texture( textureSource, Texcoord.st + vec2( 3.0*ScaleU.x, -3.0*ScaleU.y ) ) * 0.015625;\n"
" outColor = vec4(color.xyz, 1.0);\n"
"}\n";
static const char* const vs_shadow_vsm = "//" FILELINE "\n"
"uniform mat4 model;\n"
"uniform mat4 cameraToShadowView;\n"
"uniform mat4 cameraToShadowProjector;\n"
"in vec3 position;\n"
"out vec4 v_position;\n"
"void main() {\n"
" gl_Position = cameraToShadowProjector * model * vec4(position, 1.0);\n"
#if VSMCUBE
" v_position = cameraToShadowView * model * vec4(position, 1.0);\n"
#else
" v_position = gl_Position;\n"
#endif
"}\n";
static const char* const fs_shadow_vsm = "//" FILELINE "\n"
"in vec4 v_position;\n"
"out vec4 outColor;\n"
"void main() {\n"
#if VSMCUBE
" float depth = length( vec3(v_position) ) / 20;\n"
#else
" float depth = v_position.z / v_position.w;\n"
" depth = depth * 0.5 + 0.5;\n"
#endif
" float moment1 = depth;\n"
" float moment2 = depth * depth;\n"
" float dx = dFdx(depth);\n"
" float dy = dFdy(depth);\n"
" moment2 += 0.25*(dx*dx+dy*dy);\n"
" outColor = vec4( moment1, moment2, 0.0, 0.0);\n"
"}\n";
struct shadow {
// Resources
GLuint shadowProgram, blurProgram;
Mesh quadMesh;
GLuint shadowMapFBO, shadowMapTex, shadowMapTexDepth;
GLuint blurFBO, blurTex;
// Options
bool do_blur;
bool do_debugshadow;
float BLUR_SCALE; // Amount of blurring [0..100]
GLuint SHADOWMAP_WIDTH; // 256,512,1024,2048
// State
int shadow_active;
vec3 lightPos;
vec3 lightAimPos;
// VSM cubemap
GLuint cubeTex, cubeDepthTex, cubeFBOs[6];
GLuint currentSideTex, currentSideDepthTex;
GLuint toCurrentSideFBO;
} shadow;
#define shadowProgram shadow.shadowProgram
#define blurProgram shadow.blurProgram
#define quadMesh shadow.quadMesh
#define shadowMapFBO shadow.shadowMapFBO
#define shadowMapTex shadow.shadowMapTex
#define shadowMapTexDepth shadow.shadowMapTexDepth
#define blurFBO shadow.blurFBO
#define blurTex shadow.blurTex
#define do_blur shadow.do_blur
#define do_debugshadow shadow.do_debugshadow
#define BLUR_SCALE shadow.BLUR_SCALE
#define SHADOWMAP_WIDTH shadow.SHADOWMAP_WIDTH
#define shadow_active shadow.shadow_active
#define lightPos shadow.lightPos
#define lightAimPos shadow.lightAimPos
// vsm cubemap
#define cubeTex shadow.cubeTex
#define cubeDepthTex shadow.cubeDepthTex
#define cubeFBOs shadow.cubeFBOs
#define currentSideTex shadow.currentSideTex
#define currentSideDepthTex shadow.currentSideDepthTex
#define toCurrentSideFBO shadow.toCurrentSideFBO
static GLuint cubemap_create(GLsizei size, int flags) {
GLenum texel = flags & TEXTURE_DEPTH ? GL_DEPTH_COMPONENT : GL_RGB32F;
GLenum pixel = flags & TEXTURE_DEPTH ? GL_DEPTH_COMPONENT : GL_RGB;
GLenum storage = flags & TEXTURE_DEPTH ? GL_FLOAT : GL_UNSIGNED_BYTE; // swap?
GLuint id;
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_CUBE_MAP, id);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, texel, size, size, 0, pixel, storage, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, texel, size, size, 0, pixel, storage, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, texel, size, size, 0, pixel, storage, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, texel, size, size, 0, pixel, storage, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, texel, size, size, 0, pixel, storage, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, texel, size, size, 0, pixel, storage, NULL);
if( flags & TEXTURE_DEPTH ) {
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
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_EDGE);
} else {
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
return id;
}
static void framebuffer_cube_create(GLuint cube_fbo[6], GLuint cube_texture, GLuint cube_texture_depth) {
glGenFramebuffers(6, cube_fbo);
for (int i = 0; i < 6; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, cube_fbo[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cube_texture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cube_texture_depth, 0);
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (GL_FRAMEBUFFER_COMPLETE != result) {
printf("ERROR: Framebuffer is not complete.\n");
}
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
static void set_shadow_matrix_uniform(const GLuint program, int dir) {
if(dir<0) return;
mat44 P, V, PV;
perspective44(P, 90.0f, 1.0f, 0.5f, 100.0f);
/**/ if(dir == 0) lookat44(V, lightPos, add3(lightPos, vec3(+1, 0, 0)), vec3(0, -1, 0)); // +X
else if(dir == 1) lookat44(V, lightPos, add3(lightPos, vec3(-1, 0, 0)), vec3(0, -1, 0)); // -X
else if(dir == 2) lookat44(V, lightPos, add3(lightPos, vec3( 0, +1, 0)), vec3(0, 0, +1)); // +Y
else if(dir == 3) lookat44(V, lightPos, add3(lightPos, vec3( 0, -1, 0)), vec3(0, 0, -1)); // -Y
else if(dir == 4) lookat44(V, lightPos, add3(lightPos, vec3( 0, 0, +1)), vec3(0, -1, 0)); // +Z
else /*dir == 5*/ lookat44(V, lightPos, add3(lightPos, vec3( 0, 0, -1)), vec3(0, -1, 0)); // -Z
multiply44x2(PV, P, V); // -Z
shader_bind(program); shader_mat44("cameraToShadowView", V);
shader_bind(program); shader_mat44("cameraToShadowProjector", PV);
}
void shadow_create(int RESOLUTION) {
do_blur = 1;
do_debugshadow = 0;
BLUR_SCALE = 0.5; // Amount of blurring [0..1]
SHADOWMAP_WIDTH = RESOLUTION; // 256,512,1024,2048
lightPos = vec3(-2, 2.0, -2);
lightAimPos = vec3(0.0, 0, -5.0);
// Create programs
shadowProgram = shader(vs_shadow_vsm, fs_shadow_vsm, "position", "outColor", 0);
blurProgram = shader(vs_shadow_blur, fs_shadow_blur, "position,,texcoord", "outColor", 0);
// ShadowMap-textures and FBO // @todo: GL_RG32F+GL_RG also GL_CLAMP to remove artifacts
shadowMapTex = texture_create(SHADOWMAP_WIDTH, SHADOWMAP_WIDTH, 2, NULL, TEXTURE_FLOAT).id;
shadowMapTexDepth = texture_create(SHADOWMAP_WIDTH, SHADOWMAP_WIDTH, 0, NULL, TEXTURE_DEPTH | TEXTURE_FLOAT).id;
shadowMapFBO = fbo(shadowMapTex, shadowMapTexDepth, 0);
// Textures and FBO to perform blurring
blurTex = texture_create(SHADOWMAP_WIDTH, SHADOWMAP_WIDTH, 2, NULL, TEXTURE_FLOAT).id;
blurFBO = fbo(blurTex, 0, 0);
#if VSMCUBE
// Create cubemap
cubeTex = cubemap_create(SHADOWMAP_WIDTH, 0);
cubeDepthTex = cubemap_create(SHADOWMAP_WIDTH, TEXTURE_DEPTH);
framebuffer_cube_create(cubeFBOs, cubeTex, cubeDepthTex);
// Temporary storage
currentSideTex = texture_create(SHADOWMAP_WIDTH, SHADOWMAP_WIDTH, 2, NULL, TEXTURE_FLOAT /*| TEXTURE_EDGE*/ ).id;
currentSideDepthTex = texture_create(SHADOWMAP_WIDTH, SHADOWMAP_WIDTH, 0, NULL, TEXTURE_DEPTH | TEXTURE_FLOAT).id;
toCurrentSideFBO = fbo(currentSideTex, currentSideDepthTex, 0);
#endif
}
void shadow_destroy() {
glDeleteProgram(shadowProgram);
glDeleteProgram(blurProgram);
glDeleteTextures(1, &blurTex);
glDeleteFramebuffers(1, &blurFBO);
glDeleteTextures(1, &shadowMapTex);
glDeleteTextures(1, &shadowMapTexDepth);
glDeleteFramebuffers(1, &shadowMapFBO);
}
bool shadow_is_active() {
return shadow_active;
}
void shadow_pv_matrix(mat44 PV) {
mat44 P,V;
// perspective44(P, 45.0f, 1.0f, 2.0f, 100.0f);
perspective44(P, 45*2, window_width() / ((float)window_height()+!window_height()), 1.f, 100.f);
lookat44(V, lightPos, lightAimPos, vec3(0, 1, 0)); // Point toward object regardless of position
multiply44x2(PV, P, V);
}
void shadow_position(vec3 p) {
lightPos = p;
}
void shadow_target(vec3 p) {
lightAimPos = p;
}
void shadow_begin() {
shadow_active = 1;
// shadow_state() {
glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS);
glEnable(GL_CULL_FACE); glCullFace(GL_BACK);
glFrontFace(GL_CW);
glBlendFunc(1, 0);
#if VSMCUBE
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
#endif
// }
glBindFramebuffer(GL_FRAMEBUFFER, shadowMapFBO);
glViewport(0, 0, SHADOWMAP_WIDTH, SHADOWMAP_WIDTH);
//glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
mat44 PV; shadow_pv_matrix(PV);
shader_bind(shadowProgram);shader_mat44("cameraToShadowProjector", PV);
}
static void draw_fullscreen_quad() {
//delete_mesh(quadMesh);
if(!quadMesh.vao) quadMesh = create_quad();
glBindVertexArray(quadMesh.vao);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
static void shadow_blur() {
// remap 0(min)..1(max) -> 2(min)..epsilon(max)
float blur_scale = 1.999 * (1 - BLUR_SCALE) + 0.001;
glDisable(GL_DEPTH_TEST);
// Blur shadowMapTex (horizontally) to blurTex
glBindFramebuffer(GL_FRAMEBUFFER, blurFBO);
glViewport(0, 0, SHADOWMAP_WIDTH, SHADOWMAP_WIDTH);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, shadowMapTex); //Input-texture
shader_bind(blurProgram); shader_vec2("ScaleU", vec2(1.0 / (SHADOWMAP_WIDTH*blur_scale), 0));
shader_int("textureSource",0);
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw_fullscreen_quad();
// Blur blurTex vertically and write to shadowMapTex
glBindFramebuffer(GL_FRAMEBUFFER, shadowMapFBO);
glViewport(0, 0, SHADOWMAP_WIDTH, SHADOWMAP_WIDTH);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, blurTex);
shader_bind(blurProgram); shader_vec2("ScaleU", vec2(0, 1.0 / (SHADOWMAP_WIDTH*blur_scale)));
shader_int("textureSource",0);
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw_fullscreen_quad();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glEnable(GL_DEPTH_TEST);
}
void shadow_end() {
// Reset
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
if(do_blur) shadow_blur();
shadow_active = 0;
glViewport(0, 0, window_width(), window_height());
}
void shadow_ui() {
// UI
if( ui_panel("Shadow", 0) ) {
if(ui_toggle("Debug", &do_debugshadow)) {}
if(ui_toggle("Blur shadow", &do_blur)) {}
if(ui_slider("Blur amount", &BLUR_SCALE)) {}
ui_panel_end();
}
if(do_debugshadow) {
// Blur and draw to screen
shader_bind(blurProgram); shader_vec2("ScaleU", vec2(0, 0)); // ... but make sure we don't actually blur
glBindTexture(GL_TEXTURE_2D, shadowMapTex);
draw_fullscreen_quad();
glBindTexture(GL_TEXTURE_2D, 0);
}
}
#define USER_DRAWCALL(shader) do { \
model_render(sponza, camera_get_active()->proj, camera_get_active()->view, sponza.pivot, shader); \
} while(0)
static void vsm_cube_draw()
{
glViewport(0, 0, SHADOWMAP_WIDTH, SHADOWMAP_WIDTH);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
shader_bind(shadowProgram);
// For each side of cubemap
for (int i = 0; i < 6; ++i) {
#if VSMBLUR
// Draw to temp. storage
shader_bind(shadowProgram);
glBindFramebuffer(GL_FRAMEBUFFER, toCurrentSideFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
set_shadow_matrix_uniform(shadowProgram, i);
USER_DRAWCALL(shadowProgram);
// Blur horizontally to blurTex
glDisable(GL_DEPTH_TEST);
shader_bind(blurProgram);
shader_vec2("ScaleU", vec2(1.0 / SHADOWMAP_WIDTH, 0));
glBindFramebuffer(GL_FRAMEBUFFER, blurFBO);
glBindTexture(GL_TEXTURE_2D, currentSideTex);
glClear(GL_COLOR_BUFFER_BIT);
draw_fullscreen_quad();
// Blur vertically to actual cubemap
glBindFramebuffer(GL_FRAMEBUFFER, cubeFBOs[i]);
glBindTexture(GL_TEXTURE_2D, blurTex);
shader_bind(blurProgram);
shader_vec2("ScaleU", vec2(0, 1.0 / SHADOWMAP_WIDTH));
draw_fullscreen_quad();
glEnable(GL_DEPTH_TEST);
#else
// Draw directly to cubemap
glBindFramebuffer(GL_FRAMEBUFFER, cubeFBOs[i]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
set_shadow_matrix_uniform(shadowProgram, i);
USER_DRAWCALL(shadowProgram);
#endif
}
// Reset state
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
}
#undef shadowProgram
#undef blurProgram
#undef quadMesh
#undef shadowMapFBO
#undef shadowMapTex
#undef shadowMapTexDepth
#undef blurFBO
#undef blurTex
#undef do_blur
#undef do_debugshadow
#undef BLUR_SCALE
#undef SHADOWMAP_WIDTH
#undef shadow_active
#undef lightPos
#undef lightAimPos
// Geometry (world coordinates)
static bool do_animate = 1;
static void color_begin(const GLuint program) {
glCullFace(GL_BACK);
#if 1 // VSMCUBE
glViewport(0, 0, window_width(), window_height());
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#endif
// Upload uniforms
shader_bind(program);
shader_mat44("view", camera_get_active()->view);
// shader_mat44("proj", camera_get_active()->proj);
shader_vec3("lightPos", shadow.lightPos);
#if VSMCUBE
set_shadow_matrix_uniform(program, -1);
#else
mat44 PV; shadow_pv_matrix(PV);
shader_mat44("cameraToShadowProjector", PV);
#endif
glActiveTexture(GL_TEXTURE0+1);
#if VSMCUBE
glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTex);
#else
glBindTexture(GL_TEXTURE_2D, shadow.shadowMapTex); //Input-texture
#endif
shader_int("shadowMap",1);
}
static void color_end() {
#if VSMCUBE
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
#else
glBindTexture(GL_TEXTURE_2D, 0);
#endif
}
int main(int argc, char **argv)
{
window_create(0.75f, 0);
camera_t cam = camera();
// init shadowing 384x384 // 512x512
shadow_create(argc > 1 ? atoi(argv[1]) : 384);
sponza = model("sponza.obj", 0);
translation44(sponza.pivot, 0,-1,0);
rotate44(sponza.pivot, -90,1,0,0);
scale44(sponza.pivot, 10,10,10);
model_t suzanne = model("suzanne.obj", 0);
// create geometry
GLuint vsm_program = sponza.iqm->program;
#if 1
const char *tpl[] = {
"{{include-shadowmap}}", fs_0_0_shadowmap_lit,
};
vsm_program = shader(strlerp(1,tpl,vs_323444143_16_332_model), strlerp(1,tpl,fs_32_4_model), "att_position,att_texcoord,att_normal", "fragColor");
#endif
while (window_swap())
{
// input
if (input(KEY_ESC))
break;
// fps camera
bool active = ui_active() ? 0 : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R);
vec3 wasdec = scale3( vec3(input(KEY_D)-input(KEY_A),input(KEY_E)-input(KEY_C),input(KEY_W)-input(KEY_S)), 0.2f);
vec2 mouse = scale2( vec2(input_diff(MOUSE_X), -input_diff(MOUSE_Y)), 0.2f * active);
camera_move(&cam, wasdec.x,wasdec.y,wasdec.z);
camera_fps(&cam, mouse.x,mouse.y);
window_cursor( !active );
#if 1
// animate light
if( do_animate ) {
static vec3 lightPos;
do_once {
lightPos = cam.position;
};
vec3 offs = vec3(sin(window_time()) * 15, 0, cos(window_time()) * 15);
shadow_position(add3(lightPos, offs));
// shadow_target(vec3(0,0,0)); // good for pointlight
shadow_target(add3(add3(lightPos, offs), vec3(0,0,-1)));
}
#else
shadow_position(cam.position);
shadow_target(add3(cam.position, cam.look));
#endif
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// render
#if VSMCUBE
shadow_begin();
vsm_cube_draw();
shadow_end();
#else
shadow_begin();
model_render(sponza, camera_get_active()->proj, camera_get_active()->view, sponza.pivot, shadow.shadowProgram); //< typo!, but works!
shadow_end();
#endif
color_begin(vsm_program);
model_render(sponza, camera_get_active()->proj, camera_get_active()->view, sponza.pivot, vsm_program); // does not work without program
color_end();
// light bulb (suzanne)
{
mat44 M; scaling44(M, 10,10,10); relocate44(M, shadow.lightPos.x, shadow.lightPos.y, shadow.lightPos.z );
model_render(suzanne, camera_get_active()->proj, camera_get_active()->view, M, 0);
}
if( ui_panel("App", 0) ) {
if(ui_toggle("Animate light", &do_animate)) {}
ui_panel_end();
}
shadow_ui();
}
}

View File

@ -0,0 +1,82 @@
# Blender 4.2.1 LTS MTL File: 'None'
# www.blender.org
newmtl backWall
Ns 10.000005
Ka 1.000000 1.000000 1.000000
Kd 0.725000 0.710000 0.680000
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 1
newmtl ceiling
Ns 10.000005
Ka 1.000000 1.000000 1.000000
Kd 0.725000 0.710000 0.680000
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 1
newmtl floor
Ns 10.000005
Ka 1.000000 1.000000 1.000000
Kd 0.725000 0.710000 0.680000
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 1
newmtl leftWall
Ns 10.000005
Ka 1.000000 1.000000 1.000000
Kd 0.630000 0.065000 0.050000
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 1
newmtl light
Ns 10.000005
Ka 1.000000 1.000000 1.000000
Kd 0.780000 0.780000 0.780000
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 1
newmtl rightWall
Ns 10.000005
Ka 1.000000 1.000000 1.000000
Kd 0.140000 0.450000 0.091000
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 1
newmtl shortBox
Ns 10.000005
Ka 1.000000 1.000000 1.000000
Kd 0.725000 0.710000 0.680000
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 1
newmtl tallBox
Ns 10.000005
Ka 1.000000 1.000000 1.000000
Kd 0.725000 0.710000 0.680000
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 1

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
#ifndef MODEL_FS_GLSL #ifndef MODEL_FS_GLSL
#define MODEL_FS_GLSL #define MODEL_FS_GLSL
uniform mat4 model, view; uniform mat4 model, view, inv_view;
uniform sampler2D u_texture2d; uniform sampler2D u_texture2d;
uniform vec3 u_coefficients_sh[9]; uniform vec3 u_coefficients_sh[9];
uniform bool u_textured; /// set:1 uniform bool u_textured; /// set:1
@ -34,15 +34,6 @@ in vec3 v_vertcolor;
in float v_depth; in float v_depth;
out vec4 fragcolor; out vec4 fragcolor;
#include "shadowmap.glsl"
in vec4 vpeye;
in vec4 vneye;
in vec4 sc;
vec4 shadowing() {
return shadowmap(vpeye, vneye, v_texcoord, sc);
}
uniform float u_global_alpha; /// set:1.0 uniform float u_global_alpha; /// set:1.0
uniform float u_global_opacity; /// set:1.0 uniform float u_global_opacity; /// set:1.0
uniform vec3 u_cam_pos; uniform vec3 u_cam_pos;

View File

@ -76,13 +76,17 @@ out float v_depth;
// shadow // shadow
uniform mat4 model, view, inv_view; uniform mat4 model, view, inv_view;
uniform mat4 cameraToShadowProjector; uniform mat4 cameraToShadowProjector;
out vec4 vneye; uniform mat4 cameraToShadowView;
out vec4 vpeye; out vec4 vpeye;
out vec4 sc; out vec4 vneye;
void do_shadow() { void do_shadow(mat4 modelMat, vec3 objPos, vec3 objNormal) {
vneye = view * model * vec4(att_normal, 0.0f); #ifdef SHADOW_CAST
vpeye = view * model * vec4(att_position, 1.0); gl_Position = cameraToShadowProjector * modelMat * vec4(objPos, 1.0f);
sc = cameraToShadowProjector * model * vec4(att_position, 1.0f); v_position = (cameraToShadowView * modelMat * vec4(objPos, 1.0f)).xyz;
#else
vpeye = view * modelMat * vec4(objPos, 1.0f);
vneye = view * modelMat * vec4(objNormal, 0.0f);
#endif
} }

View File

@ -0,0 +1,118 @@
// WIP
#ifndef SHADOWMAP_GLSL
#define SHADOWMAP_GLSL
// uniform mat4 view = mat4(1.0);
uniform vec3 lightPos; /// set:1,1,1
uniform float doTexture; /// set:1
#ifdef VSMCUBE
uniform samplerCube shadowMap; // VSMCUBE
#else
uniform sampler2D shadowMap; // !VSMCUBE
#endif
struct light {
vec3 position; // world-space
vec4 diffuse;
vec4 specular;
float constantAttenuation, linearAttenuation, quadraticAttenuation;
};
light light0 = light(
vec3(1,1,1), // lightPos
vec4(1,1,1,1), // diffuse
vec4(1,1,1,1), // specular
1.0, 0.0, 0.0 // attenuation (const, linear, quad)
);
// From http://fabiensanglard.net/shadowmappingVSM/index.php
#ifdef VSMCUBE
float chebyshevUpperBound(float distance, vec3 dir) {
distance = distance/20 ;
vec2 moments = texture(shadowMap, dir).rg;
#else
float chebyshevUpperBound(float distance, vec4 scPostW) {
vec2 moments = texture(shadowMap,scPostW.xy).rg;
#endif
// Surface is fully lit. as the current fragment is before the light occluder
if (distance <= moments.x)
return 1.0;
// The fragment is either in shadow or penumbra. We now use chebyshev's upperBound to check
// How likely this pixel is to be lit (p_max)
float variance = moments.y - (moments.x*moments.x);
//variance = max(variance, 0.000002);
variance = max(variance, 0.00002);
float d = distance - moments.x;
float p_max = variance / (variance + d*d);
return p_max;
}
vec4 shadowmap(in vec4 vpeye, in vec4 vneye, in vec2 uv, in vec4 sc) {
#ifndef VSMCUBE
return vec4(1.);
#else
vec3 fragment = vec3(vpeye);
vec3 normal = vec3(normalize(vneye));
vec3 viewDir = normalize(-fragment);
// Lighting
// Convert to eye-space
vec3 light = vec3(view * vec4(light0.position, 1.0));
#ifdef VSMCUBE
// Vectors
vec3 fragmentToLight = light - fragment;
vec3 fragmentToLightDir = normalize(fragmentToLight);
// Shadows
vec4 fragmentToLight_world = inverse(view) * vec4(fragmentToLightDir, 0.0);
float shadowFactor = chebyshevUpperBound(length(fragmentToLight), -fragmentToLight_world.xyz);
#else
// Shadows
vec4 scPostW = sc / sc.w;
scPostW = scPostW * 0.5 + 0.5;
float shadowFactor = 1.0; // Not in shadow
bool outsideShadowMap = sc.w <= 0.0f || (scPostW.x < 0 || scPostW.y < 0) || (scPostW.x >= 1 || scPostW.y >= 1);
if (!outsideShadowMap) {
shadowFactor = chebyshevUpperBound(scPostW.z, scPostW);
}
#endif
vec4 diffColor = vec4(1,1,1,1);
#ifdef VSMCUBE
if(doTexture != 0) diffColor = vec4(vec3(texture(shadowMap, -fragmentToLight_world.xyz).r), 1.0);
#else
if(doTexture != 0) diffColor = vec4(vec3(texture(shadowMap, vec2(uv.x, 1.0 - uv.y)).r), 1.0);
#endif
#if 1
vec3 positionToLight = light - fragment;
vec3 lightDir = normalize(positionToLight);
// Angle between fragment-normal and incoming light
float cosAngIncidence = dot(lightDir, normal);
cosAngIncidence = clamp(cosAngIncidence, 0, 1);
float attenuation = 1.0f;
attenuation = 1.0 / (light0.constantAttenuation + light0.linearAttenuation * length(positionToLight) + light0.quadraticAttenuation * pow(length(positionToLight),2));
vec4 diffuse = diffColor * light0.diffuse * cosAngIncidence * attenuation;
vec4 total_lighting;
total_lighting += vec4(0.1, 0.1, 0.1, 1.0) * diffColor; // Ambient
total_lighting += diffuse * shadowFactor; // Diffuse
#else
vec4 total_lighting = diffColor;
#endif
return vec4(clamp(vec3(total_lighting), 0., 1.), 1.0);
#endif
}
#endif

View File

@ -1,118 +1,67 @@
// WIP in vec4 vpeye;
#ifndef SHADOWMAP_GLSL in vec4 vneye;
#define SHADOWMAP_GLSL uniform bool u_shadow_receiver;
uniform samplerCube shadowMap[MAX_LIGHTS];
// uniform mat4 view = mat4(1.0); //// From http://fabiensanglard.net/shadowmappingVSM/index.php
uniform vec3 lightPos; /// set:1,1,1 float chebyshevUpperBound(float distance, vec3 dir, int light_index) {
uniform float doTexture; /// set:1 distance = distance/20;
#ifdef VSMCUBE vec2 moments = texture(shadowMap[light_index], dir).rg;
uniform samplerCube shadowMap; // VSMCUBE
#else
uniform sampler2D shadowMap; // !VSMCUBE
#endif
// Surface is fully lit. as the current fragment is before the light occluder
if (distance <= moments.x) {
return 1.0;
}
struct light { // The fragment is either in shadow or penumbra. We now use chebyshev's upperBound to check
vec3 position; // world-space // How likely this pixel is to be lit (p_max)
vec4 diffuse; float variance = moments.y - (moments.x*moments.x);
vec4 specular; //variance = max(variance, 0.000002);
float constantAttenuation, linearAttenuation, quadraticAttenuation; variance = max(variance, 0.00002);
};
light light0 = light( float d = distance - moments.x;
vec3(1,1,1), // lightPos float p_max = variance / (variance + d*d);
vec4(1,1,1,1), // diffuse
vec4(1,1,1,1), // specular
1.0, 0.0, 0.0 // attenuation (const, linear, quad)
);
// From http://fabiensanglard.net/shadowmappingVSM/index.php return p_max;
#ifdef VSMCUBE }
float chebyshevUpperBound(float distance, vec3 dir) {
distance = distance/20 ;
vec2 moments = texture(shadowMap, dir).rg;
#else
float chebyshevUpperBound(float distance, vec4 scPostW) {
vec2 moments = texture(shadowMap,scPostW.xy).rg;
#endif
// Surface is fully lit. as the current fragment is before the light occluder
if (distance <= moments.x)
return 1.0;
// The fragment is either in shadow or penumbra. We now use chebyshev's upperBound to check vec4 shadowmap(in vec4 peye, in vec4 neye) {
// How likely this pixel is to be lit (p_max) float shadowFactor = 0.0;
float variance = moments.y - (moments.x*moments.x); vec3 fragment = vec3(peye);
//variance = max(variance, 0.000002);
variance = max(variance, 0.00002);
float d = distance - moments.x; int total_casters = 0;
float p_max = variance / (variance + d*d); for (int i = 0; i < u_num_lights; i++) {
light_t light = u_lights[i];
float factor = 0.0;
return p_max; if (light.type == LIGHT_DIRECTIONAL) {
// shadowFactor = chebyshevUpperBound(distance, light.dir);
} 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 += chebyshevUpperBound(length(dir), -sc.xyz, i);
} else if (light.type == LIGHT_SPOT) {
// shadowFactor = chebyshevUpperBound(distance, light.pos);
} }
shadowFactor += factor;
}
vec4 shadowmap(in vec4 vpeye, in vec4 vneye, in vec2 uv, in vec4 sc) { if (u_num_lights == 0) {
#ifndef VSMCUBE shadowFactor = 1.0;
return vec4(1.); } else {
#else shadowFactor /= total_casters;
vec3 fragment = vec3(vpeye); }
vec3 normal = vec3(normalize(vneye));
vec3 viewDir = normalize(-fragment);
// Lighting return vec4(vec3(shadowFactor), 1.0);
// Convert to eye-space }
vec3 light = vec3(view * vec4(light0.position, 1.0));
#ifdef VSMCUBE vec4 shadowing() {
// Vectors if (u_shadow_receiver) {
vec3 fragmentToLight = light - fragment; return shadowmap(vpeye, vneye);
vec3 fragmentToLightDir = normalize(fragmentToLight); } else {
return vec4(1.0);
// Shadows }
vec4 fragmentToLight_world = inverse(view) * vec4(fragmentToLightDir, 0.0); }
float shadowFactor = chebyshevUpperBound(length(fragmentToLight), -fragmentToLight_world.xyz);
#else
// Shadows
vec4 scPostW = sc / sc.w;
scPostW = scPostW * 0.5 + 0.5;
float shadowFactor = 1.0; // Not in shadow
bool outsideShadowMap = sc.w <= 0.0f || (scPostW.x < 0 || scPostW.y < 0) || (scPostW.x >= 1 || scPostW.y >= 1);
if (!outsideShadowMap) {
shadowFactor = chebyshevUpperBound(scPostW.z, scPostW);
}
#endif
vec4 diffColor = vec4(1,1,1,1);
#ifdef VSMCUBE
if(doTexture != 0) diffColor = vec4(vec3(texture(shadowMap, -fragmentToLight_world.xyz).r), 1.0);
#else
if(doTexture != 0) diffColor = vec4(vec3(texture(shadowMap, vec2(uv.x, 1.0 - uv.y)).r), 1.0);
#endif
#if 1
vec3 positionToLight = light - fragment;
vec3 lightDir = normalize(positionToLight);
// Angle between fragment-normal and incoming light
float cosAngIncidence = dot(lightDir, normal);
cosAngIncidence = clamp(cosAngIncidence, 0, 1);
float attenuation = 1.0f;
attenuation = 1.0 / (light0.constantAttenuation + light0.linearAttenuation * length(positionToLight) + light0.quadraticAttenuation * pow(length(positionToLight),2));
vec4 diffuse = diffColor * light0.diffuse * cosAngIncidence * attenuation;
vec4 total_lighting;
total_lighting += vec4(0.1, 0.1, 0.1, 1.0) * diffColor; // Ambient
total_lighting += diffuse * shadowFactor; // Diffuse
#else
vec4 total_lighting = diffColor;
#endif
return vec4(clamp(vec3(total_lighting), 0., 1.), 1.0);
#endif
}
#endif

View File

@ -4,6 +4,7 @@
#include "sh_lighting.glsl" #include "sh_lighting.glsl"
#include "rimlight.glsl" #include "rimlight.glsl"
#include "light.glsl" #include "light.glsl"
#include "shadowmap.glsl"
struct surface_t { struct surface_t {
vec3 normal; vec3 normal;
@ -194,10 +195,12 @@ surface_t surface() {
s.albedo *= v_color; s.albedo *= v_color;
s.light_direct *= shadowing().xyz;
s.fragcolor = s.albedo; s.fragcolor = s.albedo;
s.fragcolor.rgb *= s.light_direct + s.light_indirect; s.fragcolor.rgb *= s.light_direct + s.light_indirect;
s.fragcolor.rgb += s.emissive; s.fragcolor.rgb += s.emissive;
s.fragcolor *= shadowing(); // s.fragcolor *= shadowing();
s.fragcolor.rgb += get_rimlight(); s.fragcolor.rgb += get_rimlight();
s.fragcolor.a *= u_global_alpha; s.fragcolor.a *= u_global_alpha;
s.fragcolor *= vec4(u_global_opacity); s.fragcolor *= vec4(u_global_opacity);

View File

@ -1,13 +1,8 @@
in vec4 v_position; in vec3 v_position;
out vec4 outColor; out vec4 fragcolor;
void main() { void main() {
#ifdef VSMCUBE float depth = length(v_position) / 20;
float depth = length( vec3(v_position) ) / 20;
#else
float depth = v_position.z / v_position.w;
depth = depth * 0.5 + 0.5;
#endif
float moment1 = depth; float moment1 = depth;
float moment2 = depth * depth; float moment2 = depth * depth;
@ -15,5 +10,5 @@ void main() {
float dx = dFdx(depth); float dx = dFdx(depth);
float dy = dFdy(depth); float dy = dFdy(depth);
moment2 += 0.25*(dx*dx+dy*dy); moment2 += 0.25*(dx*dx+dy*dy);
outColor = vec4( moment1, moment2, 0.0, 0.0); fragcolor = vec4( moment1, moment2, 0.0, 1.0);
} }

View File

@ -43,5 +43,5 @@ void main() {
gl_Position = P * finalPos; gl_Position = P * finalPos;
// Prepare shadow data for shadow mapping // Prepare shadow data for shadow mapping
do_shadow(); do_shadow(att_instanced_matrix, objPos, v_normal);
} }

View File

@ -1,15 +0,0 @@
uniform mat4 model;
uniform mat4 cameraToShadowView;
uniform mat4 cameraToShadowProjector;
in vec3 position;
out vec4 v_position;
void main() {
gl_Position = cameraToShadowProjector * model * vec4(position, 1.0);
#ifdef VSMCUBE
v_position = cameraToShadowView * model * vec4(position, 1.0);
#else
v_position = gl_Position;
#endif
}

View File

@ -17033,6 +17033,9 @@ typedef struct renderstate_t {
// Scissor test // Scissor test
bool scissor_test_enabled; bool scissor_test_enabled;
// bool Seamless Cubemap
bool seamless_cubemap;
} renderstate_t; } renderstate_t;
API renderstate_t renderstate(); API renderstate_t renderstate();
@ -17232,6 +17235,7 @@ API void cubemap_sh_shader(cubemap_t *c);
API void cubemap_sh_add_light(cubemap_t *c, vec3 light, vec3 dir, float strength); API void cubemap_sh_add_light(cubemap_t *c, vec3 light, vec3 dir, float strength);
// lighting probe blending // lighting probe blending
// @note: uploads SH coefficients to shader
API void cubemap_sh_blend(vec3 pos, float max_dist, unsigned count, cubemap_t *probes); API void cubemap_sh_blend(vec3 pos, float max_dist, unsigned count, cubemap_t *probes);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -17242,6 +17246,54 @@ API void fbo_bind(unsigned id);
API void fbo_unbind(); API void fbo_unbind();
API void fbo_destroy(unsigned id); API void fbo_destroy(unsigned id);
// -----------------------------------------------------------------------------
// lights
enum {
MAX_LIGHTS = 16,
};
enum LIGHT_TYPE {
LIGHT_DIRECTIONAL,
LIGHT_POINT,
LIGHT_SPOT,
};
typedef struct light_t {
char type;
vec3 diffuse, specular, ambient;
vec3 pos, dir;
struct {
float constant, linear, quadratic;
} falloff;
float radius;
float specularPower;
float innerCone, outerCone;
//@todo: cookie, flare
// Shadowmapping
bool cast_shadows;
float shadow_distance;
float shadow_bias;
// internals
bool cached; //< used by scene to invalidate cached light data
} light_t;
API light_t light();
// API void light_flags(int flags);
API void light_type(light_t* l, char type);
API void light_diffuse(light_t* l, vec3 color);
API void light_specular(light_t* l, vec3 color);
API void light_ambient(light_t* l, vec3 color);
API void light_teleport(light_t* l, vec3 pos);
API void light_dir(light_t* l, vec3 dir);
API void light_power(light_t* l, float power);
API void light_radius(light_t* l, float radius);
API void light_falloff(light_t* l, float constant, float linear, float quadratic);
API void light_cone(light_t* l, float innerCone, float outerCone);
API void light_update(unsigned num_lights, light_t *lv);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// shadowmaps // shadowmaps
@ -17253,15 +17305,19 @@ API void fbo_destroy(unsigned id);
// #endif // #endif
typedef struct shadowmap_t { typedef struct shadowmap_t {
mat44 shadowmatrix; mat44 V;
mat44 mvp; mat44 PV;
mat44 mv;
mat44 proj;
vec4 light_position;
int saved_fb;
int saved_viewport[4];
handle fbo, texture;
int texture_width; int texture_width;
int step;
int light_step;
struct {
handle fbos[6], texture, depth_texture;
} maps[MAX_LIGHTS];
handle saved_fb;
handle saved_pass;
int saved_vp[4];
} shadowmap_t; } shadowmap_t;
API shadowmap_t shadowmap(int texture_width); // = 1024 API shadowmap_t shadowmap(int texture_width); // = 1024
@ -17269,13 +17325,10 @@ 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_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 void shadowmap_light(shadowmap_t *s, light_t *l);
API void shadowmap_end(shadowmap_t *s); API void shadowmap_end(shadowmap_t *s);
// shadowmap utils
API void shadowmatrix_proj(mat44 shm_proj, float aLightFov, float znear, float zfar);
API void shadowmatrix_ortho(mat44 shm_proj, float left, float right, float bottom, float top, float znear, float zfar);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// shaders // shaders
@ -17664,6 +17717,9 @@ typedef struct model_t {
unsigned num_anims; unsigned num_anims;
unsigned num_frames; unsigned num_frames;
handle program; handle program;
handle shadow_program;
shadowmap_t *shadow_map;
bool shadow_receiver;
float curframe; float curframe;
mat44 pivot; mat44 pivot;
@ -17715,6 +17771,7 @@ API void model_lod(model_t*, float lo_detail, float hi_detail, float morph);
API void model_shading(model_t*, int shading); API void model_shading(model_t*, int shading);
API void model_shading_custom(model_t*, int shading, const char *vs, const char *fs, const char *defines); API void model_shading_custom(model_t*, int shading, const char *vs, const char *fs, const char *defines);
API void model_skybox(model_t*, skybox_t sky, bool load_sh); API void model_skybox(model_t*, skybox_t sky, bool load_sh);
API void model_shadow(model_t*, shadowmap_t *sm);
API void model_fog(model_t*, unsigned mode, vec3 color, float start, float end, float density); API void model_fog(model_t*, unsigned mode, vec3 color, float start, float end, float density);
API void model_render(model_t, mat44 proj, mat44 view, mat44 model, int shader); API void model_render(model_t, mat44 proj, mat44 view, mat44 model, int shader);
API void model_render_skeleton(model_t, mat44 model); API void model_render_skeleton(model_t, mat44 model);
@ -17950,48 +18007,6 @@ API void object_billboard(object_t *obj, unsigned mode);
// object_pose(transform); // @todo // object_pose(transform); // @todo
// light
enum LIGHT_TYPE {
LIGHT_DIRECTIONAL,
LIGHT_POINT,
LIGHT_SPOT,
};
enum LIGHT_FLAGS {
LIGHT_CAST_SHADOWS = 1,
};
typedef struct light_t {
char type;
vec3 diffuse, specular, ambient;
vec3 pos, dir;
struct {
float constant, linear, quadratic;
} falloff;
float radius;
float specularPower;
float innerCone, outerCone;
//@todo: cookie, flare
// internals
bool cached; //< used by scene to invalidate cached light data
} light_t;
API light_t light();
// API void light_flags(int flags);
API void light_type(light_t* l, char type);
API void light_diffuse(light_t* l, vec3 color);
API void light_specular(light_t* l, vec3 color);
API void light_ambient(light_t* l, vec3 color);
API void light_teleport(light_t* l, vec3 pos);
API void light_dir(light_t* l, vec3 dir);
API void light_power(light_t* l, float power);
API void light_radius(light_t* l, float radius);
API void light_falloff(light_t* l, float constant, float linear, float quadratic);
API void light_cone(light_t* l, float innerCone, float outerCone);
API void light_update(unsigned num_lights, light_t *lv);
// scene // scene
enum SCENE_FLAGS { enum SCENE_FLAGS {
@ -381806,6 +381821,9 @@ renderstate_t renderstate() {
// Disable scissor test by default // Disable scissor test by default
state.scissor_test_enabled = GL_FALSE; state.scissor_test_enabled = GL_FALSE;
// Enable seamless cubemap by default
state.seamless_cubemap = GL_TRUE;
return state; return state;
} }
@ -381912,6 +381930,13 @@ void renderstate_apply(const renderstate_t *state) {
} else { } else {
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
} }
// Apply seamless cubemap
if (state->seamless_cubemap) {
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
} else {
glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
} }
} }
@ -383136,92 +383161,256 @@ texture_t texture_compressed(const char *pathfile, unsigned flags) {
return texture_compressed_from_mem(data, size, flags); return texture_compressed_from_mem(data, size, flags);
} }
// -----------------------------------------------------------------------------
light_t light() {
light_t l = {0};
l.diffuse = vec3(1,1,1);
l.dir = vec3(1,-1,-1);
l.falloff.constant = 1.0f;
l.falloff.linear = 0.09f;
l.falloff.quadratic = 0.0032f;
l.specularPower = 32.f;
l.innerCone = 0.85f;// 31 deg
l.outerCone = 0.9f; // 25 deg
l.shadow_distance = 100.0f;
l.shadow_bias = 0.5f;
return l;
}
void light_type(light_t* l, char type) {
l->cached = 0;
l->type = type;
}
void light_diffuse(light_t* l, vec3 color) {
l->cached = 0;
l->diffuse = color;
}
void light_specular(light_t* l, vec3 color) {
l->cached = 0;
l->specular = color;
}
void light_ambient(light_t* l, vec3 color) {
l->cached = 0;
l->ambient = color;
}
void light_teleport(light_t* l, vec3 pos) {
l->cached = 0;
l->pos = pos;
}
void light_dir(light_t* l, vec3 dir) {
l->cached = 0;
l->dir = dir;
}
void light_power(light_t* l, float power) {
l->cached = 0;
l->specularPower = power;
}
void light_falloff(light_t* l, float constant, float linear, float quadratic) {
l->cached = 0;
l->falloff.constant = constant;
l->falloff.linear = linear;
l->falloff.quadratic = quadratic;
}
void light_radius(light_t* l, float radius) {
l->cached = 0;
l->radius = radius;
}
void light_cone(light_t* l, float innerCone, float outerCone) {
l->cached = 0;
l->innerCone = acos(innerCone);
l->outerCone = acos(outerCone);
}
void light_update(unsigned num_lights, light_t *lv) {
if (num_lights > MAX_LIGHTS) {
PRINTF("WARNING: num_lights > MAX_LIGHTS, clamping to MAX_LIGHTS\n");
num_lights = MAX_LIGHTS;
}
shader_int("u_num_lights", num_lights);
for (unsigned i=0; i < num_lights; ++i) {
lv[i].cached = 1;
shader_int(va("u_lights[%d].type", i), lv[i].type);
shader_vec3(va("u_lights[%d].pos", i), lv[i].pos);
shader_vec3(va("u_lights[%d].dir", i), lv[i].dir);
shader_vec3(va("u_lights[%d].diffuse", i), lv[i].diffuse);
shader_vec3(va("u_lights[%d].specular", i), lv[i].specular);
shader_vec3(va("u_lights[%d].ambient", i), lv[i].ambient);
shader_float(va("u_lights[%d].power", i), lv[i].specularPower);
shader_float(va("u_lights[%d].radius", i), lv[i].radius);
shader_float(va("u_lights[%d].constant", i), lv[i].falloff.constant);
shader_float(va("u_lights[%d].linear", i), lv[i].falloff.linear);
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);
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// shadowmaps // shadowmaps
static inline
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
// Create a cubemap color texture
glGenTextures(1, &s->maps[light_index].texture);
glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].texture);
for (int i = 0; i < 6; i++) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_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_MAG_FILTER, GL_LINEAR);
// Create a cubemap depth texture
glGenTextures(1, &s->maps[light_index].depth_texture);
glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].depth_texture);
for (int i = 0; i < 6; i++) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
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_EDGE);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
// Create 6 framebuffers for each face of the cubemap
glGenFramebuffers(6, s->maps[light_index].fbos);
for (int i = 0; i < 6; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbos[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, s->maps[light_index].texture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, s->maps[light_index].depth_texture, 0);
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (GL_FRAMEBUFFER_COMPLETE != result) {
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
}
}
}
shadowmap_t shadowmap(int texture_width) { // = 1024 shadowmap_t shadowmap(int texture_width) { // = 1024
shadowmap_t s = {0}; shadowmap_t s = {0};
s.texture_width = texture_width; s.texture_width = texture_width;
s.saved_fb = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb);
glGenFramebuffers(1, &s.fbo); for (int i = 0; i < MAX_LIGHTS; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, s.fbo); shadowmap_init_caster(&s, i, texture_width);
}
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &s.texture);
glBindTexture(GL_TEXTURE_2D, s.texture);
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);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s.texture, 0);
#if is(ems)
GLenum nones[] = { GL_NONE };
glDrawBuffers(1, nones);
glReadBuffer(GL_NONE);
#else
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
#endif
glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb); glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb);
return s; return s;
} }
void shadowmap_destroy(shadowmap_t *s) { void shadowmap_destroy(shadowmap_t *s) {
if (s->texture) { for (int i = 0; i < MAX_LIGHTS; i++) {
glDeleteTextures(1, &s->texture); glDeleteFramebuffers(6, s->maps[i].fbos);
} glDeleteTextures(1, &s->maps[i].texture);
if (s->fbo) { glDeleteTextures(1, &s->maps[i].depth_texture);
glDeleteFramebuffers(1, &s->fbo);
} }
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) { void shadowmap_set_shadowmatrix(shadowmap_t *s, vec3 aLightPos, vec3 aLightAt, vec3 aLightUp, const mat44 projection) {
copy44(s->proj, projection); // copy44(s->proj, projection);
s->light_position = vec4(aLightPos.x, aLightPos.y, aLightPos.z, 1); // lookat44(s->mv, aLightPos, aLightAt, aLightUp);
lookat44(s->mv, aLightPos, aLightAt, aLightUp);
mat44 bias = { // mat44 bias = {
0.5, 0.0, 0.0, 0.0, // 0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0, // 0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0, // 0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0 }; // 0.5, 0.5, 0.5, 1.0 };
// s->shadowmatrix = bias; // multiply44x3(s->shadowmatrix, bias, s->proj, s->mv);
// s->shadowmatrix *= s->proj; // multiply44x2(s->mvp, projection, s->mv);
// s->shadowmatrix *= s->mv;
// multiply44x3(s->shadowmatrix, s->mv, s->proj, bias);
multiply44x3(s->shadowmatrix, bias, s->proj, s->mv);
// mvp = projection * s->mv;
// multiply44x2(s->mvp, s->mv, projection);
multiply44x2(s->mvp, projection, s->mv);
} }
static shadowmap_t *active_shadowmap = NULL;
void shadowmap_begin(shadowmap_t *s) { void shadowmap_begin(shadowmap_t *s) {
glGetIntegerv(GL_VIEWPORT, &s->saved_viewport[0]); glGetIntegerv(GL_VIEWPORT, s->saved_vp);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s->saved_fb); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s->saved_fb);
glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
s->step = 0;
s->light_step = 0;
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) {
if(dir<0) return;
mat44 P, V, PV;
perspective44(P, 90.0f, 1.0f, l->shadow_bias, l->shadow_distance);
vec3 lightPos = l->pos;
/**/ if(dir == 0) lookat44(V, lightPos, add3(lightPos, vec3(+1, 0, 0)), vec3(0, -1, 0)); // +X
else if(dir == 1) lookat44(V, lightPos, add3(lightPos, vec3(-1, 0, 0)), vec3(0, -1, 0)); // -X
else if(dir == 2) lookat44(V, lightPos, add3(lightPos, vec3( 0, +1, 0)), vec3(0, 0, +1)); // +Y
else if(dir == 3) lookat44(V, lightPos, add3(lightPos, vec3( 0, -1, 0)), vec3(0, 0, -1)); // -Y
else if(dir == 4) lookat44(V, lightPos, add3(lightPos, vec3( 0, 0, +1)), vec3(0, -1, 0)); // +Z
else /*dir == 5*/ lookat44(V, lightPos, add3(lightPos, vec3( 0, 0, -1)), vec3(0, -1, 0)); // -Z
multiply44x2(PV, P, V); // -Z
copy44(s->V, V);
copy44(s->PV, PV);
}
bool shadowmap_step(shadowmap_t *s) {
if (s->step >= 6) {
s->step = 0;
s->light_step++;
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);
glClearDepth(1); s->step++;
glClear(GL_DEPTH_BUFFER_BIT); return true;
}
void shadowmap_light(shadowmap_t *s, light_t *l) {
if (l->cast_shadows) {
int step = s->step - 1;
if (l->type == LIGHT_POINT) {
shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_SPOT) {
// spot light
} else if (l->type == LIGHT_DIRECTIONAL) {
// directional light
}
}
} }
void shadowmap_end(shadowmap_t *s) { void shadowmap_end(shadowmap_t *s) {
glViewport(s->saved_viewport[0], s->saved_viewport[1], s->saved_viewport[2], s->saved_viewport[3]); glViewport(s->saved_vp[0], s->saved_vp[1], s->saved_vp[2], s->saved_vp[3]);
glBindFramebuffer(GL_FRAMEBUFFER, s->saved_fb); glBindFramebuffer(GL_FRAMEBUFFER, s->saved_fb);
model_setpass(s->saved_pass);
active_shadowmap = NULL;
} }
// shadowmap utils // shadowmap utils
@ -385397,6 +385586,25 @@ void model_set_uniforms(model_t m, int shader, mat44 mv, mat44 proj, mat44 view,
glUniform1f(loc, (float)window_time()); glUniform1f(loc, (float)window_time());
} }
// shadow casting
if (shader == m.shadow_program) {
shadowmap_t *sm = active_shadowmap;
ASSERT(sm);
shader_mat44("cameraToShadowView", sm->V);
shader_mat44("cameraToShadowProjector", sm->PV);
}
// shadow receiving
if (m.shadow_receiver) {
ASSERT(m.shadow_map);
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);
}
} else {
shader_bool("u_shadow_receiver", GL_FALSE);
}
if (m.shading == SHADING_PBR) { if (m.shading == SHADING_PBR) {
handle old_shader = last_shader; handle old_shader = last_shader;
shader_bind(shader); shader_bind(shader);
@ -386010,12 +386218,13 @@ void model_set_renderstates(model_t *m) {
transparent_rs->front_face = GL_CW; transparent_rs->front_face = GL_CW;
} }
// Shadow pass @todo // Shadow pass
renderstate_t *shadow_rs = &m->rs[RENDER_PASS_SHADOW]; renderstate_t *shadow_rs = &m->rs[RENDER_PASS_SHADOW];
{ {
shadow_rs->blend_enabled = 1; shadow_rs->blend_enabled = 1;
shadow_rs->blend_src = GL_SRC_ALPHA; shadow_rs->blend_src = GL_SRC_ALPHA;
shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA;
shadow_rs->cull_face_enabled = 1;
shadow_rs->cull_face_mode = GL_BACK; shadow_rs->cull_face_mode = GL_BACK;
shadow_rs->front_face = GL_CW; shadow_rs->front_face = GL_CW;
} }
@ -386414,6 +386623,7 @@ void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_
int rs_idx = model_getpass(); int rs_idx = model_getpass();
renderstate_t *rs = &m.rs[rs_idx]; renderstate_t *rs = &m.rs[rs_idx];
renderstate_apply(rs);
glBindVertexArray( q->vao ); glBindVertexArray( q->vao );
@ -386497,31 +386707,33 @@ void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_
renderstate_apply(rs); renderstate_apply(rs);
} }
if (m.shading != SHADING_PBR) { if (rs_idx != RENDER_PASS_SHADOW) {
shader_texture_unit("u_texture2d", q->textures[i], texture_unit()); if (m.shading != SHADING_PBR) {
shader_texture("u_lightmap", m.lightmap); shader_texture_unit("u_texture2d", q->textures[i], texture_unit());
shader_texture("u_lightmap", m.lightmap);
int loc; int loc;
if ((loc = glGetUniformLocation(shader, "u_textured")) >= 0) { if ((loc = glGetUniformLocation(shader, "u_textured")) >= 0) {
bool textured = !!q->textures[i] && q->textures[i] != texture_checker().id; // m.materials[i].layer[0].texture != texture_checker().id; bool textured = !!q->textures[i] && q->textures[i] != texture_checker().id; // m.materials[i].layer[0].texture != texture_checker().id;
glUniform1i(loc, textured ? GL_TRUE : GL_FALSE); glUniform1i(loc, textured ? GL_TRUE : GL_FALSE);
if ((loc = glGetUniformLocation(shader, "u_diffuse")) >= 0) { if ((loc = glGetUniformLocation(shader, "u_diffuse")) >= 0) {
glUniform4f(loc, m.materials[i].layer[0].map.color.r, m.materials[i].layer[0].map.color.g, m.materials[i].layer[0].map.color.b, m.materials[i].layer[0].map.color.a); glUniform4f(loc, m.materials[i].layer[0].map.color.r, m.materials[i].layer[0].map.color.g, m.materials[i].layer[0].map.color.b, m.materials[i].layer[0].map.color.a);
}
} }
}
} else { } else {
const material_t *material = &m.materials[i]; const material_t *material = &m.materials[i];
shader_colormap_model_internal( "map_diffuse.color", "map_diffuse.has_tex", "map_diffuse_tex", material->layer[MATERIAL_CHANNEL_DIFFUSE].map ); shader_colormap_model_internal( "map_diffuse.color", "map_diffuse.has_tex", "map_diffuse_tex", material->layer[MATERIAL_CHANNEL_DIFFUSE].map );
shader_colormap_model_internal( "map_normals.color", "map_normals.has_tex", "map_normals_tex", material->layer[MATERIAL_CHANNEL_NORMALS].map ); shader_colormap_model_internal( "map_normals.color", "map_normals.has_tex", "map_normals_tex", material->layer[MATERIAL_CHANNEL_NORMALS].map );
shader_colormap_model_internal( "map_specular.color", "map_specular.has_tex", "map_specular_tex", material->layer[MATERIAL_CHANNEL_SPECULAR].map ); shader_colormap_model_internal( "map_specular.color", "map_specular.has_tex", "map_specular_tex", material->layer[MATERIAL_CHANNEL_SPECULAR].map );
shader_colormap_model_internal( "map_albedo.color", "map_albedo.has_tex", "map_albedo_tex", material->layer[MATERIAL_CHANNEL_ALBEDO].map ); shader_colormap_model_internal( "map_albedo.color", "map_albedo.has_tex", "map_albedo_tex", material->layer[MATERIAL_CHANNEL_ALBEDO].map );
shader_colormap_model_internal( "map_roughness.color", "map_roughness.has_tex", "map_roughness_tex", material->layer[MATERIAL_CHANNEL_ROUGHNESS].map ); shader_colormap_model_internal( "map_roughness.color", "map_roughness.has_tex", "map_roughness_tex", material->layer[MATERIAL_CHANNEL_ROUGHNESS].map );
shader_colormap_model_internal( "map_metallic.color", "map_metallic.has_tex", "map_metallic_tex", material->layer[MATERIAL_CHANNEL_METALLIC].map ); shader_colormap_model_internal( "map_metallic.color", "map_metallic.has_tex", "map_metallic_tex", material->layer[MATERIAL_CHANNEL_METALLIC].map );
shader_colormap_model_internal( "map_ao.color", "map_ao.has_tex", "map_ao_tex", material->layer[MATERIAL_CHANNEL_AO].map ); shader_colormap_model_internal( "map_ao.color", "map_ao.has_tex", "map_ao_tex", material->layer[MATERIAL_CHANNEL_AO].map );
shader_colormap_model_internal( "map_ambient.color", "map_ambient.has_tex", "map_ambient_tex", material->layer[MATERIAL_CHANNEL_AMBIENT].map ); shader_colormap_model_internal( "map_ambient.color", "map_ambient.has_tex", "map_ambient_tex", material->layer[MATERIAL_CHANNEL_AMBIENT].map );
shader_colormap_model_internal( "map_emissive.color", "map_emissive.has_tex", "map_emissive_tex", material->layer[MATERIAL_CHANNEL_EMISSIVE].map ); shader_colormap_model_internal( "map_emissive.color", "map_emissive.has_tex", "map_emissive_tex", material->layer[MATERIAL_CHANNEL_EMISSIVE].map );
// shader_float( "specular_shininess", material->specular_shininess ); // unused, basic_specgloss.fs only // shader_float( "specular_shininess", material->specular_shininess ); // unused, basic_specgloss.fs only
}
} }
glDrawElementsInstanced(GL_TRIANGLES, 3*im->num_triangles, GL_UNSIGNED_INT, &tris[im->first_triangle], m.num_instances); glDrawElementsInstanced(GL_TRIANGLES, 3*im->num_triangles, GL_UNSIGNED_INT, &tris[im->first_triangle], m.num_instances);
@ -386546,6 +386758,10 @@ void model_render_instanced_pass(model_t m, mat44 proj, mat44 view, mat44* model
model_set_state(m); model_set_state(m);
} }
if (model_getpass() == RENDER_PASS_SHADOW) {
shader = m.shadow_program;
}
model_set_uniforms(m, shader > 0 ? shader : m.program, mv, proj, view, models[0]); model_set_uniforms(m, shader > 0 ? shader : m.program, mv, proj, view, models[0]);
model_draw_call(m, shader > 0 ? shader : m.program, pass, pos44(view), models[0]); model_draw_call(m, shader > 0 ? shader : m.program, pass, pos44(view), models[0]);
} }
@ -386699,11 +386915,20 @@ void model_shading_custom(model_t *m, int shading, const char *vs, const char *f
glUseProgram(0); glUseProgram(0);
if (m->program) if (m->program)
glDeleteProgram(m->program); glDeleteProgram(m->program);
const char *symbols[] = { "{{include-shadowmap}}", vfs_read("shaders/fs_0_0_shadowmap_lit.glsl") }; // #define RIM
int shaderprog = shader(strlerp(1,symbols,vs), strlerp(1,symbols,fs), //fs, {
"att_position,att_texcoord,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent,att_texcoord2","fragColor", int shaderprog = shader(vs, fs, //fs,
va("%s,%s,%s", defines ? defines : "NO_CUSTOM_DEFINES", shading_define, (flags&MODEL_RIMLIGHT)?"RIM":"")); "att_position,att_texcoord,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent,att_texcoord2","fragColor",
m->program = shaderprog; va("%s,%s,%s", defines ? defines : "NO_CUSTOM_DEFINES", shading_define, (flags&MODEL_RIMLIGHT)?"RIM":""));
m->program = shaderprog;
}
{
int shaderprog = shader(vs, vfs_read("shaders/fs_shadow_vsm.glsl"), //fs,
"att_position,att_texcoord,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent,att_texcoord2","fragcolor",
va("SHADOW_CAST,%s", defines ? defines : "NO_CUSTOM_DEFINES"));
m->shadow_program = shaderprog;
}
model_init_uniforms(m); model_init_uniforms(m);
} }
@ -386723,6 +386948,16 @@ void model_skybox(model_t *mdl, skybox_t sky, bool load_sh) {
mdl->sky_env = sky.env; mdl->sky_env = sky.env;
} }
void model_shadow(model_t *mdl, shadowmap_t *sm) {
if (sm) {
mdl->shadow_receiver = true;
mdl->shadow_map = sm;
} else {
mdl->shadow_receiver = false;
mdl->shadow_map = NULL;
}
}
void model_fog(model_t *mdl, unsigned mode, vec3 color, float start, float end, float density) { void model_fog(model_t *mdl, unsigned mode, vec3 color, float start, float end, float density) {
unsigned oldprog = last_shader; unsigned oldprog = last_shader;
shader_bind(mdl->program); shader_bind(mdl->program);
@ -388379,96 +388614,6 @@ void object_billboard(object_t *obj, unsigned mode) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
light_t light() {
light_t l = {0};
l.diffuse = vec3(1,1,1);
l.dir = vec3(1,-1,-1);
l.falloff.constant = 1.0f;
l.falloff.linear = 0.09f;
l.falloff.quadratic = 0.0032f;
l.specularPower = 32.f;
l.innerCone = 0.85f;// 31 deg
l.outerCone = 0.9f; // 25 deg
return l;
}
void light_type(light_t* l, char type) {
l->cached = 0;
l->type = type;
}
void light_diffuse(light_t* l, vec3 color) {
l->cached = 0;
l->diffuse = color;
}
void light_specular(light_t* l, vec3 color) {
l->cached = 0;
l->specular = color;
}
void light_ambient(light_t* l, vec3 color) {
l->cached = 0;
l->ambient = color;
}
void light_teleport(light_t* l, vec3 pos) {
l->cached = 0;
l->pos = pos;
}
void light_dir(light_t* l, vec3 dir) {
l->cached = 0;
l->dir = dir;
}
void light_power(light_t* l, float power) {
l->cached = 0;
l->specularPower = power;
}
void light_falloff(light_t* l, float constant, float linear, float quadratic) {
l->cached = 0;
l->falloff.constant = constant;
l->falloff.linear = linear;
l->falloff.quadratic = quadratic;
}
void light_radius(light_t* l, float radius) {
l->cached = 0;
l->radius = radius;
}
void light_cone(light_t* l, float innerCone, float outerCone) {
l->cached = 0;
l->innerCone = acos(innerCone);
l->outerCone = acos(outerCone);
}
void light_update(unsigned num_lights, light_t *lv) {
shader_int("u_num_lights", num_lights);
for (unsigned i=0; i < num_lights; ++i) {
lv[i].cached = 1;
shader_int(va("u_lights[%d].type", i), lv[i].type);
shader_vec3(va("u_lights[%d].pos", i), lv[i].pos);
shader_vec3(va("u_lights[%d].dir", i), lv[i].dir);
shader_vec3(va("u_lights[%d].diffuse", i), lv[i].diffuse);
shader_vec3(va("u_lights[%d].specular", i), lv[i].specular);
shader_vec3(va("u_lights[%d].ambient", i), lv[i].ambient);
shader_float(va("u_lights[%d].power", i), lv[i].specularPower);
shader_float(va("u_lights[%d].radius", i), lv[i].radius);
shader_float(va("u_lights[%d].constant", i), lv[i].falloff.constant);
shader_float(va("u_lights[%d].linear", i), lv[i].falloff.linear);
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);
}
}
// -----------------------------------------------------------------------------
array(scene_t*) scenes; array(scene_t*) scenes;
scene_t* last_scene; scene_t* last_scene;

View File

@ -119,6 +119,9 @@ renderstate_t renderstate() {
// Disable scissor test by default // Disable scissor test by default
state.scissor_test_enabled = GL_FALSE; state.scissor_test_enabled = GL_FALSE;
// Enable seamless cubemap by default
state.seamless_cubemap = GL_TRUE;
return state; return state;
} }
@ -225,6 +228,13 @@ void renderstate_apply(const renderstate_t *state) {
} else { } else {
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
} }
// Apply seamless cubemap
if (state->seamless_cubemap) {
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
} else {
glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
} }
} }
@ -1449,92 +1459,256 @@ texture_t texture_compressed(const char *pathfile, unsigned flags) {
return texture_compressed_from_mem(data, size, flags); return texture_compressed_from_mem(data, size, flags);
} }
// -----------------------------------------------------------------------------
light_t light() {
light_t l = {0};
l.diffuse = vec3(1,1,1);
l.dir = vec3(1,-1,-1);
l.falloff.constant = 1.0f;
l.falloff.linear = 0.09f;
l.falloff.quadratic = 0.0032f;
l.specularPower = 32.f;
l.innerCone = 0.85f;// 31 deg
l.outerCone = 0.9f; // 25 deg
l.shadow_distance = 100.0f;
l.shadow_bias = 0.5f;
return l;
}
void light_type(light_t* l, char type) {
l->cached = 0;
l->type = type;
}
void light_diffuse(light_t* l, vec3 color) {
l->cached = 0;
l->diffuse = color;
}
void light_specular(light_t* l, vec3 color) {
l->cached = 0;
l->specular = color;
}
void light_ambient(light_t* l, vec3 color) {
l->cached = 0;
l->ambient = color;
}
void light_teleport(light_t* l, vec3 pos) {
l->cached = 0;
l->pos = pos;
}
void light_dir(light_t* l, vec3 dir) {
l->cached = 0;
l->dir = dir;
}
void light_power(light_t* l, float power) {
l->cached = 0;
l->specularPower = power;
}
void light_falloff(light_t* l, float constant, float linear, float quadratic) {
l->cached = 0;
l->falloff.constant = constant;
l->falloff.linear = linear;
l->falloff.quadratic = quadratic;
}
void light_radius(light_t* l, float radius) {
l->cached = 0;
l->radius = radius;
}
void light_cone(light_t* l, float innerCone, float outerCone) {
l->cached = 0;
l->innerCone = acos(innerCone);
l->outerCone = acos(outerCone);
}
void light_update(unsigned num_lights, light_t *lv) {
if (num_lights > MAX_LIGHTS) {
PRINTF("WARNING: num_lights > MAX_LIGHTS, clamping to MAX_LIGHTS\n");
num_lights = MAX_LIGHTS;
}
shader_int("u_num_lights", num_lights);
for (unsigned i=0; i < num_lights; ++i) {
lv[i].cached = 1;
shader_int(va("u_lights[%d].type", i), lv[i].type);
shader_vec3(va("u_lights[%d].pos", i), lv[i].pos);
shader_vec3(va("u_lights[%d].dir", i), lv[i].dir);
shader_vec3(va("u_lights[%d].diffuse", i), lv[i].diffuse);
shader_vec3(va("u_lights[%d].specular", i), lv[i].specular);
shader_vec3(va("u_lights[%d].ambient", i), lv[i].ambient);
shader_float(va("u_lights[%d].power", i), lv[i].specularPower);
shader_float(va("u_lights[%d].radius", i), lv[i].radius);
shader_float(va("u_lights[%d].constant", i), lv[i].falloff.constant);
shader_float(va("u_lights[%d].linear", i), lv[i].falloff.linear);
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);
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// shadowmaps // shadowmaps
static inline
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
// Create a cubemap color texture
glGenTextures(1, &s->maps[light_index].texture);
glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].texture);
for (int i = 0; i < 6; i++) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_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_MAG_FILTER, GL_LINEAR);
// Create a cubemap depth texture
glGenTextures(1, &s->maps[light_index].depth_texture);
glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].depth_texture);
for (int i = 0; i < 6; i++) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
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_EDGE);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
// Create 6 framebuffers for each face of the cubemap
glGenFramebuffers(6, s->maps[light_index].fbos);
for (int i = 0; i < 6; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbos[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, s->maps[light_index].texture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, s->maps[light_index].depth_texture, 0);
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (GL_FRAMEBUFFER_COMPLETE != result) {
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
}
}
}
shadowmap_t shadowmap(int texture_width) { // = 1024 shadowmap_t shadowmap(int texture_width) { // = 1024
shadowmap_t s = {0}; shadowmap_t s = {0};
s.texture_width = texture_width; s.texture_width = texture_width;
s.saved_fb = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb);
glGenFramebuffers(1, &s.fbo); for (int i = 0; i < MAX_LIGHTS; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, s.fbo); shadowmap_init_caster(&s, i, texture_width);
}
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &s.texture);
glBindTexture(GL_TEXTURE_2D, s.texture);
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);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s.texture, 0);
#if is(ems)
GLenum nones[] = { GL_NONE };
glDrawBuffers(1, nones);
glReadBuffer(GL_NONE);
#else
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
#endif
glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb); glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb);
return s; return s;
} }
void shadowmap_destroy(shadowmap_t *s) { void shadowmap_destroy(shadowmap_t *s) {
if (s->texture) { for (int i = 0; i < MAX_LIGHTS; i++) {
glDeleteTextures(1, &s->texture); glDeleteFramebuffers(6, s->maps[i].fbos);
} glDeleteTextures(1, &s->maps[i].texture);
if (s->fbo) { glDeleteTextures(1, &s->maps[i].depth_texture);
glDeleteFramebuffers(1, &s->fbo);
} }
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) { void shadowmap_set_shadowmatrix(shadowmap_t *s, vec3 aLightPos, vec3 aLightAt, vec3 aLightUp, const mat44 projection) {
copy44(s->proj, projection); // copy44(s->proj, projection);
s->light_position = vec4(aLightPos.x, aLightPos.y, aLightPos.z, 1); // lookat44(s->mv, aLightPos, aLightAt, aLightUp);
lookat44(s->mv, aLightPos, aLightAt, aLightUp);
mat44 bias = { // mat44 bias = {
0.5, 0.0, 0.0, 0.0, // 0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0, // 0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0, // 0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0 }; // 0.5, 0.5, 0.5, 1.0 };
// s->shadowmatrix = bias; // multiply44x3(s->shadowmatrix, bias, s->proj, s->mv);
// s->shadowmatrix *= s->proj; // multiply44x2(s->mvp, projection, s->mv);
// s->shadowmatrix *= s->mv;
// multiply44x3(s->shadowmatrix, s->mv, s->proj, bias);
multiply44x3(s->shadowmatrix, bias, s->proj, s->mv);
// mvp = projection * s->mv;
// multiply44x2(s->mvp, s->mv, projection);
multiply44x2(s->mvp, projection, s->mv);
} }
static shadowmap_t *active_shadowmap = NULL;
void shadowmap_begin(shadowmap_t *s) { void shadowmap_begin(shadowmap_t *s) {
glGetIntegerv(GL_VIEWPORT, &s->saved_viewport[0]); glGetIntegerv(GL_VIEWPORT, s->saved_vp);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s->saved_fb); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s->saved_fb);
glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
s->step = 0;
s->light_step = 0;
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) {
if(dir<0) return;
mat44 P, V, PV;
perspective44(P, 90.0f, 1.0f, l->shadow_bias, l->shadow_distance);
vec3 lightPos = l->pos;
/**/ if(dir == 0) lookat44(V, lightPos, add3(lightPos, vec3(+1, 0, 0)), vec3(0, -1, 0)); // +X
else if(dir == 1) lookat44(V, lightPos, add3(lightPos, vec3(-1, 0, 0)), vec3(0, -1, 0)); // -X
else if(dir == 2) lookat44(V, lightPos, add3(lightPos, vec3( 0, +1, 0)), vec3(0, 0, +1)); // +Y
else if(dir == 3) lookat44(V, lightPos, add3(lightPos, vec3( 0, -1, 0)), vec3(0, 0, -1)); // -Y
else if(dir == 4) lookat44(V, lightPos, add3(lightPos, vec3( 0, 0, +1)), vec3(0, -1, 0)); // +Z
else /*dir == 5*/ lookat44(V, lightPos, add3(lightPos, vec3( 0, 0, -1)), vec3(0, -1, 0)); // -Z
multiply44x2(PV, P, V); // -Z
copy44(s->V, V);
copy44(s->PV, PV);
}
bool shadowmap_step(shadowmap_t *s) {
if (s->step >= 6) {
s->step = 0;
s->light_step++;
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);
glClearDepth(1); s->step++;
glClear(GL_DEPTH_BUFFER_BIT); return true;
}
void shadowmap_light(shadowmap_t *s, light_t *l) {
if (l->cast_shadows) {
int step = s->step - 1;
if (l->type == LIGHT_POINT) {
shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_SPOT) {
// spot light
} else if (l->type == LIGHT_DIRECTIONAL) {
// directional light
}
}
} }
void shadowmap_end(shadowmap_t *s) { void shadowmap_end(shadowmap_t *s) {
glViewport(s->saved_viewport[0], s->saved_viewport[1], s->saved_viewport[2], s->saved_viewport[3]); glViewport(s->saved_vp[0], s->saved_vp[1], s->saved_vp[2], s->saved_vp[3]);
glBindFramebuffer(GL_FRAMEBUFFER, s->saved_fb); glBindFramebuffer(GL_FRAMEBUFFER, s->saved_fb);
model_setpass(s->saved_pass);
active_shadowmap = NULL;
} }
// shadowmap utils // shadowmap utils
@ -3710,6 +3884,25 @@ void model_set_uniforms(model_t m, int shader, mat44 mv, mat44 proj, mat44 view,
glUniform1f(loc, (float)window_time()); glUniform1f(loc, (float)window_time());
} }
// shadow casting
if (shader == m.shadow_program) {
shadowmap_t *sm = active_shadowmap;
ASSERT(sm);
shader_mat44("cameraToShadowView", sm->V);
shader_mat44("cameraToShadowProjector", sm->PV);
}
// shadow receiving
if (m.shadow_receiver) {
ASSERT(m.shadow_map);
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);
}
} else {
shader_bool("u_shadow_receiver", GL_FALSE);
}
if (m.shading == SHADING_PBR) { if (m.shading == SHADING_PBR) {
handle old_shader = last_shader; handle old_shader = last_shader;
shader_bind(shader); shader_bind(shader);
@ -4323,12 +4516,13 @@ void model_set_renderstates(model_t *m) {
transparent_rs->front_face = GL_CW; transparent_rs->front_face = GL_CW;
} }
// Shadow pass @todo // Shadow pass
renderstate_t *shadow_rs = &m->rs[RENDER_PASS_SHADOW]; renderstate_t *shadow_rs = &m->rs[RENDER_PASS_SHADOW];
{ {
shadow_rs->blend_enabled = 1; shadow_rs->blend_enabled = 1;
shadow_rs->blend_src = GL_SRC_ALPHA; shadow_rs->blend_src = GL_SRC_ALPHA;
shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA;
shadow_rs->cull_face_enabled = 1;
shadow_rs->cull_face_mode = GL_BACK; shadow_rs->cull_face_mode = GL_BACK;
shadow_rs->front_face = GL_CW; shadow_rs->front_face = GL_CW;
} }
@ -4727,6 +4921,7 @@ void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_
int rs_idx = model_getpass(); int rs_idx = model_getpass();
renderstate_t *rs = &m.rs[rs_idx]; renderstate_t *rs = &m.rs[rs_idx];
renderstate_apply(rs);
glBindVertexArray( q->vao ); glBindVertexArray( q->vao );
@ -4810,31 +5005,33 @@ void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_
renderstate_apply(rs); renderstate_apply(rs);
} }
if (m.shading != SHADING_PBR) { if (rs_idx != RENDER_PASS_SHADOW) {
shader_texture_unit("u_texture2d", q->textures[i], texture_unit()); if (m.shading != SHADING_PBR) {
shader_texture("u_lightmap", m.lightmap); shader_texture_unit("u_texture2d", q->textures[i], texture_unit());
shader_texture("u_lightmap", m.lightmap);
int loc; int loc;
if ((loc = glGetUniformLocation(shader, "u_textured")) >= 0) { if ((loc = glGetUniformLocation(shader, "u_textured")) >= 0) {
bool textured = !!q->textures[i] && q->textures[i] != texture_checker().id; // m.materials[i].layer[0].texture != texture_checker().id; bool textured = !!q->textures[i] && q->textures[i] != texture_checker().id; // m.materials[i].layer[0].texture != texture_checker().id;
glUniform1i(loc, textured ? GL_TRUE : GL_FALSE); glUniform1i(loc, textured ? GL_TRUE : GL_FALSE);
if ((loc = glGetUniformLocation(shader, "u_diffuse")) >= 0) { if ((loc = glGetUniformLocation(shader, "u_diffuse")) >= 0) {
glUniform4f(loc, m.materials[i].layer[0].map.color.r, m.materials[i].layer[0].map.color.g, m.materials[i].layer[0].map.color.b, m.materials[i].layer[0].map.color.a); glUniform4f(loc, m.materials[i].layer[0].map.color.r, m.materials[i].layer[0].map.color.g, m.materials[i].layer[0].map.color.b, m.materials[i].layer[0].map.color.a);
}
} }
}
} else { } else {
const material_t *material = &m.materials[i]; const material_t *material = &m.materials[i];
shader_colormap_model_internal( "map_diffuse.color", "map_diffuse.has_tex", "map_diffuse_tex", material->layer[MATERIAL_CHANNEL_DIFFUSE].map ); shader_colormap_model_internal( "map_diffuse.color", "map_diffuse.has_tex", "map_diffuse_tex", material->layer[MATERIAL_CHANNEL_DIFFUSE].map );
shader_colormap_model_internal( "map_normals.color", "map_normals.has_tex", "map_normals_tex", material->layer[MATERIAL_CHANNEL_NORMALS].map ); shader_colormap_model_internal( "map_normals.color", "map_normals.has_tex", "map_normals_tex", material->layer[MATERIAL_CHANNEL_NORMALS].map );
shader_colormap_model_internal( "map_specular.color", "map_specular.has_tex", "map_specular_tex", material->layer[MATERIAL_CHANNEL_SPECULAR].map ); shader_colormap_model_internal( "map_specular.color", "map_specular.has_tex", "map_specular_tex", material->layer[MATERIAL_CHANNEL_SPECULAR].map );
shader_colormap_model_internal( "map_albedo.color", "map_albedo.has_tex", "map_albedo_tex", material->layer[MATERIAL_CHANNEL_ALBEDO].map ); shader_colormap_model_internal( "map_albedo.color", "map_albedo.has_tex", "map_albedo_tex", material->layer[MATERIAL_CHANNEL_ALBEDO].map );
shader_colormap_model_internal( "map_roughness.color", "map_roughness.has_tex", "map_roughness_tex", material->layer[MATERIAL_CHANNEL_ROUGHNESS].map ); shader_colormap_model_internal( "map_roughness.color", "map_roughness.has_tex", "map_roughness_tex", material->layer[MATERIAL_CHANNEL_ROUGHNESS].map );
shader_colormap_model_internal( "map_metallic.color", "map_metallic.has_tex", "map_metallic_tex", material->layer[MATERIAL_CHANNEL_METALLIC].map ); shader_colormap_model_internal( "map_metallic.color", "map_metallic.has_tex", "map_metallic_tex", material->layer[MATERIAL_CHANNEL_METALLIC].map );
shader_colormap_model_internal( "map_ao.color", "map_ao.has_tex", "map_ao_tex", material->layer[MATERIAL_CHANNEL_AO].map ); shader_colormap_model_internal( "map_ao.color", "map_ao.has_tex", "map_ao_tex", material->layer[MATERIAL_CHANNEL_AO].map );
shader_colormap_model_internal( "map_ambient.color", "map_ambient.has_tex", "map_ambient_tex", material->layer[MATERIAL_CHANNEL_AMBIENT].map ); shader_colormap_model_internal( "map_ambient.color", "map_ambient.has_tex", "map_ambient_tex", material->layer[MATERIAL_CHANNEL_AMBIENT].map );
shader_colormap_model_internal( "map_emissive.color", "map_emissive.has_tex", "map_emissive_tex", material->layer[MATERIAL_CHANNEL_EMISSIVE].map ); shader_colormap_model_internal( "map_emissive.color", "map_emissive.has_tex", "map_emissive_tex", material->layer[MATERIAL_CHANNEL_EMISSIVE].map );
// shader_float( "specular_shininess", material->specular_shininess ); // unused, basic_specgloss.fs only // shader_float( "specular_shininess", material->specular_shininess ); // unused, basic_specgloss.fs only
}
} }
glDrawElementsInstanced(GL_TRIANGLES, 3*im->num_triangles, GL_UNSIGNED_INT, &tris[im->first_triangle], m.num_instances); glDrawElementsInstanced(GL_TRIANGLES, 3*im->num_triangles, GL_UNSIGNED_INT, &tris[im->first_triangle], m.num_instances);
@ -4859,6 +5056,10 @@ void model_render_instanced_pass(model_t m, mat44 proj, mat44 view, mat44* model
model_set_state(m); model_set_state(m);
} }
if (model_getpass() == RENDER_PASS_SHADOW) {
shader = m.shadow_program;
}
model_set_uniforms(m, shader > 0 ? shader : m.program, mv, proj, view, models[0]); model_set_uniforms(m, shader > 0 ? shader : m.program, mv, proj, view, models[0]);
model_draw_call(m, shader > 0 ? shader : m.program, pass, pos44(view), models[0]); model_draw_call(m, shader > 0 ? shader : m.program, pass, pos44(view), models[0]);
} }
@ -5012,11 +5213,20 @@ void model_shading_custom(model_t *m, int shading, const char *vs, const char *f
glUseProgram(0); glUseProgram(0);
if (m->program) if (m->program)
glDeleteProgram(m->program); glDeleteProgram(m->program);
const char *symbols[] = { "{{include-shadowmap}}", vfs_read("shaders/fs_0_0_shadowmap_lit.glsl") }; // #define RIM
int shaderprog = shader(strlerp(1,symbols,vs), strlerp(1,symbols,fs), //fs, {
"att_position,att_texcoord,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent,att_texcoord2","fragColor", int shaderprog = shader(vs, fs, //fs,
va("%s,%s,%s", defines ? defines : "NO_CUSTOM_DEFINES", shading_define, (flags&MODEL_RIMLIGHT)?"RIM":"")); "att_position,att_texcoord,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent,att_texcoord2","fragColor",
m->program = shaderprog; va("%s,%s,%s", defines ? defines : "NO_CUSTOM_DEFINES", shading_define, (flags&MODEL_RIMLIGHT)?"RIM":""));
m->program = shaderprog;
}
{
int shaderprog = shader(vs, vfs_read("shaders/fs_shadow_vsm.glsl"), //fs,
"att_position,att_texcoord,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent,att_texcoord2","fragcolor",
va("SHADOW_CAST,%s", defines ? defines : "NO_CUSTOM_DEFINES"));
m->shadow_program = shaderprog;
}
model_init_uniforms(m); model_init_uniforms(m);
} }
@ -5036,6 +5246,16 @@ void model_skybox(model_t *mdl, skybox_t sky, bool load_sh) {
mdl->sky_env = sky.env; mdl->sky_env = sky.env;
} }
void model_shadow(model_t *mdl, shadowmap_t *sm) {
if (sm) {
mdl->shadow_receiver = true;
mdl->shadow_map = sm;
} else {
mdl->shadow_receiver = false;
mdl->shadow_map = NULL;
}
}
void model_fog(model_t *mdl, unsigned mode, vec3 color, float start, float end, float density) { void model_fog(model_t *mdl, unsigned mode, vec3 color, float start, float end, float density) {
unsigned oldprog = last_shader; unsigned oldprog = last_shader;
shader_bind(mdl->program); shader_bind(mdl->program);

View File

@ -65,6 +65,9 @@ typedef struct renderstate_t {
// Scissor test // Scissor test
bool scissor_test_enabled; bool scissor_test_enabled;
// bool Seamless Cubemap
bool seamless_cubemap;
} renderstate_t; } renderstate_t;
API renderstate_t renderstate(); API renderstate_t renderstate();
@ -264,6 +267,7 @@ API void cubemap_sh_shader(cubemap_t *c);
API void cubemap_sh_add_light(cubemap_t *c, vec3 light, vec3 dir, float strength); API void cubemap_sh_add_light(cubemap_t *c, vec3 light, vec3 dir, float strength);
// lighting probe blending // lighting probe blending
// @note: uploads SH coefficients to shader
API void cubemap_sh_blend(vec3 pos, float max_dist, unsigned count, cubemap_t *probes); API void cubemap_sh_blend(vec3 pos, float max_dist, unsigned count, cubemap_t *probes);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -274,6 +278,54 @@ API void fbo_bind(unsigned id);
API void fbo_unbind(); API void fbo_unbind();
API void fbo_destroy(unsigned id); API void fbo_destroy(unsigned id);
// -----------------------------------------------------------------------------
// lights
enum {
MAX_LIGHTS = 16,
};
enum LIGHT_TYPE {
LIGHT_DIRECTIONAL,
LIGHT_POINT,
LIGHT_SPOT,
};
typedef struct light_t {
char type;
vec3 diffuse, specular, ambient;
vec3 pos, dir;
struct {
float constant, linear, quadratic;
} falloff;
float radius;
float specularPower;
float innerCone, outerCone;
//@todo: cookie, flare
// Shadowmapping
bool cast_shadows;
float shadow_distance;
float shadow_bias;
// internals
bool cached; //< used by scene to invalidate cached light data
} light_t;
API light_t light();
// API void light_flags(int flags);
API void light_type(light_t* l, char type);
API void light_diffuse(light_t* l, vec3 color);
API void light_specular(light_t* l, vec3 color);
API void light_ambient(light_t* l, vec3 color);
API void light_teleport(light_t* l, vec3 pos);
API void light_dir(light_t* l, vec3 dir);
API void light_power(light_t* l, float power);
API void light_radius(light_t* l, float radius);
API void light_falloff(light_t* l, float constant, float linear, float quadratic);
API void light_cone(light_t* l, float innerCone, float outerCone);
API void light_update(unsigned num_lights, light_t *lv);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// shadowmaps // shadowmaps
@ -285,15 +337,19 @@ API void fbo_destroy(unsigned id);
// #endif // #endif
typedef struct shadowmap_t { typedef struct shadowmap_t {
mat44 shadowmatrix; mat44 V;
mat44 mvp; mat44 PV;
mat44 mv;
mat44 proj;
vec4 light_position;
int saved_fb;
int saved_viewport[4];
handle fbo, texture;
int texture_width; int texture_width;
int step;
int light_step;
struct {
handle fbos[6], texture, depth_texture;
} maps[MAX_LIGHTS];
handle saved_fb;
handle saved_pass;
int saved_vp[4];
} shadowmap_t; } shadowmap_t;
API shadowmap_t shadowmap(int texture_width); // = 1024 API shadowmap_t shadowmap(int texture_width); // = 1024
@ -301,13 +357,10 @@ 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_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 void shadowmap_light(shadowmap_t *s, light_t *l);
API void shadowmap_end(shadowmap_t *s); API void shadowmap_end(shadowmap_t *s);
// shadowmap utils
API void shadowmatrix_proj(mat44 shm_proj, float aLightFov, float znear, float zfar);
API void shadowmatrix_ortho(mat44 shm_proj, float left, float right, float bottom, float top, float znear, float zfar);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// shaders // shaders
@ -696,6 +749,9 @@ typedef struct model_t {
unsigned num_anims; unsigned num_anims;
unsigned num_frames; unsigned num_frames;
handle program; handle program;
handle shadow_program;
shadowmap_t *shadow_map;
bool shadow_receiver;
float curframe; float curframe;
mat44 pivot; mat44 pivot;
@ -747,6 +803,7 @@ API void model_lod(model_t*, float lo_detail, float hi_detail, float morph);
API void model_shading(model_t*, int shading); API void model_shading(model_t*, int shading);
API void model_shading_custom(model_t*, int shading, const char *vs, const char *fs, const char *defines); API void model_shading_custom(model_t*, int shading, const char *vs, const char *fs, const char *defines);
API void model_skybox(model_t*, skybox_t sky, bool load_sh); API void model_skybox(model_t*, skybox_t sky, bool load_sh);
API void model_shadow(model_t*, shadowmap_t *sm);
API void model_fog(model_t*, unsigned mode, vec3 color, float start, float end, float density); API void model_fog(model_t*, unsigned mode, vec3 color, float start, float end, float density);
API void model_render(model_t, mat44 proj, mat44 view, mat44 model, int shader); API void model_render(model_t, mat44 proj, mat44 view, mat44 model, int shader);
API void model_render_skeleton(model_t, mat44 model); API void model_render_skeleton(model_t, mat44 model);

View File

@ -297,96 +297,6 @@ void object_billboard(object_t *obj, unsigned mode) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
light_t light() {
light_t l = {0};
l.diffuse = vec3(1,1,1);
l.dir = vec3(1,-1,-1);
l.falloff.constant = 1.0f;
l.falloff.linear = 0.09f;
l.falloff.quadratic = 0.0032f;
l.specularPower = 32.f;
l.innerCone = 0.85f;// 31 deg
l.outerCone = 0.9f; // 25 deg
return l;
}
void light_type(light_t* l, char type) {
l->cached = 0;
l->type = type;
}
void light_diffuse(light_t* l, vec3 color) {
l->cached = 0;
l->diffuse = color;
}
void light_specular(light_t* l, vec3 color) {
l->cached = 0;
l->specular = color;
}
void light_ambient(light_t* l, vec3 color) {
l->cached = 0;
l->ambient = color;
}
void light_teleport(light_t* l, vec3 pos) {
l->cached = 0;
l->pos = pos;
}
void light_dir(light_t* l, vec3 dir) {
l->cached = 0;
l->dir = dir;
}
void light_power(light_t* l, float power) {
l->cached = 0;
l->specularPower = power;
}
void light_falloff(light_t* l, float constant, float linear, float quadratic) {
l->cached = 0;
l->falloff.constant = constant;
l->falloff.linear = linear;
l->falloff.quadratic = quadratic;
}
void light_radius(light_t* l, float radius) {
l->cached = 0;
l->radius = radius;
}
void light_cone(light_t* l, float innerCone, float outerCone) {
l->cached = 0;
l->innerCone = acos(innerCone);
l->outerCone = acos(outerCone);
}
void light_update(unsigned num_lights, light_t *lv) {
shader_int("u_num_lights", num_lights);
for (unsigned i=0; i < num_lights; ++i) {
lv[i].cached = 1;
shader_int(va("u_lights[%d].type", i), lv[i].type);
shader_vec3(va("u_lights[%d].pos", i), lv[i].pos);
shader_vec3(va("u_lights[%d].dir", i), lv[i].dir);
shader_vec3(va("u_lights[%d].diffuse", i), lv[i].diffuse);
shader_vec3(va("u_lights[%d].specular", i), lv[i].specular);
shader_vec3(va("u_lights[%d].ambient", i), lv[i].ambient);
shader_float(va("u_lights[%d].power", i), lv[i].specularPower);
shader_float(va("u_lights[%d].radius", i), lv[i].radius);
shader_float(va("u_lights[%d].constant", i), lv[i].falloff.constant);
shader_float(va("u_lights[%d].linear", i), lv[i].falloff.linear);
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);
}
}
// -----------------------------------------------------------------------------
array(scene_t*) scenes; array(scene_t*) scenes;
scene_t* last_scene; scene_t* last_scene;

View File

@ -75,48 +75,6 @@ API void object_billboard(object_t *obj, unsigned mode);
// object_pose(transform); // @todo // object_pose(transform); // @todo
// light
enum LIGHT_TYPE {
LIGHT_DIRECTIONAL,
LIGHT_POINT,
LIGHT_SPOT,
};
enum LIGHT_FLAGS {
LIGHT_CAST_SHADOWS = 1,
};
typedef struct light_t {
char type;
vec3 diffuse, specular, ambient;
vec3 pos, dir;
struct {
float constant, linear, quadratic;
} falloff;
float radius;
float specularPower;
float innerCone, outerCone;
//@todo: cookie, flare
// internals
bool cached; //< used by scene to invalidate cached light data
} light_t;
API light_t light();
// API void light_flags(int flags);
API void light_type(light_t* l, char type);
API void light_diffuse(light_t* l, vec3 color);
API void light_specular(light_t* l, vec3 color);
API void light_ambient(light_t* l, vec3 color);
API void light_teleport(light_t* l, vec3 pos);
API void light_dir(light_t* l, vec3 dir);
API void light_power(light_t* l, float power);
API void light_radius(light_t* l, float radius);
API void light_falloff(light_t* l, float constant, float linear, float quadratic);
API void light_cone(light_t* l, float innerCone, float outerCone);
API void light_update(unsigned num_lights, light_t *lv);
// scene // scene
enum SCENE_FLAGS { enum SCENE_FLAGS {

View File

@ -16918,6 +16918,9 @@ renderstate_t renderstate() {
// Disable scissor test by default // Disable scissor test by default
state.scissor_test_enabled = GL_FALSE; state.scissor_test_enabled = GL_FALSE;
// Enable seamless cubemap by default
state.seamless_cubemap = GL_TRUE;
return state; return state;
} }
@ -17024,6 +17027,13 @@ void renderstate_apply(const renderstate_t *state) {
} else { } else {
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
} }
// Apply seamless cubemap
if (state->seamless_cubemap) {
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
} else {
glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
} }
} }
@ -18248,92 +18258,256 @@ texture_t texture_compressed(const char *pathfile, unsigned flags) {
return texture_compressed_from_mem(data, size, flags); return texture_compressed_from_mem(data, size, flags);
} }
// -----------------------------------------------------------------------------
light_t light() {
light_t l = {0};
l.diffuse = vec3(1,1,1);
l.dir = vec3(1,-1,-1);
l.falloff.constant = 1.0f;
l.falloff.linear = 0.09f;
l.falloff.quadratic = 0.0032f;
l.specularPower = 32.f;
l.innerCone = 0.85f;// 31 deg
l.outerCone = 0.9f; // 25 deg
l.shadow_distance = 100.0f;
l.shadow_bias = 0.5f;
return l;
}
void light_type(light_t* l, char type) {
l->cached = 0;
l->type = type;
}
void light_diffuse(light_t* l, vec3 color) {
l->cached = 0;
l->diffuse = color;
}
void light_specular(light_t* l, vec3 color) {
l->cached = 0;
l->specular = color;
}
void light_ambient(light_t* l, vec3 color) {
l->cached = 0;
l->ambient = color;
}
void light_teleport(light_t* l, vec3 pos) {
l->cached = 0;
l->pos = pos;
}
void light_dir(light_t* l, vec3 dir) {
l->cached = 0;
l->dir = dir;
}
void light_power(light_t* l, float power) {
l->cached = 0;
l->specularPower = power;
}
void light_falloff(light_t* l, float constant, float linear, float quadratic) {
l->cached = 0;
l->falloff.constant = constant;
l->falloff.linear = linear;
l->falloff.quadratic = quadratic;
}
void light_radius(light_t* l, float radius) {
l->cached = 0;
l->radius = radius;
}
void light_cone(light_t* l, float innerCone, float outerCone) {
l->cached = 0;
l->innerCone = acos(innerCone);
l->outerCone = acos(outerCone);
}
void light_update(unsigned num_lights, light_t *lv) {
if (num_lights > MAX_LIGHTS) {
PRINTF("WARNING: num_lights > MAX_LIGHTS, clamping to MAX_LIGHTS\n");
num_lights = MAX_LIGHTS;
}
shader_int("u_num_lights", num_lights);
for (unsigned i=0; i < num_lights; ++i) {
lv[i].cached = 1;
shader_int(va("u_lights[%d].type", i), lv[i].type);
shader_vec3(va("u_lights[%d].pos", i), lv[i].pos);
shader_vec3(va("u_lights[%d].dir", i), lv[i].dir);
shader_vec3(va("u_lights[%d].diffuse", i), lv[i].diffuse);
shader_vec3(va("u_lights[%d].specular", i), lv[i].specular);
shader_vec3(va("u_lights[%d].ambient", i), lv[i].ambient);
shader_float(va("u_lights[%d].power", i), lv[i].specularPower);
shader_float(va("u_lights[%d].radius", i), lv[i].radius);
shader_float(va("u_lights[%d].constant", i), lv[i].falloff.constant);
shader_float(va("u_lights[%d].linear", i), lv[i].falloff.linear);
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);
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// shadowmaps // shadowmaps
static inline
shadowmap_init_caster(shadowmap_t *s, int light_index, int texture_width) {
// Create a cubemap color texture
glGenTextures(1, &s->maps[light_index].texture);
glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].texture);
for (int i = 0; i < 6; i++) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB32F, texture_width, texture_width, 0, GL_RGB, GL_FLOAT, 0);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_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_MAG_FILTER, GL_LINEAR);
// Create a cubemap depth texture
glGenTextures(1, &s->maps[light_index].depth_texture);
glBindTexture(GL_TEXTURE_CUBE_MAP, s->maps[light_index].depth_texture);
for (int i = 0; i < 6; i++) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, texture_width, texture_width, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
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_EDGE);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
// Create 6 framebuffers for each face of the cubemap
glGenFramebuffers(6, s->maps[light_index].fbos);
for (int i = 0; i < 6; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, s->maps[light_index].fbos[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, s->maps[light_index].texture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, s->maps[light_index].depth_texture, 0);
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (GL_FRAMEBUFFER_COMPLETE != result) {
PANIC("ERROR: Framebuffer is not complete: %x\n", result);
}
}
}
shadowmap_t shadowmap(int texture_width) { // = 1024 shadowmap_t shadowmap(int texture_width) { // = 1024
shadowmap_t s = {0}; shadowmap_t s = {0};
s.texture_width = texture_width; s.texture_width = texture_width;
s.saved_fb = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s.saved_fb);
glGenFramebuffers(1, &s.fbo); for (int i = 0; i < MAX_LIGHTS; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, s.fbo); shadowmap_init_caster(&s, i, texture_width);
}
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &s.texture);
glBindTexture(GL_TEXTURE_2D, s.texture);
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);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, s.texture, 0);
#if is(ems)
GLenum nones[] = { GL_NONE };
glDrawBuffers(1, nones);
glReadBuffer(GL_NONE);
#else
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
#endif
glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb); glBindFramebuffer(GL_FRAMEBUFFER, s.saved_fb);
return s; return s;
} }
void shadowmap_destroy(shadowmap_t *s) { void shadowmap_destroy(shadowmap_t *s) {
if (s->texture) { for (int i = 0; i < MAX_LIGHTS; i++) {
glDeleteTextures(1, &s->texture); glDeleteFramebuffers(6, s->maps[i].fbos);
} glDeleteTextures(1, &s->maps[i].texture);
if (s->fbo) { glDeleteTextures(1, &s->maps[i].depth_texture);
glDeleteFramebuffers(1, &s->fbo);
} }
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) { void shadowmap_set_shadowmatrix(shadowmap_t *s, vec3 aLightPos, vec3 aLightAt, vec3 aLightUp, const mat44 projection) {
copy44(s->proj, projection); // copy44(s->proj, projection);
s->light_position = vec4(aLightPos.x, aLightPos.y, aLightPos.z, 1); // lookat44(s->mv, aLightPos, aLightAt, aLightUp);
lookat44(s->mv, aLightPos, aLightAt, aLightUp);
mat44 bias = { // mat44 bias = {
0.5, 0.0, 0.0, 0.0, // 0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0, // 0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0, // 0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0 }; // 0.5, 0.5, 0.5, 1.0 };
// s->shadowmatrix = bias; // multiply44x3(s->shadowmatrix, bias, s->proj, s->mv);
// s->shadowmatrix *= s->proj; // multiply44x2(s->mvp, projection, s->mv);
// s->shadowmatrix *= s->mv;
// multiply44x3(s->shadowmatrix, s->mv, s->proj, bias);
multiply44x3(s->shadowmatrix, bias, s->proj, s->mv);
// mvp = projection * s->mv;
// multiply44x2(s->mvp, s->mv, projection);
multiply44x2(s->mvp, projection, s->mv);
} }
static shadowmap_t *active_shadowmap = NULL;
void shadowmap_begin(shadowmap_t *s) { void shadowmap_begin(shadowmap_t *s) {
glGetIntegerv(GL_VIEWPORT, &s->saved_viewport[0]); glGetIntegerv(GL_VIEWPORT, s->saved_vp);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s->saved_fb); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &s->saved_fb);
glBindFramebuffer(GL_FRAMEBUFFER, s->fbo); s->saved_pass = model_setpass(RENDER_PASS_SHADOW);
s->step = 0;
s->light_step = 0;
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) {
if(dir<0) return;
mat44 P, V, PV;
perspective44(P, 90.0f, 1.0f, l->shadow_bias, l->shadow_distance);
vec3 lightPos = l->pos;
/**/ if(dir == 0) lookat44(V, lightPos, add3(lightPos, vec3(+1, 0, 0)), vec3(0, -1, 0)); // +X
else if(dir == 1) lookat44(V, lightPos, add3(lightPos, vec3(-1, 0, 0)), vec3(0, -1, 0)); // -X
else if(dir == 2) lookat44(V, lightPos, add3(lightPos, vec3( 0, +1, 0)), vec3(0, 0, +1)); // +Y
else if(dir == 3) lookat44(V, lightPos, add3(lightPos, vec3( 0, -1, 0)), vec3(0, 0, -1)); // -Y
else if(dir == 4) lookat44(V, lightPos, add3(lightPos, vec3( 0, 0, +1)), vec3(0, -1, 0)); // +Z
else /*dir == 5*/ lookat44(V, lightPos, add3(lightPos, vec3( 0, 0, -1)), vec3(0, -1, 0)); // -Z
multiply44x2(PV, P, V); // -Z
copy44(s->V, V);
copy44(s->PV, PV);
}
bool shadowmap_step(shadowmap_t *s) {
if (s->step >= 6) {
s->step = 0;
s->light_step++;
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);
glClearDepth(1); s->step++;
glClear(GL_DEPTH_BUFFER_BIT); return true;
}
void shadowmap_light(shadowmap_t *s, light_t *l) {
if (l->cast_shadows) {
int step = s->step - 1;
if (l->type == LIGHT_POINT) {
shadowmap_light_point(s, l, step);
} else if (l->type == LIGHT_SPOT) {
// spot light
} else if (l->type == LIGHT_DIRECTIONAL) {
// directional light
}
}
} }
void shadowmap_end(shadowmap_t *s) { void shadowmap_end(shadowmap_t *s) {
glViewport(s->saved_viewport[0], s->saved_viewport[1], s->saved_viewport[2], s->saved_viewport[3]); glViewport(s->saved_vp[0], s->saved_vp[1], s->saved_vp[2], s->saved_vp[3]);
glBindFramebuffer(GL_FRAMEBUFFER, s->saved_fb); glBindFramebuffer(GL_FRAMEBUFFER, s->saved_fb);
model_setpass(s->saved_pass);
active_shadowmap = NULL;
} }
// shadowmap utils // shadowmap utils
@ -20509,6 +20683,25 @@ void model_set_uniforms(model_t m, int shader, mat44 mv, mat44 proj, mat44 view,
glUniform1f(loc, (float)window_time()); glUniform1f(loc, (float)window_time());
} }
// shadow casting
if (shader == m.shadow_program) {
shadowmap_t *sm = active_shadowmap;
ASSERT(sm);
shader_mat44("cameraToShadowView", sm->V);
shader_mat44("cameraToShadowProjector", sm->PV);
}
// shadow receiving
if (m.shadow_receiver) {
ASSERT(m.shadow_map);
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);
}
} else {
shader_bool("u_shadow_receiver", GL_FALSE);
}
if (m.shading == SHADING_PBR) { if (m.shading == SHADING_PBR) {
handle old_shader = last_shader; handle old_shader = last_shader;
shader_bind(shader); shader_bind(shader);
@ -21122,12 +21315,13 @@ void model_set_renderstates(model_t *m) {
transparent_rs->front_face = GL_CW; transparent_rs->front_face = GL_CW;
} }
// Shadow pass @todo // Shadow pass
renderstate_t *shadow_rs = &m->rs[RENDER_PASS_SHADOW]; renderstate_t *shadow_rs = &m->rs[RENDER_PASS_SHADOW];
{ {
shadow_rs->blend_enabled = 1; shadow_rs->blend_enabled = 1;
shadow_rs->blend_src = GL_SRC_ALPHA; shadow_rs->blend_src = GL_SRC_ALPHA;
shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA; shadow_rs->blend_dst = GL_ONE_MINUS_SRC_ALPHA;
shadow_rs->cull_face_enabled = 1;
shadow_rs->cull_face_mode = GL_BACK; shadow_rs->cull_face_mode = GL_BACK;
shadow_rs->front_face = GL_CW; shadow_rs->front_face = GL_CW;
} }
@ -21526,6 +21720,7 @@ void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_
int rs_idx = model_getpass(); int rs_idx = model_getpass();
renderstate_t *rs = &m.rs[rs_idx]; renderstate_t *rs = &m.rs[rs_idx];
renderstate_apply(rs);
glBindVertexArray( q->vao ); glBindVertexArray( q->vao );
@ -21609,31 +21804,33 @@ void model_draw_call(model_t m, int shader, int pass, vec3 cam_pos, mat44 model_
renderstate_apply(rs); renderstate_apply(rs);
} }
if (m.shading != SHADING_PBR) { if (rs_idx != RENDER_PASS_SHADOW) {
shader_texture_unit("u_texture2d", q->textures[i], texture_unit()); if (m.shading != SHADING_PBR) {
shader_texture("u_lightmap", m.lightmap); shader_texture_unit("u_texture2d", q->textures[i], texture_unit());
shader_texture("u_lightmap", m.lightmap);
int loc; int loc;
if ((loc = glGetUniformLocation(shader, "u_textured")) >= 0) { if ((loc = glGetUniformLocation(shader, "u_textured")) >= 0) {
bool textured = !!q->textures[i] && q->textures[i] != texture_checker().id; // m.materials[i].layer[0].texture != texture_checker().id; bool textured = !!q->textures[i] && q->textures[i] != texture_checker().id; // m.materials[i].layer[0].texture != texture_checker().id;
glUniform1i(loc, textured ? GL_TRUE : GL_FALSE); glUniform1i(loc, textured ? GL_TRUE : GL_FALSE);
if ((loc = glGetUniformLocation(shader, "u_diffuse")) >= 0) { if ((loc = glGetUniformLocation(shader, "u_diffuse")) >= 0) {
glUniform4f(loc, m.materials[i].layer[0].map.color.r, m.materials[i].layer[0].map.color.g, m.materials[i].layer[0].map.color.b, m.materials[i].layer[0].map.color.a); glUniform4f(loc, m.materials[i].layer[0].map.color.r, m.materials[i].layer[0].map.color.g, m.materials[i].layer[0].map.color.b, m.materials[i].layer[0].map.color.a);
}
} }
}
} else { } else {
const material_t *material = &m.materials[i]; const material_t *material = &m.materials[i];
shader_colormap_model_internal( "map_diffuse.color", "map_diffuse.has_tex", "map_diffuse_tex", material->layer[MATERIAL_CHANNEL_DIFFUSE].map ); shader_colormap_model_internal( "map_diffuse.color", "map_diffuse.has_tex", "map_diffuse_tex", material->layer[MATERIAL_CHANNEL_DIFFUSE].map );
shader_colormap_model_internal( "map_normals.color", "map_normals.has_tex", "map_normals_tex", material->layer[MATERIAL_CHANNEL_NORMALS].map ); shader_colormap_model_internal( "map_normals.color", "map_normals.has_tex", "map_normals_tex", material->layer[MATERIAL_CHANNEL_NORMALS].map );
shader_colormap_model_internal( "map_specular.color", "map_specular.has_tex", "map_specular_tex", material->layer[MATERIAL_CHANNEL_SPECULAR].map ); shader_colormap_model_internal( "map_specular.color", "map_specular.has_tex", "map_specular_tex", material->layer[MATERIAL_CHANNEL_SPECULAR].map );
shader_colormap_model_internal( "map_albedo.color", "map_albedo.has_tex", "map_albedo_tex", material->layer[MATERIAL_CHANNEL_ALBEDO].map ); shader_colormap_model_internal( "map_albedo.color", "map_albedo.has_tex", "map_albedo_tex", material->layer[MATERIAL_CHANNEL_ALBEDO].map );
shader_colormap_model_internal( "map_roughness.color", "map_roughness.has_tex", "map_roughness_tex", material->layer[MATERIAL_CHANNEL_ROUGHNESS].map ); shader_colormap_model_internal( "map_roughness.color", "map_roughness.has_tex", "map_roughness_tex", material->layer[MATERIAL_CHANNEL_ROUGHNESS].map );
shader_colormap_model_internal( "map_metallic.color", "map_metallic.has_tex", "map_metallic_tex", material->layer[MATERIAL_CHANNEL_METALLIC].map ); shader_colormap_model_internal( "map_metallic.color", "map_metallic.has_tex", "map_metallic_tex", material->layer[MATERIAL_CHANNEL_METALLIC].map );
shader_colormap_model_internal( "map_ao.color", "map_ao.has_tex", "map_ao_tex", material->layer[MATERIAL_CHANNEL_AO].map ); shader_colormap_model_internal( "map_ao.color", "map_ao.has_tex", "map_ao_tex", material->layer[MATERIAL_CHANNEL_AO].map );
shader_colormap_model_internal( "map_ambient.color", "map_ambient.has_tex", "map_ambient_tex", material->layer[MATERIAL_CHANNEL_AMBIENT].map ); shader_colormap_model_internal( "map_ambient.color", "map_ambient.has_tex", "map_ambient_tex", material->layer[MATERIAL_CHANNEL_AMBIENT].map );
shader_colormap_model_internal( "map_emissive.color", "map_emissive.has_tex", "map_emissive_tex", material->layer[MATERIAL_CHANNEL_EMISSIVE].map ); shader_colormap_model_internal( "map_emissive.color", "map_emissive.has_tex", "map_emissive_tex", material->layer[MATERIAL_CHANNEL_EMISSIVE].map );
// shader_float( "specular_shininess", material->specular_shininess ); // unused, basic_specgloss.fs only // shader_float( "specular_shininess", material->specular_shininess ); // unused, basic_specgloss.fs only
}
} }
glDrawElementsInstanced(GL_TRIANGLES, 3*im->num_triangles, GL_UNSIGNED_INT, &tris[im->first_triangle], m.num_instances); glDrawElementsInstanced(GL_TRIANGLES, 3*im->num_triangles, GL_UNSIGNED_INT, &tris[im->first_triangle], m.num_instances);
@ -21658,6 +21855,10 @@ void model_render_instanced_pass(model_t m, mat44 proj, mat44 view, mat44* model
model_set_state(m); model_set_state(m);
} }
if (model_getpass() == RENDER_PASS_SHADOW) {
shader = m.shadow_program;
}
model_set_uniforms(m, shader > 0 ? shader : m.program, mv, proj, view, models[0]); model_set_uniforms(m, shader > 0 ? shader : m.program, mv, proj, view, models[0]);
model_draw_call(m, shader > 0 ? shader : m.program, pass, pos44(view), models[0]); model_draw_call(m, shader > 0 ? shader : m.program, pass, pos44(view), models[0]);
} }
@ -21811,11 +22012,20 @@ void model_shading_custom(model_t *m, int shading, const char *vs, const char *f
glUseProgram(0); glUseProgram(0);
if (m->program) if (m->program)
glDeleteProgram(m->program); glDeleteProgram(m->program);
const char *symbols[] = { "{{include-shadowmap}}", vfs_read("shaders/fs_0_0_shadowmap_lit.glsl") }; // #define RIM
int shaderprog = shader(strlerp(1,symbols,vs), strlerp(1,symbols,fs), //fs, {
"att_position,att_texcoord,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent,att_texcoord2","fragColor", int shaderprog = shader(vs, fs, //fs,
va("%s,%s,%s", defines ? defines : "NO_CUSTOM_DEFINES", shading_define, (flags&MODEL_RIMLIGHT)?"RIM":"")); "att_position,att_texcoord,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent,att_texcoord2","fragColor",
m->program = shaderprog; va("%s,%s,%s", defines ? defines : "NO_CUSTOM_DEFINES", shading_define, (flags&MODEL_RIMLIGHT)?"RIM":""));
m->program = shaderprog;
}
{
int shaderprog = shader(vs, vfs_read("shaders/fs_shadow_vsm.glsl"), //fs,
"att_position,att_texcoord,att_normal,att_tangent,att_instanced_matrix,,,,att_indexes,att_weights,att_vertexindex,att_color,att_bitangent,att_texcoord2","fragcolor",
va("SHADOW_CAST,%s", defines ? defines : "NO_CUSTOM_DEFINES"));
m->shadow_program = shaderprog;
}
model_init_uniforms(m); model_init_uniforms(m);
} }
@ -21835,6 +22045,16 @@ void model_skybox(model_t *mdl, skybox_t sky, bool load_sh) {
mdl->sky_env = sky.env; mdl->sky_env = sky.env;
} }
void model_shadow(model_t *mdl, shadowmap_t *sm) {
if (sm) {
mdl->shadow_receiver = true;
mdl->shadow_map = sm;
} else {
mdl->shadow_receiver = false;
mdl->shadow_map = NULL;
}
}
void model_fog(model_t *mdl, unsigned mode, vec3 color, float start, float end, float density) { void model_fog(model_t *mdl, unsigned mode, vec3 color, float start, float end, float density) {
unsigned oldprog = last_shader; unsigned oldprog = last_shader;
shader_bind(mdl->program); shader_bind(mdl->program);
@ -23491,96 +23711,6 @@ void object_billboard(object_t *obj, unsigned mode) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
light_t light() {
light_t l = {0};
l.diffuse = vec3(1,1,1);
l.dir = vec3(1,-1,-1);
l.falloff.constant = 1.0f;
l.falloff.linear = 0.09f;
l.falloff.quadratic = 0.0032f;
l.specularPower = 32.f;
l.innerCone = 0.85f;// 31 deg
l.outerCone = 0.9f; // 25 deg
return l;
}
void light_type(light_t* l, char type) {
l->cached = 0;
l->type = type;
}
void light_diffuse(light_t* l, vec3 color) {
l->cached = 0;
l->diffuse = color;
}
void light_specular(light_t* l, vec3 color) {
l->cached = 0;
l->specular = color;
}
void light_ambient(light_t* l, vec3 color) {
l->cached = 0;
l->ambient = color;
}
void light_teleport(light_t* l, vec3 pos) {
l->cached = 0;
l->pos = pos;
}
void light_dir(light_t* l, vec3 dir) {
l->cached = 0;
l->dir = dir;
}
void light_power(light_t* l, float power) {
l->cached = 0;
l->specularPower = power;
}
void light_falloff(light_t* l, float constant, float linear, float quadratic) {
l->cached = 0;
l->falloff.constant = constant;
l->falloff.linear = linear;
l->falloff.quadratic = quadratic;
}
void light_radius(light_t* l, float radius) {
l->cached = 0;
l->radius = radius;
}
void light_cone(light_t* l, float innerCone, float outerCone) {
l->cached = 0;
l->innerCone = acos(innerCone);
l->outerCone = acos(outerCone);
}
void light_update(unsigned num_lights, light_t *lv) {
shader_int("u_num_lights", num_lights);
for (unsigned i=0; i < num_lights; ++i) {
lv[i].cached = 1;
shader_int(va("u_lights[%d].type", i), lv[i].type);
shader_vec3(va("u_lights[%d].pos", i), lv[i].pos);
shader_vec3(va("u_lights[%d].dir", i), lv[i].dir);
shader_vec3(va("u_lights[%d].diffuse", i), lv[i].diffuse);
shader_vec3(va("u_lights[%d].specular", i), lv[i].specular);
shader_vec3(va("u_lights[%d].ambient", i), lv[i].ambient);
shader_float(va("u_lights[%d].power", i), lv[i].specularPower);
shader_float(va("u_lights[%d].radius", i), lv[i].radius);
shader_float(va("u_lights[%d].constant", i), lv[i].falloff.constant);
shader_float(va("u_lights[%d].linear", i), lv[i].falloff.linear);
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);
}
}
// -----------------------------------------------------------------------------
array(scene_t*) scenes; array(scene_t*) scenes;
scene_t* last_scene; scene_t* last_scene;

View File

@ -3100,6 +3100,9 @@ typedef struct renderstate_t {
// Scissor test // Scissor test
bool scissor_test_enabled; bool scissor_test_enabled;
// bool Seamless Cubemap
bool seamless_cubemap;
} renderstate_t; } renderstate_t;
API renderstate_t renderstate(); API renderstate_t renderstate();
@ -3299,6 +3302,7 @@ API void cubemap_sh_shader(cubemap_t *c);
API void cubemap_sh_add_light(cubemap_t *c, vec3 light, vec3 dir, float strength); API void cubemap_sh_add_light(cubemap_t *c, vec3 light, vec3 dir, float strength);
// lighting probe blending // lighting probe blending
// @note: uploads SH coefficients to shader
API void cubemap_sh_blend(vec3 pos, float max_dist, unsigned count, cubemap_t *probes); API void cubemap_sh_blend(vec3 pos, float max_dist, unsigned count, cubemap_t *probes);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -3309,6 +3313,54 @@ API void fbo_bind(unsigned id);
API void fbo_unbind(); API void fbo_unbind();
API void fbo_destroy(unsigned id); API void fbo_destroy(unsigned id);
// -----------------------------------------------------------------------------
// lights
enum {
MAX_LIGHTS = 16,
};
enum LIGHT_TYPE {
LIGHT_DIRECTIONAL,
LIGHT_POINT,
LIGHT_SPOT,
};
typedef struct light_t {
char type;
vec3 diffuse, specular, ambient;
vec3 pos, dir;
struct {
float constant, linear, quadratic;
} falloff;
float radius;
float specularPower;
float innerCone, outerCone;
//@todo: cookie, flare
// Shadowmapping
bool cast_shadows;
float shadow_distance;
float shadow_bias;
// internals
bool cached; //< used by scene to invalidate cached light data
} light_t;
API light_t light();
// API void light_flags(int flags);
API void light_type(light_t* l, char type);
API void light_diffuse(light_t* l, vec3 color);
API void light_specular(light_t* l, vec3 color);
API void light_ambient(light_t* l, vec3 color);
API void light_teleport(light_t* l, vec3 pos);
API void light_dir(light_t* l, vec3 dir);
API void light_power(light_t* l, float power);
API void light_radius(light_t* l, float radius);
API void light_falloff(light_t* l, float constant, float linear, float quadratic);
API void light_cone(light_t* l, float innerCone, float outerCone);
API void light_update(unsigned num_lights, light_t *lv);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// shadowmaps // shadowmaps
@ -3320,15 +3372,19 @@ API void fbo_destroy(unsigned id);
// #endif // #endif
typedef struct shadowmap_t { typedef struct shadowmap_t {
mat44 shadowmatrix; mat44 V;
mat44 mvp; mat44 PV;
mat44 mv;
mat44 proj;
vec4 light_position;
int saved_fb;
int saved_viewport[4];
handle fbo, texture;
int texture_width; int texture_width;
int step;
int light_step;
struct {
handle fbos[6], texture, depth_texture;
} maps[MAX_LIGHTS];
handle saved_fb;
handle saved_pass;
int saved_vp[4];
} shadowmap_t; } shadowmap_t;
API shadowmap_t shadowmap(int texture_width); // = 1024 API shadowmap_t shadowmap(int texture_width); // = 1024
@ -3336,13 +3392,10 @@ 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_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 void shadowmap_light(shadowmap_t *s, light_t *l);
API void shadowmap_end(shadowmap_t *s); API void shadowmap_end(shadowmap_t *s);
// shadowmap utils
API void shadowmatrix_proj(mat44 shm_proj, float aLightFov, float znear, float zfar);
API void shadowmatrix_ortho(mat44 shm_proj, float left, float right, float bottom, float top, float znear, float zfar);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// shaders // shaders
@ -3731,6 +3784,9 @@ typedef struct model_t {
unsigned num_anims; unsigned num_anims;
unsigned num_frames; unsigned num_frames;
handle program; handle program;
handle shadow_program;
shadowmap_t *shadow_map;
bool shadow_receiver;
float curframe; float curframe;
mat44 pivot; mat44 pivot;
@ -3782,6 +3838,7 @@ API void model_lod(model_t*, float lo_detail, float hi_detail, float morph);
API void model_shading(model_t*, int shading); API void model_shading(model_t*, int shading);
API void model_shading_custom(model_t*, int shading, const char *vs, const char *fs, const char *defines); API void model_shading_custom(model_t*, int shading, const char *vs, const char *fs, const char *defines);
API void model_skybox(model_t*, skybox_t sky, bool load_sh); API void model_skybox(model_t*, skybox_t sky, bool load_sh);
API void model_shadow(model_t*, shadowmap_t *sm);
API void model_fog(model_t*, unsigned mode, vec3 color, float start, float end, float density); API void model_fog(model_t*, unsigned mode, vec3 color, float start, float end, float density);
API void model_render(model_t, mat44 proj, mat44 view, mat44 model, int shader); API void model_render(model_t, mat44 proj, mat44 view, mat44 model, int shader);
API void model_render_skeleton(model_t, mat44 model); API void model_render_skeleton(model_t, mat44 model);
@ -4017,48 +4074,6 @@ API void object_billboard(object_t *obj, unsigned mode);
// object_pose(transform); // @todo // object_pose(transform); // @todo
// light
enum LIGHT_TYPE {
LIGHT_DIRECTIONAL,
LIGHT_POINT,
LIGHT_SPOT,
};
enum LIGHT_FLAGS {
LIGHT_CAST_SHADOWS = 1,
};
typedef struct light_t {
char type;
vec3 diffuse, specular, ambient;
vec3 pos, dir;
struct {
float constant, linear, quadratic;
} falloff;
float radius;
float specularPower;
float innerCone, outerCone;
//@todo: cookie, flare
// internals
bool cached; //< used by scene to invalidate cached light data
} light_t;
API light_t light();
// API void light_flags(int flags);
API void light_type(light_t* l, char type);
API void light_diffuse(light_t* l, vec3 color);
API void light_specular(light_t* l, vec3 color);
API void light_ambient(light_t* l, vec3 color);
API void light_teleport(light_t* l, vec3 pos);
API void light_dir(light_t* l, vec3 dir);
API void light_power(light_t* l, float power);
API void light_radius(light_t* l, float radius);
API void light_falloff(light_t* l, float constant, float linear, float quadratic);
API void light_cone(light_t* l, float innerCone, float outerCone);
API void light_update(unsigned num_lights, light_t *lv);
// scene // scene
enum SCENE_FLAGS { enum SCENE_FLAGS {