From 624f61b025393cb9414dc69bdbe48bc5107c26c2 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Sun, 17 May 2020 17:37:06 +0200 Subject: [PATCH] First time that multiple environment probes work --- neo/renderer/Framebuffer.h | 3 + neo/renderer/ImageManager.cpp | 2 +- neo/renderer/Image_load.cpp | 1 + neo/renderer/OpenGL/Image_GL.cpp | 3 +- neo/renderer/OpenGL/RenderDebug_GL.cpp | 7 +- neo/renderer/RenderBackend.cpp | 19 +- neo/renderer/RenderSystem_init.cpp | 540 +--------------------- neo/renderer/RenderWorld_defs.cpp | 38 +- neo/renderer/RenderWorld_envprobes.cpp | 604 ++++++++++++++++++++++++- neo/renderer/tr_frontend_main.cpp | 18 + neo/renderer/tr_frontend_subview.cpp | 2 + 11 files changed, 657 insertions(+), 580 deletions(-) diff --git a/neo/renderer/Framebuffer.h b/neo/renderer/Framebuffer.h index e6fd5630..2fdf59fd 100644 --- a/neo/renderer/Framebuffer.h +++ b/neo/renderer/Framebuffer.h @@ -34,6 +34,9 @@ static const int MAX_BLOOM_BUFFERS = 2; static const int MAX_SSAO_BUFFERS = 2; static const int MAX_HIERARCHICAL_ZBUFFERS = 6; // native resolution + 5 MIP LEVELS +static const int RADIANCE_CUBEMAP_SIZE = 256; +static const int IRRADIANCE_CUBEMAP_SIZE = 32; + #if 1 static int shadowMapResolutions[MAX_SHADOWMAP_RESOLUTIONS] = { 2048, 1024, 512, 512, 256 }; #else diff --git a/neo/renderer/ImageManager.cpp b/neo/renderer/ImageManager.cpp index d19348f4..837c6855 100644 --- a/neo/renderer/ImageManager.cpp +++ b/neo/renderer/ImageManager.cpp @@ -445,7 +445,7 @@ idImage* idImageManager::ImageFromFile( const char* _name, textureFilter_t filte image->levelLoadReferenced = true; // load it if we aren't in a level preload - if( !insideLevelLoad || preloadingMapImages ) + if( ( !insideLevelLoad || preloadingMapImages ) && idLib::IsMainThread() ) { image->referencedOutsideLevelLoad = ( !insideLevelLoad && !preloadingMapImages ); image->ActuallyLoadImage( false ); // load is from front end diff --git a/neo/renderer/Image_load.cpp b/neo/renderer/Image_load.cpp index 059440ac..0fe0ed64 100644 --- a/neo/renderer/Image_load.cpp +++ b/neo/renderer/Image_load.cpp @@ -441,6 +441,7 @@ void idImage::ActuallyLoadImage( bool fromBackEnd ) if( !R_LoadCubeImages( GetName(), cubeFiles, pics, &size, &sourceFileTime ) || size == 0 ) { idLib::Warning( "Couldn't load cube image: %s", GetName() ); + defaulted = true; // RB return; } diff --git a/neo/renderer/OpenGL/Image_GL.cpp b/neo/renderer/OpenGL/Image_GL.cpp index 144180b3..9a662c07 100644 --- a/neo/renderer/OpenGL/Image_GL.cpp +++ b/neo/renderer/OpenGL/Image_GL.cpp @@ -94,7 +94,8 @@ void idImage::Bind() RENDERLOG_PRINTF( "idImage::Bind( %s )\n", GetName() ); // load the image if necessary (FIXME: not SMP safe!) - if( !IsLoaded() ) + // RB: don't try again if last time failed + if( !IsLoaded() && !defaulted ) { // load the image on demand here, which isn't our normal game operating mode ActuallyLoadImage( true ); diff --git a/neo/renderer/OpenGL/RenderDebug_GL.cpp b/neo/renderer/OpenGL/RenderDebug_GL.cpp index 99904b49..39fd100b 100644 --- a/neo/renderer/OpenGL/RenderDebug_GL.cpp +++ b/neo/renderer/OpenGL/RenderDebug_GL.cpp @@ -1760,16 +1760,13 @@ void idRenderBackend::DBG_ShowViewEnvprobes() GL_SelectTexture( 0 ); if( r_showViewEnvprobes.GetInteger() >= 2 ) { - globalImages->defaultUACIrradianceCube->Bind(); + vProbe->irradianceImage->Bind(); } else { - globalImages->defaultUACRadianceCube->Bind(); + vProbe->radianceImage->Bind(); } - //GL_SelectTexture( 1 ); - //globalImages->flatNormalMap->Bind(); - DrawElementsWithCounters( &zeroOneCubeSurface ); } } diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index e551c257..f897b3b6 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -1363,11 +1363,26 @@ void idRenderBackend::DrawSingleInteraction( drawInteraction_t* din, bool useFas globalImages->ambientOcclusionImage[0]->Bind(); } + // TODO bind the 3 closest probes GL_SelectTexture( INTERACTION_TEXUNIT_AMBIENT_CUBE1 ); - globalImages->defaultUACIrradianceCube->Bind(); + if( viewDef->irradianceImage ) + { + viewDef->irradianceImage->Bind(); + } + else + { + globalImages->defaultUACIrradianceCube->Bind(); + } GL_SelectTexture( INTERACTION_TEXUNIT_SPECULAR_CUBE1 ); - globalImages->defaultUACRadianceCube->Bind(); + if( viewDef->radianceImage ) + { + viewDef->radianceImage->Bind(); + } + else + { + globalImages->defaultUACRadianceCube->Bind(); + } } else if( setInteractionShader ) { diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 39f44c77..2136481e 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -299,7 +299,7 @@ idCVar r_useHierarchicalDepthBuffer( "r_useHierarchicalDepthBuffer", "1", CVAR_R idCVar r_usePBR( "r_usePBR", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "use PBR and Image Based Lighting instead of old Quake 4 style ambient lighting" ); idCVar r_pbrDebug( "r_pbrDebug", "0", CVAR_RENDERER | CVAR_INTEGER, "show which materials have PBR support (green = PBR, red = oldschool D3)" ); -idCVar r_showViewEnvprobes( "r_showViewEnvprobes", "0", CVAR_RENDERER | CVAR_INTEGER, "1 = displays the bounding boxes of all view environment probes, 2 = show irradiance" ); +idCVar r_showViewEnvprobes( "r_showViewEnvprobes", "1", CVAR_RENDERER | CVAR_INTEGER, "1 = displays the bounding boxes of all view environment probes, 2 = show irradiance" ); idCVar r_exposure( "r_exposure", "0.5", CVAR_ARCHIVE | CVAR_RENDERER | CVAR_FLOAT, "HDR exposure or LDR brightness [0.0 .. 1.0]", 0.0f, 1.0f ); // RB end @@ -1239,543 +1239,7 @@ void R_EnvShot_f( const idCmdArgs& args ) static idMat3 cubeAxis[6]; -/* -================== -R_SampleCubeMap -================== -*/ -void R_SampleCubeMap( const idVec3& dir, int size, byte* buffers[6], byte result[4] ) -{ - float adir[3]; - int axis, x, y; - adir[0] = fabs( dir[0] ); - adir[1] = fabs( dir[1] ); - adir[2] = fabs( dir[2] ); - - if( dir[0] >= adir[1] && dir[0] >= adir[2] ) - { - axis = 0; - } - else if( -dir[0] >= adir[1] && -dir[0] >= adir[2] ) - { - axis = 1; - } - else if( dir[1] >= adir[0] && dir[1] >= adir[2] ) - { - axis = 2; - } - else if( -dir[1] >= adir[0] && -dir[1] >= adir[2] ) - { - axis = 3; - } - else if( dir[2] >= adir[1] && dir[2] >= adir[2] ) - { - axis = 4; - } - else - { - axis = 5; - } - - float fx = ( dir * cubeAxis[axis][1] ) / ( dir * cubeAxis[axis][0] ); - float fy = ( dir * cubeAxis[axis][2] ) / ( dir * cubeAxis[axis][0] ); - - fx = -fx; - fy = -fy; - x = size * 0.5 * ( fx + 1 ); - y = size * 0.5 * ( fy + 1 ); - if( x < 0 ) - { - x = 0; - } - else if( x >= size ) - { - x = size - 1; - } - if( y < 0 ) - { - y = 0; - } - else if( y >= size ) - { - y = size - 1; - } - - result[0] = buffers[axis][( y * size + x ) * 4 + 0]; - result[1] = buffers[axis][( y * size + x ) * 4 + 1]; - result[2] = buffers[axis][( y * size + x ) * 4 + 2]; - result[3] = buffers[axis][( y * size + x ) * 4 + 3]; -} - -class CommandlineProgressBar -{ -private: - size_t tics = 0; - size_t nextTicCount = 0; - int count = 0; - int expectedCount = 0; - -public: - CommandlineProgressBar( int _expectedCount ) - { - expectedCount = _expectedCount; - - common->Printf( "0%% 10 20 30 40 50 60 70 80 90 100%%\n" ); - common->Printf( "|----|----|----|----|----|----|----|----|----|----|\n" ); - - common->UpdateScreen( false ); - } - - void Increment() - { - if( ( count + 1 ) >= nextTicCount ) - { - size_t ticsNeeded = ( size_t )( ( ( double )( count + 1 ) / expectedCount ) * 50.0 ); - - do - { - common->Printf( "*" ); - } - while( ++tics < ticsNeeded ); - - nextTicCount = ( size_t )( ( tics / 50.0 ) * expectedCount ); - if( count == ( expectedCount - 1 ) ) - { - if( tics < 51 ) - { - common->Printf( "*" ); - } - common->Printf( "\n" ); - } - - common->UpdateScreen( false ); - } - - count++; - } -}; - - -// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html - -// To implement the Hammersley point set we only need an efficent way to implement the Van der Corput radical inverse phi2(i). -// Since it is in base 2 we can use some basic bit operations to achieve this. -// The brilliant book Hacker's Delight [warren01] provides us a a simple way to reverse the bits in a given 32bit integer. Using this, the following code then implements phi2(i) - -/* -GLSL version - -float radicalInverse_VdC( uint bits ) -{ - bits = (bits << 16u) | (bits >> 16u); - bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); - bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); - bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); - bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); - return float(bits) * 2.3283064365386963e-10; // / 0x100000000 -} -*/ - -// RB: radical inverse implementation from the Mitsuba PBR system - -// Van der Corput radical inverse in base 2 with single precision -inline float RadicalInverse_VdC( uint32_t n, uint32_t scramble = 0U ) -{ - /* Efficiently reverse the bits in 'n' using binary operations */ -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))) || defined(__clang__) - n = __builtin_bswap32( n ); -#else - n = ( n << 16 ) | ( n >> 16 ); - n = ( ( n & 0x00ff00ff ) << 8 ) | ( ( n & 0xff00ff00 ) >> 8 ); -#endif - n = ( ( n & 0x0f0f0f0f ) << 4 ) | ( ( n & 0xf0f0f0f0 ) >> 4 ); - n = ( ( n & 0x33333333 ) << 2 ) | ( ( n & 0xcccccccc ) >> 2 ); - n = ( ( n & 0x55555555 ) << 1 ) | ( ( n & 0xaaaaaaaa ) >> 1 ); - - // Account for the available precision and scramble - n = ( n >> ( 32 - 24 ) ) ^ ( scramble & ~ -( 1 << 24 ) ); - - return ( float ) n / ( float )( 1U << 24 ); -} - -// The ith point xi is then computed by -inline idVec2 Hammersley2D( uint i, uint N ) -{ - return idVec2( float( i ) / float( N ), RadicalInverse_VdC( i ) ); -} - -idVec3 ImportanceSampleGGX( const idVec2& Xi, const idVec3& N, float roughness ) -{ - float a = roughness * roughness; - - // cosinus distributed direction (Z-up or tangent space) from the hammersley point xi - float Phi = 2 * idMath::PI * Xi.x; - float cosTheta = idMath::Sqrt( ( 1 - Xi.y ) / ( 1 + ( a * a - 1 ) * Xi.y ) ); - float sinTheta = idMath::Sqrt( 1 - cosTheta * cosTheta ); - - idVec3 H; - H.x = sinTheta * idMath::Cos( Phi ); - H.y = sinTheta * idMath::Sin( Phi ); - H.z = cosTheta; - - // rotate from tangent space to world space along N - idVec3 upVector = abs( N.z ) < 0.999f ? idVec3( 0, 0, 1 ) : idVec3( 1, 0, 0 ); - idVec3 tangentX = upVector.Cross( N ); - tangentX.Normalize(); - idVec3 tangentY = N.Cross( tangentX ); - - idVec3 sampleVec = tangentX * H.x + tangentY * H.y + N * H.z; - sampleVec.Normalize(); - - return sampleVec; -} - -float Geometry_SchlickGGX( float NdotV, float roughness ) -{ - // note that we use a different k for IBL - float a = roughness; - float k = ( a * a ) / 2.0; - - float nom = NdotV; - float denom = NdotV * ( 1.0 - k ) + k; - - return nom / denom; -} - -float Geometry_Smith( idVec3 N, idVec3 V, idVec3 L, float roughness ) -{ - float NdotV = Max( ( N * V ), 0.0f ); - float NdotL = Max( ( N * L ), 0.0f ); - - float ggx2 = Geometry_SchlickGGX( NdotV, roughness ); - float ggx1 = Geometry_SchlickGGX( NdotL, roughness ); - - return ggx1 * ggx2; -} - -idVec2 IntegrateBRDF( float NdotV, float roughness, int sampleCount ) -{ - idVec3 V; - V.x = sqrt( 1.0 - NdotV * NdotV ); - V.y = 0.0; - V.z = NdotV; - - float A = 0.0; - float B = 0.0; - - idVec3 N( 0.0f, 0.0f, 1.0f ); - for( int i = 0; i < sampleCount; ++i ) - { - // generates a sample vector that's biased towards the - // preferred alignment direction (importance sampling). - idVec2 Xi = Hammersley2D( i, sampleCount ); - - idVec3 H = ImportanceSampleGGX( Xi, N, roughness ); - idVec3 L = ( 2.0 * ( V * H ) * H - V ); - L.Normalize(); - - float NdotL = Max( L.z, 0.0f ); - float NdotH = Max( H.z, 0.0f ); - float VdotH = Max( ( V * H ), 0.0f ); - - if( NdotL > 0.0 ) - { - float G = Geometry_Smith( N, V, L, roughness ); - float G_Vis = ( G * VdotH ) / ( NdotH * NdotV ); - float Fc = idMath::Pow( 1.0 - VdotH, 5.0 ); - - A += ( 1.0 - Fc ) * G_Vis; - B += Fc * G_Vis; - } - } - - A /= float( sampleCount ); - B /= float( sampleCount ); - - return idVec2( A, B ); -} - -void R_MakeBrdfLut_f( const idCmdArgs& args ) -{ - int outSize = 256; - int width = 0, height = 0; - - //if( args.Argc() != 2 ) - //{ - // common->Printf( "USAGE: makeBrdfLut [size]\n" ); - // return; - //} - - //if( args.Argc() == 2 ) - //{ - // outSize = atoi( args.Argv( 1 ) ); - //} - - bool pacifier = true; - - // resample with hemispherical blending - int samples = 1024; - - int ldrBufferSize = outSize * outSize * 4; - byte* ldrBuffer = ( byte* )Mem_Alloc( ldrBufferSize, TAG_TEMP ); - - int hdrBufferSize = outSize * outSize * 2 * sizeof( halfFloat_t ); - halfFloat_t* hdrBuffer = ( halfFloat_t* )Mem_Alloc( hdrBufferSize, TAG_TEMP ); - - CommandlineProgressBar progressBar( outSize * outSize ); - - int start = Sys_Milliseconds(); - - for( int x = 0 ; x < outSize ; x++ ) - { - float NdotV = ( x + 0.5f ) / outSize; - - for( int y = 0 ; y < outSize ; y++ ) - { - float roughness = ( y + 0.5f ) / outSize; - - idVec2 output = IntegrateBRDF( NdotV, roughness, samples ); - - ldrBuffer[( y * outSize + x ) * 4 + 0] = byte( output.x * 255 ); - ldrBuffer[( y * outSize + x ) * 4 + 1] = byte( output.y * 255 ); - ldrBuffer[( y * outSize + x ) * 4 + 2] = 0; - ldrBuffer[( y * outSize + x ) * 4 + 3] = 255; - - halfFloat_t half1 = F32toF16( output.x ); - halfFloat_t half2 = F32toF16( output.y ); - - hdrBuffer[( y * outSize + x ) * 2 + 0] = half1; - hdrBuffer[( y * outSize + x ) * 2 + 1] = half2; - //hdrBuffer[( y * outSize + x ) * 4 + 2] = 0; - //hdrBuffer[( y * outSize + x ) * 4 + 3] = 1; - - progressBar.Increment(); - } - } - - idStr fullname = "env/_brdfLut.png"; - idLib::Printf( "writing %s\n", fullname.c_str() ); - - R_WritePNG( fullname, ldrBuffer, 4, outSize, outSize, true, "fs_basepath" ); - //R_WriteEXR( "env/_brdfLut.exr", hdrBuffer, 4, outSize, outSize, "fs_basepath" ); - - - idFileLocal headerFile( fileSystem->OpenFileWrite( "env/Image_brdfLut.h", "fs_basepath" ) ); - - static const char* intro = R"( -#ifndef BRDFLUT_TEX_H -#define BRDFLUT_TEX_H - -#define BRDFLUT_TEX_WIDTH 256 -#define BRDFLUT_TEX_HEIGHT 256 -#define BRDFLUT_TEX_PITCH (BRDFLUT_TEX_WIDTH * 2) -#define BRDFLUT_TEX_SIZE (BRDFLUT_TEX_WIDTH * BRDFLUT_TEX_PITCH) - -// Stored in R16G16F format -static const unsigned char brfLutTexBytes[] = -{ -)"; - - headerFile->Printf( "%s\n", intro ); - - const byte* hdrBytes = (const byte* ) hdrBuffer; - for( int i = 0; i < hdrBufferSize; i++ ) - { - byte b = hdrBytes[i]; - - if( i < ( hdrBufferSize - 1 ) ) - { - headerFile->Printf( "0x%02hhx, ", b ); - } - else - { - headerFile->Printf( "0x%02hhx", b ); - } - - if( i % 12 == 0 ) - { - headerFile->Printf( "\n" ); - } - } - headerFile->Printf( "\n};\n#endif\n" ); - - int end = Sys_Milliseconds(); - - common->Printf( "%s integrated in %5.1f seconds\n\n", fullname.c_str(), ( end - start ) * 0.001f ); - - Mem_Free( ldrBuffer ); - Mem_Free( hdrBuffer ); -} - -/* -================== -R_MakeAmbientMap_f - -R_MakeAmbientMap_f [size] - -Saves out env/_amb_ft.tga, etc -================== -*/ -void R_MakeAmbientMap_f( const idCmdArgs& args ) -{ - idStr fullname; - const char* baseName; - int i; - renderView_t ref; - viewDef_t primary; - int downSample; - int outSize; - byte* buffers[6]; - int width = 0, height = 0; - - if( args.Argc() != 2 && args.Argc() != 3 ) - { - common->Printf( "USAGE: makeAmbientMap [size]\n" ); - return; - } - baseName = args.Argv( 1 ); - - downSample = 0; - if( args.Argc() == 3 ) - { - outSize = atoi( args.Argv( 2 ) ); - } - else - { - outSize = 32; - } - - memset( &cubeAxis, 0, sizeof( cubeAxis ) ); - cubeAxis[0][0][0] = 1; - cubeAxis[0][1][2] = 1; - cubeAxis[0][2][1] = 1; - - cubeAxis[1][0][0] = -1; - cubeAxis[1][1][2] = -1; - cubeAxis[1][2][1] = 1; - - cubeAxis[2][0][1] = 1; - cubeAxis[2][1][0] = -1; - cubeAxis[2][2][2] = -1; - - cubeAxis[3][0][1] = -1; - cubeAxis[3][1][0] = -1; - cubeAxis[3][2][2] = 1; - - cubeAxis[4][0][2] = 1; - cubeAxis[4][1][0] = -1; - cubeAxis[4][2][1] = 1; - - cubeAxis[5][0][2] = -1; - cubeAxis[5][1][0] = 1; - cubeAxis[5][2][1] = 1; - - // read all of the images - for( i = 0 ; i < 6 ; i++ ) - { - fullname.Format( "env/%s%s.%s", baseName, envDirection[i], fileExten[TGA] ); - common->Printf( "loading %s\n", fullname.c_str() ); - const bool captureToImage = false; - common->UpdateScreen( captureToImage ); - R_LoadImage( fullname, &buffers[i], &width, &height, NULL, true, NULL ); - if( !buffers[i] ) - { - common->Printf( "failed.\n" ); - for( i-- ; i >= 0 ; i-- ) - { - Mem_Free( buffers[i] ); - } - return; - } - } - - bool pacifier = true; - - // resample with hemispherical blending - int samples = 1000; - - byte* outBuffer = ( byte* )_alloca( outSize * outSize * 4 ); - - for( int map = 0 ; map < 2 ; map++ ) - { - CommandlineProgressBar progressBar( outSize * outSize * 6 ); - - int start = Sys_Milliseconds(); - - for( i = 0 ; i < 6 ; i++ ) - { - for( int x = 0 ; x < outSize ; x++ ) - { - for( int y = 0 ; y < outSize ; y++ ) - { - idVec3 dir; - float total[3]; - - dir = cubeAxis[i][0] + -( -1 + 2.0 * x / ( outSize - 1 ) ) * cubeAxis[i][1] + -( -1 + 2.0 * y / ( outSize - 1 ) ) * cubeAxis[i][2]; - dir.Normalize(); - total[0] = total[1] = total[2] = 0; - - float roughness = map ? 0.1 : 0.95; // small for specular, almost hemisphere for ambient - - for( int s = 0 ; s < samples ; s++ ) - { - idVec2 Xi = Hammersley2D( s, samples ); - idVec3 test = ImportanceSampleGGX( Xi, dir, roughness ); - - byte result[4]; - //test = dir; - R_SampleCubeMap( test, width, buffers, result ); - total[0] += result[0]; - total[1] += result[1]; - total[2] += result[2]; - } - outBuffer[( y * outSize + x ) * 4 + 0] = total[0] / samples; - outBuffer[( y * outSize + x ) * 4 + 1] = total[1] / samples; - outBuffer[( y * outSize + x ) * 4 + 2] = total[2] / samples; - outBuffer[( y * outSize + x ) * 4 + 3] = 255; - - progressBar.Increment(); - } - } - - if( map == 0 ) - { - fullname.Format( "env/%s_amb%s.%s", baseName, envDirection[i], fileExten[PNG] ); - } - else - { - fullname.Format( "env/%s_spec%s.%s", baseName, envDirection[i], fileExten[PNG] ); - } - //common->Printf( "writing %s\n", fullname.c_str() ); - - const bool captureToImage = false; - common->UpdateScreen( captureToImage ); - - //R_WriteTGA( fullname, outBuffer, outSize, outSize, false, "fs_basepath" ); - R_WritePNG( fullname, outBuffer, 4, outSize, outSize, true, "fs_basepath" ); - } - - int end = Sys_Milliseconds(); - - if( map == 0 ) - { - common->Printf( "env/%s_amb convolved in %5.1f seconds\n\n", baseName, ( end - start ) * 0.001f ); - } - else - { - common->Printf( "env/%s_spec convolved in %5.1f seconds\n\n", baseName, ( end - start ) * 0.001f ); - } - } - - for( i = 0 ; i < 6 ; i++ ) - { - if( buffers[i] ) - { - Mem_Free( buffers[i] ); - } - } -} void R_TransformCubemap( const char* orgDirection[6], const char* orgDir, const char* destDirection[6], const char* destDir, const char* baseName ) { @@ -2151,8 +1615,6 @@ void R_InitCommands() cmdSystem->AddCommand( "touchGui", R_TouchGui_f, CMD_FL_RENDERER, "touches a gui" ); cmdSystem->AddCommand( "screenshot", R_ScreenShot_f, CMD_FL_RENDERER, "takes a screenshot" ); cmdSystem->AddCommand( "envshot", R_EnvShot_f, CMD_FL_RENDERER, "takes an environment shot" ); - cmdSystem->AddCommand( "makeBrfdLut", R_MakeBrdfLut_f, CMD_FL_RENDERER | CMD_FL_CHEAT, "make a GGX BRDF lookup table" ); // RB - cmdSystem->AddCommand( "makeAmbientMap", R_MakeAmbientMap_f, CMD_FL_RENDERER | CMD_FL_CHEAT, "makes an ambient map" ); cmdSystem->AddCommand( "envToSky", R_TransformEnvToSkybox_f, CMD_FL_RENDERER | CMD_FL_CHEAT, "transforms environment textures to sky box textures" ); cmdSystem->AddCommand( "skyToEnv", R_TransformSkyboxToEnv_f, CMD_FL_RENDERER | CMD_FL_CHEAT, "transforms sky box textures to environment textures" ); cmdSystem->AddCommand( "gfxInfo", GfxInfo_f, CMD_FL_RENDERER, "show graphics info" ); diff --git a/neo/renderer/RenderWorld_defs.cpp b/neo/renderer/RenderWorld_defs.cpp index fea0f2bf..b7fe2bbb 100644 --- a/neo/renderer/RenderWorld_defs.cpp +++ b/neo/renderer/RenderWorld_defs.cpp @@ -760,39 +760,19 @@ ENVPROBE DEFS void R_DeriveEnvprobeData( RenderEnvprobeLocal* probe ) { - // TODO get images + idStr basename = tr.primaryWorld->mapName; + basename.StripFileExtension(); + idStr fullname; - /* - light->falloffImage = light->lightShader->LightFalloffImage(); + int probeIndex = tr.primaryWorld->envprobeDefs.Num() - 1; - if( light->falloffImage == NULL ) - { - // use the falloff from the default shader of the correct type - const idMaterial* defaultShader; + // TODO get preconvolved cubemaps + fullname.Format( "env/%s/envprobe%i_amb", basename.c_str(), probeIndex ); + probe->irradianceImage = globalImages->ImageFromFile( fullname, TF_DEFAULT, TR_CLAMP, TD_HIGHQUALITY_CUBE, CF_NATIVE ); - if( light->parms.pointLight ) - { - defaultShader = tr.defaultPointLight; - - // Touch the default shader. to make sure it's decl has been parsed ( it might have been purged ). - declManager->Touch( static_cast< const idDecl*>( defaultShader ) ); - - light->falloffImage = defaultShader->LightFalloffImage(); - - } - else - { - // projected lights by default don't diminish with distance - defaultShader = tr.defaultProjectedLight; - - // Touch the light shader. to make sure it's decl has been parsed ( it might have been purged ). - declManager->Touch( static_cast< const idDecl*>( defaultShader ) ); - - light->falloffImage = defaultShader->LightFalloffImage(); - } - } - */ + fullname.Format( "env/%s/envprobe%i_spec", basename.c_str(), probeIndex ); + probe->radianceImage = globalImages->ImageFromFile( fullname, TF_DEFAULT, TR_CLAMP, TD_HIGHQUALITY_CUBE, CF_NATIVE ); // ------------------------------------ // compute the light projection matrix diff --git a/neo/renderer/RenderWorld_envprobes.cpp b/neo/renderer/RenderWorld_envprobes.cpp index 5b0ac745..ad4c4c1c 100644 --- a/neo/renderer/RenderWorld_envprobes.cpp +++ b/neo/renderer/RenderWorld_envprobes.cpp @@ -62,6 +62,24 @@ viewEnvprobe_t* R_SetEnvprobeDefViewEnvprobe( RenderEnvprobeLocal* probe ) vProbe->globalOrigin = probe->parms.origin; vProbe->inverseBaseLightProject = probe->inverseBaseLightProject; + //if( probe->irradianceImage->IsLoaded() ) + { + vProbe->irradianceImage = probe->irradianceImage; + } + //else + //{ + // vProbe->irradianceImage = globalImages->defaultUACIrradianceCube; + //} + + //if( probe->radianceImage->IsLoaded() ) + { + vProbe->radianceImage = probe->radianceImage; + } + //else + //{ + // vProbe->radianceImage = globalImages->defaultUACRadianceCube; + //} + // link the view light vProbe->next = tr.viewDef->viewEnvprobes; tr.viewDef->viewEnvprobes = vProbe; @@ -203,7 +221,566 @@ void idRenderWorldLocal::AddAreaViewEnvprobes( int areaNum, const portalStack_t* } } -CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", idCmdSystem::ArgCompletion_MapName ) +/* +================== +R_SampleCubeMap +================== +*/ +static idMat3 cubeAxis[6]; +static const char* envDirection[6] = { "_px", "_nx", "_py", "_ny", "_pz", "_nz" }; + +void R_SampleCubeMap( const idVec3& dir, int size, byte* buffers[6], byte result[4] ) +{ + float adir[3]; + int axis, x, y; + + adir[0] = fabs( dir[0] ); + adir[1] = fabs( dir[1] ); + adir[2] = fabs( dir[2] ); + + if( dir[0] >= adir[1] && dir[0] >= adir[2] ) + { + axis = 0; + } + else if( -dir[0] >= adir[1] && -dir[0] >= adir[2] ) + { + axis = 1; + } + else if( dir[1] >= adir[0] && dir[1] >= adir[2] ) + { + axis = 2; + } + else if( -dir[1] >= adir[0] && -dir[1] >= adir[2] ) + { + axis = 3; + } + else if( dir[2] >= adir[1] && dir[2] >= adir[2] ) + { + axis = 4; + } + else + { + axis = 5; + } + + float fx = ( dir * cubeAxis[axis][1] ) / ( dir * cubeAxis[axis][0] ); + float fy = ( dir * cubeAxis[axis][2] ) / ( dir * cubeAxis[axis][0] ); + + fx = -fx; + fy = -fy; + x = size * 0.5 * ( fx + 1 ); + y = size * 0.5 * ( fy + 1 ); + if( x < 0 ) + { + x = 0; + } + else if( x >= size ) + { + x = size - 1; + } + if( y < 0 ) + { + y = 0; + } + else if( y >= size ) + { + y = size - 1; + } + + result[0] = buffers[axis][( y * size + x ) * 4 + 0]; + result[1] = buffers[axis][( y * size + x ) * 4 + 1]; + result[2] = buffers[axis][( y * size + x ) * 4 + 2]; + result[3] = buffers[axis][( y * size + x ) * 4 + 3]; +} + +class CommandlineProgressBar +{ +private: + size_t tics = 0; + size_t nextTicCount = 0; + int count = 0; + int expectedCount = 0; + +public: + CommandlineProgressBar( int _expectedCount ) + { + expectedCount = _expectedCount; + + common->Printf( "0%% 10 20 30 40 50 60 70 80 90 100%%\n" ); + common->Printf( "|----|----|----|----|----|----|----|----|----|----|\n" ); + + common->UpdateScreen( false ); + } + + void Increment() + { + if( ( count + 1 ) >= nextTicCount ) + { + size_t ticsNeeded = ( size_t )( ( ( double )( count + 1 ) / expectedCount ) * 50.0 ); + + do + { + common->Printf( "*" ); + } + while( ++tics < ticsNeeded ); + + nextTicCount = ( size_t )( ( tics / 50.0 ) * expectedCount ); + if( count == ( expectedCount - 1 ) ) + { + if( tics < 51 ) + { + common->Printf( "*" ); + } + common->Printf( "\n" ); + } + + common->UpdateScreen( false ); + } + + count++; + } +}; + + +// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html + +// To implement the Hammersley point set we only need an efficent way to implement the Van der Corput radical inverse phi2(i). +// Since it is in base 2 we can use some basic bit operations to achieve this. +// The brilliant book Hacker's Delight [warren01] provides us a a simple way to reverse the bits in a given 32bit integer. Using this, the following code then implements phi2(i) + +/* +GLSL version + +float radicalInverse_VdC( uint bits ) +{ + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 +} +*/ + +// RB: radical inverse implementation from the Mitsuba PBR system + +// Van der Corput radical inverse in base 2 with single precision +inline float RadicalInverse_VdC( uint32_t n, uint32_t scramble = 0U ) +{ + /* Efficiently reverse the bits in 'n' using binary operations */ +#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))) || defined(__clang__) + n = __builtin_bswap32( n ); +#else + n = ( n << 16 ) | ( n >> 16 ); + n = ( ( n & 0x00ff00ff ) << 8 ) | ( ( n & 0xff00ff00 ) >> 8 ); +#endif + n = ( ( n & 0x0f0f0f0f ) << 4 ) | ( ( n & 0xf0f0f0f0 ) >> 4 ); + n = ( ( n & 0x33333333 ) << 2 ) | ( ( n & 0xcccccccc ) >> 2 ); + n = ( ( n & 0x55555555 ) << 1 ) | ( ( n & 0xaaaaaaaa ) >> 1 ); + + // Account for the available precision and scramble + n = ( n >> ( 32 - 24 ) ) ^ ( scramble & ~ -( 1 << 24 ) ); + + return ( float ) n / ( float )( 1U << 24 ); +} + +// The ith point xi is then computed by +inline idVec2 Hammersley2D( uint i, uint N ) +{ + return idVec2( float( i ) / float( N ), RadicalInverse_VdC( i ) ); +} + +idVec3 ImportanceSampleGGX( const idVec2& Xi, const idVec3& N, float roughness ) +{ + float a = roughness * roughness; + + // cosinus distributed direction (Z-up or tangent space) from the hammersley point xi + float Phi = 2 * idMath::PI * Xi.x; + float cosTheta = idMath::Sqrt( ( 1 - Xi.y ) / ( 1 + ( a * a - 1 ) * Xi.y ) ); + float sinTheta = idMath::Sqrt( 1 - cosTheta * cosTheta ); + + idVec3 H; + H.x = sinTheta * idMath::Cos( Phi ); + H.y = sinTheta * idMath::Sin( Phi ); + H.z = cosTheta; + + // rotate from tangent space to world space along N + idVec3 upVector = abs( N.z ) < 0.999f ? idVec3( 0, 0, 1 ) : idVec3( 1, 0, 0 ); + idVec3 tangentX = upVector.Cross( N ); + tangentX.Normalize(); + idVec3 tangentY = N.Cross( tangentX ); + + idVec3 sampleVec = tangentX * H.x + tangentY * H.y + N * H.z; + sampleVec.Normalize(); + + return sampleVec; +} + +float Geometry_SchlickGGX( float NdotV, float roughness ) +{ + // note that we use a different k for IBL + float a = roughness; + float k = ( a * a ) / 2.0; + + float nom = NdotV; + float denom = NdotV * ( 1.0 - k ) + k; + + return nom / denom; +} + +float Geometry_Smith( idVec3 N, idVec3 V, idVec3 L, float roughness ) +{ + float NdotV = Max( ( N * V ), 0.0f ); + float NdotL = Max( ( N * L ), 0.0f ); + + float ggx2 = Geometry_SchlickGGX( NdotV, roughness ); + float ggx1 = Geometry_SchlickGGX( NdotL, roughness ); + + return ggx1 * ggx2; +} + +idVec2 IntegrateBRDF( float NdotV, float roughness, int sampleCount ) +{ + idVec3 V; + V.x = sqrt( 1.0 - NdotV * NdotV ); + V.y = 0.0; + V.z = NdotV; + + float A = 0.0; + float B = 0.0; + + idVec3 N( 0.0f, 0.0f, 1.0f ); + for( int i = 0; i < sampleCount; ++i ) + { + // generates a sample vector that's biased towards the + // preferred alignment direction (importance sampling). + idVec2 Xi = Hammersley2D( i, sampleCount ); + + idVec3 H = ImportanceSampleGGX( Xi, N, roughness ); + idVec3 L = ( 2.0 * ( V * H ) * H - V ); + L.Normalize(); + + float NdotL = Max( L.z, 0.0f ); + float NdotH = Max( H.z, 0.0f ); + float VdotH = Max( ( V * H ), 0.0f ); + + if( NdotL > 0.0 ) + { + float G = Geometry_Smith( N, V, L, roughness ); + float G_Vis = ( G * VdotH ) / ( NdotH * NdotV ); + float Fc = idMath::Pow( 1.0 - VdotH, 5.0 ); + + A += ( 1.0 - Fc ) * G_Vis; + B += Fc * G_Vis; + } + } + + A /= float( sampleCount ); + B /= float( sampleCount ); + + return idVec2( A, B ); +} + +//void R_MakeBrdfLut_f( const idCmdArgs& args ) +CONSOLE_COMMAND( makeBrdfLUT, "make a GGX BRDF lookup table", NULL ) +{ + int outSize = 256; + int width = 0, height = 0; + + //if( args.Argc() != 2 ) + //{ + // common->Printf( "USAGE: makeBrdfLut [size]\n" ); + // return; + //} + + //if( args.Argc() == 2 ) + //{ + // outSize = atoi( args.Argv( 1 ) ); + //} + + bool pacifier = true; + + // resample with hemispherical blending + int samples = 1024; + + int ldrBufferSize = outSize * outSize * 4; + byte* ldrBuffer = ( byte* )Mem_Alloc( ldrBufferSize, TAG_TEMP ); + + int hdrBufferSize = outSize * outSize * 2 * sizeof( halfFloat_t ); + halfFloat_t* hdrBuffer = ( halfFloat_t* )Mem_Alloc( hdrBufferSize, TAG_TEMP ); + + CommandlineProgressBar progressBar( outSize * outSize ); + + int start = Sys_Milliseconds(); + + for( int x = 0 ; x < outSize ; x++ ) + { + float NdotV = ( x + 0.5f ) / outSize; + + for( int y = 0 ; y < outSize ; y++ ) + { + float roughness = ( y + 0.5f ) / outSize; + + idVec2 output = IntegrateBRDF( NdotV, roughness, samples ); + + ldrBuffer[( y * outSize + x ) * 4 + 0] = byte( output.x * 255 ); + ldrBuffer[( y * outSize + x ) * 4 + 1] = byte( output.y * 255 ); + ldrBuffer[( y * outSize + x ) * 4 + 2] = 0; + ldrBuffer[( y * outSize + x ) * 4 + 3] = 255; + + halfFloat_t half1 = F32toF16( output.x ); + halfFloat_t half2 = F32toF16( output.y ); + + hdrBuffer[( y * outSize + x ) * 2 + 0] = half1; + hdrBuffer[( y * outSize + x ) * 2 + 1] = half2; + //hdrBuffer[( y * outSize + x ) * 4 + 2] = 0; + //hdrBuffer[( y * outSize + x ) * 4 + 3] = 1; + + progressBar.Increment(); + } + } + + idStr fullname = "env/_brdfLut.png"; + idLib::Printf( "writing %s\n", fullname.c_str() ); + + R_WritePNG( fullname, ldrBuffer, 4, outSize, outSize, true, "fs_basepath" ); + //R_WriteEXR( "env/_brdfLut.exr", hdrBuffer, 4, outSize, outSize, "fs_basepath" ); + + + idFileLocal headerFile( fileSystem->OpenFileWrite( "env/Image_brdfLut.h", "fs_basepath" ) ); + + static const char* intro = R"( +#ifndef BRDFLUT_TEX_H +#define BRDFLUT_TEX_H + +#define BRDFLUT_TEX_WIDTH 256 +#define BRDFLUT_TEX_HEIGHT 256 +#define BRDFLUT_TEX_PITCH (BRDFLUT_TEX_WIDTH * 2) +#define BRDFLUT_TEX_SIZE (BRDFLUT_TEX_WIDTH * BRDFLUT_TEX_PITCH) + +// Stored in R16G16F format +static const unsigned char brfLutTexBytes[] = +{ +)"; + + headerFile->Printf( "%s\n", intro ); + + const byte* hdrBytes = (const byte* ) hdrBuffer; + for( int i = 0; i < hdrBufferSize; i++ ) + { + byte b = hdrBytes[i]; + + if( i < ( hdrBufferSize - 1 ) ) + { + headerFile->Printf( "0x%02hhx, ", b ); + } + else + { + headerFile->Printf( "0x%02hhx", b ); + } + + if( i % 12 == 0 ) + { + headerFile->Printf( "\n" ); + } + } + headerFile->Printf( "\n};\n#endif\n" ); + + int end = Sys_Milliseconds(); + + common->Printf( "%s integrated in %5.1f seconds\n\n", fullname.c_str(), ( end - start ) * 0.001f ); + + Mem_Free( ldrBuffer ); + Mem_Free( hdrBuffer ); +} + +/* +================== +R_MakeAmbientMap_f + +R_MakeAmbientMap_f [size] + +Saves out env/_amb_ft.tga, etc +================== +*/ +void R_MakeAmbientMap( const char* baseName, const char* suffix, int outSize, float roughness ) +{ + idStr fullname; + renderView_t ref; + viewDef_t primary; + byte* buffers[6]; + int width = 0, height = 0; + + memset( &cubeAxis, 0, sizeof( cubeAxis ) ); + cubeAxis[0][0][0] = 1; + cubeAxis[0][1][2] = 1; + cubeAxis[0][2][1] = 1; + + cubeAxis[1][0][0] = -1; + cubeAxis[1][1][2] = -1; + cubeAxis[1][2][1] = 1; + + cubeAxis[2][0][1] = 1; + cubeAxis[2][1][0] = -1; + cubeAxis[2][2][2] = -1; + + cubeAxis[3][0][1] = -1; + cubeAxis[3][1][0] = -1; + cubeAxis[3][2][2] = 1; + + cubeAxis[4][0][2] = 1; + cubeAxis[4][1][0] = -1; + cubeAxis[4][2][1] = 1; + + cubeAxis[5][0][2] = -1; + cubeAxis[5][1][0] = 1; + cubeAxis[5][2][1] = 1; + + // read all of the images + for( int i = 0 ; i < 6 ; i++ ) + { + fullname.Format( "env/%s%s.png", baseName, envDirection[i] ); + common->Printf( "loading %s\n", fullname.c_str() ); + const bool captureToImage = false; + common->UpdateScreen( captureToImage ); + R_LoadImage( fullname, &buffers[i], &width, &height, NULL, true, NULL ); + if( !buffers[i] ) + { + common->Printf( "failed.\n" ); + for( i-- ; i >= 0 ; i-- ) + { + Mem_Free( buffers[i] ); + } + return; + } + } + + bool pacifier = true; + + // resample with hemispherical blending + int samples = 1000; + + byte* outBuffer = ( byte* )_alloca( outSize * outSize * 4 ); + + //for( int map = 0 ; map < 2 ; map++ ) + { + CommandlineProgressBar progressBar( outSize * outSize * 6 ); + + int start = Sys_Milliseconds(); + + for( int i = 0 ; i < 6 ; i++ ) + { + for( int x = 0 ; x < outSize ; x++ ) + { + for( int y = 0 ; y < outSize ; y++ ) + { + idVec3 dir; + float total[3]; + + dir = cubeAxis[i][0] + -( -1 + 2.0 * x / ( outSize - 1 ) ) * cubeAxis[i][1] + -( -1 + 2.0 * y / ( outSize - 1 ) ) * cubeAxis[i][2]; + dir.Normalize(); + total[0] = total[1] = total[2] = 0; + + //float roughness = map ? 0.1 : 0.95; // small for specular, almost hemisphere for ambient + + for( int s = 0 ; s < samples ; s++ ) + { + idVec2 Xi = Hammersley2D( s, samples ); + idVec3 test = ImportanceSampleGGX( Xi, dir, roughness ); + + byte result[4]; + //test = dir; + R_SampleCubeMap( test, width, buffers, result ); + total[0] += result[0]; + total[1] += result[1]; + total[2] += result[2]; + } + outBuffer[( y * outSize + x ) * 4 + 0] = total[0] / samples; + outBuffer[( y * outSize + x ) * 4 + 1] = total[1] / samples; + outBuffer[( y * outSize + x ) * 4 + 2] = total[2] / samples; + outBuffer[( y * outSize + x ) * 4 + 3] = 255; + + progressBar.Increment(); + } + } + + + fullname.Format( "env/%s%s%s.png", baseName, suffix, envDirection[i] ); + //common->Printf( "writing %s\n", fullname.c_str() ); + + const bool captureToImage = false; + common->UpdateScreen( captureToImage ); + + //R_WriteTGA( fullname, outBuffer, outSize, outSize, false, "fs_basepath" ); + R_WritePNG( fullname, outBuffer, 4, outSize, outSize, true, "fs_basepath" ); + } + + int end = Sys_Milliseconds(); + + common->Printf( "env/%s convolved in %5.1f seconds\n\n", baseName, ( end - start ) * 0.001f ); + } + + for( int i = 0 ; i < 6 ; i++ ) + { + if( buffers[i] ) + { + Mem_Free( buffers[i] ); + } + } +} + +/* +================== +R_MakeAmbientMap_f + +R_MakeAmbientMap_f [size] + +Saves out env/_amb_ft.tga, etc +================== +*/ +//void R_MakeAmbientMap_f( const idCmdArgs& args ) +CONSOLE_COMMAND( makeAmbientMap, "Saves out env/_amb_ft.tga, etc", NULL ) +{ + const char* baseName; + int outSize; + float roughness; + + if( args.Argc() != 2 && args.Argc() != 3 && args.Argc() != 4 ) + { + common->Printf( "USAGE: makeAmbientMap [size]\n" ); + return; + } + baseName = args.Argv( 1 ); + + if( args.Argc() == 3 ) + { + outSize = atoi( args.Argv( 2 ) ); + } + else + { + outSize = 32; + } + + if( args.Argc() == 4 ) + { + roughness = atof( args.Argv( 3 ) ); + } + else + { + roughness = 0.95; + } + + if( roughness > 0.8f ) + { + R_MakeAmbientMap( baseName, "_amb", outSize, roughness ); + } + else + { + R_MakeAmbientMap( baseName, "_spec", outSize, roughness ); + } +} + +CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", NULL ) { idStr fullname; idStr baseName; @@ -224,7 +801,7 @@ CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", idCmd baseName = tr.primaryWorld->mapName; baseName.StripFileExtension(); - size = 256; + size = RADIANCE_CUBEMAP_SIZE; blends = 1; if( !tr.primaryView ) @@ -267,6 +844,10 @@ CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", idCmd axis[5][1][0] = 1; axis[5][2][1] = 1; + //-------------------------------------------- + // CAPTURE SCENE LIGHTING + //-------------------------------------------- + // let's get the game window to a "size" resolution if( ( res_w != size ) || ( res_h != size ) ) { @@ -299,7 +880,7 @@ CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", idCmd ref.vieworg = def->parms.origin; ref.viewaxis = axis[j]; - fullname.Format( "env/%s_envprobe%i%s", baseName.c_str(), i, extension ); + fullname.Format( "env/%s/envprobe%i%s", baseName.c_str(), i, extension ); tr.TakeScreenshot( size, size, fullname, blends, &ref, PNG ); //tr.CaptureRenderToFile( fullname, false ); @@ -316,5 +897,22 @@ CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", idCmd R_SetNewMode( false ); // the same as "vid_restart" common->Printf( "Wrote a env set with the name %s\n", baseName ); + + //-------------------------------------------- + // CONVOLVE CUBEMAPS + //-------------------------------------------- + for( int i = 0; i < tr.primaryWorld->envprobeDefs.Num(); i++ ) + { + RenderEnvprobeLocal* def = tr.primaryWorld->envprobeDefs[i]; + if( def == NULL ) + { + continue; + } + + fullname.Format( "%s/envprobe%i", baseName.c_str(), i ); + + R_MakeAmbientMap( fullname.c_str(), "_amb", IRRADIANCE_CUBEMAP_SIZE, 0.95f ); + R_MakeAmbientMap( fullname.c_str(), "_spec", RADIANCE_CUBEMAP_SIZE, 0.1f ); + } } diff --git a/neo/renderer/tr_frontend_main.cpp b/neo/renderer/tr_frontend_main.cpp index 1d5fc3d7..54e104fa 100644 --- a/neo/renderer/tr_frontend_main.cpp +++ b/neo/renderer/tr_frontend_main.cpp @@ -539,6 +539,24 @@ void R_RenderView( viewDef_t* parms ) } } + // RB: find closest environment probe + if( tr.viewDef->areaNum != -1 && !tr.viewDef->isSubview ) + { + float bestDist = 90000.0f; + + for( viewEnvprobe_t* vProbe = tr.viewDef->viewEnvprobes; vProbe != NULL; vProbe = vProbe->next ) + { + float dist = ( tr.viewDef->renderView.vieworg - vProbe->globalOrigin ).LengthSqr(); + if( dist < bestDist ) + { + tr.viewDef->irradianceImage = vProbe->irradianceImage; + tr.viewDef->radianceImage = vProbe->radianceImage; + + bestDist = dist; + } + } + } + // write everything needed to the demo file if( common->WriteDemo() ) { diff --git a/neo/renderer/tr_frontend_subview.cpp b/neo/renderer/tr_frontend_subview.cpp index 6401920e..22e9e8f6 100644 --- a/neo/renderer/tr_frontend_subview.cpp +++ b/neo/renderer/tr_frontend_subview.cpp @@ -640,6 +640,7 @@ bool R_GenerateSubViews( const drawSurf_t* const drawSurfs[], const int numDrawS } // RB: generate subviews for environment probes that need an update +#if 0 if( tr.viewDef->areaNum != -1 ) { // go through each visible probe @@ -657,6 +658,7 @@ bool R_GenerateSubViews( const drawSurf_t* const drawSurfs[], const int numDrawS } } } +#endif // RB end return subviews;