mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-03-14 06:34:10 +00:00
Implemented GGX Split Sum approximation using a 2D atlas trick
This commit is contained in:
parent
779534626f
commit
2391ba1b43
14 changed files with 845 additions and 372 deletions
|
@ -1,11 +1,11 @@
|
|||
astyle.exe -v --formatted --options=astyle-options.ini --exclude="libs" --exclude="extern" --recursive *.h
|
||||
astyle.exe -v --formatted --options=astyle-options.ini --exclude="libs" --exclude="extern" --exclude="d3xp/gamesys/SysCvar.cpp" --exclude="d3xp/gamesys/Callbacks.cpp" --exclude="sys/win32/win_cpu.cpp" --exclude="sys/win32/win_main.cpp" --recursive *.cpp
|
||||
|
||||
REM astyle.exe -v --formatted --options=astyle-options.ini --recursive libs/imgui/*.h
|
||||
REM astyle.exe -v --formatted --options=astyle-options.ini --recursive libs/imgui/*.cpp
|
||||
astyle.exe -v --formatted --options=astyle-options.ini --recursive libs/imgui/*.h
|
||||
astyle.exe -v --formatted --options=astyle-options.ini --recursive libs/imgui/*.cpp
|
||||
|
||||
REM astyle.exe -v --formatted --options=astyle-options.ini --recursive libs/stb/*.h
|
||||
REM astyle.exe -v --formatted --options=astyle-options.ini --recursive libs/tinyexr/*.h
|
||||
astyle.exe -v --formatted --options=astyle-options.ini --recursive libs/stb/*.h
|
||||
astyle.exe -v --formatted --options=astyle-options.ini --recursive libs/tinyexr/*.h
|
||||
|
||||
astyle.exe -v -Q --options=astyle-options.ini --recursive ../base/renderprogs/*.hlsl
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
Doom 3 BFG Edition GPL Source Code
|
||||
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
||||
Copyright (C) 2014-2016 Robert Beckebans
|
||||
Copyright (C) 2014-2021 Robert Beckebans
|
||||
Copyright (C) 2014-2016 Kot in Action Creative Artel
|
||||
|
||||
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
||||
|
@ -43,6 +43,8 @@ If you have questions concerning this license or the applicable additional terms
|
|||
#include "DXT/DXTCodec.h"
|
||||
#include "Color/ColorSpace.h"
|
||||
|
||||
#include "../libs/mesa/format_r11g11b10f.h"
|
||||
|
||||
idCVar image_highQualityCompression( "image_highQualityCompression", "0", CVAR_BOOL, "Use high quality (slow) compression" );
|
||||
idCVar r_useHighQualitySky( "r_useHighQualitySky", "1", CVAR_BOOL | CVAR_ARCHIVE, "Use high quality skyboxes" );
|
||||
|
||||
|
@ -303,6 +305,319 @@ void idBinaryImage::Load2DFromMemory( int width, int height, const byte* pic_con
|
|||
Mem_Free( pic );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
========================
|
||||
RB idBinaryImage::Load2DAtlasMipchainFromMemory
|
||||
========================
|
||||
*/
|
||||
void idBinaryImage::Load2DAtlasMipchainFromMemory( int width, int height, const byte* pic_const, int numLevels, textureFormat_t& textureFormat, textureColor_t& colorFormat )
|
||||
{
|
||||
int sourceWidth = width * ( 2.0f / 3.0f ); // RB
|
||||
|
||||
fileData.textureType = TT_2D;
|
||||
fileData.format = textureFormat;
|
||||
fileData.colorFormat = CFM_DEFAULT;
|
||||
fileData.width = sourceWidth;
|
||||
fileData.height = height;
|
||||
fileData.numLevels = numLevels;
|
||||
|
||||
commonLocal.LoadPacifierBinarizeInfo( va( "(%d x %d)", width, height ) );
|
||||
|
||||
byte* sourcePic = ( byte* )Mem_Alloc( width * height * 4, TAG_TEMP );
|
||||
memcpy( sourcePic, pic_const, width * height * 4 );
|
||||
|
||||
if( colorFormat == CFM_YCOCG_DXT5 )
|
||||
{
|
||||
// convert the image data to YCoCg and use the YCoCgDXT5 compressor
|
||||
idColorSpace::ConvertRGBToCoCg_Y( sourcePic, sourcePic, width, height );
|
||||
}
|
||||
else if( colorFormat == CFM_NORMAL_DXT5 )
|
||||
{
|
||||
// Blah, HQ swizzles automatically, Fast doesn't
|
||||
if( !image_highQualityCompression.GetBool() )
|
||||
{
|
||||
for( int i = 0; i < width * height; i++ )
|
||||
{
|
||||
sourcePic[i * 4 + 3] = sourcePic[i * 4 + 0];
|
||||
sourcePic[i * 4 + 0] = 0;
|
||||
sourcePic[i * 4 + 2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( colorFormat == CFM_GREEN_ALPHA )
|
||||
{
|
||||
for( int i = 0; i < width * height; i++ )
|
||||
{
|
||||
sourcePic[i * 4 + 1] = sourcePic[i * 4 + 3];
|
||||
sourcePic[i * 4 + 0] = 0;
|
||||
sourcePic[i * 4 + 2] = 0;
|
||||
sourcePic[i * 4 + 3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
images.SetNum( numLevels );
|
||||
|
||||
const int numColors = 5;
|
||||
static idVec4 colors[numColors] = { colorBlue, colorCyan, colorGreen, colorYellow, colorRed };
|
||||
|
||||
for( int level = 0; level < images.Num(); level++ )
|
||||
{
|
||||
idBinaryImageData& img = images[ level ];
|
||||
|
||||
// RB: create shrunk image which is a copy of the sub image in the atlas
|
||||
idVec4 rect = R_CalculateMipRect( sourceWidth, level );
|
||||
|
||||
int scaledWidth = rect.z;
|
||||
int scaledHeight = rect.w;
|
||||
|
||||
byte* pic = ( byte* )Mem_Alloc( scaledWidth * scaledHeight * 4, TAG_TEMP );
|
||||
|
||||
for( int x = rect.x; x < ( rect.x + rect.z ); x++ )
|
||||
//for( int x = 0; x < rect.z; x++ )
|
||||
{
|
||||
for( int y = rect.y; y < ( rect.y + rect.w ); y++ )
|
||||
//for( int y = 0; y < rect.w; y++ )
|
||||
{
|
||||
int sx = x - rect.x;
|
||||
int sy = y - rect.y;
|
||||
|
||||
#if 1
|
||||
pic[( sy * scaledWidth + sx ) * 4 + 0] = sourcePic[( y * width + x ) * 4 + 0];
|
||||
pic[( sy * scaledWidth + sx ) * 4 + 1] = sourcePic[( y * width + x ) * 4 + 1];
|
||||
pic[( sy * scaledWidth + sx ) * 4 + 2] = sourcePic[( y * width + x ) * 4 + 2];
|
||||
pic[( sy * scaledWidth + sx ) * 4 + 3] = sourcePic[( y * width + x ) * 4 + 3];
|
||||
#else
|
||||
int colorIdx = level % numColors;
|
||||
float color[3];
|
||||
color[0] = colors[ colorIdx ].x;
|
||||
color[0] = colors[ colorIdx ].y;
|
||||
color[0] = colors[ colorIdx ].z;
|
||||
|
||||
uint32_t value = float3_to_r11g11b10f( color );
|
||||
|
||||
union
|
||||
{
|
||||
uint32 i;
|
||||
byte b[4];
|
||||
} tmp;
|
||||
|
||||
tmp.i = value;
|
||||
|
||||
//*( uint32_t* )pic[( sy * scaledWidth + sx ) * 3] = value;
|
||||
|
||||
pic[( sy * scaledWidth + sx ) * 4 + 0] = tmp.b[0];
|
||||
pic[( sy * scaledWidth + sx ) * 4 + 1] = tmp.b[1];
|
||||
pic[( sy * scaledWidth + sx ) * 4 + 2] = tmp.b[2];
|
||||
pic[( sy * scaledWidth + sx ) * 4 + 3] = tmp.b[3];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// RB end
|
||||
|
||||
commonLocal.LoadPacifierBinarizeMiplevel( level + 1, numLevels );
|
||||
|
||||
// Images that are going to be DXT compressed and aren't multiples of 4 need to be
|
||||
// padded out before compressing.
|
||||
byte* dxtPic = pic;
|
||||
int dxtWidth = 0;
|
||||
int dxtHeight = 0;
|
||||
if( textureFormat == FMT_DXT5 || textureFormat == FMT_DXT1 )
|
||||
{
|
||||
if( ( scaledWidth & 3 ) || ( scaledHeight & 3 ) )
|
||||
{
|
||||
dxtWidth = ( scaledWidth + 3 ) & ~3;
|
||||
dxtHeight = ( scaledHeight + 3 ) & ~3;
|
||||
dxtPic = ( byte* )Mem_ClearedAlloc( dxtWidth * 4 * dxtHeight, TAG_IMAGE );
|
||||
for( int i = 0; i < scaledHeight; i++ )
|
||||
{
|
||||
memcpy( dxtPic + i * dxtWidth * 4, pic + i * scaledWidth * 4, scaledWidth * 4 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dxtPic = pic;
|
||||
dxtWidth = scaledWidth;
|
||||
dxtHeight = scaledHeight;
|
||||
}
|
||||
}
|
||||
|
||||
img.level = level;
|
||||
img.destZ = 0;
|
||||
img.width = scaledWidth;
|
||||
img.height = scaledHeight;
|
||||
|
||||
// compress data or convert floats as necessary
|
||||
if( textureFormat == FMT_DXT1 )
|
||||
{
|
||||
idDxtEncoder dxt;
|
||||
img.Alloc( dxtWidth * dxtHeight / 2 );
|
||||
if( image_highQualityCompression.GetBool() )
|
||||
{
|
||||
commonLocal.LoadPacifierBinarizeInfo( va( "(%d x %d) - DXT1HQ", width, height ) );
|
||||
|
||||
dxt.CompressImageDXT1HQ( dxtPic, img.data, dxtWidth, dxtHeight );
|
||||
}
|
||||
else
|
||||
{
|
||||
commonLocal.LoadPacifierBinarizeInfo( va( "(%d x %d) - DXT1Fast", width, height ) );
|
||||
|
||||
dxt.CompressImageDXT1Fast( dxtPic, img.data, dxtWidth, dxtHeight );
|
||||
}
|
||||
}
|
||||
else if( textureFormat == FMT_DXT5 )
|
||||
{
|
||||
idDxtEncoder dxt;
|
||||
img.Alloc( dxtWidth * dxtHeight );
|
||||
if( colorFormat == CFM_NORMAL_DXT5 )
|
||||
{
|
||||
if( image_highQualityCompression.GetBool() )
|
||||
{
|
||||
commonLocal.LoadPacifierBinarizeInfo( va( "(%d x %d) - NormalMapDXT5HQ", width, height ) );
|
||||
|
||||
dxt.CompressNormalMapDXT5HQ( dxtPic, img.data, dxtWidth, dxtHeight );
|
||||
}
|
||||
else
|
||||
{
|
||||
commonLocal.LoadPacifierBinarizeInfo( va( "(%d x %d) - NormalMapDXT5Fast", width, height ) );
|
||||
|
||||
dxt.CompressNormalMapDXT5Fast( dxtPic, img.data, dxtWidth, dxtHeight );
|
||||
}
|
||||
}
|
||||
else if( colorFormat == CFM_YCOCG_DXT5 )
|
||||
{
|
||||
if( image_highQualityCompression.GetBool() )
|
||||
{
|
||||
commonLocal.LoadPacifierBinarizeInfo( va( "(%d x %d) - YCoCgDXT5HQ", width, height ) );
|
||||
|
||||
dxt.CompressYCoCgDXT5HQ( dxtPic, img.data, dxtWidth, dxtHeight );
|
||||
}
|
||||
else
|
||||
{
|
||||
commonLocal.LoadPacifierBinarizeInfo( va( "(%d x %d) - YCoCgDXT5Fast", width, height ) );
|
||||
|
||||
dxt.CompressYCoCgDXT5Fast( dxtPic, img.data, dxtWidth, dxtHeight );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fileData.colorFormat = colorFormat = CFM_DEFAULT;
|
||||
if( image_highQualityCompression.GetBool() )
|
||||
{
|
||||
commonLocal.LoadPacifierBinarizeInfo( va( "(%d x %d) - DXT5HQ", width, height ) );
|
||||
|
||||
dxt.CompressImageDXT5HQ( dxtPic, img.data, dxtWidth, dxtHeight );
|
||||
}
|
||||
else
|
||||
{
|
||||
commonLocal.LoadPacifierBinarizeInfo( va( "(%d x %d) - DXT5Fast", width, height ) );
|
||||
|
||||
dxt.CompressImageDXT5Fast( dxtPic, img.data, dxtWidth, dxtHeight );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( textureFormat == FMT_LUM8 || textureFormat == FMT_INT8 )
|
||||
{
|
||||
// LUM8 and INT8 just read the red channel
|
||||
img.Alloc( scaledWidth * scaledHeight );
|
||||
for( int i = 0; i < img.dataSize; i++ )
|
||||
{
|
||||
img.data[ i ] = pic[ i * 4 ];
|
||||
}
|
||||
}
|
||||
else if( textureFormat == FMT_ALPHA )
|
||||
{
|
||||
// ALPHA reads the alpha channel
|
||||
img.Alloc( scaledWidth * scaledHeight );
|
||||
for( int i = 0; i < img.dataSize; i++ )
|
||||
{
|
||||
img.data[ i ] = pic[ i * 4 + 3 ];
|
||||
}
|
||||
}
|
||||
else if( textureFormat == FMT_L8A8 )
|
||||
{
|
||||
// L8A8 reads the alpha and red channels
|
||||
img.Alloc( scaledWidth * scaledHeight * 2 );
|
||||
for( int i = 0; i < img.dataSize / 2; i++ )
|
||||
{
|
||||
img.data[ i * 2 + 0 ] = pic[ i * 4 + 0 ];
|
||||
img.data[ i * 2 + 1 ] = pic[ i * 4 + 3 ];
|
||||
}
|
||||
}
|
||||
else if( textureFormat == FMT_RGB565 )
|
||||
{
|
||||
img.Alloc( scaledWidth * scaledHeight * 2 );
|
||||
for( int i = 0; i < img.dataSize / 2; i++ )
|
||||
{
|
||||
unsigned short color = ( ( pic[ i * 4 + 0 ] >> 3 ) << 11 ) | ( ( pic[ i * 4 + 1 ] >> 2 ) << 5 ) | ( pic[ i * 4 + 2 ] >> 3 );
|
||||
img.data[ i * 2 + 0 ] = ( color >> 8 ) & 0xFF;
|
||||
img.data[ i * 2 + 1 ] = color & 0xFF;
|
||||
}
|
||||
}
|
||||
else if( textureFormat == FMT_RG16F )
|
||||
{
|
||||
// RB: copy it as it was a RGBA8 because of the same size
|
||||
img.Alloc( scaledWidth * scaledHeight * 4 );
|
||||
for( int i = 0; i < img.dataSize; i++ )
|
||||
{
|
||||
img.data[ i ] = pic[ i ];
|
||||
}
|
||||
}
|
||||
else if( textureFormat == FMT_R11G11B10F )
|
||||
{
|
||||
// RB: copy it as it was a RGBA8 because of the same size
|
||||
img.Alloc( scaledWidth * scaledHeight * 4 );
|
||||
for( int i = 0; i < img.dataSize; i++ )
|
||||
{
|
||||
img.data[ i ] = pic[ i ];
|
||||
}
|
||||
}
|
||||
else if( textureFormat == FMT_RGBA16F )
|
||||
{
|
||||
img.Alloc( scaledWidth * scaledHeight * 8 );
|
||||
for( int i = 0; i < img.dataSize; i++ )
|
||||
{
|
||||
img.data[ i ] = pic[ i ];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fileData.format = textureFormat = FMT_RGBA8;
|
||||
img.Alloc( scaledWidth * scaledHeight * 4 );
|
||||
for( int i = 0; i < img.dataSize; i++ )
|
||||
{
|
||||
img.data[ i ] = pic[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
// if we had to pad to quads, free the padded version
|
||||
if( pic != dxtPic )
|
||||
{
|
||||
Mem_Free( dxtPic );
|
||||
dxtPic = NULL;
|
||||
}
|
||||
|
||||
// downsample for the next level
|
||||
/*
|
||||
byte* shrunk = NULL;
|
||||
if( gammaMips )
|
||||
{
|
||||
shrunk = R_MipMapWithGamma( pic, scaledWidth, scaledHeight );
|
||||
}
|
||||
else
|
||||
{
|
||||
shrunk = R_MipMap( pic, scaledWidth, scaledHeight );
|
||||
}
|
||||
Mem_Free( pic );
|
||||
pic = shrunk;
|
||||
*/
|
||||
|
||||
Mem_Free( pic );
|
||||
}
|
||||
|
||||
Mem_Free( sourcePic );
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
PadImageTo4x4
|
||||
|
|
|
@ -53,6 +53,7 @@ public:
|
|||
}
|
||||
|
||||
void Load2DFromMemory( int width, int height, const byte* pic_const, int numLevels, textureFormat_t& textureFormat, textureColor_t& colorFormat, bool gammaMips );
|
||||
void Load2DAtlasMipchainFromMemory( int width, int height, const byte* pic_const, int numLevels, textureFormat_t& textureFormat, textureColor_t& colorFormat );
|
||||
void LoadCubeFromMemory( int width, const byte* pics[6], int numLevels, textureFormat_t& textureFormat, bool gammaMips );
|
||||
|
||||
ID_TIME_T LoadFromGeneratedFile( ID_TIME_T sourceFileTime );
|
||||
|
|
|
@ -35,7 +35,7 @@ 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;
|
||||
static const int IRRADIANCE_CUBEMAP_SIZE = 128;
|
||||
|
||||
#if 1
|
||||
static int shadowMapResolutions[MAX_SHADOWMAP_RESOLUTIONS] = { 2048, 1024, 512, 512, 256 };
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
Doom 3 BFG Edition GPL Source Code
|
||||
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
||||
Copyright (C) 2013-2017 Robert Beckebans
|
||||
Copyright (C) 2013-2021 Robert Beckebans
|
||||
Copyright (C) 2016-2017 Dustin Land
|
||||
|
||||
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
||||
|
@ -236,7 +236,9 @@ typedef enum
|
|||
CF_2D, // not a cube map
|
||||
CF_NATIVE, // _px, _nx, _py, etc, directly sent to GL
|
||||
CF_CAMERA, // _forward, _back, etc, rotated and flipped as needed before sending to GL
|
||||
CF_2D_ARRAY // not a cube map but not a single 2d texture either
|
||||
CF_PANORAMA, // TODO latlong encoded HDRI panorama typically used by Substance or Blender
|
||||
CF_2D_ARRAY, // not a cube map but not a single 2d texture either
|
||||
CF_2D_PACKED_MIPCHAIN // usually 2d but can be an octahedron, packed mipmaps into single 2d texture atlas and limited to dim^2
|
||||
} cubeFiles_t;
|
||||
|
||||
enum imageFileType_t
|
||||
|
@ -643,6 +645,9 @@ void R_VerticalFlip( byte* data, int width, int height );
|
|||
void R_RotatePic( byte* data, int width );
|
||||
void R_ApplyCubeMapTransforms( int i, byte* data, int size );
|
||||
|
||||
idVec4 R_CalculateMipRect( uint dimensions, uint mip );
|
||||
int R_CalculateUsedAtlasPixels( int dimensions );
|
||||
|
||||
/*
|
||||
====================================================================
|
||||
|
||||
|
|
|
@ -981,6 +981,9 @@ static void LoadEXR( const char* filename, unsigned char** pic, int* width, int*
|
|||
free( rgba );
|
||||
}
|
||||
|
||||
// RB: EXR needs to be flipped to match the .tga behavior
|
||||
//R_VerticalFlip( *pic, *width, *height );
|
||||
|
||||
Mem_Free( ( void* )fbuffer );
|
||||
}
|
||||
|
||||
|
|
|
@ -1056,8 +1056,8 @@ void idImageManager::CreateIntrinsicImages()
|
|||
|
||||
// RB begin
|
||||
// FIXME change back to TF_DEFAULT
|
||||
defaultUACIrradianceCube = ImageFromFile( "env/UAC3_amb", TF_NEAREST, TR_CLAMP, TD_R11G11B10F, CF_2D );
|
||||
defaultUACRadianceCube = ImageFromFile( "env/UAC3_spec", TF_NEAREST, TR_CLAMP, TD_R11G11B10F, CF_2D );
|
||||
defaultUACIrradianceCube = ImageFromFile( "env/UAC5_amb", TF_DEFAULT, TR_CLAMP, TD_R11G11B10F, CF_2D_PACKED_MIPCHAIN );
|
||||
defaultUACRadianceCube = ImageFromFile( "env/UAC5_spec", TF_DEFAULT, TR_CLAMP, TD_R11G11B10F, CF_2D_PACKED_MIPCHAIN );
|
||||
// RB end
|
||||
|
||||
release_assert( loadingIconImage->referencedOutsideLevelLoad );
|
||||
|
|
|
@ -319,7 +319,7 @@ void idImage::ActuallyLoadImage( bool fromBackEnd )
|
|||
{
|
||||
opts.textureType = TT_2D_ARRAY;
|
||||
}
|
||||
else if( cubeFiles != CF_2D )
|
||||
else if( cubeFiles == CF_NATIVE || cubeFiles == CF_CAMERA )
|
||||
{
|
||||
opts.textureType = TT_CUBIC;
|
||||
repeat = TR_CLAMP;
|
||||
|
@ -439,7 +439,7 @@ void idImage::ActuallyLoadImage( bool fromBackEnd )
|
|||
//else if( toolUsage )
|
||||
// binarizeReason = va( "binarize: tool usage '%s'", generatedName.c_str() );
|
||||
|
||||
if( cubeFiles != CF_2D )
|
||||
if( cubeFiles == CF_NATIVE || cubeFiles == CF_CAMERA )
|
||||
{
|
||||
int size;
|
||||
byte* pics[6];
|
||||
|
@ -520,6 +520,13 @@ void idImage::ActuallyLoadImage( bool fromBackEnd )
|
|||
opts.width = width;
|
||||
opts.height = height;
|
||||
opts.numLevels = 0;
|
||||
|
||||
// RB
|
||||
if( cubeFiles == CF_2D_PACKED_MIPCHAIN )
|
||||
{
|
||||
opts.width = width * ( 2.0f / 3.0f );
|
||||
}
|
||||
|
||||
DeriveOpts();
|
||||
|
||||
// foresthale 2014-05-30: give a nice progress display when binarizing
|
||||
|
@ -547,7 +554,14 @@ void idImage::ActuallyLoadImage( bool fromBackEnd )
|
|||
}
|
||||
|
||||
// RB: convert to compressed DXT or whatever choosen target format
|
||||
im.Load2DFromMemory( opts.width, opts.height, pic, opts.numLevels, opts.format, opts.colorFormat, opts.gammaMips );
|
||||
if( cubeFiles == CF_2D_PACKED_MIPCHAIN )
|
||||
{
|
||||
im.Load2DAtlasMipchainFromMemory( width, opts.height, pic, opts.numLevels, opts.format, opts.colorFormat );
|
||||
}
|
||||
else
|
||||
{
|
||||
im.Load2DFromMemory( opts.width, opts.height, pic, opts.numLevels, opts.format, opts.colorFormat, opts.gammaMips );
|
||||
}
|
||||
commonLocal.LoadPacifierBinarizeEnd();
|
||||
|
||||
Mem_Free( pic );
|
||||
|
@ -650,6 +664,7 @@ void idImage::Print() const
|
|||
NAME_FORMAT( RGBA16F );
|
||||
NAME_FORMAT( RGBA32F );
|
||||
NAME_FORMAT( R32F );
|
||||
NAME_FORMAT( R11G11B10F );
|
||||
// RB end
|
||||
NAME_FORMAT( DEPTH );
|
||||
NAME_FORMAT( X16 );
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
Doom 3 BFG Edition GPL Source Code
|
||||
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
||||
Copyright (C) 2021 Robert Beckebans
|
||||
|
||||
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
||||
|
||||
|
@ -570,3 +571,35 @@ void R_ApplyCubeMapTransforms( int iter, byte* data, int size )
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// This is the most efficient way to atlas a mip chain to a 2d texture
|
||||
// https://twitter.com/SebAaltonen/status/1327188239451611139
|
||||
|
||||
idVec4 R_CalculateMipRect( uint dimensions, uint mip )
|
||||
{
|
||||
uint pixels_mip = dimensions >> mip;
|
||||
idVec4 uv_rect = idVec4( 0, 0, pixels_mip, pixels_mip );
|
||||
|
||||
if( mip > 0 )
|
||||
{
|
||||
uv_rect.x = dimensions;
|
||||
uv_rect.y = dimensions - pixels_mip * 2;
|
||||
}
|
||||
|
||||
return uv_rect;
|
||||
}
|
||||
|
||||
int R_CalculateUsedAtlasPixels( int dimensions )
|
||||
{
|
||||
int numPixels = 0;
|
||||
const int numMips = idMath::BitsForInteger( dimensions );
|
||||
|
||||
for( int mip = 0; mip < numMips; mip++ )
|
||||
{
|
||||
idVec4 dstRect = R_CalculateMipRect( dimensions, mip );
|
||||
|
||||
numPixels += ( dstRect.z * dstRect.w );
|
||||
}
|
||||
|
||||
return numPixels;
|
||||
}
|
||||
|
|
|
@ -2096,6 +2096,11 @@ void idRenderBackend::AmbientPass( const drawSurf_t* const* drawSurfs, int numDr
|
|||
return;
|
||||
}
|
||||
|
||||
if( viewDef->renderView.rdflags & RDF_NOAMBIENT )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined( USE_VULKAN )
|
||||
if( fillGbuffer )
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
Doom 3 BFG Edition GPL Source Code
|
||||
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
||||
Copyright (C) 2012-2020 Robert Beckebans
|
||||
Copyright (C) 2012-2021 Robert Beckebans
|
||||
Copyright (C) 2014-2016 Kot in Action Creative Artel
|
||||
|
||||
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
||||
|
|
|
@ -838,6 +838,24 @@ void R_ReadTiledPixels( int width, int height, byte* buffer, renderView_t* ref =
|
|||
glPixelStorei( GL_PACK_ROW_LENGTH, RADIANCE_CUBEMAP_SIZE );
|
||||
glReadPixels( 0, 0, w, h, GL_RGB, GL_HALF_FLOAT, buffer );
|
||||
|
||||
#if 0
|
||||
// TODO vertical flip with half floats
|
||||
{
|
||||
int i, j;
|
||||
uint64 temp;
|
||||
|
||||
for( i = 0 ; i < width ; i++ )
|
||||
{
|
||||
for( j = 0 ; j < height / 2 ; j++ )
|
||||
{
|
||||
temp = *( ( uint64* )buffer + j * width + i );
|
||||
*( ( uint64* )buffer + j * width + i ) = *( ( uint64* )buffer + ( height - 1 - j ) * width + i );
|
||||
*( ( uint64* )buffer + ( height - 1 - j ) * width + i ) = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Framebuffer::Unbind();
|
||||
}
|
||||
else
|
||||
|
|
|
@ -769,10 +769,10 @@ void R_DeriveEnvprobeData( RenderEnvprobeLocal* probe )
|
|||
|
||||
// TODO get preconvolved cubemaps
|
||||
fullname.Format( "env/%s/envprobe%i_amb", basename.c_str(), probeIndex );
|
||||
probe->irradianceImage = globalImages->ImageFromFile( fullname, TF_NEAREST, TR_CLAMP, TD_R11G11B10F, CF_2D );
|
||||
probe->irradianceImage = globalImages->ImageFromFile( fullname, TF_DEFAULT, TR_CLAMP, TD_R11G11B10F, CF_2D_PACKED_MIPCHAIN );
|
||||
|
||||
fullname.Format( "env/%s/envprobe%i_spec", basename.c_str(), probeIndex );
|
||||
probe->radianceImage = globalImages->ImageFromFile( fullname, TF_NEAREST, TR_CLAMP, TD_R11G11B10F, CF_2D );
|
||||
probe->radianceImage = globalImages->ImageFromFile( fullname, TF_DEFAULT, TR_CLAMP, TD_R11G11B10F, CF_2D_PACKED_MIPCHAIN );
|
||||
|
||||
// ------------------------------------
|
||||
// compute the light projection matrix
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 BFG Edition GPL Source Code
|
||||
|
@ -496,6 +496,439 @@ idVec2 IntegrateBRDF( float NdotV, float roughness, int sampleCount )
|
|||
return idVec2( A, B );
|
||||
}
|
||||
|
||||
|
||||
// Compute normalized oct coord, mapping top left of top left pixel to (-1,-1)
|
||||
idVec2 NormalizedOctCoord( int x, int y, const int probeSideLength )
|
||||
{
|
||||
const int margin = 0;
|
||||
|
||||
int probeWithBorderSide = probeSideLength + margin;
|
||||
|
||||
idVec2 octFragCoord = idVec2( ( x - margin ) % probeWithBorderSide, ( y - margin ) % probeWithBorderSide );
|
||||
|
||||
// Add back the half pixel to get pixel center normalized coordinates
|
||||
return ( idVec2( octFragCoord ) + idVec2( 0.5f, 0.5f ) ) * ( 2.0f / float( probeSideLength ) ) - idVec2( 1.0f, 1.0f );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
R_MakeAmbientMap_f
|
||||
|
||||
R_MakeAmbientMap_f <basename> [size]
|
||||
|
||||
Saves out env/<basename>_amb_ft.tga, etc
|
||||
==================
|
||||
*/
|
||||
void R_MakeAmbientMap( const char* baseName, const char* suffix, int outSize, bool specular )
|
||||
{
|
||||
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.exr", baseName, envDirection[i] );
|
||||
|
||||
const bool captureToImage = false;
|
||||
common->UpdateScreen( captureToImage );
|
||||
|
||||
R_LoadImage( fullname, &buffers[i], &width, &height, NULL, true, NULL );
|
||||
if( !buffers[i] )
|
||||
{
|
||||
common->Printf( "loading %s failed.\n", fullname.c_str() );
|
||||
for( i-- ; i >= 0 ; i-- )
|
||||
{
|
||||
Mem_Free( buffers[i] );
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool pacifier = true;
|
||||
|
||||
// resample with hemispherical blending
|
||||
int samples = 1000;
|
||||
|
||||
int outWidth = int( outSize * 1.5f );
|
||||
int outHeight = outSize;
|
||||
|
||||
//halfFloat_t* outBuffer = ( halfFloat_t* )_alloca( outSize * outSize * 3 * sizeof( halfFloat_t ) );
|
||||
halfFloat_t* outBuffer = ( halfFloat_t* )R_StaticAlloc( idMath::Ceil( outSize * outSize * 3 * sizeof( halfFloat_t ) * 1.5f ), TAG_IMAGE );
|
||||
|
||||
{
|
||||
// output an octahedron probe
|
||||
|
||||
CommandlineProgressBar progressBar( R_CalculateUsedAtlasPixels( outSize ) );
|
||||
|
||||
int start = Sys_Milliseconds();
|
||||
|
||||
const float invDstSize = 1.0f / float( outSize );
|
||||
|
||||
const int numMips = idMath::BitsForInteger( outSize );
|
||||
|
||||
// reset image to black
|
||||
for( int x = 0; x < outWidth; x++ )
|
||||
{
|
||||
for( int y = 0; y < outHeight; y++ )
|
||||
{
|
||||
outBuffer[( y * outWidth + x ) * 3 + 0] = F32toF16( 0 );
|
||||
outBuffer[( y * outWidth + x ) * 3 + 1] = F32toF16( 0 );
|
||||
outBuffer[( y * outWidth + x ) * 3 + 2] = F32toF16( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
for( int mip = 0; mip < numMips; mip++ )
|
||||
{
|
||||
float roughness = ( float )mip / ( float )( numMips - 1 );
|
||||
|
||||
idVec4 dstRect = R_CalculateMipRect( outSize, mip );
|
||||
|
||||
for( int x = dstRect.x; x < ( dstRect.x + dstRect.z ); x++ )
|
||||
{
|
||||
for( int y = dstRect.y; y < ( dstRect.y + dstRect.w ); y++ )
|
||||
{
|
||||
idVec2 octCoord;
|
||||
if( mip > 0 )
|
||||
{
|
||||
// move back to [0, 1] coords
|
||||
octCoord = NormalizedOctCoord( x - dstRect.x, y - dstRect.y, dstRect.z );
|
||||
}
|
||||
else
|
||||
{
|
||||
octCoord = NormalizedOctCoord( x, y, dstRect.z );
|
||||
}
|
||||
|
||||
// convert UV coord to 3D direction
|
||||
idVec3 N;
|
||||
|
||||
N.FromOctahedral( octCoord );
|
||||
|
||||
#if 1
|
||||
// RB: Split Sum approximation explanation
|
||||
|
||||
// Epic Games makes a further approximation by assuming the view direction
|
||||
// (and thus the specular reflection direction) to be equal to the output sample direction ωo.
|
||||
// This translates itself to the following code:
|
||||
const idVec3 R = N;
|
||||
const idVec3 V = R;
|
||||
|
||||
idVec3 prefilteredColor( 0, 0, 0 );
|
||||
|
||||
if( specular )
|
||||
{
|
||||
float totalWeight = 0.0f;
|
||||
|
||||
for( int s = 0; s < samples; s++ )
|
||||
{
|
||||
idVec2 Xi = Hammersley2D( s, samples );
|
||||
idVec3 H = ImportanceSampleGGX( Xi, N, roughness );
|
||||
idVec3 L = ( 2.0 * ( H * ( V * H ) ) - V );
|
||||
|
||||
float NdotL = Max( ( N * L ), 0.0f );
|
||||
if( NdotL > 0.0 )
|
||||
{
|
||||
float sample[3];
|
||||
|
||||
R_SampleCubeMapHDR( H, width, buffers, sample );
|
||||
|
||||
prefilteredColor[0] += sample[0] * NdotL;
|
||||
prefilteredColor[1] += sample[1] * NdotL;
|
||||
prefilteredColor[2] += sample[2] * NdotL;
|
||||
|
||||
totalWeight += NdotL;
|
||||
}
|
||||
}
|
||||
|
||||
prefilteredColor[0] /= totalWeight;
|
||||
prefilteredColor[1] /= totalWeight;
|
||||
prefilteredColor[2] /= totalWeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int s = 0; s < samples; s++ )
|
||||
{
|
||||
idVec2 Xi = Hammersley2D( s, samples );
|
||||
idVec3 H = ImportanceSampleGGX( Xi, N, 0.95f );
|
||||
|
||||
float sample[3];
|
||||
|
||||
R_SampleCubeMapHDR( H, width, buffers, sample );
|
||||
|
||||
prefilteredColor[0] += sample[0];
|
||||
prefilteredColor[1] += sample[1];
|
||||
prefilteredColor[2] += sample[2];
|
||||
}
|
||||
|
||||
prefilteredColor[0] /= samples;
|
||||
prefilteredColor[1] /= samples;
|
||||
prefilteredColor[2] /= samples;
|
||||
}
|
||||
|
||||
outBuffer[( y * outWidth + x ) * 3 + 0] = F32toF16( prefilteredColor[0] );
|
||||
outBuffer[( y * outWidth + x ) * 3 + 1] = F32toF16( prefilteredColor[1] );
|
||||
outBuffer[( y * outWidth + x ) * 3 + 2] = F32toF16( prefilteredColor[2] );
|
||||
#else
|
||||
outBuffer[( y * outWidth + x ) * 3 + 0] = F32toF16( ( N.x * 0.5f + 0.5f ) );
|
||||
outBuffer[( y * outWidth + x ) * 3 + 1] = F32toF16( ( N.y * 0.5f + 0.5f ) );
|
||||
outBuffer[( y * outWidth + x ) * 3 + 2] = F32toF16( ( N.z * 0.5f + 0.5f ) );
|
||||
#endif
|
||||
|
||||
progressBar.Increment();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fullname.Format( "env/%s%s.exr", baseName, suffix );
|
||||
//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" );
|
||||
R_WriteEXR( fullname, ( byte* )outBuffer, 3, outWidth, outHeight, "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] );
|
||||
}
|
||||
}
|
||||
|
||||
Mem_Free( outBuffer );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
R_MakeAmbientMap_f
|
||||
|
||||
R_MakeAmbientMap_f <basename> [size]
|
||||
|
||||
Saves out env/<basename>_amb_ft.tga, etc
|
||||
==================
|
||||
*/
|
||||
//void R_MakeAmbientMap_f( const idCmdArgs& args )
|
||||
CONSOLE_COMMAND( makeAmbientMap, "Saves out env/<basename>_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 <basename> [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.95f;
|
||||
}
|
||||
|
||||
if( roughness > 0.8f )
|
||||
{
|
||||
R_MakeAmbientMap( baseName, "_amb", outSize, false );
|
||||
}
|
||||
else
|
||||
{
|
||||
R_MakeAmbientMap( baseName, "_spec", outSize, true );
|
||||
}
|
||||
}
|
||||
|
||||
CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", NULL )
|
||||
{
|
||||
idStr fullname;
|
||||
idStr baseName;
|
||||
idMat3 axis[6], oldAxis;
|
||||
idVec3 oldPosition;
|
||||
renderView_t ref;
|
||||
int blends;
|
||||
const char* extension;
|
||||
int size;
|
||||
int old_fov_x, old_fov_y;
|
||||
|
||||
static const char* envDirection[6] = { "_px", "_nx", "_py", "_ny", "_pz", "_nz" };
|
||||
|
||||
if( !tr.primaryWorld )
|
||||
{
|
||||
common->Printf( "No primary world loaded.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
baseName = tr.primaryWorld->mapName;
|
||||
baseName.StripFileExtension();
|
||||
|
||||
size = RADIANCE_CUBEMAP_SIZE;
|
||||
blends = 1;
|
||||
|
||||
if( !tr.primaryView )
|
||||
{
|
||||
common->Printf( "No primary view.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
const viewDef_t primary = *tr.primaryView;
|
||||
|
||||
memset( &axis, 0, sizeof( axis ) );
|
||||
|
||||
// +X
|
||||
axis[0][0][0] = 1;
|
||||
axis[0][1][2] = 1;
|
||||
axis[0][2][1] = 1;
|
||||
|
||||
// -X
|
||||
axis[1][0][0] = -1;
|
||||
axis[1][1][2] = -1;
|
||||
axis[1][2][1] = 1;
|
||||
|
||||
// +Y
|
||||
axis[2][0][1] = 1;
|
||||
axis[2][1][0] = -1;
|
||||
axis[2][2][2] = -1;
|
||||
|
||||
// -Y
|
||||
axis[3][0][1] = -1;
|
||||
axis[3][1][0] = -1;
|
||||
axis[3][2][2] = 1;
|
||||
|
||||
// +Z
|
||||
axis[4][0][2] = 1;
|
||||
axis[4][1][0] = -1;
|
||||
axis[4][2][1] = 1;
|
||||
|
||||
// -Z
|
||||
axis[5][0][2] = -1;
|
||||
axis[5][1][0] = 1;
|
||||
axis[5][2][1] = 1;
|
||||
|
||||
//--------------------------------------------
|
||||
// CAPTURE SCENE LIGHTING TO CUBEMAPS
|
||||
//--------------------------------------------
|
||||
|
||||
// so we return to that axis and fov after the fact.
|
||||
oldPosition = primary.renderView.vieworg;
|
||||
oldAxis = primary.renderView.viewaxis;
|
||||
old_fov_x = primary.renderView.fov_x;
|
||||
old_fov_y = primary.renderView.fov_y;
|
||||
|
||||
for( int i = 0; i < tr.primaryWorld->envprobeDefs.Num(); i++ )
|
||||
{
|
||||
RenderEnvprobeLocal* def = tr.primaryWorld->envprobeDefs[i];
|
||||
if( def == NULL )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for( int j = 0 ; j < 6 ; j++ )
|
||||
{
|
||||
ref = primary.renderView;
|
||||
|
||||
ref.rdflags = RDF_NOAMBIENT | RDF_IRRADIANCE;
|
||||
ref.fov_x = ref.fov_y = 90;
|
||||
|
||||
ref.vieworg = def->parms.origin;
|
||||
ref.viewaxis = axis[j];
|
||||
|
||||
extension = envDirection[ j ];
|
||||
fullname.Format( "env/%s/envprobe%i%s", baseName.c_str(), i, extension );
|
||||
|
||||
tr.TakeScreenshot( size, size, fullname, blends, &ref, EXR );
|
||||
//tr.CaptureRenderToFile( fullname, false );
|
||||
}
|
||||
}
|
||||
|
||||
// restore the original axis and fov
|
||||
/*
|
||||
ref.vieworg = oldPosition;
|
||||
ref.viewaxis = oldAxis;
|
||||
ref.fov_x = old_fov_x;
|
||||
ref.fov_y = old_fov_y;
|
||||
cvarSystem->SetCVarInteger( "r_windowWidth", res_w );
|
||||
cvarSystem->SetCVarInteger( "r_windowHeight", res_h );
|
||||
R_SetNewMode( false ); // the same as "vid_restart"
|
||||
*/
|
||||
|
||||
common->Printf( "Wrote a env set with the name %s\n", baseName.c_str() );
|
||||
|
||||
//--------------------------------------------
|
||||
// CONVOLVE CUBEMAPS
|
||||
//--------------------------------------------
|
||||
|
||||
int start = Sys_Milliseconds();
|
||||
|
||||
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, false );
|
||||
R_MakeAmbientMap( fullname.c_str(), "_spec", RADIANCE_CUBEMAP_SIZE, true );
|
||||
}
|
||||
|
||||
int end = Sys_Milliseconds();
|
||||
|
||||
common->Printf( "convolved probes in %5.1f seconds\n\n", ( end - start ) * 0.001f );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//void R_MakeBrdfLut_f( const idCmdArgs& args )
|
||||
CONSOLE_COMMAND( makeBrdfLUT, "make a GGX BRDF lookup table", NULL )
|
||||
{
|
||||
|
@ -608,358 +1041,3 @@ static const unsigned char brfLutTexBytes[] =
|
|||
Mem_Free( ldrBuffer );
|
||||
Mem_Free( hdrBuffer );
|
||||
}
|
||||
|
||||
|
||||
// Compute normalized oct coord, mapping top left of top left pixel to (-1,-1)
|
||||
idVec2 NormalizedOctCoord( int x, int y, const int probeSideLength )
|
||||
{
|
||||
const int margin = 0;
|
||||
|
||||
int probeWithBorderSide = probeSideLength + margin;
|
||||
|
||||
idVec2 octFragCoord = idVec2( ( x - margin ) % probeWithBorderSide, ( y - margin ) % probeWithBorderSide );
|
||||
|
||||
// Add back the half pixel to get pixel center normalized coordinates
|
||||
return ( idVec2( octFragCoord ) + idVec2( 0.5f, 0.5f ) ) * ( 2.0f / float( probeSideLength ) ) - idVec2( 1.0f, 1.0f );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
R_MakeAmbientMap_f
|
||||
|
||||
R_MakeAmbientMap_f <basename> [size]
|
||||
|
||||
Saves out env/<basename>_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.exr", baseName, envDirection[i] );
|
||||
|
||||
const bool captureToImage = false;
|
||||
common->UpdateScreen( captureToImage );
|
||||
|
||||
R_LoadImage( fullname, &buffers[i], &width, &height, NULL, true, NULL );
|
||||
if( !buffers[i] )
|
||||
{
|
||||
common->Printf( "loading %s failed.\n", fullname.c_str() );
|
||||
for( i-- ; i >= 0 ; i-- )
|
||||
{
|
||||
Mem_Free( buffers[i] );
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool pacifier = true;
|
||||
|
||||
// resample with hemispherical blending
|
||||
int samples = 1000;
|
||||
|
||||
//halfFloat_t* outBuffer = ( halfFloat_t* )_alloca( outSize * outSize * 3 * sizeof( halfFloat_t ) );
|
||||
halfFloat_t* outBuffer = ( halfFloat_t* )R_StaticAlloc( outSize * outSize * 3 * sizeof( halfFloat_t ) , TAG_IMAGE );
|
||||
|
||||
{
|
||||
// output an octahedron probe
|
||||
|
||||
CommandlineProgressBar progressBar( outSize * outSize );
|
||||
|
||||
int start = Sys_Milliseconds();
|
||||
|
||||
const float invDstSize = 1.0f / float( outSize );
|
||||
|
||||
for( int x = 0 ; x < outSize ; x++ )
|
||||
{
|
||||
for( int y = 0 ; y < outSize ; y++ )
|
||||
{
|
||||
idVec3 dir;
|
||||
float total[3];
|
||||
|
||||
// convert UV coord from [0, 1] to [-1, 1] space
|
||||
const float u = 2.0f * x * invDstSize - 1.0f;
|
||||
const float v = 2.0f * y * invDstSize - 1.0f;
|
||||
|
||||
idVec2 octCoord = NormalizedOctCoord( x, y, outSize );
|
||||
|
||||
// convert UV coord to 3D direction
|
||||
dir.FromOctahedral( octCoord );
|
||||
|
||||
#if 1
|
||||
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 );
|
||||
|
||||
float result[3];
|
||||
//test = dir;
|
||||
|
||||
R_SampleCubeMapHDR( test, width, buffers, result );
|
||||
|
||||
total[0] += result[0];
|
||||
total[1] += result[1];
|
||||
total[2] += result[2];
|
||||
}
|
||||
|
||||
outBuffer[( y * outSize + x ) * 3 + 0] = F32toF16( total[0] / samples );
|
||||
outBuffer[( y * outSize + x ) * 3 + 1] = F32toF16( total[1] / samples );
|
||||
outBuffer[( y * outSize + x ) * 3 + 2] = F32toF16( total[2] / samples );
|
||||
#else
|
||||
outBuffer[( y * outSize + x ) * 3 + 0] = F32toF16( ( dir.x * 0.5f + 0.5f ) );
|
||||
outBuffer[( y * outSize + x ) * 3 + 1] = F32toF16( ( dir.y * 0.5f + 0.5f ) );
|
||||
outBuffer[( y * outSize + x ) * 3 + 2] = F32toF16( ( dir.z * 0.5f + 0.5f ) );
|
||||
#endif
|
||||
|
||||
progressBar.Increment();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fullname.Format( "env/%s%s.exr", baseName, suffix );
|
||||
//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" );
|
||||
R_WriteEXR( fullname, (byte*)outBuffer, 3, outSize, outSize, "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] );
|
||||
}
|
||||
}
|
||||
|
||||
Mem_Free( outBuffer );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
R_MakeAmbientMap_f
|
||||
|
||||
R_MakeAmbientMap_f <basename> [size]
|
||||
|
||||
Saves out env/<basename>_amb_ft.tga, etc
|
||||
==================
|
||||
*/
|
||||
//void R_MakeAmbientMap_f( const idCmdArgs& args )
|
||||
CONSOLE_COMMAND( makeAmbientMap, "Saves out env/<basename>_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 <basename> [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.95f;
|
||||
}
|
||||
|
||||
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;
|
||||
idMat3 axis[6], oldAxis;
|
||||
idVec3 oldPosition;
|
||||
renderView_t ref;
|
||||
int blends;
|
||||
const char* extension;
|
||||
int size;
|
||||
int old_fov_x, old_fov_y;
|
||||
|
||||
static const char* envDirection[6] = { "_px", "_nx", "_py", "_ny", "_pz", "_nz" };
|
||||
|
||||
baseName = tr.primaryWorld->mapName;
|
||||
baseName.StripFileExtension();
|
||||
|
||||
size = RADIANCE_CUBEMAP_SIZE;
|
||||
blends = 1;
|
||||
|
||||
if( !tr.primaryView )
|
||||
{
|
||||
common->Printf( "No primary view.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
const viewDef_t primary = *tr.primaryView;
|
||||
|
||||
memset( &axis, 0, sizeof( axis ) );
|
||||
|
||||
// +X
|
||||
axis[0][0][0] = 1;
|
||||
axis[0][1][2] = 1;
|
||||
axis[0][2][1] = 1;
|
||||
|
||||
// -X
|
||||
axis[1][0][0] = -1;
|
||||
axis[1][1][2] = -1;
|
||||
axis[1][2][1] = 1;
|
||||
|
||||
// +Y
|
||||
axis[2][0][1] = 1;
|
||||
axis[2][1][0] = -1;
|
||||
axis[2][2][2] = -1;
|
||||
|
||||
// -Y
|
||||
axis[3][0][1] = -1;
|
||||
axis[3][1][0] = -1;
|
||||
axis[3][2][2] = 1;
|
||||
|
||||
// +Z
|
||||
axis[4][0][2] = 1;
|
||||
axis[4][1][0] = -1;
|
||||
axis[4][2][1] = 1;
|
||||
|
||||
// -Z
|
||||
axis[5][0][2] = -1;
|
||||
axis[5][1][0] = 1;
|
||||
axis[5][2][1] = 1;
|
||||
|
||||
//--------------------------------------------
|
||||
// CAPTURE SCENE LIGHTING TO CUBEMAPS
|
||||
//--------------------------------------------
|
||||
|
||||
// so we return to that axis and fov after the fact.
|
||||
oldPosition = primary.renderView.vieworg;
|
||||
oldAxis = primary.renderView.viewaxis;
|
||||
old_fov_x = primary.renderView.fov_x;
|
||||
old_fov_y = primary.renderView.fov_y;
|
||||
|
||||
for( int i = 0; i < tr.primaryWorld->envprobeDefs.Num(); i++ )
|
||||
{
|
||||
RenderEnvprobeLocal* def = tr.primaryWorld->envprobeDefs[i];
|
||||
if( def == NULL )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for( int j = 0 ; j < 6 ; j++ )
|
||||
{
|
||||
ref = primary.renderView;
|
||||
|
||||
ref.rdflags = RDF_NOAMBIENT | RDF_IRRADIANCE;
|
||||
ref.fov_x = ref.fov_y = 90;
|
||||
|
||||
ref.vieworg = def->parms.origin;
|
||||
ref.viewaxis = axis[j];
|
||||
|
||||
extension = envDirection[ j ];
|
||||
fullname.Format( "env/%s/envprobe%i%s", baseName.c_str(), i, extension );
|
||||
|
||||
tr.TakeScreenshot( size, size, fullname, blends, &ref, EXR );
|
||||
//tr.CaptureRenderToFile( fullname, false );
|
||||
}
|
||||
}
|
||||
|
||||
// restore the original axis and fov
|
||||
/*
|
||||
ref.vieworg = oldPosition;
|
||||
ref.viewaxis = oldAxis;
|
||||
ref.fov_x = old_fov_x;
|
||||
ref.fov_y = old_fov_y;
|
||||
cvarSystem->SetCVarInteger( "r_windowWidth", res_w );
|
||||
cvarSystem->SetCVarInteger( "r_windowHeight", res_h );
|
||||
R_SetNewMode( false ); // the same as "vid_restart"
|
||||
*/
|
||||
|
||||
common->Printf( "Wrote a env set with the name %s\n", baseName.c_str() );
|
||||
|
||||
//--------------------------------------------
|
||||
// CONVOLVE CUBEMAPS
|
||||
//--------------------------------------------
|
||||
|
||||
int start = Sys_Milliseconds();
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
int end = Sys_Milliseconds();
|
||||
|
||||
common->Printf( "convolved probes in %5.1f seconds\n\n", ( end - start ) * 0.001f );
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue