From 0b8d85f81c71d9e2cc141098265e00cf01a8710d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Madar=C3=A1sz?= Date: Wed, 28 Aug 2024 12:42:58 +0200 Subject: [PATCH] WIP: light probes support --- .cursorignore | 6 + .gitignore | 1 + _editor.bat | 2 +- bind/v4k.lua | 19 +- demos/09-cubemap.c | 15 +- demos/09-envmap.c | 118 ++++++++ demos/art/meshes/CornellBox-Original.mtl | 82 +++++ demos/art/meshes/CornellBox-Original.obj | 106 +++++++ engine/art/shaderlib/model_fs.glsl | 1 + engine/art/shaderlib/surface.glsl | 1 + engine/joint/v4k.h | 367 +++++++++++++++-------- engine/split/v4k_render.c | 338 +++++++++++++-------- engine/split/v4k_render.h | 29 +- engine/v4k.c | 338 +++++++++++++-------- engine/v4k.h | 29 +- 15 files changed, 1063 insertions(+), 389 deletions(-) create mode 100644 .cursorignore create mode 100644 demos/09-envmap.c create mode 100644 demos/art/meshes/CornellBox-Original.mtl create mode 100644 demos/art/meshes/CornellBox-Original.obj diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 0000000..f8dc407 --- /dev/null +++ b/.cursorignore @@ -0,0 +1,6 @@ +# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv) +*.* +!*.c +!*.cpp +!*.inl +!*.h \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1d813ba..76a64e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ default .art*.zip +*.rdi __pycache__ .vs .vscode diff --git a/_editor.bat b/_editor.bat index 75d6c52..6353653 100644 --- a/_editor.bat +++ b/_editor.bat @@ -3,5 +3,5 @@ call razzle amd64 if exist .env.bat ( call .env.bat ) -code . +cursor . exit \ No newline at end of file diff --git a/bind/v4k.lua b/bind/v4k.lua index 8d02f0b..63d50eb 100644 --- a/bind/v4k.lua +++ b/bind/v4k.lua @@ -1110,11 +1110,24 @@ typedef struct colormap_t { typedef struct cubemap_t { unsigned id; vec3 sh[9]; + int framebuffers[6]; + int textures[6]; + int depth_buffers[6]; + unsigned width, height; + float *pixels; + int step; + vec3 pos; } cubemap_t; cubemap_t cubemap( const image_t image, int flags ); cubemap_t cubemap6( const image_t images[6], int flags ); void cubemap_destroy(cubemap_t *c); cubemap_t* cubemap_get_active(); + void cubemap_bake_begin(cubemap_t *c, vec3 pos, unsigned width, unsigned height); + bool cubemap_bake_step(cubemap_t *c, mat44 proj , mat44 view ); + void cubemap_bake_end(cubemap_t *c, float sky_intensity); + void cubemap_sh_reset(cubemap_t *c); + void cubemap_sh_shader(cubemap_t *c); + void cubemap_sh_add_light(cubemap_t *c, vec3 light, vec3 dir, float strength); unsigned fbo( unsigned texture_color, unsigned texture_depth, int wr_flags ); void fbo_bind(unsigned id); void fbo_unbind(); @@ -1235,13 +1248,11 @@ enum SKYBOX_FLAGS { SKYBOX_PBR, }; typedef struct skybox_t { - handle program; + handle program, rayleigh_program; mesh_t geometry; cubemap_t cubemap; int flags; - int framebuffers[6]; - int textures[6]; - float *pixels; + bool rayleigh_immediate; texture_t sky, refl, env; } skybox_t; skybox_t skybox(const char *panorama_or_cubemap_folder, int flags); diff --git a/demos/09-cubemap.c b/demos/09-cubemap.c index c2a5bf9..5fdbe3c 100644 --- a/demos/09-cubemap.c +++ b/demos/09-cubemap.c @@ -38,6 +38,7 @@ int main(int argc, char** argv) { initialized = 1; sky = skybox(flag("--mie") ? 0 : SKY_DIRS[SKY_DIR], 0); mdl = model(OBJ_MDLS[OBJ_MDL], 0); + // sky.rayleigh_immediate = 1; rotation44(mdl.pivot, 0, 1,0,0); // @fixme: -90,1,0,0 -> should we rotate SHMs as well? compensate rotation in shader? } @@ -54,12 +55,14 @@ int main(int argc, char** argv) { // render mat44 mvp; multiply44x2(mvp, cam.proj, cam.view); { - if (flag("--mie")) { - // skybox_sh_reset(&sky); - skybox_mie_calc_sh(&sky, 4.0f); - // float x = cosf((float)window_time())*4; - // skybox_sh_add_light(&sky, vec3(0.3,0.3,0.3), vec3(0,1,0), 16*absf(cosf((float)window_time()*2))+2); - // skybox_sh_add_light(&sky, vec3(0.6,0,0), vec3(x,1,0), 2); + do_once { + if (flag("--mie")) { + // skybox_sh_reset(&sky); + skybox_mie_calc_sh(&sky, 4.0f); + // float x = cosf((float)window_time())*4; + // skybox_sh_add_light(&sky, vec3(0.3,0.3,0.3), vec3(0,1,0), 16*absf(cosf((float)window_time()*2))+2); + // skybox_sh_add_light(&sky, vec3(0.6,0,0), vec3(x,1,0), 2); + } } skybox_render(&sky, cam.proj, cam.view); diff --git a/demos/09-envmap.c b/demos/09-envmap.c new file mode 100644 index 0000000..b4bf817 --- /dev/null +++ b/demos/09-envmap.c @@ -0,0 +1,118 @@ +#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/CornellBox-Original.obj", + "meshes/sphere.obj", + "meshes/suzanne.obj", + "meshes/gazebo.obj", +}; + +int main(int argc, char** argv) { + window_create(85, WINDOW_MSAA8); + + 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}; + skybox_t env_probe = {0}; + model_t mdl = {0}; + + 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); + env_probe = skybox(0, 0); + mdl = model(OBJ_MDLS[OBJ_MDL], 0); + // rotation44(mdl.pivot, 0, 1,0,0); // @fixme: -90,1,0,0 -> should we rotate SHMs as well? compensate rotation in shader? + } + + // 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); + + static bool first_time = true; + static bool animate_probe_pos = false; + static vec3 probe_pos; + if (input_down(KEY_T)) { + animate_probe_pos = !animate_probe_pos; + } + if (animate_probe_pos) { + probe_pos = vec3(0, 5, 0); + probe_pos.x = sinf(window_time()*2)*2.0f; + } + if (input_down(KEY_SPACE) || first_time || animate_probe_pos) { + first_time = false; + mat44 probe_proj, probe_view; + if (!animate_probe_pos) { + probe_pos = cam.position; + } + cubemap_bake_begin(&env_probe.cubemap, probe_pos, 1024, 1024); + while (cubemap_bake_step(&env_probe.cubemap, probe_proj, probe_view)) { + skybox_render(&sky, probe_proj, probe_view); + shader_bind(mdl.program); + shader_vec3v("u_coefficients_sh", 9, sky.cubemap.sh); + + model_render(mdl, probe_proj, probe_view, mdl.pivot, 0); + } + cubemap_bake_end(&env_probe.cubemap, 1.2f); + } + + ddraw_sphere(probe_pos, 0.1f); + + // render + mat44 mvp; multiply44x2(mvp, cam.proj, cam.view); + { + // skybox_render(&env_probe, cam.proj, cam.view); + skybox_render(&sky, cam.proj, cam.view); + + shader_bind(mdl.program); + shader_vec3v("u_coefficients_sh", 9, env_probe.cubemap.sh); + shader_int("u_textured", false); + + 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_separator(); + for (int i = 0; i < 9; i++) { + ui_color3f(va("SH Coefficient [%d]", i), &sky.cubemap.sh[i].x); + } + ui_panel_end(); + } + } +} diff --git a/demos/art/meshes/CornellBox-Original.mtl b/demos/art/meshes/CornellBox-Original.mtl new file mode 100644 index 0000000..e995d13 --- /dev/null +++ b/demos/art/meshes/CornellBox-Original.mtl @@ -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 diff --git a/demos/art/meshes/CornellBox-Original.obj b/demos/art/meshes/CornellBox-Original.obj new file mode 100644 index 0000000..e1fcc23 --- /dev/null +++ b/demos/art/meshes/CornellBox-Original.obj @@ -0,0 +1,106 @@ +# Blender 4.2.1 LTS +# www.blender.org +mtllib CornellBox-Original.mtl +o CornellBox-Original +v -4.040000 3.960000 -0.000000 +v 4.000000 3.960000 -0.000000 +v 4.000000 -4.159999 0.000000 +v -3.960000 -4.160000 0.000000 +v -4.080000 3.959999 -7.960001 +v -4.079999 -4.160001 -7.960000 +v 4.000000 -4.160000 -7.960000 +v 4.000000 3.960000 -7.960001 +v -3.960000 -4.160000 0.000000 +v 4.000000 -4.159999 0.000000 +v 4.000000 -4.160000 -7.960000 +v -4.079999 -4.160001 -7.960000 +v 4.000000 -4.159999 0.000000 +v 4.000000 3.960000 -0.000000 +v 4.000000 3.960000 -7.960001 +v 4.000000 -4.160000 -7.960000 +v -4.040000 3.960000 -0.000000 +v -3.960000 -4.160000 0.000000 +v -4.079999 -4.160001 -7.960000 +v -4.080000 3.959999 -7.960001 +v 2.120000 3.000000 -2.400000 +v 2.800000 0.680000 -2.400000 +v 0.520000 -0.000000 -2.400000 +v -0.200000 2.280000 -2.400000 +v -0.200000 2.280000 -0.000000 +v -0.200000 2.280000 -2.400000 +v 0.520000 -0.000000 -2.400000 +v 0.520000 0.000000 0.000000 +v 2.120000 3.000000 -0.000000 +v 2.120000 3.000000 -2.400000 +v -0.200000 2.280000 -2.400000 +v -0.200000 2.280000 -0.000000 +v 2.800000 0.680000 -0.000000 +v 2.800000 0.680000 -2.400000 +v 2.120000 3.000000 -2.400000 +v 2.120000 3.000000 -0.000000 +v 0.520000 0.000000 0.000000 +v 0.520000 -0.000000 -2.400000 +v 2.800000 0.680000 -2.400000 +v 2.800000 0.680000 -0.000000 +v -2.120000 0.359999 -4.800000 +v 0.160000 -0.360000 -4.800000 +v -0.560000 -2.680001 -4.800000 +v -2.840000 -1.960001 -4.800000 +v -2.120000 0.360000 -0.000000 +v -2.120000 0.359999 -4.800000 +v -2.840000 -1.960001 -4.800000 +v -2.840000 -1.960000 0.000000 +v -2.840000 -1.960000 0.000000 +v -2.840000 -1.960001 -4.800000 +v -0.560000 -2.680001 -4.800000 +v -0.560000 -2.680000 0.000000 +v -0.560000 -2.680000 0.000000 +v -0.560000 -2.680001 -4.800000 +v 0.160000 -0.360000 -4.800000 +v 0.160000 -0.360000 0.000000 +v 0.160000 -0.360000 0.000000 +v 0.160000 -0.360000 -4.800000 +v -2.120000 0.359999 -4.800000 +v -2.120000 0.360000 -0.000000 +v -0.960000 0.639999 -7.920000 +v -0.960000 -0.880001 -7.920000 +v 0.920000 -0.880001 -7.920000 +v 0.920000 0.639999 -7.920000 +vn -0.0000 -0.0000 -1.0000 +vn -0.0000 -0.0000 1.0000 +vn -0.0000 1.0000 -0.0000 +vn -1.0000 -0.0000 -0.0000 +vn 0.9999 0.0049 -0.0100 +vn -0.9536 -0.3011 -0.0000 +vn -0.2964 0.9551 -0.0000 +vn 0.9596 0.2813 -0.0000 +vn 0.2858 -0.9583 -0.0000 +vn -0.9551 0.2964 -0.0000 +vn -0.3011 -0.9536 -0.0000 +vn 0.9551 -0.2964 -0.0000 +vn 0.3011 0.9536 -0.0000 +s 0 +usemtl floor +f 1//1 2//1 3//1 4//1 +usemtl ceiling +f 5//2 6//2 7//2 8//2 +usemtl backWall +f 9//3 10//3 11//3 12//3 +usemtl rightWall +f 13//4 14//4 15//4 16//4 +usemtl leftWall +f 17//5 18//5 19//5 20//5 +usemtl shortBox +f 21//1 22//1 23//1 24//1 +f 25//6 26//6 27//6 28//6 +f 29//7 30//7 31//7 32//7 +f 33//8 34//8 35//8 36//8 +f 37//9 38//9 39//9 40//9 +usemtl tallBox +f 41//1 42//1 43//1 44//1 +f 45//10 46//10 47//10 48//10 +f 49//11 50//11 51//11 52//11 +f 53//12 54//12 55//12 56//12 +f 57//13 58//13 59//13 60//13 +usemtl light +f 61//2 62//2 63//2 64//2 diff --git a/engine/art/shaderlib/model_fs.glsl b/engine/art/shaderlib/model_fs.glsl index 7cdfb8b..a8ce291 100644 --- a/engine/art/shaderlib/model_fs.glsl +++ b/engine/art/shaderlib/model_fs.glsl @@ -44,6 +44,7 @@ vec4 shadowing() { } uniform float u_global_alpha; /// set:1.0 +uniform float u_global_opacity; /// set:1.0 uniform vec3 u_cam_pos; uniform vec3 u_cam_dir; uniform float frame_time; diff --git a/engine/art/shaderlib/surface.glsl b/engine/art/shaderlib/surface.glsl index 38cdff4..6d6833e 100644 --- a/engine/art/shaderlib/surface.glsl +++ b/engine/art/shaderlib/surface.glsl @@ -200,6 +200,7 @@ surface_t surface() { s.fragcolor *= shadowing(); s.fragcolor.rgb += get_rimlight(); s.fragcolor.a *= u_global_alpha; + s.fragcolor *= vec4(u_global_opacity); #ifdef SHADING_PBR { diff --git a/engine/joint/v4k.h b/engine/joint/v4k.h index c0831f9..5632872 100644 --- a/engine/joint/v4k.h +++ b/engine/joint/v4k.h @@ -17209,12 +17209,27 @@ API void fullscreen_quad_ycbcr_flipped( texture_t texture_YCbCr[3] ); typedef struct cubemap_t { unsigned id; // texture id vec3 sh[9]; // precomputed spherical harmonics coefficients + + // bake data + int framebuffers[6]; + int textures[6]; + int depth_buffers[6]; + unsigned width, height; + float *pixels; + int step; + vec3 pos; } cubemap_t; API cubemap_t cubemap( const image_t image, int flags ); // 1 equirectangular panorama API cubemap_t cubemap6( const image_t images[6], int flags ); // 6 cubemap faces API void cubemap_destroy(cubemap_t *c); API cubemap_t* cubemap_get_active(); +API void cubemap_bake_begin(cubemap_t *c, vec3 pos, unsigned width, unsigned height); +API bool cubemap_bake_step(cubemap_t *c, mat44 proj /* out */, mat44 view /* out */); +API void cubemap_bake_end(cubemap_t *c, float sky_intensity); +API void cubemap_sh_reset(cubemap_t *c); +API void cubemap_sh_shader(cubemap_t *c); +API void cubemap_sh_add_light(cubemap_t *c, vec3 light, vec3 dir, float strength); // ----------------------------------------------------------------------------- // fbos @@ -17453,15 +17468,11 @@ enum SKYBOX_FLAGS { }; typedef struct skybox_t { - handle program; + handle program, rayleigh_program; mesh_t geometry; cubemap_t cubemap; int flags; - - // mie - int framebuffers[6]; - int textures[6]; - float *pixels; + bool rayleigh_immediate; // pbr texture_t sky, refl, env; @@ -17472,9 +17483,9 @@ API skybox_t skybox_pbr(const char *sky_map, const char *refl_map, const char *e API int skybox_render(skybox_t *sky, mat44 proj, mat44 view); API void skybox_destroy(skybox_t *sky); API void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity); -API void skybox_sh_reset(skybox_t *sky); -API void skybox_sh_shader(skybox_t *sky); -API void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength); +API void skybox_sh_reset(skybox_t *sky); /* @deprecated */ +API void skybox_sh_shader(skybox_t *sky); /* @deprecated */ +API void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength); /* @deprecated */ API int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view); // @to deprecate API int skybox_pop_state(); // @to deprecate @@ -383606,6 +383617,13 @@ cubemap_t cubemap( const image_t in, int flags ) { void cubemap_destroy(cubemap_t *c) { glDeleteTextures(1, &c->id); c->id = 0; // do not destroy SH coefficients still. they might be useful in the future. + + if (c->pixels) { + FREE(c->pixels); + glDeleteFramebuffers(6, c->framebuffers); + glDeleteTextures(6, c->textures); + glDeleteRenderbuffers(6, c->depth_buffers); + } } static cubemap_t *last_cubemap; @@ -383614,6 +383632,166 @@ cubemap_t* cubemap_get_active() { return last_cubemap; } +// cubemap baker + +static int sky_last_fb; +static int sky_last_vp[4]; +void cubemap_bake_begin(cubemap_t *c, vec3 pos, unsigned width, unsigned height) { + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &sky_last_fb); + glGetIntegerv(GL_VIEWPORT, sky_last_vp); + c->step = 0; + c->pos = pos; + + if (!c->pixels || (c->width != width || c->height != height)) { + c->pixels = REALLOC(c->pixels, width*height*12); + c->width = width; + c->height = height; + + if (!c->framebuffers[0]) { + glDeleteFramebuffers(6, c->framebuffers); + glDeleteTextures(6, c->textures); + glDeleteRenderbuffers(6, c->depth_buffers); + for(int i = 0; i < 6; ++i) { + c->framebuffers[i] = 0; + } + } + } + + if (!c->framebuffers[0]) { + for(int i = 0; i < 6; ++i) { + glGenFramebuffers(1, &c->framebuffers[i]); + glBindFramebuffer(GL_FRAMEBUFFER, c->framebuffers[i]); + + glGenTextures(1, &c->textures[i]); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, c->textures[i]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, c->textures[i], 0); + + // attach depth buffer + glGenRenderbuffers(1, &c->depth_buffers[i]); + glBindRenderbuffer(GL_RENDERBUFFER, c->depth_buffers[i]); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, c->depth_buffers[i]); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } + } +} + +bool cubemap_bake_step(cubemap_t *c, mat44 proj /* out */, mat44 view /* out */) { + if (c->step >= 6) return false; + + static vec3 directions[6] = {{ 1, 0, 0},{-1, 0, 0},{ 0, 1, 0},{ 0,-1, 0},{ 0, 0, 1},{ 0, 0,-1}}; + + glBindFramebuffer(GL_FRAMEBUFFER, c->framebuffers[c->step]); + glViewport(0, 0, c->width, c->height); + glClearColor(0, 0, 0, 1); + glClearDepth(1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + perspective44(proj, 90.0f, c->width / (float)c->height, 0.1f, 1000.f); + lookat44(view, c->pos, add3(c->pos, directions[c->step]), vec3(0,-1,0)); + ++c->step; + + return true; +} + +void cubemap_bake_end(cubemap_t *c, float sky_intensity) { + if (!sky_intensity) { + sky_intensity = 1.0f; + } + + if (c->id) { + glDeleteTextures(1, &c->id); + c->id = 0; + } + + glGenTextures(1, &c->id); + glBindTexture(GL_TEXTURE_CUBE_MAP, c->id); + + int samples = 0; + for (int i = 0; i < 6; i++) { + glBindFramebuffer(GL_FRAMEBUFFER, c->framebuffers[i]); + glReadPixels(0, 0, c->width, c->height, GL_RGB, GL_FLOAT, c->pixels); + + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, c->width, c->height, 0, GL_RGB, GL_FLOAT, c->pixels); + + // calculate SH coefficients (@ands) + // copied from cubemap6 method + const vec3 skyDir[] = {{ 1, 0, 0},{-1, 0, 0},{ 0, 1, 0},{ 0,-1, 0},{ 0, 0, 1},{ 0, 0,-1}}; + const vec3 skyX[] = {{ 0, 0,-1},{ 0, 0, 1},{ 1, 0, 0},{ 1, 0, 0},{ 1, 0, 0},{-1, 0, 0}}; + const vec3 skyY[] = {{ 0, 1, 0},{ 0, 1, 0},{ 0, 0,-1},{ 0, 0, 1},{ 0, 1, 0},{ 0, 1, 0}}; + int step = 16; + for (int y = 0; y < c->height; y += step) { + float *p = (float*)(c->pixels + y * c->width * 3); + for (int x = 0; x < c->width; x += step) { + vec3 n = add3( + add3( + scale3(skyX[i], 2.0f * (x / (c->width - 1.0f)) - 1.0f), + scale3(skyY[i], -2.0f * (y / (c->height - 1.0f)) + 1.0f)), + skyDir[i]); // texelDirection; + float l = len3(n); + vec3 light = scale3(vec3(p[0], p[1], p[2]), (1 / (l * l * l)) * sky_intensity); // texelSolidAngle * texel_radiance; + n = norm3(n); + c->sh[0] = add3(c->sh[0], scale3(light, 0.282095f)); + c->sh[1] = add3(c->sh[1], scale3(light, -0.488603f * n.y * 2.0 / 3.0)); + c->sh[2] = add3(c->sh[2], scale3(light, 0.488603f * n.z * 2.0 / 3.0)); + c->sh[3] = add3(c->sh[3], scale3(light, -0.488603f * n.x * 2.0 / 3.0)); + c->sh[4] = add3(c->sh[4], scale3(light, 1.092548f * n.x * n.y / 4.0)); + c->sh[5] = add3(c->sh[5], scale3(light, -1.092548f * n.y * n.z / 4.0)); + c->sh[6] = add3(c->sh[6], scale3(light, 0.315392f * (3.0f * n.z * n.z - 1.0f) / 4.0)); + c->sh[7] = add3(c->sh[7], scale3(light, -1.092548f * n.x * n.z / 4.0)); + c->sh[8] = add3(c->sh[8], scale3(light, 0.546274f * (n.x * n.x - n.y * n.y) / 4.0)); + p += 3 * step; + samples++; + } + } + } + + for (int s = 0; s < 9; s++) { + c->sh[s] = scale3(c->sh[s], 32.f / samples); + } + + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_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); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, sky_last_fb); + glViewport(sky_last_vp[0], sky_last_vp[1], sky_last_vp[2], sky_last_vp[3]); +} + +void cubemap_sh_reset(cubemap_t *c) { + for (int s = 0; s < 9; s++) { + c->sh[s] = vec3(0,0,0); + } +} + +void cubemap_sh_shader(cubemap_t *c) { + shader_vec3v("u_coefficients_sh", 9, c->sh); +} + +void cubemap_sh_add_light(cubemap_t *c, vec3 light, vec3 dir, float strength) { + // Normalize the direction + vec3 norm_dir = norm3(dir); + + // Scale the light color and intensity + vec3 scaled_light = scale3(light, strength); + + // Add light to the SH coefficients + c->sh[0] = add3(c->sh[0], scale3(scaled_light, 0.282095f)); + c->sh[1] = add3(c->sh[1], scale3(scaled_light, -0.488603f * norm_dir.y)); + c->sh[2] = add3(c->sh[2], scale3(scaled_light, 0.488603f * norm_dir.z)); + c->sh[3] = add3(c->sh[3], scale3(scaled_light, -0.488603f * norm_dir.x)); +} + + // ----------------------------------------------------------------------------- // skyboxes @@ -383628,7 +383806,10 @@ skybox_t skybox(const char *asset, int flags) { // sky program sky.flags = flags && flags != SKYBOX_PBR ? flags : !!asset ? SKYBOX_CUBEMAP : SKYBOX_RAYLEIGH; // either cubemap or rayleigh sky.program = shader(vfs_read("shaders/vs_3_3_skybox.glsl"), - sky.flags ? vfs_read("fs_3_4_skybox.glsl") : vfs_read("shaders/fs_3_4_skybox_rayleigh.glsl"), + vfs_read("fs_3_4_skybox.glsl"), + "att_position", "fragcolor", NULL); + sky.rayleigh_program = shader(vfs_read("shaders/vs_3_3_skybox.glsl"), + vfs_read("shaders/fs_3_4_skybox_rayleigh.glsl"), "att_position", "fragcolor", NULL); // sky cubemap & SH @@ -383652,7 +383833,7 @@ skybox_t skybox(const char *asset, int flags) { } } else { // set up mie defaults // @fixme: use shader params instead - shader_bind(sky.program); + shader_bind(sky.rayleigh_program); shader_vec3("uSunPos", vec3( 0, 0.1, -1 )); shader_vec3("uRayOrigin", vec3(0.0, 6372000.0, 0.0)); shader_float("uSunIntensity", 22.0); @@ -383723,120 +383904,58 @@ skybox_t skybox_pbr(const char *sky_map, const char *refl_map, const char *env_m return sky; } +static renderstate_t skybox_rs; +API vec4 window_getcolor_(); // internal use, not public + +static inline +void skybox_render_rayleigh(skybox_t *sky, mat44 proj, mat44 view) { + last_cubemap = &sky->cubemap; + + do_once { + skybox_rs = renderstate(); + skybox_rs.depth_test_enabled = 1; + skybox_rs.cull_face_enabled = 0; + skybox_rs.front_face = GL_CCW; + } + + // we have to reset clear color here, because of wrong alpha compositing issues on native transparent windows otherwise + // vec4 bgcolor = window_getcolor_(); + // skybox_rs.clear_color[0] = bgcolor.r; + // skybox_rs.clear_color[1] = bgcolor.g; + // skybox_rs.clear_color[2] = bgcolor.b; + // skybox_rs.clear_color[3] = 1; // @transparent + + mat44 mvp; multiply44x2(mvp, proj, view); + + //glDepthMask(GL_FALSE); + shader_bind(sky->rayleigh_program); + shader_mat44("u_mvp", mvp); + + renderstate_apply(&skybox_rs); + mesh_render(&sky->geometry); +} + void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity) { - unsigned WIDTH = 1024, HEIGHT = 1024; - int last_fb; - int vp[4]; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &last_fb); - glGetIntegerv(GL_VIEWPORT, vp); - - if (!sky_intensity) { - sky_intensity = 1.0f; + cubemap_bake_begin(&sky->cubemap, vec3(0, 0, 0), 1024, 1024); + mat44 proj, view; + while (cubemap_bake_step(&sky->cubemap, proj, view)) { + skybox_render_rayleigh(sky, proj, view); } - - if (!sky->pixels) - sky->pixels = MALLOC(WIDTH*HEIGHT*12); - - if (!sky->framebuffers[0]) { - for(int i = 0; i < 6; ++i) { - glGenFramebuffers(1, &sky->framebuffers[i]); - glBindFramebuffer(GL_FRAMEBUFFER, sky->framebuffers[i]); - - glGenTextures(1, &sky->textures[i]); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, sky->textures[i]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, GL_FLOAT, NULL); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glBindTexture(GL_TEXTURE_2D, 0); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sky->textures[i], 0); - } - } - - static vec3 directions[6] = {{ 1, 0, 0},{-1, 0, 0},{ 0, 1, 0},{ 0,-1, 0},{ 0, 0, 1},{ 0, 0,-1}}; - - int samples = 0; - for(int i = 0; i < 6; ++i) { - glBindFramebuffer(GL_FRAMEBUFFER, sky->framebuffers[i]); - glViewport(0, 0, WIDTH, HEIGHT); - glUseProgram(sky->program); - - mat44 proj; perspective44(proj, 90.0f, WIDTH / (float)HEIGHT, 0.1f, 500.f); - mat44 view; lookat44(view, vec3(0,0,0), directions[i], vec3(0,-1,0)); - - skybox_render(sky, proj, view); - - glReadPixels(0, 0, WIDTH, HEIGHT, GL_RGB, GL_FLOAT, sky->pixels); - - // calculate SH coefficients (@ands) - // copied from cubemap6 method - const vec3 skyDir[] = {{ 1, 0, 0},{-1, 0, 0},{ 0, 1, 0},{ 0,-1, 0},{ 0, 0, 1},{ 0, 0,-1}}; - const vec3 skyX[] = {{ 0, 0,-1},{ 0, 0, 1},{ 1, 0, 0},{ 1, 0, 0},{ 1, 0, 0},{-1, 0, 0}}; - const vec3 skyY[] = {{ 0, 1, 0},{ 0, 1, 0},{ 0, 0,-1},{ 0, 0, 1},{ 0, 1, 0},{ 0, 1, 0}}; - int step = 16; - for (int y = 0; y < HEIGHT; y += step) { - float *p = (float*)(sky->pixels + y * WIDTH * 3); - for (int x = 0; x < WIDTH; x += step) { - vec3 n = add3( - add3( - scale3(skyX[i], 2.0f * (x / (WIDTH - 1.0f)) - 1.0f), - scale3(skyY[i], -2.0f * (y / (HEIGHT - 1.0f)) + 1.0f)), - skyDir[i]); // texelDirection; - float l = len3(n); - vec3 light = scale3(vec3(p[0], p[1], p[2]), (1 / (l * l * l)) * sky_intensity); // texelSolidAngle * texel_radiance; - n = norm3(n); - sky->cubemap.sh[0] = add3(sky->cubemap.sh[0], scale3(light, 0.282095f)); - sky->cubemap.sh[1] = add3(sky->cubemap.sh[1], scale3(light, -0.488603f * n.y * 2.0 / 3.0)); - sky->cubemap.sh[2] = add3(sky->cubemap.sh[2], scale3(light, 0.488603f * n.z * 2.0 / 3.0)); - sky->cubemap.sh[3] = add3(sky->cubemap.sh[3], scale3(light, -0.488603f * n.x * 2.0 / 3.0)); - sky->cubemap.sh[4] = add3(sky->cubemap.sh[4], scale3(light, 1.092548f * n.x * n.y / 4.0)); - sky->cubemap.sh[5] = add3(sky->cubemap.sh[5], scale3(light, -1.092548f * n.y * n.z / 4.0)); - sky->cubemap.sh[6] = add3(sky->cubemap.sh[6], scale3(light, 0.315392f * (3.0f * n.z * n.z - 1.0f) / 4.0)); - sky->cubemap.sh[7] = add3(sky->cubemap.sh[7], scale3(light, -1.092548f * n.x * n.z / 4.0)); - sky->cubemap.sh[8] = add3(sky->cubemap.sh[8], scale3(light, 0.546274f * (n.x * n.x - n.y * n.y) / 4.0)); - p += 3 * step; - samples++; - } - } - } - - for (int s = 0; s < 9; s++) { - sky->cubemap.sh[s] = scale3(sky->cubemap.sh[s], 32.f / samples); - } - - glBindFramebuffer(GL_FRAMEBUFFER, last_fb); - glViewport(vp[0], vp[1], vp[2], vp[3]); + cubemap_bake_end(&sky->cubemap, sky_intensity); } void skybox_sh_reset(skybox_t *sky) { - for (int s = 0; s < 9; s++) { - sky->cubemap.sh[s] = vec3(0,0,0); - } + cubemap_sh_reset(&sky->cubemap); } void skybox_sh_shader(skybox_t *sky) { - shader_vec3v("u_coefficients_sh", 9, sky->cubemap.sh); + cubemap_sh_shader(&sky->cubemap); } void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength) { - // Normalize the direction - vec3 norm_dir = norm3(dir); - - // Scale the light color and intensity - vec3 scaled_light = scale3(light, strength); - - // Add light to the SH coefficients - sky->cubemap.sh[0] = add3(sky->cubemap.sh[0], scale3(scaled_light, 0.282095f)); - sky->cubemap.sh[1] = add3(sky->cubemap.sh[1], scale3(scaled_light, -0.488603f * norm_dir.y)); - sky->cubemap.sh[2] = add3(sky->cubemap.sh[2], scale3(scaled_light, 0.488603f * norm_dir.z)); - sky->cubemap.sh[3] = add3(sky->cubemap.sh[3], scale3(scaled_light, -0.488603f * norm_dir.x)); + cubemap_sh_add_light(&sky->cubemap, light, dir, strength); } -API vec4 window_getcolor_(); // internal use, not public - -static renderstate_t skybox_rs; - int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view) { last_cubemap = &sky->cubemap; @@ -383848,20 +383967,18 @@ int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view) { } // we have to reset clear color here, because of wrong alpha compositing issues on native transparent windows otherwise - vec4 bgcolor = window_getcolor_(); - skybox_rs.clear_color[0] = bgcolor.r; - skybox_rs.clear_color[1] = bgcolor.g; - skybox_rs.clear_color[2] = bgcolor.b; - skybox_rs.clear_color[3] = 1; // @transparent + // vec4 bgcolor = window_getcolor_(); + // skybox_rs.clear_color[0] = bgcolor.r; + // skybox_rs.clear_color[1] = bgcolor.g; + // skybox_rs.clear_color[2] = bgcolor.b; + // skybox_rs.clear_color[3] = 1; // @transparent mat44 mvp; multiply44x2(mvp, proj, view); //glDepthMask(GL_FALSE); shader_bind(sky->program); shader_mat44("u_mvp", mvp); - if( sky->flags ) { - shader_cubemap("u_cubemap", sky->cubemap.id); - } + shader_cubemap("u_cubemap", sky->cubemap.id); renderstate_apply(&skybox_rs); return 0; // @fixme: return sortable hash here? @@ -383873,6 +383990,10 @@ int skybox_pop_state() { return 0; } int skybox_render(skybox_t *sky, mat44 proj, mat44 view) { + if (sky->rayleigh_immediate && !sky->flags) { + skybox_render_rayleigh(sky, proj, view); + return 0; + } skybox_push_state(sky, proj, view); mesh_render(&sky->geometry); skybox_pop_state(); @@ -383882,12 +384003,6 @@ void skybox_destroy(skybox_t *sky) { glDeleteProgram(sky->program); cubemap_destroy(&sky->cubemap); mesh_destroy(&sky->geometry); - - if (sky->pixels) { - FREE(sky->pixels); - glDeleteFramebuffers(6, sky->framebuffers); - glDeleteTextures(6, sky->textures); - } } // ----------------------------------------------------------------------------- diff --git a/engine/split/v4k_render.c b/engine/split/v4k_render.c index c2d30e5..6da6645 100644 --- a/engine/split/v4k_render.c +++ b/engine/split/v4k_render.c @@ -1933,6 +1933,13 @@ cubemap_t cubemap( const image_t in, int flags ) { void cubemap_destroy(cubemap_t *c) { glDeleteTextures(1, &c->id); c->id = 0; // do not destroy SH coefficients still. they might be useful in the future. + + if (c->pixels) { + FREE(c->pixels); + glDeleteFramebuffers(6, c->framebuffers); + glDeleteTextures(6, c->textures); + glDeleteRenderbuffers(6, c->depth_buffers); + } } static cubemap_t *last_cubemap; @@ -1941,6 +1948,166 @@ cubemap_t* cubemap_get_active() { return last_cubemap; } +// cubemap baker + +static int sky_last_fb; +static int sky_last_vp[4]; +void cubemap_bake_begin(cubemap_t *c, vec3 pos, unsigned width, unsigned height) { + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &sky_last_fb); + glGetIntegerv(GL_VIEWPORT, sky_last_vp); + c->step = 0; + c->pos = pos; + + if (!c->pixels || (c->width != width || c->height != height)) { + c->pixels = REALLOC(c->pixels, width*height*12); + c->width = width; + c->height = height; + + if (!c->framebuffers[0]) { + glDeleteFramebuffers(6, c->framebuffers); + glDeleteTextures(6, c->textures); + glDeleteRenderbuffers(6, c->depth_buffers); + for(int i = 0; i < 6; ++i) { + c->framebuffers[i] = 0; + } + } + } + + if (!c->framebuffers[0]) { + for(int i = 0; i < 6; ++i) { + glGenFramebuffers(1, &c->framebuffers[i]); + glBindFramebuffer(GL_FRAMEBUFFER, c->framebuffers[i]); + + glGenTextures(1, &c->textures[i]); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, c->textures[i]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, c->textures[i], 0); + + // attach depth buffer + glGenRenderbuffers(1, &c->depth_buffers[i]); + glBindRenderbuffer(GL_RENDERBUFFER, c->depth_buffers[i]); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, c->depth_buffers[i]); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } + } +} + +bool cubemap_bake_step(cubemap_t *c, mat44 proj /* out */, mat44 view /* out */) { + if (c->step >= 6) return false; + + static vec3 directions[6] = {{ 1, 0, 0},{-1, 0, 0},{ 0, 1, 0},{ 0,-1, 0},{ 0, 0, 1},{ 0, 0,-1}}; + + glBindFramebuffer(GL_FRAMEBUFFER, c->framebuffers[c->step]); + glViewport(0, 0, c->width, c->height); + glClearColor(0, 0, 0, 1); + glClearDepth(1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + perspective44(proj, 90.0f, c->width / (float)c->height, 0.1f, 1000.f); + lookat44(view, c->pos, add3(c->pos, directions[c->step]), vec3(0,-1,0)); + ++c->step; + + return true; +} + +void cubemap_bake_end(cubemap_t *c, float sky_intensity) { + if (!sky_intensity) { + sky_intensity = 1.0f; + } + + if (c->id) { + glDeleteTextures(1, &c->id); + c->id = 0; + } + + glGenTextures(1, &c->id); + glBindTexture(GL_TEXTURE_CUBE_MAP, c->id); + + int samples = 0; + for (int i = 0; i < 6; i++) { + glBindFramebuffer(GL_FRAMEBUFFER, c->framebuffers[i]); + glReadPixels(0, 0, c->width, c->height, GL_RGB, GL_FLOAT, c->pixels); + + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, c->width, c->height, 0, GL_RGB, GL_FLOAT, c->pixels); + + // calculate SH coefficients (@ands) + // copied from cubemap6 method + const vec3 skyDir[] = {{ 1, 0, 0},{-1, 0, 0},{ 0, 1, 0},{ 0,-1, 0},{ 0, 0, 1},{ 0, 0,-1}}; + const vec3 skyX[] = {{ 0, 0,-1},{ 0, 0, 1},{ 1, 0, 0},{ 1, 0, 0},{ 1, 0, 0},{-1, 0, 0}}; + const vec3 skyY[] = {{ 0, 1, 0},{ 0, 1, 0},{ 0, 0,-1},{ 0, 0, 1},{ 0, 1, 0},{ 0, 1, 0}}; + int step = 16; + for (int y = 0; y < c->height; y += step) { + float *p = (float*)(c->pixels + y * c->width * 3); + for (int x = 0; x < c->width; x += step) { + vec3 n = add3( + add3( + scale3(skyX[i], 2.0f * (x / (c->width - 1.0f)) - 1.0f), + scale3(skyY[i], -2.0f * (y / (c->height - 1.0f)) + 1.0f)), + skyDir[i]); // texelDirection; + float l = len3(n); + vec3 light = scale3(vec3(p[0], p[1], p[2]), (1 / (l * l * l)) * sky_intensity); // texelSolidAngle * texel_radiance; + n = norm3(n); + c->sh[0] = add3(c->sh[0], scale3(light, 0.282095f)); + c->sh[1] = add3(c->sh[1], scale3(light, -0.488603f * n.y * 2.0 / 3.0)); + c->sh[2] = add3(c->sh[2], scale3(light, 0.488603f * n.z * 2.0 / 3.0)); + c->sh[3] = add3(c->sh[3], scale3(light, -0.488603f * n.x * 2.0 / 3.0)); + c->sh[4] = add3(c->sh[4], scale3(light, 1.092548f * n.x * n.y / 4.0)); + c->sh[5] = add3(c->sh[5], scale3(light, -1.092548f * n.y * n.z / 4.0)); + c->sh[6] = add3(c->sh[6], scale3(light, 0.315392f * (3.0f * n.z * n.z - 1.0f) / 4.0)); + c->sh[7] = add3(c->sh[7], scale3(light, -1.092548f * n.x * n.z / 4.0)); + c->sh[8] = add3(c->sh[8], scale3(light, 0.546274f * (n.x * n.x - n.y * n.y) / 4.0)); + p += 3 * step; + samples++; + } + } + } + + for (int s = 0; s < 9; s++) { + c->sh[s] = scale3(c->sh[s], 32.f / samples); + } + + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_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); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, sky_last_fb); + glViewport(sky_last_vp[0], sky_last_vp[1], sky_last_vp[2], sky_last_vp[3]); +} + +void cubemap_sh_reset(cubemap_t *c) { + for (int s = 0; s < 9; s++) { + c->sh[s] = vec3(0,0,0); + } +} + +void cubemap_sh_shader(cubemap_t *c) { + shader_vec3v("u_coefficients_sh", 9, c->sh); +} + +void cubemap_sh_add_light(cubemap_t *c, vec3 light, vec3 dir, float strength) { + // Normalize the direction + vec3 norm_dir = norm3(dir); + + // Scale the light color and intensity + vec3 scaled_light = scale3(light, strength); + + // Add light to the SH coefficients + c->sh[0] = add3(c->sh[0], scale3(scaled_light, 0.282095f)); + c->sh[1] = add3(c->sh[1], scale3(scaled_light, -0.488603f * norm_dir.y)); + c->sh[2] = add3(c->sh[2], scale3(scaled_light, 0.488603f * norm_dir.z)); + c->sh[3] = add3(c->sh[3], scale3(scaled_light, -0.488603f * norm_dir.x)); +} + + // ----------------------------------------------------------------------------- // skyboxes @@ -1955,7 +2122,10 @@ skybox_t skybox(const char *asset, int flags) { // sky program sky.flags = flags && flags != SKYBOX_PBR ? flags : !!asset ? SKYBOX_CUBEMAP : SKYBOX_RAYLEIGH; // either cubemap or rayleigh sky.program = shader(vfs_read("shaders/vs_3_3_skybox.glsl"), - sky.flags ? vfs_read("fs_3_4_skybox.glsl") : vfs_read("shaders/fs_3_4_skybox_rayleigh.glsl"), + vfs_read("fs_3_4_skybox.glsl"), + "att_position", "fragcolor", NULL); + sky.rayleigh_program = shader(vfs_read("shaders/vs_3_3_skybox.glsl"), + vfs_read("shaders/fs_3_4_skybox_rayleigh.glsl"), "att_position", "fragcolor", NULL); // sky cubemap & SH @@ -1979,7 +2149,7 @@ skybox_t skybox(const char *asset, int flags) { } } else { // set up mie defaults // @fixme: use shader params instead - shader_bind(sky.program); + shader_bind(sky.rayleigh_program); shader_vec3("uSunPos", vec3( 0, 0.1, -1 )); shader_vec3("uRayOrigin", vec3(0.0, 6372000.0, 0.0)); shader_float("uSunIntensity", 22.0); @@ -2050,120 +2220,58 @@ skybox_t skybox_pbr(const char *sky_map, const char *refl_map, const char *env_m return sky; } +static renderstate_t skybox_rs; +API vec4 window_getcolor_(); // internal use, not public + +static inline +void skybox_render_rayleigh(skybox_t *sky, mat44 proj, mat44 view) { + last_cubemap = &sky->cubemap; + + do_once { + skybox_rs = renderstate(); + skybox_rs.depth_test_enabled = 1; + skybox_rs.cull_face_enabled = 0; + skybox_rs.front_face = GL_CCW; + } + + // we have to reset clear color here, because of wrong alpha compositing issues on native transparent windows otherwise + // vec4 bgcolor = window_getcolor_(); + // skybox_rs.clear_color[0] = bgcolor.r; + // skybox_rs.clear_color[1] = bgcolor.g; + // skybox_rs.clear_color[2] = bgcolor.b; + // skybox_rs.clear_color[3] = 1; // @transparent + + mat44 mvp; multiply44x2(mvp, proj, view); + + //glDepthMask(GL_FALSE); + shader_bind(sky->rayleigh_program); + shader_mat44("u_mvp", mvp); + + renderstate_apply(&skybox_rs); + mesh_render(&sky->geometry); +} + void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity) { - unsigned WIDTH = 1024, HEIGHT = 1024; - int last_fb; - int vp[4]; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &last_fb); - glGetIntegerv(GL_VIEWPORT, vp); - - if (!sky_intensity) { - sky_intensity = 1.0f; + cubemap_bake_begin(&sky->cubemap, vec3(0, 0, 0), 1024, 1024); + mat44 proj, view; + while (cubemap_bake_step(&sky->cubemap, proj, view)) { + skybox_render_rayleigh(sky, proj, view); } - - if (!sky->pixels) - sky->pixels = MALLOC(WIDTH*HEIGHT*12); - - if (!sky->framebuffers[0]) { - for(int i = 0; i < 6; ++i) { - glGenFramebuffers(1, &sky->framebuffers[i]); - glBindFramebuffer(GL_FRAMEBUFFER, sky->framebuffers[i]); - - glGenTextures(1, &sky->textures[i]); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, sky->textures[i]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, GL_FLOAT, NULL); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glBindTexture(GL_TEXTURE_2D, 0); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sky->textures[i], 0); - } - } - - static vec3 directions[6] = {{ 1, 0, 0},{-1, 0, 0},{ 0, 1, 0},{ 0,-1, 0},{ 0, 0, 1},{ 0, 0,-1}}; - - int samples = 0; - for(int i = 0; i < 6; ++i) { - glBindFramebuffer(GL_FRAMEBUFFER, sky->framebuffers[i]); - glViewport(0, 0, WIDTH, HEIGHT); - glUseProgram(sky->program); - - mat44 proj; perspective44(proj, 90.0f, WIDTH / (float)HEIGHT, 0.1f, 500.f); - mat44 view; lookat44(view, vec3(0,0,0), directions[i], vec3(0,-1,0)); - - skybox_render(sky, proj, view); - - glReadPixels(0, 0, WIDTH, HEIGHT, GL_RGB, GL_FLOAT, sky->pixels); - - // calculate SH coefficients (@ands) - // copied from cubemap6 method - const vec3 skyDir[] = {{ 1, 0, 0},{-1, 0, 0},{ 0, 1, 0},{ 0,-1, 0},{ 0, 0, 1},{ 0, 0,-1}}; - const vec3 skyX[] = {{ 0, 0,-1},{ 0, 0, 1},{ 1, 0, 0},{ 1, 0, 0},{ 1, 0, 0},{-1, 0, 0}}; - const vec3 skyY[] = {{ 0, 1, 0},{ 0, 1, 0},{ 0, 0,-1},{ 0, 0, 1},{ 0, 1, 0},{ 0, 1, 0}}; - int step = 16; - for (int y = 0; y < HEIGHT; y += step) { - float *p = (float*)(sky->pixels + y * WIDTH * 3); - for (int x = 0; x < WIDTH; x += step) { - vec3 n = add3( - add3( - scale3(skyX[i], 2.0f * (x / (WIDTH - 1.0f)) - 1.0f), - scale3(skyY[i], -2.0f * (y / (HEIGHT - 1.0f)) + 1.0f)), - skyDir[i]); // texelDirection; - float l = len3(n); - vec3 light = scale3(vec3(p[0], p[1], p[2]), (1 / (l * l * l)) * sky_intensity); // texelSolidAngle * texel_radiance; - n = norm3(n); - sky->cubemap.sh[0] = add3(sky->cubemap.sh[0], scale3(light, 0.282095f)); - sky->cubemap.sh[1] = add3(sky->cubemap.sh[1], scale3(light, -0.488603f * n.y * 2.0 / 3.0)); - sky->cubemap.sh[2] = add3(sky->cubemap.sh[2], scale3(light, 0.488603f * n.z * 2.0 / 3.0)); - sky->cubemap.sh[3] = add3(sky->cubemap.sh[3], scale3(light, -0.488603f * n.x * 2.0 / 3.0)); - sky->cubemap.sh[4] = add3(sky->cubemap.sh[4], scale3(light, 1.092548f * n.x * n.y / 4.0)); - sky->cubemap.sh[5] = add3(sky->cubemap.sh[5], scale3(light, -1.092548f * n.y * n.z / 4.0)); - sky->cubemap.sh[6] = add3(sky->cubemap.sh[6], scale3(light, 0.315392f * (3.0f * n.z * n.z - 1.0f) / 4.0)); - sky->cubemap.sh[7] = add3(sky->cubemap.sh[7], scale3(light, -1.092548f * n.x * n.z / 4.0)); - sky->cubemap.sh[8] = add3(sky->cubemap.sh[8], scale3(light, 0.546274f * (n.x * n.x - n.y * n.y) / 4.0)); - p += 3 * step; - samples++; - } - } - } - - for (int s = 0; s < 9; s++) { - sky->cubemap.sh[s] = scale3(sky->cubemap.sh[s], 32.f / samples); - } - - glBindFramebuffer(GL_FRAMEBUFFER, last_fb); - glViewport(vp[0], vp[1], vp[2], vp[3]); + cubemap_bake_end(&sky->cubemap, sky_intensity); } void skybox_sh_reset(skybox_t *sky) { - for (int s = 0; s < 9; s++) { - sky->cubemap.sh[s] = vec3(0,0,0); - } + cubemap_sh_reset(&sky->cubemap); } void skybox_sh_shader(skybox_t *sky) { - shader_vec3v("u_coefficients_sh", 9, sky->cubemap.sh); + cubemap_sh_shader(&sky->cubemap); } void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength) { - // Normalize the direction - vec3 norm_dir = norm3(dir); - - // Scale the light color and intensity - vec3 scaled_light = scale3(light, strength); - - // Add light to the SH coefficients - sky->cubemap.sh[0] = add3(sky->cubemap.sh[0], scale3(scaled_light, 0.282095f)); - sky->cubemap.sh[1] = add3(sky->cubemap.sh[1], scale3(scaled_light, -0.488603f * norm_dir.y)); - sky->cubemap.sh[2] = add3(sky->cubemap.sh[2], scale3(scaled_light, 0.488603f * norm_dir.z)); - sky->cubemap.sh[3] = add3(sky->cubemap.sh[3], scale3(scaled_light, -0.488603f * norm_dir.x)); + cubemap_sh_add_light(&sky->cubemap, light, dir, strength); } -API vec4 window_getcolor_(); // internal use, not public - -static renderstate_t skybox_rs; - int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view) { last_cubemap = &sky->cubemap; @@ -2175,20 +2283,18 @@ int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view) { } // we have to reset clear color here, because of wrong alpha compositing issues on native transparent windows otherwise - vec4 bgcolor = window_getcolor_(); - skybox_rs.clear_color[0] = bgcolor.r; - skybox_rs.clear_color[1] = bgcolor.g; - skybox_rs.clear_color[2] = bgcolor.b; - skybox_rs.clear_color[3] = 1; // @transparent + // vec4 bgcolor = window_getcolor_(); + // skybox_rs.clear_color[0] = bgcolor.r; + // skybox_rs.clear_color[1] = bgcolor.g; + // skybox_rs.clear_color[2] = bgcolor.b; + // skybox_rs.clear_color[3] = 1; // @transparent mat44 mvp; multiply44x2(mvp, proj, view); //glDepthMask(GL_FALSE); shader_bind(sky->program); shader_mat44("u_mvp", mvp); - if( sky->flags ) { - shader_cubemap("u_cubemap", sky->cubemap.id); - } + shader_cubemap("u_cubemap", sky->cubemap.id); renderstate_apply(&skybox_rs); return 0; // @fixme: return sortable hash here? @@ -2200,6 +2306,10 @@ int skybox_pop_state() { return 0; } int skybox_render(skybox_t *sky, mat44 proj, mat44 view) { + if (sky->rayleigh_immediate && !sky->flags) { + skybox_render_rayleigh(sky, proj, view); + return 0; + } skybox_push_state(sky, proj, view); mesh_render(&sky->geometry); skybox_pop_state(); @@ -2209,12 +2319,6 @@ void skybox_destroy(skybox_t *sky) { glDeleteProgram(sky->program); cubemap_destroy(&sky->cubemap); mesh_destroy(&sky->geometry); - - if (sky->pixels) { - FREE(sky->pixels); - glDeleteFramebuffers(6, sky->framebuffers); - glDeleteTextures(6, sky->textures); - } } // ----------------------------------------------------------------------------- diff --git a/engine/split/v4k_render.h b/engine/split/v4k_render.h index 93359b5..0d13d73 100644 --- a/engine/split/v4k_render.h +++ b/engine/split/v4k_render.h @@ -241,12 +241,27 @@ API void fullscreen_quad_ycbcr_flipped( texture_t texture_YCbCr[3] ); typedef struct cubemap_t { unsigned id; // texture id vec3 sh[9]; // precomputed spherical harmonics coefficients + + // bake data + int framebuffers[6]; + int textures[6]; + int depth_buffers[6]; + unsigned width, height; + float *pixels; + int step; + vec3 pos; } cubemap_t; API cubemap_t cubemap( const image_t image, int flags ); // 1 equirectangular panorama API cubemap_t cubemap6( const image_t images[6], int flags ); // 6 cubemap faces API void cubemap_destroy(cubemap_t *c); API cubemap_t* cubemap_get_active(); +API void cubemap_bake_begin(cubemap_t *c, vec3 pos, unsigned width, unsigned height); +API bool cubemap_bake_step(cubemap_t *c, mat44 proj /* out */, mat44 view /* out */); +API void cubemap_bake_end(cubemap_t *c, float sky_intensity); +API void cubemap_sh_reset(cubemap_t *c); +API void cubemap_sh_shader(cubemap_t *c); +API void cubemap_sh_add_light(cubemap_t *c, vec3 light, vec3 dir, float strength); // ----------------------------------------------------------------------------- // fbos @@ -485,15 +500,11 @@ enum SKYBOX_FLAGS { }; typedef struct skybox_t { - handle program; + handle program, rayleigh_program; mesh_t geometry; cubemap_t cubemap; int flags; - - // mie - int framebuffers[6]; - int textures[6]; - float *pixels; + bool rayleigh_immediate; // pbr texture_t sky, refl, env; @@ -504,9 +515,9 @@ API skybox_t skybox_pbr(const char *sky_map, const char *refl_map, const char *e API int skybox_render(skybox_t *sky, mat44 proj, mat44 view); API void skybox_destroy(skybox_t *sky); API void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity); -API void skybox_sh_reset(skybox_t *sky); -API void skybox_sh_shader(skybox_t *sky); -API void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength); +API void skybox_sh_reset(skybox_t *sky); /* @deprecated */ +API void skybox_sh_shader(skybox_t *sky); /* @deprecated */ +API void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength); /* @deprecated */ API int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view); // @to deprecate API int skybox_pop_state(); // @to deprecate diff --git a/engine/v4k.c b/engine/v4k.c index c7260a5..51d7ac6 100644 --- a/engine/v4k.c +++ b/engine/v4k.c @@ -18732,6 +18732,13 @@ cubemap_t cubemap( const image_t in, int flags ) { void cubemap_destroy(cubemap_t *c) { glDeleteTextures(1, &c->id); c->id = 0; // do not destroy SH coefficients still. they might be useful in the future. + + if (c->pixels) { + FREE(c->pixels); + glDeleteFramebuffers(6, c->framebuffers); + glDeleteTextures(6, c->textures); + glDeleteRenderbuffers(6, c->depth_buffers); + } } static cubemap_t *last_cubemap; @@ -18740,6 +18747,166 @@ cubemap_t* cubemap_get_active() { return last_cubemap; } +// cubemap baker + +static int sky_last_fb; +static int sky_last_vp[4]; +void cubemap_bake_begin(cubemap_t *c, vec3 pos, unsigned width, unsigned height) { + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &sky_last_fb); + glGetIntegerv(GL_VIEWPORT, sky_last_vp); + c->step = 0; + c->pos = pos; + + if (!c->pixels || (c->width != width || c->height != height)) { + c->pixels = REALLOC(c->pixels, width*height*12); + c->width = width; + c->height = height; + + if (!c->framebuffers[0]) { + glDeleteFramebuffers(6, c->framebuffers); + glDeleteTextures(6, c->textures); + glDeleteRenderbuffers(6, c->depth_buffers); + for(int i = 0; i < 6; ++i) { + c->framebuffers[i] = 0; + } + } + } + + if (!c->framebuffers[0]) { + for(int i = 0; i < 6; ++i) { + glGenFramebuffers(1, &c->framebuffers[i]); + glBindFramebuffer(GL_FRAMEBUFFER, c->framebuffers[i]); + + glGenTextures(1, &c->textures[i]); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, c->textures[i]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, c->textures[i], 0); + + // attach depth buffer + glGenRenderbuffers(1, &c->depth_buffers[i]); + glBindRenderbuffer(GL_RENDERBUFFER, c->depth_buffers[i]); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, c->depth_buffers[i]); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } + } +} + +bool cubemap_bake_step(cubemap_t *c, mat44 proj /* out */, mat44 view /* out */) { + if (c->step >= 6) return false; + + static vec3 directions[6] = {{ 1, 0, 0},{-1, 0, 0},{ 0, 1, 0},{ 0,-1, 0},{ 0, 0, 1},{ 0, 0,-1}}; + + glBindFramebuffer(GL_FRAMEBUFFER, c->framebuffers[c->step]); + glViewport(0, 0, c->width, c->height); + glClearColor(0, 0, 0, 1); + glClearDepth(1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + perspective44(proj, 90.0f, c->width / (float)c->height, 0.1f, 1000.f); + lookat44(view, c->pos, add3(c->pos, directions[c->step]), vec3(0,-1,0)); + ++c->step; + + return true; +} + +void cubemap_bake_end(cubemap_t *c, float sky_intensity) { + if (!sky_intensity) { + sky_intensity = 1.0f; + } + + if (c->id) { + glDeleteTextures(1, &c->id); + c->id = 0; + } + + glGenTextures(1, &c->id); + glBindTexture(GL_TEXTURE_CUBE_MAP, c->id); + + int samples = 0; + for (int i = 0; i < 6; i++) { + glBindFramebuffer(GL_FRAMEBUFFER, c->framebuffers[i]); + glReadPixels(0, 0, c->width, c->height, GL_RGB, GL_FLOAT, c->pixels); + + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, c->width, c->height, 0, GL_RGB, GL_FLOAT, c->pixels); + + // calculate SH coefficients (@ands) + // copied from cubemap6 method + const vec3 skyDir[] = {{ 1, 0, 0},{-1, 0, 0},{ 0, 1, 0},{ 0,-1, 0},{ 0, 0, 1},{ 0, 0,-1}}; + const vec3 skyX[] = {{ 0, 0,-1},{ 0, 0, 1},{ 1, 0, 0},{ 1, 0, 0},{ 1, 0, 0},{-1, 0, 0}}; + const vec3 skyY[] = {{ 0, 1, 0},{ 0, 1, 0},{ 0, 0,-1},{ 0, 0, 1},{ 0, 1, 0},{ 0, 1, 0}}; + int step = 16; + for (int y = 0; y < c->height; y += step) { + float *p = (float*)(c->pixels + y * c->width * 3); + for (int x = 0; x < c->width; x += step) { + vec3 n = add3( + add3( + scale3(skyX[i], 2.0f * (x / (c->width - 1.0f)) - 1.0f), + scale3(skyY[i], -2.0f * (y / (c->height - 1.0f)) + 1.0f)), + skyDir[i]); // texelDirection; + float l = len3(n); + vec3 light = scale3(vec3(p[0], p[1], p[2]), (1 / (l * l * l)) * sky_intensity); // texelSolidAngle * texel_radiance; + n = norm3(n); + c->sh[0] = add3(c->sh[0], scale3(light, 0.282095f)); + c->sh[1] = add3(c->sh[1], scale3(light, -0.488603f * n.y * 2.0 / 3.0)); + c->sh[2] = add3(c->sh[2], scale3(light, 0.488603f * n.z * 2.0 / 3.0)); + c->sh[3] = add3(c->sh[3], scale3(light, -0.488603f * n.x * 2.0 / 3.0)); + c->sh[4] = add3(c->sh[4], scale3(light, 1.092548f * n.x * n.y / 4.0)); + c->sh[5] = add3(c->sh[5], scale3(light, -1.092548f * n.y * n.z / 4.0)); + c->sh[6] = add3(c->sh[6], scale3(light, 0.315392f * (3.0f * n.z * n.z - 1.0f) / 4.0)); + c->sh[7] = add3(c->sh[7], scale3(light, -1.092548f * n.x * n.z / 4.0)); + c->sh[8] = add3(c->sh[8], scale3(light, 0.546274f * (n.x * n.x - n.y * n.y) / 4.0)); + p += 3 * step; + samples++; + } + } + } + + for (int s = 0; s < 9; s++) { + c->sh[s] = scale3(c->sh[s], 32.f / samples); + } + + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_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); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, sky_last_fb); + glViewport(sky_last_vp[0], sky_last_vp[1], sky_last_vp[2], sky_last_vp[3]); +} + +void cubemap_sh_reset(cubemap_t *c) { + for (int s = 0; s < 9; s++) { + c->sh[s] = vec3(0,0,0); + } +} + +void cubemap_sh_shader(cubemap_t *c) { + shader_vec3v("u_coefficients_sh", 9, c->sh); +} + +void cubemap_sh_add_light(cubemap_t *c, vec3 light, vec3 dir, float strength) { + // Normalize the direction + vec3 norm_dir = norm3(dir); + + // Scale the light color and intensity + vec3 scaled_light = scale3(light, strength); + + // Add light to the SH coefficients + c->sh[0] = add3(c->sh[0], scale3(scaled_light, 0.282095f)); + c->sh[1] = add3(c->sh[1], scale3(scaled_light, -0.488603f * norm_dir.y)); + c->sh[2] = add3(c->sh[2], scale3(scaled_light, 0.488603f * norm_dir.z)); + c->sh[3] = add3(c->sh[3], scale3(scaled_light, -0.488603f * norm_dir.x)); +} + + // ----------------------------------------------------------------------------- // skyboxes @@ -18754,7 +18921,10 @@ skybox_t skybox(const char *asset, int flags) { // sky program sky.flags = flags && flags != SKYBOX_PBR ? flags : !!asset ? SKYBOX_CUBEMAP : SKYBOX_RAYLEIGH; // either cubemap or rayleigh sky.program = shader(vfs_read("shaders/vs_3_3_skybox.glsl"), - sky.flags ? vfs_read("fs_3_4_skybox.glsl") : vfs_read("shaders/fs_3_4_skybox_rayleigh.glsl"), + vfs_read("fs_3_4_skybox.glsl"), + "att_position", "fragcolor", NULL); + sky.rayleigh_program = shader(vfs_read("shaders/vs_3_3_skybox.glsl"), + vfs_read("shaders/fs_3_4_skybox_rayleigh.glsl"), "att_position", "fragcolor", NULL); // sky cubemap & SH @@ -18778,7 +18948,7 @@ skybox_t skybox(const char *asset, int flags) { } } else { // set up mie defaults // @fixme: use shader params instead - shader_bind(sky.program); + shader_bind(sky.rayleigh_program); shader_vec3("uSunPos", vec3( 0, 0.1, -1 )); shader_vec3("uRayOrigin", vec3(0.0, 6372000.0, 0.0)); shader_float("uSunIntensity", 22.0); @@ -18849,120 +19019,58 @@ skybox_t skybox_pbr(const char *sky_map, const char *refl_map, const char *env_m return sky; } +static renderstate_t skybox_rs; +API vec4 window_getcolor_(); // internal use, not public + +static inline +void skybox_render_rayleigh(skybox_t *sky, mat44 proj, mat44 view) { + last_cubemap = &sky->cubemap; + + do_once { + skybox_rs = renderstate(); + skybox_rs.depth_test_enabled = 1; + skybox_rs.cull_face_enabled = 0; + skybox_rs.front_face = GL_CCW; + } + + // we have to reset clear color here, because of wrong alpha compositing issues on native transparent windows otherwise + // vec4 bgcolor = window_getcolor_(); + // skybox_rs.clear_color[0] = bgcolor.r; + // skybox_rs.clear_color[1] = bgcolor.g; + // skybox_rs.clear_color[2] = bgcolor.b; + // skybox_rs.clear_color[3] = 1; // @transparent + + mat44 mvp; multiply44x2(mvp, proj, view); + + //glDepthMask(GL_FALSE); + shader_bind(sky->rayleigh_program); + shader_mat44("u_mvp", mvp); + + renderstate_apply(&skybox_rs); + mesh_render(&sky->geometry); +} + void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity) { - unsigned WIDTH = 1024, HEIGHT = 1024; - int last_fb; - int vp[4]; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &last_fb); - glGetIntegerv(GL_VIEWPORT, vp); - - if (!sky_intensity) { - sky_intensity = 1.0f; + cubemap_bake_begin(&sky->cubemap, vec3(0, 0, 0), 1024, 1024); + mat44 proj, view; + while (cubemap_bake_step(&sky->cubemap, proj, view)) { + skybox_render_rayleigh(sky, proj, view); } - - if (!sky->pixels) - sky->pixels = MALLOC(WIDTH*HEIGHT*12); - - if (!sky->framebuffers[0]) { - for(int i = 0; i < 6; ++i) { - glGenFramebuffers(1, &sky->framebuffers[i]); - glBindFramebuffer(GL_FRAMEBUFFER, sky->framebuffers[i]); - - glGenTextures(1, &sky->textures[i]); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, sky->textures[i]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, GL_FLOAT, NULL); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glBindTexture(GL_TEXTURE_2D, 0); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sky->textures[i], 0); - } - } - - static vec3 directions[6] = {{ 1, 0, 0},{-1, 0, 0},{ 0, 1, 0},{ 0,-1, 0},{ 0, 0, 1},{ 0, 0,-1}}; - - int samples = 0; - for(int i = 0; i < 6; ++i) { - glBindFramebuffer(GL_FRAMEBUFFER, sky->framebuffers[i]); - glViewport(0, 0, WIDTH, HEIGHT); - glUseProgram(sky->program); - - mat44 proj; perspective44(proj, 90.0f, WIDTH / (float)HEIGHT, 0.1f, 500.f); - mat44 view; lookat44(view, vec3(0,0,0), directions[i], vec3(0,-1,0)); - - skybox_render(sky, proj, view); - - glReadPixels(0, 0, WIDTH, HEIGHT, GL_RGB, GL_FLOAT, sky->pixels); - - // calculate SH coefficients (@ands) - // copied from cubemap6 method - const vec3 skyDir[] = {{ 1, 0, 0},{-1, 0, 0},{ 0, 1, 0},{ 0,-1, 0},{ 0, 0, 1},{ 0, 0,-1}}; - const vec3 skyX[] = {{ 0, 0,-1},{ 0, 0, 1},{ 1, 0, 0},{ 1, 0, 0},{ 1, 0, 0},{-1, 0, 0}}; - const vec3 skyY[] = {{ 0, 1, 0},{ 0, 1, 0},{ 0, 0,-1},{ 0, 0, 1},{ 0, 1, 0},{ 0, 1, 0}}; - int step = 16; - for (int y = 0; y < HEIGHT; y += step) { - float *p = (float*)(sky->pixels + y * WIDTH * 3); - for (int x = 0; x < WIDTH; x += step) { - vec3 n = add3( - add3( - scale3(skyX[i], 2.0f * (x / (WIDTH - 1.0f)) - 1.0f), - scale3(skyY[i], -2.0f * (y / (HEIGHT - 1.0f)) + 1.0f)), - skyDir[i]); // texelDirection; - float l = len3(n); - vec3 light = scale3(vec3(p[0], p[1], p[2]), (1 / (l * l * l)) * sky_intensity); // texelSolidAngle * texel_radiance; - n = norm3(n); - sky->cubemap.sh[0] = add3(sky->cubemap.sh[0], scale3(light, 0.282095f)); - sky->cubemap.sh[1] = add3(sky->cubemap.sh[1], scale3(light, -0.488603f * n.y * 2.0 / 3.0)); - sky->cubemap.sh[2] = add3(sky->cubemap.sh[2], scale3(light, 0.488603f * n.z * 2.0 / 3.0)); - sky->cubemap.sh[3] = add3(sky->cubemap.sh[3], scale3(light, -0.488603f * n.x * 2.0 / 3.0)); - sky->cubemap.sh[4] = add3(sky->cubemap.sh[4], scale3(light, 1.092548f * n.x * n.y / 4.0)); - sky->cubemap.sh[5] = add3(sky->cubemap.sh[5], scale3(light, -1.092548f * n.y * n.z / 4.0)); - sky->cubemap.sh[6] = add3(sky->cubemap.sh[6], scale3(light, 0.315392f * (3.0f * n.z * n.z - 1.0f) / 4.0)); - sky->cubemap.sh[7] = add3(sky->cubemap.sh[7], scale3(light, -1.092548f * n.x * n.z / 4.0)); - sky->cubemap.sh[8] = add3(sky->cubemap.sh[8], scale3(light, 0.546274f * (n.x * n.x - n.y * n.y) / 4.0)); - p += 3 * step; - samples++; - } - } - } - - for (int s = 0; s < 9; s++) { - sky->cubemap.sh[s] = scale3(sky->cubemap.sh[s], 32.f / samples); - } - - glBindFramebuffer(GL_FRAMEBUFFER, last_fb); - glViewport(vp[0], vp[1], vp[2], vp[3]); + cubemap_bake_end(&sky->cubemap, sky_intensity); } void skybox_sh_reset(skybox_t *sky) { - for (int s = 0; s < 9; s++) { - sky->cubemap.sh[s] = vec3(0,0,0); - } + cubemap_sh_reset(&sky->cubemap); } void skybox_sh_shader(skybox_t *sky) { - shader_vec3v("u_coefficients_sh", 9, sky->cubemap.sh); + cubemap_sh_shader(&sky->cubemap); } void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength) { - // Normalize the direction - vec3 norm_dir = norm3(dir); - - // Scale the light color and intensity - vec3 scaled_light = scale3(light, strength); - - // Add light to the SH coefficients - sky->cubemap.sh[0] = add3(sky->cubemap.sh[0], scale3(scaled_light, 0.282095f)); - sky->cubemap.sh[1] = add3(sky->cubemap.sh[1], scale3(scaled_light, -0.488603f * norm_dir.y)); - sky->cubemap.sh[2] = add3(sky->cubemap.sh[2], scale3(scaled_light, 0.488603f * norm_dir.z)); - sky->cubemap.sh[3] = add3(sky->cubemap.sh[3], scale3(scaled_light, -0.488603f * norm_dir.x)); + cubemap_sh_add_light(&sky->cubemap, light, dir, strength); } -API vec4 window_getcolor_(); // internal use, not public - -static renderstate_t skybox_rs; - int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view) { last_cubemap = &sky->cubemap; @@ -18974,20 +19082,18 @@ int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view) { } // we have to reset clear color here, because of wrong alpha compositing issues on native transparent windows otherwise - vec4 bgcolor = window_getcolor_(); - skybox_rs.clear_color[0] = bgcolor.r; - skybox_rs.clear_color[1] = bgcolor.g; - skybox_rs.clear_color[2] = bgcolor.b; - skybox_rs.clear_color[3] = 1; // @transparent + // vec4 bgcolor = window_getcolor_(); + // skybox_rs.clear_color[0] = bgcolor.r; + // skybox_rs.clear_color[1] = bgcolor.g; + // skybox_rs.clear_color[2] = bgcolor.b; + // skybox_rs.clear_color[3] = 1; // @transparent mat44 mvp; multiply44x2(mvp, proj, view); //glDepthMask(GL_FALSE); shader_bind(sky->program); shader_mat44("u_mvp", mvp); - if( sky->flags ) { - shader_cubemap("u_cubemap", sky->cubemap.id); - } + shader_cubemap("u_cubemap", sky->cubemap.id); renderstate_apply(&skybox_rs); return 0; // @fixme: return sortable hash here? @@ -18999,6 +19105,10 @@ int skybox_pop_state() { return 0; } int skybox_render(skybox_t *sky, mat44 proj, mat44 view) { + if (sky->rayleigh_immediate && !sky->flags) { + skybox_render_rayleigh(sky, proj, view); + return 0; + } skybox_push_state(sky, proj, view); mesh_render(&sky->geometry); skybox_pop_state(); @@ -19008,12 +19118,6 @@ void skybox_destroy(skybox_t *sky) { glDeleteProgram(sky->program); cubemap_destroy(&sky->cubemap); mesh_destroy(&sky->geometry); - - if (sky->pixels) { - FREE(sky->pixels); - glDeleteFramebuffers(6, sky->framebuffers); - glDeleteTextures(6, sky->textures); - } } // ----------------------------------------------------------------------------- diff --git a/engine/v4k.h b/engine/v4k.h index 88eddd9..05f8eed 100644 --- a/engine/v4k.h +++ b/engine/v4k.h @@ -3276,12 +3276,27 @@ API void fullscreen_quad_ycbcr_flipped( texture_t texture_YCbCr[3] ); typedef struct cubemap_t { unsigned id; // texture id vec3 sh[9]; // precomputed spherical harmonics coefficients + + // bake data + int framebuffers[6]; + int textures[6]; + int depth_buffers[6]; + unsigned width, height; + float *pixels; + int step; + vec3 pos; } cubemap_t; API cubemap_t cubemap( const image_t image, int flags ); // 1 equirectangular panorama API cubemap_t cubemap6( const image_t images[6], int flags ); // 6 cubemap faces API void cubemap_destroy(cubemap_t *c); API cubemap_t* cubemap_get_active(); +API void cubemap_bake_begin(cubemap_t *c, vec3 pos, unsigned width, unsigned height); +API bool cubemap_bake_step(cubemap_t *c, mat44 proj /* out */, mat44 view /* out */); +API void cubemap_bake_end(cubemap_t *c, float sky_intensity); +API void cubemap_sh_reset(cubemap_t *c); +API void cubemap_sh_shader(cubemap_t *c); +API void cubemap_sh_add_light(cubemap_t *c, vec3 light, vec3 dir, float strength); // ----------------------------------------------------------------------------- // fbos @@ -3520,15 +3535,11 @@ enum SKYBOX_FLAGS { }; typedef struct skybox_t { - handle program; + handle program, rayleigh_program; mesh_t geometry; cubemap_t cubemap; int flags; - - // mie - int framebuffers[6]; - int textures[6]; - float *pixels; + bool rayleigh_immediate; // pbr texture_t sky, refl, env; @@ -3539,9 +3550,9 @@ API skybox_t skybox_pbr(const char *sky_map, const char *refl_map, const char *e API int skybox_render(skybox_t *sky, mat44 proj, mat44 view); API void skybox_destroy(skybox_t *sky); API void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity); -API void skybox_sh_reset(skybox_t *sky); -API void skybox_sh_shader(skybox_t *sky); -API void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength); +API void skybox_sh_reset(skybox_t *sky); /* @deprecated */ +API void skybox_sh_shader(skybox_t *sky); /* @deprecated */ +API void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength); /* @deprecated */ API int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view); // @to deprecate API int skybox_pop_state(); // @to deprecate