WIP: light probes support

main
Dominik Madarász 2024-08-28 12:42:58 +02:00
parent 3bf4a4d851
commit 0b8d85f81c
15 changed files with 1063 additions and 389 deletions

6
.cursorignore 100644
View File

@ -0,0 +1,6 @@
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)
*.*
!*.c
!*.cpp
!*.inl
!*.h

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
default default
.art*.zip .art*.zip
*.rdi
__pycache__ __pycache__
.vs .vs
.vscode .vscode

View File

@ -3,5 +3,5 @@ call razzle amd64
if exist .env.bat ( if exist .env.bat (
call .env.bat call .env.bat
) )
code . cursor .
exit exit

View File

@ -1110,11 +1110,24 @@ typedef struct colormap_t {
typedef struct cubemap_t { typedef struct cubemap_t {
unsigned id; unsigned id;
vec3 sh[9]; 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_t cubemap( const image_t image, int flags ); cubemap_t cubemap( const image_t image, int flags );
cubemap_t cubemap6( const image_t images[6], int flags ); cubemap_t cubemap6( const image_t images[6], int flags );
void cubemap_destroy(cubemap_t *c); void cubemap_destroy(cubemap_t *c);
cubemap_t* cubemap_get_active(); 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 ); unsigned fbo( unsigned texture_color, unsigned texture_depth, int wr_flags );
void fbo_bind(unsigned id); void fbo_bind(unsigned id);
void fbo_unbind(); void fbo_unbind();
@ -1235,13 +1248,11 @@ enum SKYBOX_FLAGS {
SKYBOX_PBR, SKYBOX_PBR,
}; };
typedef struct skybox_t { typedef struct skybox_t {
handle program; handle program, rayleigh_program;
mesh_t geometry; mesh_t geometry;
cubemap_t cubemap; cubemap_t cubemap;
int flags; int flags;
int framebuffers[6]; bool rayleigh_immediate;
int textures[6];
float *pixels;
texture_t sky, refl, env; texture_t sky, refl, env;
} skybox_t; } skybox_t;
skybox_t skybox(const char *panorama_or_cubemap_folder, int flags); skybox_t skybox(const char *panorama_or_cubemap_folder, int flags);

View File

@ -38,6 +38,7 @@ int main(int argc, char** argv) {
initialized = 1; initialized = 1;
sky = skybox(flag("--mie") ? 0 : SKY_DIRS[SKY_DIR], 0); sky = skybox(flag("--mie") ? 0 : SKY_DIRS[SKY_DIR], 0);
mdl = model(OBJ_MDLS[OBJ_MDL], 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? 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 // render
mat44 mvp; multiply44x2(mvp, cam.proj, cam.view); mat44 mvp; multiply44x2(mvp, cam.proj, cam.view);
{ {
if (flag("--mie")) { do_once {
// skybox_sh_reset(&sky); if (flag("--mie")) {
skybox_mie_calc_sh(&sky, 4.0f); // skybox_sh_reset(&sky);
// float x = cosf((float)window_time())*4; skybox_mie_calc_sh(&sky, 4.0f);
// skybox_sh_add_light(&sky, vec3(0.3,0.3,0.3), vec3(0,1,0), 16*absf(cosf((float)window_time()*2))+2); // float x = cosf((float)window_time())*4;
// skybox_sh_add_light(&sky, vec3(0.6,0,0), vec3(x,1,0), 2); // 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); skybox_render(&sky, cam.proj, cam.view);

118
demos/09-envmap.c 100644
View File

@ -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();
}
}
}

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

View File

@ -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

View File

@ -44,6 +44,7 @@ vec4 shadowing() {
} }
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 vec3 u_cam_pos; uniform vec3 u_cam_pos;
uniform vec3 u_cam_dir; uniform vec3 u_cam_dir;
uniform float frame_time; uniform float frame_time;

View File

@ -200,6 +200,7 @@ surface_t surface() {
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);
#ifdef SHADING_PBR #ifdef SHADING_PBR
{ {

View File

@ -17209,12 +17209,27 @@ API void fullscreen_quad_ycbcr_flipped( texture_t texture_YCbCr[3] );
typedef struct cubemap_t { typedef struct cubemap_t {
unsigned id; // texture id unsigned id; // texture id
vec3 sh[9]; // precomputed spherical harmonics coefficients 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; } cubemap_t;
API cubemap_t cubemap( const image_t image, int flags ); // 1 equirectangular panorama 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 cubemap_t cubemap6( const image_t images[6], int flags ); // 6 cubemap faces
API void cubemap_destroy(cubemap_t *c); API void cubemap_destroy(cubemap_t *c);
API cubemap_t* cubemap_get_active(); 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 // fbos
@ -17453,15 +17468,11 @@ enum SKYBOX_FLAGS {
}; };
typedef struct skybox_t { typedef struct skybox_t {
handle program; handle program, rayleigh_program;
mesh_t geometry; mesh_t geometry;
cubemap_t cubemap; cubemap_t cubemap;
int flags; int flags;
bool rayleigh_immediate;
// mie
int framebuffers[6];
int textures[6];
float *pixels;
// pbr // pbr
texture_t sky, refl, env; 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 int skybox_render(skybox_t *sky, mat44 proj, mat44 view);
API void skybox_destroy(skybox_t *sky); API void skybox_destroy(skybox_t *sky);
API void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity); API void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity);
API void skybox_sh_reset(skybox_t *sky); API void skybox_sh_reset(skybox_t *sky); /* @deprecated */
API void skybox_sh_shader(skybox_t *sky); API void skybox_sh_shader(skybox_t *sky); /* @deprecated */
API void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength); 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_push_state(skybox_t *sky, mat44 proj, mat44 view); // @to deprecate
API int skybox_pop_state(); // @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) { void cubemap_destroy(cubemap_t *c) {
glDeleteTextures(1, &c->id); glDeleteTextures(1, &c->id);
c->id = 0; // do not destroy SH coefficients still. they might be useful in the future. 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; static cubemap_t *last_cubemap;
@ -383614,6 +383632,166 @@ cubemap_t* cubemap_get_active() {
return last_cubemap; 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 // skyboxes
@ -383628,7 +383806,10 @@ skybox_t skybox(const char *asset, int flags) {
// sky program // sky program
sky.flags = flags && flags != SKYBOX_PBR ? flags : !!asset ? SKYBOX_CUBEMAP : SKYBOX_RAYLEIGH; // either cubemap or rayleigh 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.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); "att_position", "fragcolor", NULL);
// sky cubemap & SH // sky cubemap & SH
@ -383652,7 +383833,7 @@ skybox_t skybox(const char *asset, int flags) {
} }
} else { } else {
// set up mie defaults // @fixme: use shader params instead // 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("uSunPos", vec3( 0, 0.1, -1 ));
shader_vec3("uRayOrigin", vec3(0.0, 6372000.0, 0.0)); shader_vec3("uRayOrigin", vec3(0.0, 6372000.0, 0.0));
shader_float("uSunIntensity", 22.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; 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) { void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity) {
unsigned WIDTH = 1024, HEIGHT = 1024; cubemap_bake_begin(&sky->cubemap, vec3(0, 0, 0), 1024, 1024);
int last_fb; mat44 proj, view;
int vp[4]; while (cubemap_bake_step(&sky->cubemap, proj, view)) {
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &last_fb); skybox_render_rayleigh(sky, proj, view);
glGetIntegerv(GL_VIEWPORT, vp);
if (!sky_intensity) {
sky_intensity = 1.0f;
} }
cubemap_bake_end(&sky->cubemap, sky_intensity);
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]);
} }
void skybox_sh_reset(skybox_t *sky) { void skybox_sh_reset(skybox_t *sky) {
for (int s = 0; s < 9; s++) { cubemap_sh_reset(&sky->cubemap);
sky->cubemap.sh[s] = vec3(0,0,0);
}
} }
void skybox_sh_shader(skybox_t *sky) { 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) { void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength) {
// Normalize the direction cubemap_sh_add_light(&sky->cubemap, light, dir, strength);
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));
} }
API vec4 window_getcolor_(); // internal use, not public
static renderstate_t skybox_rs;
int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view) { int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view) {
last_cubemap = &sky->cubemap; 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 // we have to reset clear color here, because of wrong alpha compositing issues on native transparent windows otherwise
vec4 bgcolor = window_getcolor_(); // vec4 bgcolor = window_getcolor_();
skybox_rs.clear_color[0] = bgcolor.r; // skybox_rs.clear_color[0] = bgcolor.r;
skybox_rs.clear_color[1] = bgcolor.g; // skybox_rs.clear_color[1] = bgcolor.g;
skybox_rs.clear_color[2] = bgcolor.b; // skybox_rs.clear_color[2] = bgcolor.b;
skybox_rs.clear_color[3] = 1; // @transparent // skybox_rs.clear_color[3] = 1; // @transparent
mat44 mvp; multiply44x2(mvp, proj, view); mat44 mvp; multiply44x2(mvp, proj, view);
//glDepthMask(GL_FALSE); //glDepthMask(GL_FALSE);
shader_bind(sky->program); shader_bind(sky->program);
shader_mat44("u_mvp", mvp); 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); renderstate_apply(&skybox_rs);
return 0; // @fixme: return sortable hash here? return 0; // @fixme: return sortable hash here?
@ -383873,6 +383990,10 @@ int skybox_pop_state() {
return 0; return 0;
} }
int skybox_render(skybox_t *sky, mat44 proj, mat44 view) { 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); skybox_push_state(sky, proj, view);
mesh_render(&sky->geometry); mesh_render(&sky->geometry);
skybox_pop_state(); skybox_pop_state();
@ -383882,12 +384003,6 @@ void skybox_destroy(skybox_t *sky) {
glDeleteProgram(sky->program); glDeleteProgram(sky->program);
cubemap_destroy(&sky->cubemap); cubemap_destroy(&sky->cubemap);
mesh_destroy(&sky->geometry); mesh_destroy(&sky->geometry);
if (sky->pixels) {
FREE(sky->pixels);
glDeleteFramebuffers(6, sky->framebuffers);
glDeleteTextures(6, sky->textures);
}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -1933,6 +1933,13 @@ cubemap_t cubemap( const image_t in, int flags ) {
void cubemap_destroy(cubemap_t *c) { void cubemap_destroy(cubemap_t *c) {
glDeleteTextures(1, &c->id); glDeleteTextures(1, &c->id);
c->id = 0; // do not destroy SH coefficients still. they might be useful in the future. 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; static cubemap_t *last_cubemap;
@ -1941,6 +1948,166 @@ cubemap_t* cubemap_get_active() {
return last_cubemap; 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 // skyboxes
@ -1955,7 +2122,10 @@ skybox_t skybox(const char *asset, int flags) {
// sky program // sky program
sky.flags = flags && flags != SKYBOX_PBR ? flags : !!asset ? SKYBOX_CUBEMAP : SKYBOX_RAYLEIGH; // either cubemap or rayleigh 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.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); "att_position", "fragcolor", NULL);
// sky cubemap & SH // sky cubemap & SH
@ -1979,7 +2149,7 @@ skybox_t skybox(const char *asset, int flags) {
} }
} else { } else {
// set up mie defaults // @fixme: use shader params instead // 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("uSunPos", vec3( 0, 0.1, -1 ));
shader_vec3("uRayOrigin", vec3(0.0, 6372000.0, 0.0)); shader_vec3("uRayOrigin", vec3(0.0, 6372000.0, 0.0));
shader_float("uSunIntensity", 22.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; 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) { void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity) {
unsigned WIDTH = 1024, HEIGHT = 1024; cubemap_bake_begin(&sky->cubemap, vec3(0, 0, 0), 1024, 1024);
int last_fb; mat44 proj, view;
int vp[4]; while (cubemap_bake_step(&sky->cubemap, proj, view)) {
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &last_fb); skybox_render_rayleigh(sky, proj, view);
glGetIntegerv(GL_VIEWPORT, vp);
if (!sky_intensity) {
sky_intensity = 1.0f;
} }
cubemap_bake_end(&sky->cubemap, sky_intensity);
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]);
} }
void skybox_sh_reset(skybox_t *sky) { void skybox_sh_reset(skybox_t *sky) {
for (int s = 0; s < 9; s++) { cubemap_sh_reset(&sky->cubemap);
sky->cubemap.sh[s] = vec3(0,0,0);
}
} }
void skybox_sh_shader(skybox_t *sky) { 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) { void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength) {
// Normalize the direction cubemap_sh_add_light(&sky->cubemap, light, dir, strength);
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));
} }
API vec4 window_getcolor_(); // internal use, not public
static renderstate_t skybox_rs;
int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view) { int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view) {
last_cubemap = &sky->cubemap; 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 // we have to reset clear color here, because of wrong alpha compositing issues on native transparent windows otherwise
vec4 bgcolor = window_getcolor_(); // vec4 bgcolor = window_getcolor_();
skybox_rs.clear_color[0] = bgcolor.r; // skybox_rs.clear_color[0] = bgcolor.r;
skybox_rs.clear_color[1] = bgcolor.g; // skybox_rs.clear_color[1] = bgcolor.g;
skybox_rs.clear_color[2] = bgcolor.b; // skybox_rs.clear_color[2] = bgcolor.b;
skybox_rs.clear_color[3] = 1; // @transparent // skybox_rs.clear_color[3] = 1; // @transparent
mat44 mvp; multiply44x2(mvp, proj, view); mat44 mvp; multiply44x2(mvp, proj, view);
//glDepthMask(GL_FALSE); //glDepthMask(GL_FALSE);
shader_bind(sky->program); shader_bind(sky->program);
shader_mat44("u_mvp", mvp); 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); renderstate_apply(&skybox_rs);
return 0; // @fixme: return sortable hash here? return 0; // @fixme: return sortable hash here?
@ -2200,6 +2306,10 @@ int skybox_pop_state() {
return 0; return 0;
} }
int skybox_render(skybox_t *sky, mat44 proj, mat44 view) { 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); skybox_push_state(sky, proj, view);
mesh_render(&sky->geometry); mesh_render(&sky->geometry);
skybox_pop_state(); skybox_pop_state();
@ -2209,12 +2319,6 @@ void skybox_destroy(skybox_t *sky) {
glDeleteProgram(sky->program); glDeleteProgram(sky->program);
cubemap_destroy(&sky->cubemap); cubemap_destroy(&sky->cubemap);
mesh_destroy(&sky->geometry); mesh_destroy(&sky->geometry);
if (sky->pixels) {
FREE(sky->pixels);
glDeleteFramebuffers(6, sky->framebuffers);
glDeleteTextures(6, sky->textures);
}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -241,12 +241,27 @@ API void fullscreen_quad_ycbcr_flipped( texture_t texture_YCbCr[3] );
typedef struct cubemap_t { typedef struct cubemap_t {
unsigned id; // texture id unsigned id; // texture id
vec3 sh[9]; // precomputed spherical harmonics coefficients 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; } cubemap_t;
API cubemap_t cubemap( const image_t image, int flags ); // 1 equirectangular panorama 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 cubemap_t cubemap6( const image_t images[6], int flags ); // 6 cubemap faces
API void cubemap_destroy(cubemap_t *c); API void cubemap_destroy(cubemap_t *c);
API cubemap_t* cubemap_get_active(); 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 // fbos
@ -485,15 +500,11 @@ enum SKYBOX_FLAGS {
}; };
typedef struct skybox_t { typedef struct skybox_t {
handle program; handle program, rayleigh_program;
mesh_t geometry; mesh_t geometry;
cubemap_t cubemap; cubemap_t cubemap;
int flags; int flags;
bool rayleigh_immediate;
// mie
int framebuffers[6];
int textures[6];
float *pixels;
// pbr // pbr
texture_t sky, refl, env; 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 int skybox_render(skybox_t *sky, mat44 proj, mat44 view);
API void skybox_destroy(skybox_t *sky); API void skybox_destroy(skybox_t *sky);
API void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity); API void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity);
API void skybox_sh_reset(skybox_t *sky); API void skybox_sh_reset(skybox_t *sky); /* @deprecated */
API void skybox_sh_shader(skybox_t *sky); API void skybox_sh_shader(skybox_t *sky); /* @deprecated */
API void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength); 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_push_state(skybox_t *sky, mat44 proj, mat44 view); // @to deprecate
API int skybox_pop_state(); // @to deprecate API int skybox_pop_state(); // @to deprecate

View File

@ -18732,6 +18732,13 @@ cubemap_t cubemap( const image_t in, int flags ) {
void cubemap_destroy(cubemap_t *c) { void cubemap_destroy(cubemap_t *c) {
glDeleteTextures(1, &c->id); glDeleteTextures(1, &c->id);
c->id = 0; // do not destroy SH coefficients still. they might be useful in the future. 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; static cubemap_t *last_cubemap;
@ -18740,6 +18747,166 @@ cubemap_t* cubemap_get_active() {
return last_cubemap; 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 // skyboxes
@ -18754,7 +18921,10 @@ skybox_t skybox(const char *asset, int flags) {
// sky program // sky program
sky.flags = flags && flags != SKYBOX_PBR ? flags : !!asset ? SKYBOX_CUBEMAP : SKYBOX_RAYLEIGH; // either cubemap or rayleigh 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.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); "att_position", "fragcolor", NULL);
// sky cubemap & SH // sky cubemap & SH
@ -18778,7 +18948,7 @@ skybox_t skybox(const char *asset, int flags) {
} }
} else { } else {
// set up mie defaults // @fixme: use shader params instead // 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("uSunPos", vec3( 0, 0.1, -1 ));
shader_vec3("uRayOrigin", vec3(0.0, 6372000.0, 0.0)); shader_vec3("uRayOrigin", vec3(0.0, 6372000.0, 0.0));
shader_float("uSunIntensity", 22.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; 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) { void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity) {
unsigned WIDTH = 1024, HEIGHT = 1024; cubemap_bake_begin(&sky->cubemap, vec3(0, 0, 0), 1024, 1024);
int last_fb; mat44 proj, view;
int vp[4]; while (cubemap_bake_step(&sky->cubemap, proj, view)) {
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &last_fb); skybox_render_rayleigh(sky, proj, view);
glGetIntegerv(GL_VIEWPORT, vp);
if (!sky_intensity) {
sky_intensity = 1.0f;
} }
cubemap_bake_end(&sky->cubemap, sky_intensity);
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]);
} }
void skybox_sh_reset(skybox_t *sky) { void skybox_sh_reset(skybox_t *sky) {
for (int s = 0; s < 9; s++) { cubemap_sh_reset(&sky->cubemap);
sky->cubemap.sh[s] = vec3(0,0,0);
}
} }
void skybox_sh_shader(skybox_t *sky) { 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) { void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength) {
// Normalize the direction cubemap_sh_add_light(&sky->cubemap, light, dir, strength);
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));
} }
API vec4 window_getcolor_(); // internal use, not public
static renderstate_t skybox_rs;
int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view) { int skybox_push_state(skybox_t *sky, mat44 proj, mat44 view) {
last_cubemap = &sky->cubemap; 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 // we have to reset clear color here, because of wrong alpha compositing issues on native transparent windows otherwise
vec4 bgcolor = window_getcolor_(); // vec4 bgcolor = window_getcolor_();
skybox_rs.clear_color[0] = bgcolor.r; // skybox_rs.clear_color[0] = bgcolor.r;
skybox_rs.clear_color[1] = bgcolor.g; // skybox_rs.clear_color[1] = bgcolor.g;
skybox_rs.clear_color[2] = bgcolor.b; // skybox_rs.clear_color[2] = bgcolor.b;
skybox_rs.clear_color[3] = 1; // @transparent // skybox_rs.clear_color[3] = 1; // @transparent
mat44 mvp; multiply44x2(mvp, proj, view); mat44 mvp; multiply44x2(mvp, proj, view);
//glDepthMask(GL_FALSE); //glDepthMask(GL_FALSE);
shader_bind(sky->program); shader_bind(sky->program);
shader_mat44("u_mvp", mvp); 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); renderstate_apply(&skybox_rs);
return 0; // @fixme: return sortable hash here? return 0; // @fixme: return sortable hash here?
@ -18999,6 +19105,10 @@ int skybox_pop_state() {
return 0; return 0;
} }
int skybox_render(skybox_t *sky, mat44 proj, mat44 view) { 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); skybox_push_state(sky, proj, view);
mesh_render(&sky->geometry); mesh_render(&sky->geometry);
skybox_pop_state(); skybox_pop_state();
@ -19008,12 +19118,6 @@ void skybox_destroy(skybox_t *sky) {
glDeleteProgram(sky->program); glDeleteProgram(sky->program);
cubemap_destroy(&sky->cubemap); cubemap_destroy(&sky->cubemap);
mesh_destroy(&sky->geometry); mesh_destroy(&sky->geometry);
if (sky->pixels) {
FREE(sky->pixels);
glDeleteFramebuffers(6, sky->framebuffers);
glDeleteTextures(6, sky->textures);
}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -3276,12 +3276,27 @@ API void fullscreen_quad_ycbcr_flipped( texture_t texture_YCbCr[3] );
typedef struct cubemap_t { typedef struct cubemap_t {
unsigned id; // texture id unsigned id; // texture id
vec3 sh[9]; // precomputed spherical harmonics coefficients 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; } cubemap_t;
API cubemap_t cubemap( const image_t image, int flags ); // 1 equirectangular panorama 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 cubemap_t cubemap6( const image_t images[6], int flags ); // 6 cubemap faces
API void cubemap_destroy(cubemap_t *c); API void cubemap_destroy(cubemap_t *c);
API cubemap_t* cubemap_get_active(); 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 // fbos
@ -3520,15 +3535,11 @@ enum SKYBOX_FLAGS {
}; };
typedef struct skybox_t { typedef struct skybox_t {
handle program; handle program, rayleigh_program;
mesh_t geometry; mesh_t geometry;
cubemap_t cubemap; cubemap_t cubemap;
int flags; int flags;
bool rayleigh_immediate;
// mie
int framebuffers[6];
int textures[6];
float *pixels;
// pbr // pbr
texture_t sky, refl, env; 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 int skybox_render(skybox_t *sky, mat44 proj, mat44 view);
API void skybox_destroy(skybox_t *sky); API void skybox_destroy(skybox_t *sky);
API void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity); API void skybox_mie_calc_sh(skybox_t *sky, float sky_intensity);
API void skybox_sh_reset(skybox_t *sky); API void skybox_sh_reset(skybox_t *sky); /* @deprecated */
API void skybox_sh_shader(skybox_t *sky); API void skybox_sh_shader(skybox_t *sky); /* @deprecated */
API void skybox_sh_add_light(skybox_t *sky, vec3 light, vec3 dir, float strength); 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_push_state(skybox_t *sky, mat44 proj, mat44 view); // @to deprecate
API int skybox_pop_state(); // @to deprecate API int skybox_pop_state(); // @to deprecate