medfall

A super great game engine
Log | Files | Refs

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 }