skybox.cc (5189B)
1 #include "intrinsics.h" 2 #include "game.h" 3 #include "linear_algebra.h" 4 #include "skybox.h" 5 #include "renderer.h" 6 #include "shaders.h" 7 8 #include "hosek_rgb.h" 9 10 #include "libs/par/par_shapes.h" 11 12 struct Hosek { 13 v3 A, B, C, D, E, F, G, H, I, Z; 14 }; 15 16 static double evaluate_spline( const double * spline, size_t stride, double value ) { 17 return 18 1 * pow( 1 - value, 5 ) * spline[ 0 * stride ] + 19 5 * pow( 1 - value, 4 ) * pow( value, 1 ) * spline[ 1 * stride ] + 20 10 * pow( 1 - value, 3 ) * pow( value, 2 ) * spline[ 2 * stride ] + 21 10 * pow( 1 - value, 2 ) * pow( value, 3 ) * spline[ 3 * stride ] + 22 5 * pow( 1 - value, 1 ) * pow( value, 4 ) * spline[ 4 * stride ] + 23 1 * pow( value, 5 ) * spline[ 5 * stride ]; 24 } 25 26 static double evaluate( const double * dataset, size_t stride, float turbidity, float albedo, float sun_theta ) { 27 // splines are functions of elevation^1/3 28 double elevationK = pow( max( 0.0f, 1.0f - sun_theta / ( PI / 2.0f ) ), 1 / 3.0 ); 29 30 // table has values for turbidity 1..10 31 int turbidity0 = clamp( int( turbidity ), 1, 10 ); 32 int turbidity1 = min( turbidity0 + 1, 10 ); 33 float turbidityK = saturate( turbidity - turbidity0 ); 34 35 const double* datasetA0 = dataset; 36 const double* datasetA1 = dataset + stride * 6 * 10; 37 38 double a0t0 = evaluate_spline( datasetA0 + stride * 6 * ( turbidity0 - 1 ), stride, elevationK ); 39 double a1t0 = evaluate_spline( datasetA1 + stride * 6 * ( turbidity0 - 1 ), stride, elevationK ); 40 41 double a0t1 = evaluate_spline( datasetA0 + stride * 6 * ( turbidity1 - 1 ), stride, elevationK ); 42 double a1t1 = evaluate_spline( datasetA1 + stride * 6 * ( turbidity1 - 1 ), stride, elevationK ); 43 44 return 45 a0t0 * ( 1 - albedo ) * ( 1 - turbidityK ) + 46 a1t0 * albedo * ( 1 - turbidityK ) + 47 a0t1 * ( 1 - albedo ) * turbidityK + 48 a1t1 * albedo * turbidityK; 49 } 50 51 static v3 perez_ext(float cos_theta, float gamma, float cos_gamma, v3 A, v3 B, v3 C, v3 D, v3 E, v3 F, v3 G, v3 H, v3 I) { 52 v3 chi = v3( 1 + cos_gamma * cos_gamma ) / powf( v3( 1.0f ) + H * H - 2.0f * cos_gamma * H, v3( 1.5f ) ); 53 54 return ( v3( 1.0f ) + A * expf( B / ( cos_theta + 0.01f ) ) ) * ( C + D * expf( E * gamma ) + F * ( cos_gamma * cos_gamma ) + G * chi + I * sqrt( max( 0.0f, cos_theta ) ) ); 55 } 56 57 static Hosek compute_hosek( float sun_theta, float turbidity, float normalized_sun_y ) { 58 sun_theta = clamp( sun_theta, 0.0f, PI / 2.0f ); 59 v3 A, B, C, D, E, F, G, H, I; 60 v3 Z; 61 62 for( int i = 0; i < 3; i++ ) { 63 float albedo = 0; 64 65 A[ i ] = evaluate( datasetsRGB[ i ] + 0, 9, turbidity, albedo, sun_theta ); 66 B[ i ] = evaluate( datasetsRGB[ i ] + 1, 9, turbidity, albedo, sun_theta ); 67 C[ i ] = evaluate( datasetsRGB[ i ] + 2, 9, turbidity, albedo, sun_theta ); 68 D[ i ] = evaluate( datasetsRGB[ i ] + 3, 9, turbidity, albedo, sun_theta ); 69 E[ i ] = evaluate( datasetsRGB[ i ] + 4, 9, turbidity, albedo, sun_theta ); 70 F[ i ] = evaluate( datasetsRGB[ i ] + 5, 9, turbidity, albedo, sun_theta ); 71 G[ i ] = evaluate( datasetsRGB[ i ] + 6, 9, turbidity, albedo, sun_theta ); 72 73 // Note: H and I are swapped in the dataset; we use notation from paper 74 H[ i ] = evaluate( datasetsRGB[ i ] + 8, 9, turbidity, albedo, sun_theta ); 75 I[ i ] = evaluate( datasetsRGB[ i ] + 7, 9, turbidity, albedo, sun_theta ); 76 77 Z[ i ] = evaluate( datasetsRGBRad[ i ], 1, turbidity, albedo, sun_theta ); 78 } 79 80 if( normalized_sun_y != 0 ) { 81 v3 S = perez_ext( cosf( sun_theta ), 0, 1.0f, A, B, C, D, E, F, G, H, I ) * Z; 82 83 Z /= dot( S, v3( 0.2126, 0.7152, 0.0722 ) ); 84 Z *= normalized_sun_y; 85 } 86 87 Hosek hosek; 88 hosek.A = A; 89 hosek.B = B; 90 hosek.C = C; 91 hosek.D = D; 92 hosek.E = E; 93 hosek.F = F; 94 hosek.G = G; 95 hosek.H = H; 96 hosek.I = I; 97 hosek.Z = Z; 98 return hosek; 99 } 100 101 void skybox_init( Skybox * skybox ) { 102 par_shapes_mesh * sphere = par_shapes_create_subdivided_sphere( 3 ); 103 104 MeshConfig mesh_config; 105 mesh_config.positions = renderer_new_vb( sphere->points, sphere->npoints * sizeof( float ) * 3 ); 106 mesh_config.indices = renderer_new_ib( sphere->triangles, sphere->ntriangles * sizeof( PAR_SHAPES_T ) * 3 ); 107 mesh_config.num_vertices = sphere->ntriangles * 3; 108 skybox->mesh = renderer_new_mesh( mesh_config ); 109 110 par_shapes_free_mesh( sphere ); 111 } 112 113 void skybox_render( const Skybox * skybox, u8 pass, const m4 & Vsky, const m4 & P, float sun_angle, v3 sun_dir ) { 114 const float turbidity = 4.0f; 115 const float normalized_sun_y = 1.1f; 116 117 // the model only works in [0, pi / 2] and elevation == 0 when the sun 118 // is directly above 119 float hosek_elevation = fabsf( ( PI / 2.0f ) - sun_angle ); 120 Hosek hosek = compute_hosek( hosek_elevation, turbidity, normalized_sun_y ); 121 122 RenderState render_state; 123 render_state.pass = pass; 124 render_state.shader = get_shader( SHADER_SKYBOX ); 125 render_state.set_uniform( "view", renderer_uniforms( Vsky, P ) ); 126 render_state.set_uniform( "sky", renderer_uniforms( hosek.A, hosek.B, hosek.C, hosek.D, hosek.E, hosek.F, hosek.G, hosek.H, hosek.I, hosek.Z, sun_dir ) ); 127 render_state.set_texture( "blue_noise", renderer_blue_noise() ); 128 render_state.cull_face = CULLFACE_FRONT; 129 130 renderer_draw_mesh( skybox->mesh, render_state ); 131 } 132 133 void skybox_destroy( Skybox * skybox ) { 134 renderer_delete_mesh( skybox->mesh ); 135 }