From 28ad01ab8019216121df101bf64bb5aa8bafa388 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Sat, 18 Apr 2020 17:08:32 +0200 Subject: [PATCH] Added command makeBrdfLut for testing --- neo/renderer/Image.h | 2 + neo/renderer/Image_intrinsic.cpp | 36 +++++- neo/renderer/RenderSystem_init.cpp | 191 +++++++++++++++++++++++++++-- 3 files changed, 217 insertions(+), 12 deletions(-) diff --git a/neo/renderer/Image.h b/neo/renderer/Image.h index 5e694228..6aa29542 100644 --- a/neo/renderer/Image.h +++ b/neo/renderer/Image.h @@ -96,6 +96,7 @@ enum textureFormat_t // RB: don't change above for legacy .bimage compatibility FMT_ETC1_RGB8_OES, // 4 bpp FMT_SHADOW_ARRAY, // 32 bpp * 6 + //FMT_RG16F, // 32 bpp FMT_RGBA16F, // 64 bpp FMT_RGBA32F, // 128 bpp FMT_R32F, // 32 bpp @@ -574,6 +575,7 @@ public: idImage* hierarchicalZbufferImage; // zbuffer with mip maps to accelerate screen space ray tracing idImage* imguiFontImage; + idImage* brdfLutImage; idImage* defaultUACIrradianceCube; idImage* defaultUACRadianceCube; // RB end diff --git a/neo/renderer/Image_intrinsic.cpp b/neo/renderer/Image_intrinsic.cpp index 679122ef..2cd251d5 100644 --- a/neo/renderer/Image_intrinsic.cpp +++ b/neo/renderer/Image_intrinsic.cpp @@ -35,6 +35,7 @@ If you have questions concerning this license or the applicable additional terms #include "RenderCommon.h" #include "SMAA/AreaTex.h" #include "SMAA/SearchTex.h" +#include "Image_brdfLut.h" #define DEFAULT_SIZE 16 @@ -764,8 +765,6 @@ static void R_CreateSMAAAreaImage( idImage* image ) { static byte data[AREATEX_HEIGHT][AREATEX_WIDTH][4]; - idRandom2 random( Sys_Milliseconds() ); - for( int x = 0; x < AREATEX_WIDTH; x++ ) { for( int y = 0; y < AREATEX_HEIGHT; y++ ) @@ -791,8 +790,6 @@ static void R_CreateSMAASearchImage( idImage* image ) { static byte data[SEARCHTEX_HEIGHT][SEARCHTEX_WIDTH][4]; - idRandom2 random( Sys_Milliseconds() ); - for( int x = 0; x < SEARCHTEX_WIDTH; x++ ) { for( int y = 0; y < SEARCHTEX_HEIGHT; y++ ) @@ -840,6 +837,33 @@ static void R_CreateImGuiFontImage( idImage* image ) //io.Fonts->ClearTexData(); } +static void R_CreateBrdfLutImage( idImage* image ) +{ +#if 0 + static byte data[BRDFLUT_TEX_HEIGHT][BRDFLUT_TEX_WIDTH][4]; + + for( int x = 0; x < BRDFLUT_TEX_WIDTH; x++ ) + { + for( int y = 0; y < BRDFLUT_TEX_HEIGHT; y++ ) + { +#if 0 + data[AREATEX_HEIGHT - y][x][0] = areaTexBytes[ y * AREATEX_PITCH + x * 2 + 0 ]; + data[AREATEX_HEIGHT - y][x][1] = areaTexBytes[ y * AREATEX_PITCH + x * 2 + 1 ]; + data[AREATEX_HEIGHT - y][x][2] = 0; + data[AREATEX_HEIGHT - y][x][3] = 1; +#else + data[y][x][0] = brfLutTexBytes[ y * BRDFLUT_TEX_PITCH + x * 2 + 0 ]; + data[y][x][1] = brfLutTexBytes[ y * BRDFLUT_TEX_PITCH + x * 2 + 1 ]; + data[y][x][2] = 0; + data[y][x][3] = 1; +#endif + } + } +#endif + + image->GenerateImage( ( byte* )brfLutTexBytes, BRDFLUT_TEX_WIDTH, BRDFLUT_TEX_HEIGHT, TF_LINEAR, TR_CLAMP, TD_LOOKUP_TABLE_RGBA ); +} + // RB end /* @@ -904,6 +928,8 @@ void idImageManager::CreateIntrinsicImages() hierarchicalZbufferImage = ImageFromFunction( "_cszBuffer", R_HierarchicalZBufferImage_ResNative ); imguiFontImage = ImageFromFunction( "_imguiFont", R_CreateImGuiFontImage ); + + brdfLutImage = globalImages->ImageFromFunction( "_brdfLut", R_CreateBrdfLutImage ); // RB end // scratchImage is used for screen wipes/doublevision etc.. @@ -920,8 +946,10 @@ void idImageManager::CreateIntrinsicImages() loadingIconImage = ImageFromFile( "textures/loadingicon2", TF_DEFAULT, TR_CLAMP, TD_DEFAULT, CF_2D ); hellLoadingIconImage = ImageFromFile( "textures/loadingicon3", TF_DEFAULT, TR_CLAMP, TD_DEFAULT, CF_2D ); + // RB begin defaultUACIrradianceCube = ImageFromFile( "env/testmap_1_amb", TF_DEFAULT, TR_CLAMP, TD_HIGHQUALITY_CUBE, CF_NATIVE ); defaultUACRadianceCube = ImageFromFile( "env/testmap_1_spec", TF_DEFAULT, TR_CLAMP, TD_HIGHQUALITY_CUBE, CF_NATIVE ); + // RB end release_assert( loadingIconImage->referencedOutsideLevelLoad ); release_assert( hellLoadingIconImage->referencedOutsideLevelLoad ); diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 35d38fb5..4886e69c 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -1404,18 +1404,18 @@ inline idVec2 Hammersley2D( uint i, uint N ) return idVec2( float( i ) / float( N ), RadicalInverse_VdC( i ) ); } -idVec3 ImportanceSampleGGX( const idVec2& Xi, float roughness, const idVec3& N ) +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 = sqrt( ( 1 - Xi.y ) / ( 1 + ( a * a - 1 ) * Xi.y ) ); - float sinTheta = sqrt( 1 - cosTheta * cosTheta ); + 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 * cos( Phi ); - H.y = sinTheta * sin( Phi ); + H.x = sinTheta * idMath::Cos( Phi ); + H.y = sinTheta * idMath::Sin( Phi ); H.z = cosTheta; // rotate from tangent space to world space along N @@ -1424,7 +1424,181 @@ idVec3 ImportanceSampleGGX( const idVec2& Xi, float roughness, const idVec3& N ) tangentX.Normalize(); idVec3 tangentY = N.Cross( tangentX ); - return tangentX * H.x + tangentY * H.y + N * H.z; + 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 = 32; + 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 ); + + 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; + + progressBar.Increment(); + } + + //const bool captureToImage = false; + //common->UpdateScreen( captureToImage ); + } + + idStr fullname = "env/_brdfLut.png"; + idLib::Printf( "writing %s\n", fullname.c_str() ); + + //R_WriteTGA( fullname, outBuffer, outSize, outSize, false, "fs_basepath" ); + R_WritePNG( fullname, ldrBuffer, 4, outSize, outSize, true, "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 512 +#define BRDFLUT_TEX_HEIGHT 512 +#define BRDFLUT_TEX_PITCH (BRDFLUT_TEX_WIDTH * 2) +#define BRDFLUT_TEX_SIZE (BRDFLUT_TEX_WIDTH * BRDFLUT_TEX_PITCH) + +/** + * Stored in R8G8 format. Load it in the following format: + * - DX9: D3DFMT_A8L8 + * - DX10: DXGI_FORMAT_R8G8_UNORM + */ +static const unsigned char brfLutTexBytes[] = +{ +)"; + + headerFile->Printf( "%s\n", intro ); + + for( int i = 0; i < ldrBufferSize; i++ ) + { + byte b = ldrBuffer[i]; + + if( i < ( ldrBufferSize - 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 ); } /* @@ -1450,7 +1624,7 @@ void R_MakeAmbientMap_f( const idCmdArgs& args ) if( args.Argc() != 2 && args.Argc() != 3 ) { - common->Printf( "USAGE: ambientshot [size]\n" ); + common->Printf( "USAGE: makeAmbientMap [size]\n" ); return; } baseName = args.Argv( 1 ); @@ -1540,7 +1714,7 @@ void R_MakeAmbientMap_f( const idCmdArgs& args ) for( int s = 0 ; s < samples ; s++ ) { idVec2 Xi = Hammersley2D( s, samples ); - idVec3 test = ImportanceSampleGGX( Xi, roughness, dir ); + idVec3 test = ImportanceSampleGGX( Xi, dir, roughness ); byte result[4]; //test = dir; @@ -1968,6 +2142,7 @@ 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" );