Merge branch '946-better-blood'

This commit is contained in:
Robert Beckebans 2024-12-06 17:41:19 +01:00
commit 5df21bdfc0
25 changed files with 1515 additions and 228 deletions

View file

@ -19,6 +19,30 @@ TBD - RBDOOM-3-BFG 1.6.0
_______________________________
## .plan - November 27, 2024
Another test build.
Changelog:
* Added new PBR roughness and specular color estimation by Kennedith98
* Tuned r_lightScale influence for PBR so specular is less dominant
* Added back SMAA so r_antiAliasing 1 = SMAA, 2 = TAA
* Fixed TAA problems with 3D guis and transparent decals
* Killed 3DTV render code. It's either VR or flat.
* MOC now renders only at the half resolution for better perf
* Improved interpolation between env_probes, especially when they are parallel placed
* Updated NVRHI with Nvidia's latest patches
## .plan - September 06, 2024
This is a test build for the Masked Software Occlusion Culling (MOC) implementation. Many modern engines have a solution like Umbra integrated that automatically hides hidden objects in the renderer before they even get rendered to optimize the performance.

View file

@ -573,6 +573,7 @@ public:
idImage* blackImage; // full of 0x00
idImage* blackDiffuseImage; // full of 0x00
idImage* cyanImage; // cyan
idImage* redClayImage; // dark red
idImage* noFalloffImage; // all 255, but zero clamped
idImage* fogImage; // increasing alpha is denser fog
idImage* fogEnterImage; // adjust fogImage alpha based on terminator plane

View file

@ -170,6 +170,24 @@ static void R_CyanImage( idImage* image, nvrhi::ICommandList* commandList )
image->GenerateImage( ( byte* )data, DEFAULT_SIZE, DEFAULT_SIZE, TF_DEFAULT, TR_REPEAT, TD_DIFFUSE, commandList );
}
static void R_RedClayImage( idImage* image, nvrhi::ICommandList* commandList )
{
byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
for( int x = 0; x < DEFAULT_SIZE; x++ )
{
for( int y = 0; y < DEFAULT_SIZE; y++ )
{
data[y][x][0] = 165;
data[y][x][1] = 42;
data[y][x][2] = 42;
data[y][x][3] = 255;
}
}
image->GenerateImage( ( byte* )data, DEFAULT_SIZE, DEFAULT_SIZE, TF_DEFAULT, TR_REPEAT, TD_DIFFUSE, commandList );
}
static void R_ChromeSpecImage( idImage* image, nvrhi::ICommandList* commandList )
{
byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
@ -181,7 +199,7 @@ static void R_ChromeSpecImage( idImage* image, nvrhi::ICommandList* commandList
data[y][x][0] = 0;
data[y][x][1] = 255;
data[y][x][2] = 255;
data[y][x][3] = 255;
data[y][x][3] = 128;
}
}
@ -196,10 +214,10 @@ static void R_PlasticSpecImage( idImage* image, nvrhi::ICommandList* commandList
{
for( int y = 0; y < DEFAULT_SIZE; y++ )
{
data[y][x][0] = 0;
data[y][x][0] = 32;
data[y][x][1] = 0;
data[y][x][2] = 255;
data[y][x][3] = 255;
data[y][x][3] = 128;
}
}
@ -1046,6 +1064,7 @@ void idImageManager::CreateIntrinsicImages()
blackImage = ImageFromFunction( "_black", R_BlackImage );
blackDiffuseImage = ImageFromFunction( "_blackDiffuse", R_BlackDiffuseImage );
cyanImage = ImageFromFunction( "_cyan", R_CyanImage );
redClayImage = ImageFromFunction( "_redClay", R_RedClayImage );
flatNormalMap = ImageFromFunction( "_flat", R_FlatNormalImage );
alphaNotchImage = ImageFromFunction( "_alphaNotch", R_AlphaNotchImage );
fogImage = ImageFromFunction( "_fog", R_FogImage );

View file

@ -1918,6 +1918,10 @@ void idMaterial::ParseStage( idLexer& src, const textureRepeat_t trpDefault )
{
ts->texgen = TG_REFLECT_CUBE;
}
else if( !token.Icmp( "reflect2" ) )
{
ts->texgen = TG_REFLECT_CUBE2;
}
else if( !token.Icmp( "skybox" ) )
{
ts->texgen = TG_SKYBOX_CUBE;
@ -2759,6 +2763,11 @@ void idMaterial::ParseMaterial( idLexer& src )
mikktspace = true;
continue;
}
else if( !token.Icmp( "unlit" ) )
{
SetMaterialFlag( MF_UNLIT );
continue;
}
// lightFallofImage <imageprogram>
// specifies the image to use for the third axis of projected
// light volumes

View file

@ -164,11 +164,12 @@ typedef enum
TG_EXPLICIT,
TG_DIFFUSE_CUBE,
TG_REFLECT_CUBE,
TG_REFLECT_CUBE2, // RB interpolates 3 octahedrons like for ambient lighting
TG_SKYBOX_CUBE,
TG_WOBBLESKY_CUBE,
TG_SCREEN, // screen aligned, for mirrorRenders and screen space temporaries
TG_SCREEN2,
TG_GLASSWARP
TG_GLASSWARP,
} texgen_t;
typedef struct
@ -361,9 +362,8 @@ typedef enum
MF_LOD3 = BIT( 9 ), // motorsep 11-24-2014; material flag for LOD3 iteration
MF_LOD4 = BIT( 10 ), // motorsep 11-24-2014; material flag for LOD4 iteration
MF_LOD_PERSISTENT = BIT( 11 ), // motorsep 11-24-2014; material flag for persistent LOD iteration
MF_GUITARGET = BIT( 12 ), // Admer: this GUI surface is used to compute a GUI render map, but a GUI should NOT be drawn on it
MF_AUTOGEN_TEMPLATE = BIT( 13 ), // Admer: this material is a template for auto-generated templates
MF_ORIGIN = BIT( 14 ), // Admer: for origin brushes
MF_ORIGIN = BIT( 12 ), // Admer: for origin brushes
MF_UNLIT = BIT( 13 ), // RB: receive no lighting
} materialFlags_t;
// contents flags, NOTE: make sure to keep the defines in doom_defs.script up to date with these!
@ -595,7 +595,7 @@ public:
// stages, and don't interact with lights at all
bool ReceivesLighting() const
{
return numAmbientStages != numStages;
return ( numAmbientStages != numStages ) && ( materialFlags & MF_UNLIT ) == 0;
}
// returns true if the material should generate interactions on sides facing away

View file

@ -1787,6 +1787,102 @@ void idRenderBackend::GetCurrentBindingLayout( int type )
}
}
}
else if( type == BINDING_LAYOUT_OCTAHEDRON_CUBE || type == BINDING_LAYOUT_OCTAHEDRON_CUBE_SKINNED )
{
if( type == BINDING_LAYOUT_OCTAHEDRON_CUBE_SKINNED )
{
if( desc[0].bindings.empty() )
{
desc[0].bindings =
{
nvrhi::BindingSetItem::ConstantBuffer( 0, paramCb, range ),
nvrhi::BindingSetItem::StructuredBuffer_SRV( 11, currentJointBuffer, nvrhi::Format::UNKNOWN, nvrhi::BufferRange( currentJointOffset, sizeof( idVec4 ) * numBoneMatrices ) )
};
}
else
{
auto& bindings = desc[0].bindings;
bindings[0].resourceHandle = paramCb;
bindings[0].range = range;
bindings[1].resourceHandle = currentJointBuffer;
bindings[1].range = nvrhi::BufferRange{ currentJointOffset, sizeof( idVec4 )* numBoneMatrices };
}
}
else
{
if( desc[0].bindings.empty() )
{
desc[0].bindings =
{
nvrhi::BindingSetItem::ConstantBuffer( 0, paramCb, range ),
};
}
else
{
desc[0].bindings[0].resourceHandle = paramCb;
desc[0].bindings[0].range = range;
}
}
if( desc[1].bindings.empty() )
{
desc[1].bindings =
{
nvrhi::BindingSetItem::Texture_SRV( 0, ( nvrhi::ITexture* )GetImageAt( 0 )->GetTextureID() ),
nvrhi::BindingSetItem::Texture_SRV( 1, ( nvrhi::ITexture* )GetImageAt( 1 )->GetTextureID() ),
nvrhi::BindingSetItem::Texture_SRV( 2, ( nvrhi::ITexture* )GetImageAt( 2 )->GetTextureID() ),
nvrhi::BindingSetItem::Texture_SRV( 3, ( nvrhi::ITexture* )GetImageAt( 3 )->GetTextureID() ),
nvrhi::BindingSetItem::Texture_SRV( 4, ( nvrhi::ITexture* )GetImageAt( 4 )->GetTextureID() ),
nvrhi::BindingSetItem::Texture_SRV( 5, ( nvrhi::ITexture* )GetImageAt( 5 )->GetTextureID() ),
nvrhi::BindingSetItem::Texture_SRV( 6, ( nvrhi::ITexture* )GetImageAt( 6 )->GetTextureID() )
};
}
else
{
auto& bindings = desc[1].bindings;
bindings[0].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 0 )->GetTextureID();
bindings[1].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 1 )->GetTextureID();
bindings[2].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 2 )->GetTextureID();
bindings[3].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 3 )->GetTextureID();
bindings[4].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 4 )->GetTextureID();
bindings[5].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 5 )->GetTextureID();
bindings[6].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 6 )->GetTextureID();
}
if( R_UsePixelatedLook() )
{
if( desc[2].bindings.empty() )
{
desc[2].bindings =
{
nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_PointWrapSampler ),
nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler )
};
}
else
{
desc[2].bindings[0].resourceHandle = commonPasses.m_PointWrapSampler;
desc[2].bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler;
}
}
else
{
if( desc[2].bindings.empty() )
{
desc[2].bindings =
{
nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_AnisotropicWrapSampler ),
nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearBorderSampler )
};
}
else
{
desc[2].bindings[0].resourceHandle = commonPasses.m_AnisotropicWrapSampler;
desc[2].bindings[1].resourceHandle = commonPasses.m_LinearBorderSampler;
}
}
}
else if( type == BINDING_LAYOUT_BINK_VIDEO )
{
if( desc[0].bindings.empty() )

View file

@ -336,7 +336,14 @@ void idRenderBackend::BindVariableStageImage( const textureStage_t* texture, con
{
if( texture->image != NULL )
{
texture->image->Bind();
if( texture->image->IsLoaded() && !texture->image->IsDefaulted() )
{
texture->image->Bind();
}
else
{
globalImages->defaultImage->Bind();
}
}
}
}
@ -356,7 +363,6 @@ void idRenderBackend::PrepareStageTexturing( const shaderStage_t* pStage, const
// texgens
if( pStage->texture.texgen == TG_REFLECT_CUBE )
{
// see if there is also a bump map specified
const shaderStage_t* bumpStage = surf->material->GetBumpStage();
if( bumpStage != NULL )
@ -387,7 +393,130 @@ void idRenderBackend::PrepareStageTexturing( const shaderStage_t* pStage, const
renderProgManager.BindShader_Environment();
}
}
}
else if( pStage->texture.texgen == TG_REFLECT_CUBE2 )
{
if( r_useSSR.GetBool() && R_UseHiZ() )
{
idVec4 probeMins, probeMaxs, probeCenter;
probeMins[0] = viewDef->globalProbeBounds[0][0];
probeMins[1] = viewDef->globalProbeBounds[0][1];
probeMins[2] = viewDef->globalProbeBounds[0][2];
probeMins[3] = viewDef->globalProbeBounds.IsCleared() ? 0.0f : 1.0f;
probeMaxs[0] = viewDef->globalProbeBounds[1][0];
probeMaxs[1] = viewDef->globalProbeBounds[1][1];
probeMaxs[2] = viewDef->globalProbeBounds[1][2];
probeMaxs[3] = 0.0f;
idVec3 center = viewDef->globalProbeBounds.GetCenter();
probeCenter.Set( center.x, center.y, center.z, 1.0f );
SetVertexParm( RENDERPARM_WOBBLESKY_X, probeMins.ToFloatPtr() );
SetVertexParm( RENDERPARM_WOBBLESKY_Y, probeMaxs.ToFloatPtr() );
SetVertexParm( RENDERPARM_WOBBLESKY_Z, probeCenter.ToFloatPtr() );
SetVertexParm( RENDERPARM_TEXGEN_0_S, viewDef->probePositions[0].ToFloatPtr() );
SetVertexParm( RENDERPARM_TEXGEN_0_T, viewDef->probePositions[1].ToFloatPtr() );
SetVertexParm( RENDERPARM_TEXGEN_0_Q, viewDef->probePositions[2].ToFloatPtr() );
// specular cubemap blend weights
renderProgManager.SetUniformValue( RENDERPARM_LOCALLIGHTORIGIN, viewDef->radianceImageBlends.ToFloatPtr() );
// general SSR parms
idVec4 ssrParms;
ssrParms.x = r_ssrStride.GetFloat();
ssrParms.y = r_ssrMaxDistance.GetFloat();
ssrParms.z = r_ssrZThickness.GetFloat();
ssrParms.w = r_ssrJitter.GetFloat();
idVec4 jitterTexScale;
jitterTexScale.x = r_ssrMaxDistance.GetFloat();
jitterTexScale.y = 0;
jitterTexScale.z = 0;
jitterTexScale.w = 0;
SetFragmentParm( RENDERPARM_JITTERTEXSCALE, jitterTexScale.ToFloatPtr() ); // rpJitterTexScale
renderProgManager.SetUniformValue( RENDERPARM_GLOBALLIGHTORIGIN, ssrParms.ToFloatPtr() );
// allow reconstruction of depth buffer value to full view space position
SetVertexParms( RENDERPARM_SHADOW_MATRIX_0_X, viewDef->unprojectionToCameraRenderMatrix[0], 4 );
// we need to rotate the normals from world space to view space
idRenderMatrix viewMatrix;
idRenderMatrix::Transpose( *( idRenderMatrix* ) viewDef->worldSpace.modelViewMatrix, viewMatrix );
//SetVertexParms( RENDERPARM_MODELVIEWMATRIX_X, viewMatrix[0], 4 );
// this is the main requirement for the DDA SSR algorithm next to the linear z buffer
// we need clip space [-1..1] -> window space [0..1] -> to texture space [0..w|h]
ALIGNTYPE16 const idRenderMatrix matClipToUvzw(
0.5f, 0.0f, 0.0f, 0.5f,
0.0f, -0.5f, 0.0f, 0.5f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
// should this be the viewport width / height instead?
int w = renderSystem->GetWidth();
int h = renderSystem->GetHeight();
ALIGNTYPE16 const idRenderMatrix screenScale(
w, 0.0f, 0.0f, 0.0f,
0.0f, h, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
idRenderMatrix screenSpaceScaled;
idRenderMatrix::Multiply( screenScale, matClipToUvzw, screenSpaceScaled );
idRenderMatrix screenSpace;
idRenderMatrix::Multiply( screenSpaceScaled, viewDef->projectionRenderMatrix, screenSpace );
SetVertexParms( RENDERPARM_SHADOW_MATRIX_1_X, screenSpace[0], 4 );
// see if there is also a bump map specified
const shaderStage_t* bumpStage = surf->material->GetBumpStage();
if( bumpStage != NULL )
{
// per-pixel reflection mapping with bump mapping
GL_SelectTexture( 0 );
bumpStage->texture.image->Bind();
//globalImages->flatNormalMap->Bind();
GL_SelectTexture( 1 );
globalImages->currentRenderImage->Bind();
GL_SelectTexture( 2 );
globalImages->gbufferNormalsRoughnessImage->Bind();
GL_SelectTexture( 3 );
// use hierachical Z to avoid read & write at the same time on the depth buffer
globalImages->hierarchicalZbufferImage->Bind();
GL_SelectTexture( 4 );
viewDef->radianceImages[0]->Bind();
GL_SelectTexture( 5 );
viewDef->radianceImages[1]->Bind();
GL_SelectTexture( 6 );
viewDef->radianceImages[2]->Bind();
GL_SelectTexture( 0 );
if( surf->jointCache )
{
renderProgManager.BindShader_BumpyEnvironment2Skinned();
}
else
{
renderProgManager.BindShader_BumpyEnvironment2();
}
}
}
}
else if( pStage->texture.texgen == TG_SKYBOX_CUBE )
{
@ -395,7 +524,6 @@ void idRenderBackend::PrepareStageTexturing( const shaderStage_t* pStage, const
}
else if( pStage->texture.texgen == TG_WOBBLESKY_CUBE )
{
const int* parms = surf->material->GetTexGenRegisters();
float wobbleDegrees = surf->shaderRegisters[ parms[0] ] * ( idMath::PI / 180.0f );
@ -453,7 +581,6 @@ void idRenderBackend::PrepareStageTexturing( const shaderStage_t* pStage, const
}
else if( ( pStage->texture.texgen == TG_SCREEN ) || ( pStage->texture.texgen == TG_SCREEN2 ) )
{
useTexGenParm[0] = 1.0f;
useTexGenParm[1] = 1.0f;
useTexGenParm[2] = 1.0f;
@ -518,7 +645,7 @@ void idRenderBackend::FinishStageTexturing( const shaderStage_t* pStage, const d
GL_SelectTexture( 0 );
}
if( pStage->texture.texgen == TG_REFLECT_CUBE )
if( pStage->texture.texgen == TG_REFLECT_CUBE || pStage->texture.texgen == TG_REFLECT_CUBE2 )
{
// see if there is also a bump map specified
const shaderStage_t* bumpStage = surf->material->GetBumpStage();
@ -531,6 +658,7 @@ void idRenderBackend::FinishStageTexturing( const shaderStage_t* pStage, const d
{
// per-pixel reflection mapping without bump mapping
}
renderProgManager.Unbind();
}
}
@ -994,7 +1122,14 @@ void idRenderBackend::DrawSingleInteraction( drawInteraction_t* din, bool useFas
return;
}
if( r_skipDiffuse.GetInteger() == 2 )
if( r_skipDiffuse.GetInteger() == 3 )
{
// RB: for testing specular aliasing
din->diffuseImage = globalImages->redClayImage;
din->specularImage = globalImages->plasticSpecImage;
din->specularColor = colorWhite;
}
else if( r_skipDiffuse.GetInteger() == 2 )
{
din->diffuseImage = globalImages->whiteImage;
}
@ -1704,9 +1839,11 @@ void idRenderBackend::RenderInteractions( const drawSurf_t* surfList, const view
// apply the world-global overbright and the 2x factor for specular
idVec4 diffuseColor = lightColor;
// jmarshall
idVec4 specularColor = lightColor * 2.0f;
// RB: the BFG edition has exagerated specular lighting compared to vanilla Doom 3
// turn this back to 1.0
idVec4 specularColor = lightColor * 1.0f;
// jmarshall
if( vLight->lightDef->parms.noSpecular )
{
specularColor.Zero();
@ -2067,13 +2204,13 @@ void idRenderBackend::AmbientPass( const drawSurf_t* const* drawSurfs, int numDr
if( fillGbuffer )
{
commandList->clearTextureFloat( globalImages->gbufferNormalsRoughnessImage->GetTextureHandle(), nvrhi::AllSubresources, nvrhi::Color( 0.f ) );
commandList->clearTextureFloat( globalImages->gbufferNormalsRoughnessImage->GetTextureHandle(), nvrhi::AllSubresources, nvrhi::Color( 0.0f ) );
}
// RB: TODO remove this
if( !fillGbuffer && r_useSSAO.GetBool() && r_ssaoDebug.GetBool() )
{
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_DEPTHMASK | GLS_DEPTHFUNC_ALWAYS );
GL_State( GLS_DEPTHMASK | GLS_DEPTHFUNC_ALWAYS | GLS_CULL_TWOSIDED );
// We just want to do a quad pass - so make sure we disable any texgen and
// set the texture matrix to the identity so we don't get anomalies from
@ -5035,31 +5172,7 @@ void idRenderBackend::DrawScreenSpaceAmbientOcclusion( const viewDef_t* _viewDef
int screenWidth = renderSystem->GetWidth();
int screenHeight = renderSystem->GetHeight();
commandList->clearTextureFloat( globalImages->hierarchicalZbufferImage->GetTextureHandle(), nvrhi::AllSubresources, nvrhi::Color( 1.f ) );
commandList->clearTextureFloat( globalImages->ambientOcclusionImage[0]->GetTextureHandle(), nvrhi::AllSubresources, nvrhi::Color( 1.f ) );
// build hierarchical depth buffer
if( r_useHierarchicalDepthBuffer.GetBool() )
{
renderLog.OpenBlock( "Render_HiZ" );
//if( R_GetMSAASamples() > 1 )
//{
// commandList->resolveTexture( globalImages->hierarchicalZbufferImage->GetTextureHandle(), nvrhi::AllSubresources, globalImages->currentDepthImage->GetTextureHandle(), nvrhi::AllSubresources );
//}
//else
{
commonPasses.BlitTexture(
commandList,
globalFramebuffers.csDepthFBO[0]->GetApiObject(),
globalImages->currentDepthImage->GetTextureHandle(),
&bindingCache );
}
hiZGenPass->Dispatch( commandList, MAX_HIERARCHICAL_ZBUFFERS );
renderLog.CloseBlock();
}
commandList->clearTextureFloat( globalImages->ambientOcclusionImage[0]->GetTextureHandle(), nvrhi::AllSubresources, nvrhi::Color( 1.0f ) );
if( previousFramebuffer != NULL )
{
@ -5151,7 +5264,7 @@ void idRenderBackend::DrawScreenSpaceAmbientOcclusion( const viewDef_t* _viewDef
globalImages->gbufferNormalsRoughnessImage->Bind();
GL_SelectTexture( 1 );
if( r_useHierarchicalDepthBuffer.GetBool() )
if( R_UseHiZ() )
{
globalImages->hierarchicalZbufferImage->Bind();
}
@ -5433,8 +5546,10 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
OPTICK_GPU_CONTEXT( ( void* ) commandList->getNativeObject( commandObject ) );
//OPTICK_GPU_EVENT( "DrawView" ); // SRS - now in DrawView() for 3D vs. GUI
bool is3D = _viewDef->viewEntitys && !_viewDef->is2Dgui;
// ugly but still faster than building the string
if( !_viewDef->viewEntitys || _viewDef->is2Dgui )
if( !is3D )
{
if( stereoEye == -1 )
{
@ -5503,7 +5618,6 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
// ensures that depth writes are enabled for the depth clear
GL_State( GLS_DEFAULT | GLS_CULL_FRONTSIDED, true );
bool useHDR = !_viewDef->is2Dgui;
bool clearColor = false;
if( _viewDef->renderView.rdflags & RDF_IRRADIANCE )
@ -5511,7 +5625,7 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
globalFramebuffers.envprobeFBO->Bind();
clearColor = true;
}
else if( useHDR )
else if( is3D )
{
globalFramebuffers.hdrFBO->Bind();
}
@ -5561,9 +5675,7 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
SetFragmentParm( RENDERPARM_OVERBRIGHT, parm );
// Set Projection Matrix
float projMatrixTranspose[16];
R_MatrixTranspose( viewDef->projectionMatrix, projMatrixTranspose );
SetVertexParms( RENDERPARM_PROJMATRIX_X, projMatrixTranspose, 4 );
SetVertexParms( RENDERPARM_PROJMATRIX_X, viewDef->projectionRenderMatrix[0], 4 );
// PSX jitter parms
if( ( r_renderMode.GetInteger() == RENDERMODE_PSX ) && ( _viewDef->viewEntitys && !_viewDef->is2Dgui ) )
@ -5588,6 +5700,19 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
}
SetVertexParm( RENDERPARM_PSX_DISTORTIONS, parm );
// make sure rpWindowCoord is set even without post processing surfaces in the view
int x = viewDef->viewport.x1;
int y = viewDef->viewport.y1;
int w = viewDef->viewport.x2 - viewDef->viewport.x1 + 1;
int h = viewDef->viewport.y2 - viewDef->viewport.y1 + 1;
float windowCoordParm[4];
windowCoordParm[0] = 1.0f / w;
windowCoordParm[1] = 1.0f / h;
windowCoordParm[2] = w;
windowCoordParm[3] = h;
SetFragmentParm( RENDERPARM_WINDOWCOORD, windowCoordParm ); // rpWindowCoord
}
//-------------------------------------------------
@ -5595,12 +5720,32 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
//-------------------------------------------------
FillDepthBufferFast( drawSurfs, numDrawSurfs );
//-------------------------------------------------
// build hierarchical depth buffer
//-------------------------------------------------
if( R_UseHiZ() && is3D )
{
renderLog.OpenBlock( "Render_HiZ" );
commandList->clearTextureFloat( globalImages->hierarchicalZbufferImage->GetTextureHandle(), nvrhi::AllSubresources, nvrhi::Color( 1.f ) );
commonPasses.BlitTexture(
commandList,
globalFramebuffers.csDepthFBO[0]->GetApiObject(),
globalImages->currentDepthImage->GetTextureHandle(),
&bindingCache );
hiZGenPass->Dispatch( commandList, MAX_HIERARCHICAL_ZBUFFERS );
renderLog.CloseBlock();
}
//-------------------------------------------------
// FIXME, OPTIMIZE: merge this with FillDepthBufferFast like in a light prepass deferred renderer
//
// fill the geometric buffer with normals and roughness
//-------------------------------------------------
if( viewDef->viewEntitys ) // 3D views only
if( is3D )
{
//OPTICK_EVENT( "Render_GeometryBuffer" );
OPTICK_GPU_EVENT( "Render_GeometryBuffer" );
@ -5623,7 +5768,7 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
//-------------------------------------------------
// render static lighting and consider SSAO results
//-------------------------------------------------
if( viewDef->viewEntitys ) // 3D views only
if( is3D )
{
//OPTICK_EVENT( "Render_AmbientPass" );
OPTICK_GPU_EVENT( "Render_AmbientPass" );
@ -5641,9 +5786,35 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
//-------------------------------------------------
DrawInteractions( _viewDef );
//-------------------------------------------------
// resolve the screen for SSR
//-------------------------------------------------
{
if( R_GetMSAASamples() > 1 )
{
renderLog.OpenBlock( "Resolve to _currentRender" );
commandList->resolveTexture( globalImages->currentRenderImage->GetTextureHandle(), nvrhi::AllSubresources, globalImages->currentRenderHDRImage->GetTextureHandle(), nvrhi::AllSubresources );
}
else
{
renderLog.OpenBlock( "Blit to _currentRender" );
BlitParameters blitParms;
nvrhi::IFramebuffer* currentFB = ( nvrhi::IFramebuffer* )currentFrameBuffer->GetApiObject();
blitParms.sourceTexture = currentFB->getDesc().colorAttachments[0].texture;
blitParms.targetFramebuffer = globalFramebuffers.postProcFBO->GetApiObject(); // _currentRender image
blitParms.targetViewport = nvrhi::Viewport( renderSystem->GetWidth(), renderSystem->GetHeight() );
commonPasses.BlitTexture( commandList, blitParms, &bindingCache );
}
renderLog.CloseBlock();
}
//-------------------------------------------------
// now draw any non-light dependent shading passes
//-------------------------------------------------
int processed = 0;
if( !r_skipShaderPasses.GetBool() )
{
@ -5759,7 +5930,7 @@ void idRenderBackend::DrawViewInternal( const viewDef_t* _viewDef, const int ste
// tonemapping: convert back from HDR to LDR range
//-------------------------------------------------
if( useHDR && !( _viewDef->renderView.rdflags & RDF_IRRADIANCE ) && !_viewDef->targetRender )
if( is3D && !( _viewDef->renderView.rdflags & RDF_IRRADIANCE ) && !_viewDef->targetRender )
{
OPTICK_GPU_EVENT( "Render_ToneMapPass" );
@ -6373,23 +6544,8 @@ void idRenderBackend::PostProcess( const void* data )
globalImages->gbufferNormalsRoughnessImage->Bind();
GL_SelectTexture( 3 );
if( r_useHierarchicalDepthBuffer.GetBool() )
if( R_UseHiZ() )
{
// build hierarchical depth buffer
renderLog.OpenBlock( "Render_HiZ" );
commandList->clearTextureFloat( globalImages->hierarchicalZbufferImage->GetTextureHandle(), nvrhi::AllSubresources, nvrhi::Color( 1.f ) );
commonPasses.BlitTexture(
commandList,
globalFramebuffers.csDepthFBO[0]->GetApiObject(),
globalImages->currentDepthImage->GetTextureHandle(),
&bindingCache );
hiZGenPass->Dispatch( commandList, MAX_HIERARCHICAL_ZBUFFERS );
renderLog.CloseBlock();
globalImages->hierarchicalZbufferImage->Bind();
}
else

View file

@ -623,12 +623,13 @@ struct viewDef_t
// RB: collect environment probes like lights
viewEnvprobe_t* viewEnvprobes;
// RB: nearest probe for now
// RB: nearest 3 probes for now
idBounds globalProbeBounds;
idRenderMatrix inverseBaseEnvProbeProject; // the matrix for deforming the 'zeroOneCubeModel' to exactly cover the environent probe volume in world space
idImage* irradianceImage; // cubemap image used for diffuse IBL by backend
idImage* radianceImages[3]; // cubemap image used for specular IBL by backend
idVec4 radianceImageBlends; // blending weights
idVec4 probePositions[3]; // only used by parallax correction
Framebuffer* targetRender; // SP: The framebuffer to render to
@ -823,6 +824,9 @@ enum bindingLayoutType_t
BINDING_LAYOUT_NORMAL_CUBE,
BINDING_LAYOUT_NORMAL_CUBE_SKINNED,
BINDING_LAYOUT_OCTAHEDRON_CUBE,
BINDING_LAYOUT_OCTAHEDRON_CUBE_SKINNED,
// NO GPU SKINNING ANYMORE
BINDING_LAYOUT_POST_PROCESS_INGAME,
BINDING_LAYOUT_POST_PROCESS_FINAL,
@ -1263,6 +1267,13 @@ extern idCVar r_useLightGrid;
extern idCVar r_exposure;
extern idCVar r_useSSR;
extern idCVar r_ssrJitter;
extern idCVar r_ssrMaxDistance;
extern idCVar r_ssrMaxSteps;
extern idCVar r_ssrStride;
extern idCVar r_ssrZThickness;
extern idCVar r_useTemporalAA;
extern idCVar r_taaJitter;
extern idCVar r_taaEnableHistoryClamping;
@ -1315,6 +1326,8 @@ bool R_UsePixelatedLook();
bool R_UseTemporalAA();
bool R_UseHiZ();
uint R_GetMSAASamples();
void R_SetNewMode( const bool fullInit );

View file

@ -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-2023 Robert Beckebans
Copyright (C) 2013-2024 Robert Beckebans
Copyright (C) 2022 Stephen Pridham
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@ -368,6 +368,27 @@ void idRenderProgManager::Init( nvrhi::IDevice* device )
skinningLayout, normalCubeBindingLayout, samplerOneBindingLayout
};
auto octahedronCubeBindingLayoutDesc = nvrhi::BindingLayoutDesc()
.setVisibility( nvrhi::ShaderType::Pixel )
.addItem( nvrhi::BindingLayoutItem::Texture_SRV( 0 ) ) // normal map
.addItem( nvrhi::BindingLayoutItem::Texture_SRV( 1 ) ) // HDR _currentRender
.addItem( nvrhi::BindingLayoutItem::Texture_SRV( 2 ) ) // _currentNormals
.addItem( nvrhi::BindingLayoutItem::Texture_SRV( 3 ) ) // _currentDepth
.addItem( nvrhi::BindingLayoutItem::Texture_SRV( 4 ) ) // radiance cube map 1
.addItem( nvrhi::BindingLayoutItem::Texture_SRV( 5 ) ) // radiance cube map 2
.addItem( nvrhi::BindingLayoutItem::Texture_SRV( 6 ) ); // radiance cube map 3
auto octahedronCubeBindingLayout = device->createBindingLayout( octahedronCubeBindingLayoutDesc );
bindingLayouts[BINDING_LAYOUT_OCTAHEDRON_CUBE] =
{
uniformsLayout, octahedronCubeBindingLayout, samplerTwoBindingLayout
};
bindingLayouts[BINDING_LAYOUT_OCTAHEDRON_CUBE_SKINNED] =
{
skinningLayout, octahedronCubeBindingLayout, samplerTwoBindingLayout
};
auto binkVideoBindingLayout = nvrhi::BindingLayoutDesc()
.setVisibility( nvrhi::ShaderType::All )
.addItem( renderParmLayoutItem )
@ -538,6 +559,8 @@ void idRenderProgManager::Init( nvrhi::IDevice* device )
{ BUILTIN_ENVIRONMENT_SKINNED, "builtin/legacy/environment", "_skinned", { {"USE_GPU_SKINNING", "1" } }, true , SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT_SKINNED },
{ BUILTIN_BUMPY_ENVIRONMENT, "builtin/legacy/bumpyenvironment", "", { {"USE_GPU_SKINNING", "0" } }, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_NORMAL_CUBE },
{ BUILTIN_BUMPY_ENVIRONMENT_SKINNED, "builtin/legacy/bumpyenvironment", "_skinned", { {"USE_GPU_SKINNING", "1" } }, true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_NORMAL_CUBE_SKINNED },
{ BUILTIN_BUMPY_ENVIRONMENT2, "builtin/legacy/bumpyenvironment2", "", { {"USE_GPU_SKINNING", "0" } }, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_OCTAHEDRON_CUBE },
{ BUILTIN_BUMPY_ENVIRONMENT2_SKINNED, "builtin/legacy/bumpyenvironment2", "_skinned", { {"USE_GPU_SKINNING", "1" } }, true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_OCTAHEDRON_CUBE_SKINNED },
{ BUILTIN_DEPTH, "builtin/depth", "", { {"USE_GPU_SKINNING", "0" } }, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_CONSTANT_BUFFER_ONLY },
{ BUILTIN_DEPTH_SKINNED, "builtin/depth", "_skinned", { {"USE_GPU_SKINNING", "1" } }, true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_CONSTANT_BUFFER_ONLY_SKINNED },

View file

@ -353,10 +353,13 @@ enum
BUILTIN_DEBUG_OCTAHEDRON,
BUILTIN_DEBUG_OCTAHEDRON_SKINNED,
// RB end
BUILTIN_ENVIRONMENT,
BUILTIN_ENVIRONMENT_SKINNED,
BUILTIN_BUMPY_ENVIRONMENT,
BUILTIN_BUMPY_ENVIRONMENT_SKINNED,
BUILTIN_BUMPY_ENVIRONMENT2, // RB
BUILTIN_BUMPY_ENVIRONMENT2_SKINNED, // RB
BUILTIN_DEPTH,
BUILTIN_DEPTH_SKINNED,
@ -774,6 +777,16 @@ public:
BindShader_Builtin( BUILTIN_BUMPY_ENVIRONMENT_SKINNED );
}
void BindShader_BumpyEnvironment2()
{
BindShader_Builtin( BUILTIN_BUMPY_ENVIRONMENT2 );
}
void BindShader_BumpyEnvironment2Skinned()
{
BindShader_Builtin( BUILTIN_BUMPY_ENVIRONMENT2_SKINNED );
}
void BindShader_Depth()
{
BindShader_Builtin( BUILTIN_DEPTH );

View file

@ -82,7 +82,7 @@ idCVar r_useValidationLayers( "r_useValidationLayers", "1", CVAR_INTEGER | CVAR_
#if ID_MSAA
idCVar r_antiAliasing( "r_antiAliasing", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER | CVAR_NEW, " 0 = None\n 1 = TAA 1x\n 2 = TAA + SMAA 1x\n 3 = MSAA 2x\n 4 = MSAA 4x\n", 0, ANTI_ALIASING_MSAA_4X );
#else
idCVar r_antiAliasing( "r_antiAliasing", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER | CVAR_NEW, " 0 = None\n 1 = SMAA 1x, 2 = TAA", 0, ANTI_ALIASING_TAA );
idCVar r_antiAliasing( "r_antiAliasing", "2", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER | CVAR_NEW, " 0 = None\n 1 = SMAA 1x, 2 = TAA", 0, ANTI_ALIASING_TAA );
#endif
// RB end
idCVar r_vidMode( "r_vidMode", "0", CVAR_ARCHIVE | CVAR_RENDERER | CVAR_INTEGER, "fullscreen video mode number" );
@ -154,7 +154,7 @@ idCVar r_useLightPortalCulling( "r_useLightPortalCulling", "1", CVAR_RENDERER |
idCVar r_useLightAreaCulling( "r_useLightAreaCulling", "1", CVAR_RENDERER | CVAR_BOOL, "0 = off, 1 = on" );
idCVar r_useLightScissors( "r_useLightScissors", "3", CVAR_RENDERER | CVAR_INTEGER, "0 = no scissor, 1 = non-clipped scissor, 2 = near-clipped scissor, 3 = fully-clipped scissor", 0, 3, idCmdSystem::ArgCompletion_Integer<0, 3> );
idCVar r_useEntityPortalCulling( "r_useEntityPortalCulling", "1", CVAR_RENDERER | CVAR_INTEGER, "0 = none, 1 = cull frustum corners to plane, 2 = exact clip the frustum faces", 0, 2, idCmdSystem::ArgCompletion_Integer<0, 2> );
idCVar r_clear( "r_clear", "2", CVAR_RENDERER, "force screen clear every frame, 1 = purple, 2 = black, 'r g b' = custom" );
idCVar r_clear( "r_clear", "2", CVAR_RENDERER | CVAR_NOCHEAT, "force screen clear every frame, 1 = purple, 2 = black, 'r g b' = custom" );
idCVar r_offsetFactor( "r_offsetfactor", "0", CVAR_RENDERER | CVAR_FLOAT, "polygon offset parameter" );
// RB: offset factor was 0, and units were -600 which caused some very ugly polygon offsets on Android so I reverted the values to the same as in Q3A
@ -294,6 +294,13 @@ idCVar r_useLightGrid( "r_useLightGrid", "1", CVAR_RENDERER | CVAR_BOOL | CVAR_N
idCVar r_exposure( "r_exposure", "0.5", CVAR_ARCHIVE | CVAR_RENDERER | CVAR_FLOAT | CVAR_NEW, "HDR exposure or LDR brightness [-4.0 .. 4.0]", -4.0f, 4.0f );
idCVar r_useSSR( "r_useSSR", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL | CVAR_NEW, "" );
idCVar r_ssrJitter( "r_ssrJitter", "0", CVAR_RENDERER | CVAR_FLOAT | CVAR_NEW, "" );
idCVar r_ssrMaxDistance( "r_ssrMaxDistance", "100", CVAR_RENDERER | CVAR_FLOAT | CVAR_NEW, "In meters" );
idCVar r_ssrMaxSteps( "r_ssrMaxSteps", "100", CVAR_RENDERER | CVAR_FLOAT | CVAR_NEW, "" );
idCVar r_ssrStride( "r_ssrStride", "12", CVAR_RENDERER | CVAR_FLOAT | CVAR_NEW, "" );
idCVar r_ssrZThickness( "r_ssrZThickness", "2", CVAR_RENDERER | CVAR_FLOAT | CVAR_NEW, "" );
idCVar r_useTemporalAA( "r_useTemporalAA", "1", CVAR_RENDERER | CVAR_BOOL | CVAR_NEW, "only disable for debugging" );
idCVar r_taaJitter( "r_taaJitter", "1", CVAR_RENDERER | CVAR_INTEGER | CVAR_NEW, "0: None, 1: MSAA, 2: Halton, 3: R2 Sequence, 4: White Noise" );
idCVar r_taaEnableHistoryClamping( "r_taaEnableHistoryClamping", "1", CVAR_RENDERER | CVAR_BOOL | CVAR_NEW, "" );
@ -355,6 +362,12 @@ bool R_UseTemporalAA()
}
}
bool R_UseHiZ()
{
// TODO check for driver problems here
return r_useHierarchicalDepthBuffer.GetBool();
}
uint R_GetMSAASamples()
{
#if ID_MSAA

View file

@ -959,6 +959,14 @@ CONSOLE_COMMAND_SHIP( bakeEnvironmentProbes, "Bake environment probes", NULL )
// make sure the game / draw thread has completed
commonLocal.WaitGameThread();
// turn vsync off for faster capturing of the probes
int oldVsync = r_swapInterval.GetInteger();
r_swapInterval.SetInteger( 0 );
// turn off clear in between views so we keep the progress bar visible
int oldClear = r_clear.GetInteger();
r_clear.SetInteger( 0 );
// disable scissor, so we don't need to adjust all those rects
r_useScissor.SetBool( false );
@ -1124,6 +1132,9 @@ CONSOLE_COMMAND_SHIP( bakeEnvironmentProbes, "Bake environment probes", NULL )
}
}
// generate .bimage file
globalImages->ImageFromFile( job->filename, TF_LINEAR, TR_CLAMP, TD_R11G11B10F, CF_2D_PACKED_MIPCHAIN );
Mem_Free( job->outBuffer );
delete job;
@ -1157,6 +1168,10 @@ CONSOLE_COMMAND_SHIP( bakeEnvironmentProbes, "Bake environment probes", NULL )
idLib::Printf( "----------------------------------\n" );
idLib::Printf( "Processed %i light probes\n", totalProcessedProbes );
common->Printf( "Baked SH irradiance and GGX mip maps in %5.1f minutes\n\n", ( totalEnd - totalStart ) / ( 1000.0f * 60 ) );
// restore vsync setting
r_swapInterval.SetInteger( oldVsync );
r_clear.SetInteger( oldClear );
}
CONSOLE_COMMAND( makeBrdfLUT, "make a GGX BRDF lookup table", NULL )

View file

@ -1162,6 +1162,10 @@ CONSOLE_COMMAND_SHIP( bakeLightGrids, "Bake irradiance/vis light grid data", NUL
int oldVsync = r_swapInterval.GetInteger();
r_swapInterval.SetInteger( 0 );
// turn off clear in between views so we keep the progress bar visible
int oldClear = r_clear.GetInteger();
r_clear.SetInteger( 0 );
idLib::Printf( "----------------------------------\n" );
idLib::Printf( "Processing %i light probes in %i areas for %i bounces\n", totalProcessedProbes, totalProcessedAreas, bounces );
//common->Printf( "ETA %5.1f minutes\n\n", ( totalEnd - totalStart ) / ( 1000.0f * 60 ) );
@ -1441,5 +1445,6 @@ CONSOLE_COMMAND_SHIP( bakeLightGrids, "Bake irradiance/vis light grid data", NUL
// restore vsync setting
r_swapInterval.SetInteger( oldVsync );
r_clear.SetInteger( oldClear );
}

View file

@ -537,6 +537,114 @@ static void R_FindClosestEnvironmentProbes()
tr.viewDef->irradianceImage = nearest->irradianceImage;
}
// form a triangle of the 3 closest probes
idVec3 verts[3];
for( int i = 0; i < 3; i++ )
{
verts[i] = viewEnvprobes[0]->parms.origin;
}
for( int i = 0; i < viewEnvprobes.Num() && i < 3; i++ )
{
RenderEnvprobeLocal* vProbe = viewEnvprobes[i];
verts[i] = vProbe->parms.origin;
}
tr.viewDef->probePositions->Set( verts[0].x, verts[0].y, verts[0].z, 1 );
tr.viewDef->probePositions->Set( verts[1].x, verts[1].y, verts[1].z, 1 );
tr.viewDef->probePositions->Set( verts[2].x, verts[2].y, verts[2].z, 1 );
idVec3 closest = R_ClosestPointPointTriangle( testOrigin, verts[0], verts[1], verts[2] );
idVec3 bary;
// find the barycentric coordinates
float denom = idWinding::TriangleArea( verts[0], verts[1], verts[2] );
if( denom == 0 )
{
// triangle is line
float t;
R_ClosestPointOnLineSegment( testOrigin, verts[0], verts[1], t );
bary.Set( 1.0f - t, t, 0 );
}
else
{
float a, b, c;
a = idWinding::TriangleArea( closest, verts[1], verts[2] ) / denom;
b = idWinding::TriangleArea( closest, verts[2], verts[0] ) / denom;
c = idWinding::TriangleArea( closest, verts[0], verts[1] ) / denom;
bary.Set( a, b, c );
}
tr.viewDef->radianceImageBlends.Set( bary.x, bary.y, bary.z, 0.0f );
for( int i = 0; i < viewEnvprobes.Num() && i < 3; i++ )
{
if( !viewEnvprobes[i]->radianceImage->IsDefaulted() )
{
tr.viewDef->radianceImages[i] = viewEnvprobes[i]->radianceImage;
}
}
}
// this one tries to interpolate between probes over time
static void R_FindClosestEnvironmentProbes2()
{
// set safe defaults
tr.viewDef->globalProbeBounds.Clear();
tr.viewDef->irradianceImage = globalImages->defaultUACIrradianceCube;
tr.viewDef->radianceImageBlends.Set( 1, 0, 0, 0 );
for( int i = 0; i < 3; i++ )
{
tr.viewDef->radianceImages[i] = globalImages->defaultUACRadianceCube;
}
// early out
if( tr.viewDef->areaNum == -1 || tr.viewDef->isSubview )
{
return;
}
idList<RenderEnvprobeLocal*, TAG_RENDER_ENVPROBE> viewEnvprobes;
for( int i = 0; i < tr.primaryWorld->envprobeDefs.Num(); i++ )
{
RenderEnvprobeLocal* vProbe = tr.primaryWorld->envprobeDefs[i];
if( vProbe )
{
// check for being closed off behind a door
if( r_useLightAreaCulling.GetBool() && vProbe->areaNum != -1 && !tr.viewDef->connectedAreas[ vProbe->areaNum ] )
{
continue;
}
viewEnvprobes.AddUnique( vProbe );
}
}
if( viewEnvprobes.Num() == 0 )
{
return;
}
idVec3 testOrigin = tr.viewDef->renderView.vieworg;
// sort by distance
// RB: each Doom 3 level has ~50 - 150 probes so this should be ok for each frame
viewEnvprobes.SortWithTemplate( idSort_CompareEnvprobe( testOrigin ) );
RenderEnvprobeLocal* nearest = viewEnvprobes[0];
tr.viewDef->globalProbeBounds = nearest->globalProbeBounds;
if( nearest->irradianceImage->IsLoaded() && !nearest->irradianceImage->IsDefaulted() )
{
tr.viewDef->irradianceImage = nearest->irradianceImage;
}
static float oldBarycentricWeights[3] = {0};
static int oldIndexes[3] = {0};
static int timeInterpolateStart = 0;
@ -559,6 +667,10 @@ static void R_FindClosestEnvironmentProbes()
triIndexes[i] = vProbe->index;
}
tr.viewDef->probePositions->Set( verts[0].x, verts[0].y, verts[0].z, 1 );
tr.viewDef->probePositions->Set( verts[1].x, verts[1].y, verts[1].z, 1 );
tr.viewDef->probePositions->Set( verts[2].x, verts[2].y, verts[2].z, 1 );
// don't assume tri changed if we just moved inside a triangle and only the indixes switched
// because one vertex is closer than before

View file

@ -28,7 +28,7 @@ If you have questions concerning this license or the applicable additional terms
// Normal Distribution Function ( NDF ) or D( h )
// GGX ( Trowbridge-Reitz )
half Distribution_GGX( half hdotN, half alpha )
float Distribution_GGX( float hdotN, float alpha )
{
// alpha is assumed to be roughness^2
float a2 = alpha * alpha;
@ -38,16 +38,17 @@ half Distribution_GGX( half hdotN, half alpha )
return ( a2 / ( PI * tmp * tmp ) );
}
half Distribution_GGX_Disney( half hdotN, half alphaG )
float Distribution_GGX_Disney( float hdotN, float alphaG )
{
float a2 = alphaG * alphaG;
float tmp = ( hdotN * hdotN ) * ( a2 - 1.0 ) + 1.0;
//tmp *= tmp;
tmp *= tmp;
return ( a2 / ( PI * tmp ) );
//return ( a2 / ( PI * tmp ) );
return ( a2 / tmp );
}
half Distribution_GGX_1886( half hdotN, half alpha )
float Distribution_GGX_1886( float hdotN, float alpha )
{
// alpha is assumed to be roughness^2
return ( alpha / ( PI * pow( hdotN * hdotN * ( alpha - 1.0 ) + 1.0, 2.0 ) ) );
@ -55,16 +56,16 @@ half Distribution_GGX_1886( half hdotN, half alpha )
// Fresnel term F( v, h )
// Fnone( v, h ) = F(0) = specularColor
half3 Fresnel_Schlick( half3 specularColor, half vDotN )
float3 Fresnel_Schlick( float3 specularColor, float vDotN )
{
return specularColor + ( 1.0 - specularColor ) * pow( 1.0 - vDotN, 5.0 );
}
// Fresnel term that takes roughness into account so rough non-metal surfaces aren't too shiny [Lagarde11]
half3 Fresnel_SchlickRoughness( half3 specularColor, half vDotN, half roughness )
float3 Fresnel_SchlickRoughness( float3 specularColor, float vDotN, float roughness )
{
float oneMinusRoughness = 1.0 - roughness;
return specularColor + ( max( half3( oneMinusRoughness, oneMinusRoughness, oneMinusRoughness ), specularColor ) - specularColor ) * pow( 1.0 - vDotN, 5.0 );
return specularColor + ( max( float3( oneMinusRoughness, oneMinusRoughness, oneMinusRoughness ), specularColor ) - specularColor ) * pow( 1.0 - vDotN, 5.0 );
}
// Sebastien Lagarde proposes an empirical approach to derive the specular occlusion term from the diffuse occlusion term in [Lagarde14].
@ -77,21 +78,21 @@ float ComputeSpecularAO( float vDotN, float ao, float roughness )
// Visibility term G( l, v, h )
// Very similar to Marmoset Toolbag 2 and gives almost the same results as Smith GGX
float Visibility_Schlick( half vdotN, half ldotN, float alpha )
float Visibility_Schlick( float vdotN, float ldotN, float alpha )
{
float k = alpha * 0.5;
float schlickL = ( ldotN * ( 1.0 - k ) + k );
float schlickV = ( vdotN * ( 1.0 - k ) + k );
return ( 0.25 / ( schlickL * schlickV ) );
return ( 0.25 / max( 0.001, schlickL * schlickV ) );
//return ( ( schlickL * schlickV ) / ( 4.0 * vdotN * ldotN ) );
}
// see s2013_pbs_rad_notes.pdf
// Crafting a Next-Gen Material Pipeline for The Order: 1886
// this visibility function also provides some sort of back lighting
float Visibility_SmithGGX( half vdotN, half ldotN, float alpha )
float Visibility_SmithGGX( float vdotN, float ldotN, float alpha )
{
// alpha is already roughness^2
@ -102,7 +103,7 @@ float Visibility_SmithGGX( half vdotN, half ldotN, float alpha )
return ( 1.0 / max( V1 * V2, 0.15 ) );
}
// HACK calculate roughness from D3 gloss maps
// RB: HACK calculate roughness from D3 gloss maps
float EstimateLegacyRoughness( float3 specMapSRGB )
{
float Y = dot( LUMINANCE_SRGB.rgb, specMapSRGB );
@ -115,45 +116,143 @@ float EstimateLegacyRoughness( float3 specMapSRGB )
return roughness;
}
#define KENNY_PBR 1
// Kennedith98 begin
// takes a gamma-space specular texture
// outputs F0 color and roughness for PBR
void PBRFromSpecmap( float3 specMap, out float3 F0, out float roughness )
{
// desaturate specular
//float specLum = max( specMap.r, max( specMap.g, specMap.b ) );
float specLum = dot( LUMINANCE_SRGB.rgb, specMap );
// fresnel base
F0 = _float3( 0.04 );
// fresnel contrast (will tighten low spec and broaden high spec, stops specular looking too flat or shiny)
float contrastMid = 0.214;
float contrastAmount = 2.0;
float contrast = saturate( ( specLum - contrastMid ) / ( 1 - contrastMid ) ); //high spec
contrast += saturate( specLum / contrastMid ) - 1.0; //low spec
contrast = exp2( contrastAmount * contrast );
F0 *= contrast;
// reverse blinn BRDF to perfectly match vanilla specular brightness
// fresnel is affected when specPow is 0, experimentation is desmos showed that happens at F0/4
float linearBrightness = Linear1( 2.0 * specLum );
float specPow = max( 0.0, ( ( 8 * linearBrightness ) / F0.y ) - 2.0 );
F0 *= min( 1.0, linearBrightness / ( F0.y * 0.25 ) );
// specular power to roughness
roughness = sqrt( 2.0 / ( specPow + 2.0 ) );
#if 1
// RB: try to distinct between dielectrics and metal materials
float glossiness = saturate( 1.0 - roughness );
float metallic = step( 0.7, glossiness );
float3 glossColor = Linear3( specMap.rgb );
F0 = lerp( F0, glossColor, metallic );
#endif
// RB: do another sqrt because PBR shader squares it
roughness = sqrt( roughness );
}
// Kennedith98 end
// https://yusuketokuyoshi.com/papers/2021/Tokuyoshi2021SAA.pdf
float2x2 NonAxisAlignedNDFFiltering( float3 halfvectorTS, float2 roughness2 )
{
// Compute the derivatives of the halfvector in the projected space.
float2 halfvector2D = halfvectorTS.xy / abs( halfvectorTS.z );
float2 deltaU = ddx( halfvector2D );
float2 deltaV = ddy( halfvector2D );
// Compute 2 * covariance matrix for the filter kernel (Eq. (3)).
float SIGMA2 = 0.15915494;
float2x2 delta = {deltaU, deltaV};
float2x2 kernelRoughnessMat = 2.0 * SIGMA2 * mul( transpose( delta ), delta );
// Approximate NDF filtering (Eq. (9)).
float2x2 roughnessMat = {roughness2.x, 0.0, 0.0, roughness2.y};
float2x2 filteredRoughnessMat = roughnessMat + kernelRoughnessMat;
return filteredRoughnessMat;
}
float2 AxisAlignedNDFFiltering( float3 halfvectorTS, float2 roughness2 )
{
// Compute the bounding rectangle of halfvector derivatives.
float2 halfvector2D = halfvectorTS.xy / abs( halfvectorTS.z );
float2 bounds = fwidth( halfvector2D );
// Compute an axis-aligned filter kernel from the bounding rectangle.
float SIGMA2 = 0.15915494;
float2 kernelRoughness2 = 2.0 * SIGMA2 * ( bounds * bounds );
// Approximate NDF filtering (Eq. (9)).
// We clamp the kernel size to avoid overfiltering.
float KAPPA = 0.18;
float2 clampedKernelRoughness2 = min( kernelRoughness2, KAPPA );
float2 filteredRoughness2 = saturate( roughness2 + clampedKernelRoughness2 );
return filteredRoughness2;
}
float IsotropicNDFFiltering( float3 normal, float roughness2 )
{
const float SIGMA2 = 0.15915494;
const float KAPPA = 0.18;
float3 dndu = ddx( normal );
float3 dndv = ddy( normal );
float kernelRoughness2 = 2.0 * SIGMA2 * ( dot( dndu, dndu ) + dot( dndv, dndv ) );
float clampedKernelRoughness2 = min( kernelRoughness2, KAPPA );
float filteredRoughness2 = saturate( roughness2 + clampedKernelRoughness2 );
return filteredRoughness2;
}
// Environment BRDF approximations
// see s2013_pbs_black_ops_2_notes.pdf
/*
half a1vf( half g )
float a1vf( float g )
{
return ( 0.25 * g + 0.75 );
}
half a004( half g, half vdotN )
float a004( float g, float vdotN )
{
float t = min( 0.475 * g, exp2( -9.28 * vdotN ) );
return ( t + 0.0275 ) * g + 0.015;
}
half a0r( half g, half vdotN )
float a0r( float g, float vdotN )
{
return ( ( a004( g, vdotN ) - a1vf( g ) * 0.04 ) / 0.96 );
}
float3 EnvironmentBRDF( half g, half vdotN, float3 rf0 )
float3 EnvironmentBRDF( float g, float vdotN, float3 rf0 )
{
float4 t = float4( 1.0 / 0.96, 0.475, ( 0.0275 - 0.25 * 0.04 ) / 0.96, 0.25 );
t *= float4( g, g, g, g );
t += float4( 0.0, 0.0, ( 0.015 - 0.75 * 0.04 ) / 0.96, 0.75 );
half a0 = t.x * min( t.y, exp2( -9.28 * vdotN ) ) + t.z;
half a1 = t.w;
float a0 = t.x * min( t.y, exp2( -9.28 * vdotN ) ) + t.z;
float a1 = t.w;
return saturate( a0 + rf0 * ( a1 - a0 ) );
}
half3 EnvironmentBRDFApprox( half roughness, half vdotN, half3 specularColor )
float3 EnvironmentBRDFApprox( float roughness, float vdotN, float3 specularColor )
{
const half4 c0 = half4( -1, -0.0275, -0.572, 0.022 );
const half4 c1 = half4( 1, 0.0425, 1.04, -0.04 );
const float4 c0 = float4( -1, -0.0275, -0.572, 0.022 );
const float4 c1 = float4( 1, 0.0425, 1.04, -0.04 );
half4 r = roughness * c0 + c1;
half a004 = min( r.x * r.x, exp2( -9.28 * vdotN ) ) * r.x + r.y;
half2 AB = half2( -1.04, 1.04 ) * a004 + r.zw;
float4 r = roughness * c0 + c1;
float a004 = min( r.x * r.x, exp2( -9.28 * vdotN ) ) * r.x + r.y;
float2 AB = float2( -1.04, 1.04 ) * a004 + r.zw;
return specularColor * AB.x + AB.y;
}

View file

@ -177,6 +177,7 @@ float3 reconstructCSPosition( float2 S, float depth )
csP.w = dot4( rpModelMatrixW, clip );
csP.xyz /= csP.w;
//csP.z = abs( csP.z ); // this is still negative Z like for OpenGL
return csP.xyz;
}

View file

@ -157,7 +157,7 @@ void main( VS_IN vertex, out VS_OUT result )
//result.texcoord1.z = dot3( toEye, rpModelMatrixZ );
#if 1
// rotate into world space
// rotate from tangent space into world space
result.texcoord2.x = dot3( tangent, rpModelMatrixX );
result.texcoord3.x = dot3( tangent, rpModelMatrixY );
result.texcoord4.x = dot3( tangent, rpModelMatrixZ );
@ -171,7 +171,7 @@ void main( VS_IN vertex, out VS_OUT result )
result.texcoord4.z = dot3( normal, rpModelMatrixZ );
#else
// rotate into view space
// rotate from tangent space into view space
result.texcoord2.x = dot3( tangent, rpModelViewMatrixX );
result.texcoord3.x = dot3( tangent, rpModelViewMatrixY );
result.texcoord4.x = dot3( tangent, rpModelViewMatrixZ );

View file

@ -0,0 +1,491 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2024 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "global_inc.hlsl"
// *INDENT-OFF*
Texture2D t_NormalMap : register( t0 VK_DESCRIPTOR_SET( 1 ) );
Texture2D t_ScreenColor : register( t1 VK_DESCRIPTOR_SET( 1 ) );
Texture2D t_ScreenNormals : register( t2 VK_DESCRIPTOR_SET( 1 ) );
Texture2D t_Depth : register( t3 VK_DESCRIPTOR_SET( 1 ) );
Texture2D t_RadianceCubeMap1 : register( t4 VK_DESCRIPTOR_SET( 1 ) );
Texture2D t_RadianceCubeMap2 : register( t5 VK_DESCRIPTOR_SET( 1 ) );
Texture2D t_RadianceCubeMap3 : register( t6 VK_DESCRIPTOR_SET( 1 ) );
SamplerState s_Material : register( s0 VK_DESCRIPTOR_SET( 2 ) );
SamplerState s_LinearClamp : register( s1 VK_DESCRIPTOR_SET( 2 ) );
struct PS_IN
{
float4 position : SV_Position;
float2 texcoord0 : TEXCOORD0_centroid;
float3 texcoord1 : TEXCOORD1_centroid;
float3 texcoord2 : TEXCOORD2_centroid;
float3 texcoord3 : TEXCOORD3_centroid;
float3 texcoord4 : TEXCOORD4_centroid;
float4 texcoord5 : TEXCOORD5_centroid;
float4 color : COLOR0;
};
struct PS_OUT
{
float4 color : SV_Target0;
};
// *INDENT-ON*
float3 ReconstructPositionCS( int2 hitPixel )
{
// Load returns 0 for any value accessed out of bounds
float depth = texelFetch( t_Depth, hitPixel, 0 ).r;
float2 uv = hitPixel * rpWindowCoord.xy;
// derive clip space from the depth buffer and screen position
float3 ndc = float3( uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, depth );
float clipW = -rpProjectionMatrixZ.w / ( -rpProjectionMatrixZ.z - ndc.z );
float4 clip = float4( ndc * clipW, clipW );
// camera space position
float4 csP;
csP.x = dot4( rpShadowMatrices[0], clip );
csP.y = dot4( rpShadowMatrices[1], clip );
csP.z = dot4( rpShadowMatrices[2], clip );
csP.w = dot4( rpShadowMatrices[3], clip );
csP.xyz /= csP.w;
//csP.z = abs( csP.z ); // this is still negative Z like for OpenGL
return csP.xyz;
}
float DistanceSquared( float2 a, float2 b )
{
a -= b;
return dot( a, a );
}
void Swap( inout float a, inout float b )
{
float t = a;
a = b;
b = t;
}
bool IntersectsDepthBuffer( float z, float minZ, float maxZ, float zThickness )
{
/*
* Based on how far away from the camera the depth is,
* adding a bit of extra thickness can help improve some
* artifacts. Driving this value up too high can cause
* artifacts of its own.
*/
const float strideZCutoff = 100.0 * METERS_TO_DOOM;
//float depthScale = min( 1.0, z * strideZCutoff );
//z += zThickness + lerp( 0.0, 2.0, depthScale );
//return ( maxZ >= z ) && ( minZ - zThickness <= z );
// like original version with negative linear Z
return ( maxZ >= z - zThickness ) && ( minZ <= z );
}
// From the Efficient GPU Screen-Space Ray Tracing paper
// By Morgan McGuire and Michael Mara at Williams College 2014
// Released as open source under the BSD 2-Clause License
// http://opensource.org/licenses/BSD-2-Clause
// Returns true if the ray hit something
bool TraceScreenSpaceRay(
// Camera-space ray origin, which must be within the view volume
float3 rayStart,
// Unit length camera-space ray direction
float3 rayDir,
// Camera space thickness to ascribe to each pixel in the depth buffer
float zThickness,
// Stride samples trades quality for performance
float _stride,
// Number between 0 and 1 for how far to bump the ray in stride units
// to conceal banding artifacts. Not needed if stride == 1.
float jitter,
// Maximum number of iterations. Higher gives better images but may be slow
const float maxSteps,
// Maximum camera-space distance to trace before returning a miss
const float maxDistance,
// Pixel coordinates of the first intersection with the scene
out float2 hitPixel,
// Camera space location of the ray hit
out float3 hitPoint,
out float3 rayDebug )
{
const float nearPlaneZ = 3.0;
// Clip to the near plane
float rayLength = ( ( rayStart.z + rayDir.z * maxDistance ) < nearPlaneZ ) ?
( nearPlaneZ - rayStart.z ) / rayDir.z : maxDistance;
//float rayLength = 10000;
float4 rayEnd = float4( rayStart + rayDir * rayLength, 1.0 );
// Project into homogeneous clip space
float4 ray4D = float4( rayStart, 1.0 );
float4 H0;
H0.x = dot4( ray4D, rpShadowMatrices[4] );
H0.y = dot4( ray4D, rpShadowMatrices[5] );
H0.z = dot4( ray4D, rpShadowMatrices[6] );
H0.w = dot4( ray4D, rpShadowMatrices[7] );
float4 H1;
H1.x = dot4( rayEnd, rpShadowMatrices[4] );
H1.y = dot4( rayEnd, rpShadowMatrices[5] );
H1.z = dot4( rayEnd, rpShadowMatrices[6] );
H1.w = dot4( rayEnd, rpShadowMatrices[7] );
float k0 = 1.0f / H0.w;
float k1 = 1.0f / H1.w;
// Switch the original points to values that interpolate linearly in 2D
float3 Q0 = rayStart * k0;
float3 Q1 = rayEnd.xyz * k1;
// Screen-space endpoints
float2 P0 = H0.xy * k0;
float2 P1 = H1.xy * k1;
// Initialize to off screen
hitPixel = float2( -1.0, -1.0 );
// If the line is degenerate, make it cover at least one pixel
// to avoid handling zero-pixel extent as a special case later
P1 += ( DistanceSquared( P0, P1 ) < 0.0001 ) ? float2( 0.01, 0.01 ) : 0.0;
float2 delta = P1 - P0;
// Permute so that the primary iteration is in x to collapse
// all quadrant-specific DDA cases later
bool permute = false;
if( abs( delta.x ) < abs( delta.y ) )
{
// This is a more-vertical line
permute = true;
delta = delta.yx;
P0 = P0.yx;
P1 = P1.yx;
}
// From now on, "x" is the primary iteration direction and "y" is the secondary one
float stepDir = sign( delta.x );
float invdx = stepDir / delta.x;
float2 dP = float2( stepDir, delta.y * invdx );
// Track the derivatives of Q and k
float3 dQ = ( Q1 - Q0 ) * invdx;
float dk = ( k1 - k0 ) * invdx;
const float strideZCutoff = 100.0 * METERS_TO_DOOM;
// Scale derivatives by the desired pixel stride and then
// offset the starting values by the jitter fraction
//float strideScale = 1.0f - min( 1.0f, rayStart.z * strideZCutoff );
//float stride = 1.0f + strideScale * _stride;
float stride = _stride;
dP *= stride;
dQ *= stride;
dk *= stride;
P0 += dP * jitter;
Q0 += dQ * jitter;
k0 += dk * jitter;
// Slide P from P0 to P1, (now-homogeneous) Q from Q0 to Q1, k from k0 to k1
float3 Q = Q0;
float k = k0;
// We track the ray depth at +/- 1/2 pixel to treat pixels as clip-space solid
// voxels. Because the depth at -1/2 for a given pixel will be the same as at
// +1/2 for the previous iteration, we actually only have to compute one value
// per iteration.
float stepCount = 0.0;
float prevZMaxEstimate = rayStart.z;
float rayZMin = prevZMaxEstimate;
float rayZMax = prevZMaxEstimate;
float sceneZMax = rayZMax + 1.0 * METERS_TO_DOOM;
// P1.x is never modified after this point, so pre-scale it by
// the step direction for a signed comparison
float end = P1.x * stepDir;
// We only advance the z field of Q in the inner loop, since
// Q.xy is never used until after the loop terminates.
for( float2 P = P0;
( ( P.x * stepDir ) <= end ) &&
( stepCount < maxSteps ) &&
//!IntersectsDepthBuffer( sceneZMax, rayZMin, rayZMax, zThickness ) &&
( ( rayZMax < sceneZMax - zThickness ) || ( rayZMin > sceneZMax ) ) &&
( sceneZMax != 0.0f );
P += dP, Q.z += dQ.z, k += dk, stepCount += 1.0 )
{
hitPixel = permute ? P.yx : P;
// The depth range that the ray covers within this loop
// iteration. Assume that the ray is moving in increasing z
// and swap if backwards. Because one end of the interval is
// shared between adjacent iterations, we track the previous
// value and then swap as needed to ensure correct ordering
rayZMin = prevZMaxEstimate;
// Compute the value at 1/2 pixel into the future
rayZMax = ( dQ.z * 0.5 + Q.z ) / ( dk * 0.5 + k );
prevZMaxEstimate = rayZMax;
if( rayZMin > rayZMax )
{
Swap( rayZMin, rayZMax );
}
// You may need hitPixel.y = depthBufferSize.y - hitPixel.y; here if your vertical axis
// is different than ours in screen space
//hitPixel.x = rpWindowCoord.z - hitPixel.x;
//hitPixel.y = rpWindowCoord.w - hitPixel.y;
sceneZMax = ReconstructPositionCS( hitPixel ).z;
}
// Advance Q based on the number of steps
Q.xy += dQ.xy * stepCount;
hitPoint = Q * ( 1.0f / k );
//rayDebug.xyz = _float3( stepCount );
return IntersectsDepthBuffer( sceneZMax, rayZMin, rayZMax, zThickness );
}
float2 GetSampleVector( float3 reflectionVector )
{
float2 normalizedOctCoord = octEncode( reflectionVector );
float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + _float2( 1.0 ) ) * 0.5;
return normalizedOctCoordZeroOne;
}
void main( PS_IN fragment, out PS_OUT result )
{
float4 bump = t_NormalMap.Sample( s_Material, fragment.texcoord0 ) * 2.0f - 1.0f;
// RB begin
float3 localNormal;
#if defined(USE_NORMAL_FMT_RGB8)
localNormal = float3( bump.rg, 0.0f );
#else
localNormal = float3( bump.wy, 0.0f );
#endif
// RB end
localNormal.z = sqrt( 1.0f - dot3( localNormal, localNormal ) );
float3 globalNormal;
globalNormal.x = dot3( localNormal, fragment.texcoord2 );
globalNormal.y = dot3( localNormal, fragment.texcoord3 );
globalNormal.z = dot3( localNormal, fragment.texcoord4 );
float3 screenNormalWS = ( ( 2.0 * t_ScreenNormals.Sample( s_LinearClamp, fragment.position.xy * rpWindowCoord.xy ).rgb ) - 1.0 );
// https://blog.selfshadow.com/publications/blending-in-detail/
// UDN blending
//globalNormal = normalize( float3( screenNormalWS.xy + globalNormal.xy, screenNormalWS.z ) );
// Whiteout blending
globalNormal = normalize( float3( screenNormalWS.xy + globalNormal.xy, screenNormalWS.z * globalNormal.z ) );
float3 globalPosition = fragment.texcoord5.xyz;
float3 globalView = normalize( globalPosition - rpGlobalEyePos.xyz );
float3 reflectionVector = reflect( globalView, globalNormal );
reflectionVector = normalize( reflectionVector );
float2 octCoord0 = GetSampleVector( reflectionVector );
float2 octCoord1 = octCoord0;
float2 octCoord2 = octCoord0;
float3 rayStart = globalPosition;
#if 1
// parallax box correction using portal area bounds
float hitScale = 0.0;
float3 bounds[2];
bounds[0].x = rpWobbleSkyX.x;
bounds[0].y = rpWobbleSkyX.y;
bounds[0].z = rpWobbleSkyX.z;
bounds[1].x = rpWobbleSkyY.x;
bounds[1].y = rpWobbleSkyY.y;
bounds[1].z = rpWobbleSkyY.z;
// we can't start inside the box so move this outside and use the reverse path
rayStart += reflectionVector * 10000.0;
// only do a box <-> ray intersection test if we use a local cubemap
if( ( rpWobbleSkyX.w > 0.0 ) && AABBRayIntersection( bounds, rayStart, -reflectionVector, hitScale ) )
{
float3 hitPoint = rayStart - reflectionVector * hitScale;
// rpWobbleSkyZ is cubemap center
#if 1
reflectionVector = hitPoint - rpWobbleSkyZ.xyz;
octCoord0 = octCoord1 = octCoord2 = GetSampleVector( reflectionVector );
#else
// this should look better but only works in the case all 3 probes are in this area bbox
octCoord0 = GetSampleVector( hitPoint - rpTexGen0S.xyz );
octCoord1 = GetSampleVector( hitPoint - rpTexGen0T.xyz );
octCoord2 = GetSampleVector( hitPoint - rpTexGen0Q.xyz );
#endif
}
#endif
const float mip = 0;
float3 radiance = t_RadianceCubeMap1.SampleLevel( s_LinearClamp, octCoord0, mip ).rgb * rpLocalLightOrigin.x;
radiance += t_RadianceCubeMap2.SampleLevel( s_LinearClamp, octCoord1, mip ).rgb * rpLocalLightOrigin.y;
radiance += t_RadianceCubeMap3.SampleLevel( s_LinearClamp, octCoord2, mip ).rgb * rpLocalLightOrigin.z;
#if 1
// Screen Space Reflections
float3 rayDir;
float3 viewNormal;
// TODO this should be rpViewMatrixX
viewNormal.x = dot3( rpModelViewMatrixX, globalNormal );
viewNormal.y = dot3( rpModelViewMatrixY, globalNormal );
viewNormal.z = dot3( rpModelViewMatrixZ, globalNormal );
rayStart = ReconstructPositionCS( fragment.position.xy );
float3 V;
V = normalize( rayStart );
reflectionVector = reflect( V, viewNormal );
rayDir = normalize( reflectionVector );
// use forward vector instead of V to avoid bending
float vDotR = ( dot3( float3( 0, 0, 1 ), reflectionVector ) );
const float maxSteps = rpJitterTexScale.x;
float2 hitPixel;
float3 hitPoint;
float3 rayDebug = float3( 0, 0, 1 );
bool intersection = false;
float jitter = 1.0;
//jitter = ( int( fragment.position.x + fragment.position.y) & 1 ) * 0.5; // like in the paper but sucks
jitter = InterleavedGradientNoise( fragment.position.xy );
//jitter = InterleavedGradientNoiseAnim( fragment.position.xy, rpJitterTexOffset.w );
jitter = lerp( 1.0, jitter, rpGlobalLightOrigin.w );
// using the same jitter on probe fallback to make it seamless
// looks kinda bad because on close ups you don't want to see the noise
//radiance *= jitter;
if( vDotR <= 0 )
{
intersection = TraceScreenSpaceRay(
rayStart,
rayDir,
rpGlobalLightOrigin.z, // zThickness 0.5
rpGlobalLightOrigin.x, // stride
jitter, // jitter
maxSteps, // max steps
rpGlobalLightOrigin.y * METERS_TO_DOOM, // max Distance
hitPixel,
hitPoint,
rayDebug );
}
float2 delta = ( hitPixel * rpWindowCoord.xy ) - ( fragment.position.xy * rpWindowCoord.xy );
float deltaLen = length( delta );
if( ( hitPixel.x > rpWindowCoord.z || hitPixel.x < 0.0 || hitPixel.y > rpWindowCoord.w || hitPixel.y < 0.0 ) )
{
intersection = false;
}
if( intersection )
{
radiance = float3( 0, 1, 0 );
radiance = t_ScreenColor.Sample( s_LinearClamp, hitPixel * rpWindowCoord.xy ).rgb;
//radiance = float3( delta, 0 );
//radiance = float3( 0, deltaLen, 0 );
//radiance = rayDebug / maxSteps;
//radiance = float3( hitPixel * rpWindowCoord.xy, 0 );
}
else
{
/*
if( vDotR > 0.0 )
{
radiance = float3( 1, 0, 0 );
}
else
{
radiance = float3( 0, 0, 1 );
}
*/
//radiance = rayDebug;
//discard;
}
#endif
// give it a red blood tint
//radiance *= float3( 0.5, 0.25, 0.25 );
// make this really dark although it is already in linear RGB
radiance = sRGBToLinearRGB( radiance.xyz );
result.color = float4( radiance, 1.0 ) * fragment.color;
}

View file

@ -0,0 +1,179 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2024 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "global_inc.hlsl"
// *INDENT-OFF*
#if USE_GPU_SKINNING
StructuredBuffer<float4> matrices : register(t11);
#endif
struct VS_IN
{
float4 position : POSITION;
float2 texcoord : TEXCOORD0;
float4 normal : NORMAL;
float4 tangent : TANGENT;
float4 color : COLOR0;
float4 color2 : COLOR1;
};
struct VS_OUT
{
float4 position : SV_Position;
float2 texcoord0 : TEXCOORD0_centroid;
float3 texcoord1 : TEXCOORD1_centroid;
float3 texcoord2 : TEXCOORD2_centroid;
float3 texcoord3 : TEXCOORD3_centroid;
float3 texcoord4 : TEXCOORD4_centroid;
float4 texcoord5 : TEXCOORD5_centroid;
float4 color : COLOR0;
};
// *INDENT-ON*
void main( VS_IN vertex, out VS_OUT result )
{
float4 vNormal = vertex.normal * 2.0 - 1.0;
float4 vTangent = vertex.tangent * 2.0 - 1.0;
float3 vBitangent = cross( vNormal.xyz, vTangent.xyz ) * vTangent.w;
#if USE_GPU_SKINNING
//--------------------------------------------------------------
// GPU transformation of the normal / tangent / bitangent
//
// multiplying with 255.1 give us the same result and is faster than floor( w * 255 + 0.5 )
//--------------------------------------------------------------
const float w0 = vertex.color2.x;
const float w1 = vertex.color2.y;
const float w2 = vertex.color2.z;
const float w3 = vertex.color2.w;
float4 matX, matY, matZ; // must be float4 for vec4
int joint = int( vertex.color.x * 255.1 * 3.0 );
matX = matrices[int( joint + 0 )] * w0;
matY = matrices[int( joint + 1 )] * w0;
matZ = matrices[int( joint + 2 )] * w0;
joint = int( vertex.color.y * 255.1 * 3.0 );
matX += matrices[int( joint + 0 )] * w1;
matY += matrices[int( joint + 1 )] * w1;
matZ += matrices[int( joint + 2 )] * w1;
joint = int( vertex.color.z * 255.1 * 3.0 );
matX += matrices[int( joint + 0 )] * w2;
matY += matrices[int( joint + 1 )] * w2;
matZ += matrices[int( joint + 2 )] * w2;
joint = int( vertex.color.w * 255.1 * 3.0 );
matX += matrices[int( joint + 0 )] * w3;
matY += matrices[int( joint + 1 )] * w3;
matZ += matrices[int( joint + 2 )] * w3;
float3 normal;
normal.x = dot3( matX, vNormal );
normal.y = dot3( matY, vNormal );
normal.z = dot3( matZ, vNormal );
normal = normalize( normal );
float3 tangent;
tangent.x = dot3( matX, vTangent );
tangent.y = dot3( matY, vTangent );
tangent.z = dot3( matZ, vTangent );
tangent = normalize( tangent );
float3 bitangent;
bitangent.x = dot3( matX, vBitangent );
bitangent.y = dot3( matY, vBitangent );
bitangent.z = dot3( matZ, vBitangent );
bitangent = normalize( bitangent );
float4 modelPosition;
modelPosition.x = dot4( matX, vertex.position );
modelPosition.y = dot4( matY, vertex.position );
modelPosition.z = dot4( matZ, vertex.position );
modelPosition.w = 1.0;
#else
float4 modelPosition = vertex.position;
float3 normal = vNormal.xyz;
float3 tangent = vTangent.xyz;
float3 bitangent = vBitangent.xyz;
#endif
result.position.x = dot4( modelPosition, rpMVPmatrixX );
result.position.y = dot4( modelPosition, rpMVPmatrixY );
result.position.z = dot4( modelPosition, rpMVPmatrixZ );
result.position.w = dot4( modelPosition, rpMVPmatrixW );
result.position.xyz = psxVertexJitter( result.position );
result.texcoord0 = vertex.texcoord.xy;
// PSX affine texture mapping
#if 0
if( rpPSXDistortions.z > 0.0 )
{
float distance = length( rpLocalViewOrigin - modelPosition );
float warp = psxAffineWarp( distance );
result.texcoord0.z = warp;
result.texcoord0.xy *= warp;
result.texcoord1.xy *= warp;
result.texcoord2.xy *= warp;
}
#endif
float4 toEye = rpLocalViewOrigin - modelPosition;
result.texcoord1.x = dot3( toEye, rpModelMatrixX );
result.texcoord1.y = dot3( toEye, rpModelMatrixY );
result.texcoord1.z = dot3( toEye, rpModelMatrixZ );
// rotate from tangent space into world space
result.texcoord2.x = dot3( tangent, rpModelMatrixX );
result.texcoord3.x = dot3( tangent, rpModelMatrixY );
result.texcoord4.x = dot3( tangent, rpModelMatrixZ );
result.texcoord2.y = dot3( bitangent, rpModelMatrixX );
result.texcoord3.y = dot3( bitangent, rpModelMatrixY );
result.texcoord4.y = dot3( bitangent, rpModelMatrixZ );
result.texcoord2.z = dot3( normal, rpModelMatrixX );
result.texcoord3.z = dot3( normal, rpModelMatrixY );
result.texcoord4.z = dot3( normal, rpModelMatrixZ );
float4 worldPosition;
worldPosition.x = dot4( modelPosition, rpModelMatrixX );
worldPosition.y = dot4( modelPosition, rpModelMatrixY );
worldPosition.z = dot4( modelPosition, rpModelMatrixZ );
worldPosition.w = dot4( modelPosition, rpModelMatrixW );
result.texcoord5 = worldPosition;
result.color = rpColor;
}

View file

@ -69,61 +69,7 @@ struct PS_OUT
// *INDENT-ON*
// RB: TODO OPTIMIZE
// this is a straight port of idBounds::RayIntersection
bool AABBRayIntersection( float3 b[2], float3 start, float3 dir, out float scale )
{
int i, ax0, ax1, ax2, side, inside;
float f;
float3 hit;
ax0 = -1;
inside = 0;
for( i = 0; i < 3; i++ )
{
if( start[i] < b[0][i] )
{
side = 0;
}
else if( start[i] > b[1][i] )
{
side = 1;
}
else
{
inside++;
continue;
}
if( dir[i] == 0.0f )
{
continue;
}
f = ( start[i] - b[side][i] );
if( ax0 < 0 || abs( f ) > abs( scale * dir[i] ) )
{
scale = - ( f / dir[i] );
ax0 = i;
}
}
if( ax0 < 0 )
{
scale = 0.0f;
// return true if the start point is inside the bounds
return ( inside == 3 );
}
ax1 = ( ax0 + 1 ) % 3;
ax2 = ( ax0 + 2 ) % 3;
hit[ax1] = start[ax1] + scale * dir[ax1];
hit[ax2] = start[ax2] + scale * dir[ax2];
return ( hit[ax1] >= b[0][ax1] && hit[ax1] <= b[1][ax1] &&
hit[ax2] >= b[0][ax2] && hit[ax2] <= b[1][ax2] );
}
void main( PS_IN fragment, out PS_OUT result )
{
@ -223,11 +169,20 @@ void main( PS_IN fragment, out PS_OUT result )
float3 kS = Fresnel_SchlickRoughness( specularColor, vDotN, roughness );
float3 kD = ( float3( 1.0, 1.0, 1.0 ) - kS ) * ( 1.0 - metallic );
#else
#if KENNY_PBR
float3 diffuseColor = diffuseMap;
float3 specularColor;
float roughness;
PBRFromSpecmap( specMapSRGB.rgb, specularColor, roughness );
#else
const float roughness = EstimateLegacyRoughness( specMapSRGB.rgb );
float3 diffuseColor = diffuseMap;
float3 specularColor = specMap.rgb;
#endif
#if defined( DEBUG_PBR )
diffuseColor = float3( 0.0, 0.0, 0.0 );

View file

@ -69,61 +69,6 @@ struct PS_OUT
// *INDENT-ON*
// RB: TODO OPTIMIZE
// this is a straight port of idBounds::RayIntersection
bool AABBRayIntersection( float3 b[2], float3 start, float3 dir, out float scale )
{
int i, ax0, ax1, ax2, side, inside;
float f;
float3 hit;
ax0 = -1;
inside = 0;
for( i = 0; i < 3; i++ )
{
if( start[i] < b[0][i] )
{
side = 0;
}
else if( start[i] > b[1][i] )
{
side = 1;
}
else
{
inside++;
continue;
}
if( dir[i] == 0.0f )
{
continue;
}
f = ( start[i] - b[side][i] );
if( ax0 < 0 || abs( f ) > abs( scale * dir[i] ) )
{
scale = - ( f / dir[i] );
ax0 = i;
}
}
if( ax0 < 0 )
{
scale = 0.0f;
// return true if the start point is inside the bounds
return ( inside == 3 );
}
ax1 = ( ax0 + 1 ) % 3;
ax2 = ( ax0 + 2 ) % 3;
hit[ax1] = start[ax1] + scale * dir[ax1];
hit[ax2] = start[ax2] + scale * dir[ax2];
return ( hit[ax1] >= b[0][ax1] && hit[ax1] <= b[1][ax1] &&
hit[ax2] >= b[0][ax2] && hit[ax2] <= b[1][ax2] );
}
float2 OctTexCoord( float3 worldDir )
@ -243,11 +188,20 @@ void main( PS_IN fragment, out PS_OUT result )
float3 kS = Fresnel_SchlickRoughness( specularColor, vDotN, roughness );
float3 kD = ( float3( 1.0, 1.0, 1.0 ) - kS ) * ( 1.0 - metallic );
#else
#if KENNY_PBR
float3 diffuseColor = diffuseMap;
float3 specularColor;
float roughness;
PBRFromSpecmap( specMapSRGB.rgb, specularColor, roughness );
#else
const float roughness = EstimateLegacyRoughness( specMapSRGB.rgb );
float3 diffuseColor = diffuseMap;
float3 specularColor = specMap.rgb;
#endif
#if defined( DEBUG_PBR )
diffuseColor = float3( 0.0, 0.0, 0.0 );

View file

@ -116,9 +116,9 @@ void main( PS_IN fragment, out PS_OUT result )
float hdotN = clamp( dot3( halfAngleVector, localNormal ), 0.0, 1.0 );
#if USE_PBR
// RB: roughness 0 somehow is not shiny so we clamp it
float roughness = max( 0.05, specMapSRGB.r );
const float metallic = specMapSRGB.g;
const float roughness = specMapSRGB.r;
const float glossiness = 1.0 - roughness;
// the vast majority of real-world materials (anything not metal or gems) have F(0)
// values in a very narrow range (~0.02 - 0.08)
@ -131,6 +131,13 @@ void main( PS_IN fragment, out PS_OUT result )
float3 diffuseColor = baseColor * ( 1.0 - metallic );
float3 specularColor = lerp( dielectricColor, baseColor, metallic );
#elif KENNY_PBR
float3 diffuseColor = diffuseMap;
float3 specularColor;
float roughness;
PBRFromSpecmap( specMapSRGB.rgb, specularColor, roughness );
#else
const float roughness = EstimateLegacyRoughness( specMapSRGB.rgb );
@ -139,8 +146,10 @@ void main( PS_IN fragment, out PS_OUT result )
#endif
// RB: compensate r_lightScale 3 and the division of Pi
// RB FIXME or not: compensate r_lightScale 3 and the division of Pi
//lambert *= 1.3;
// see http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
//lambert /= PI;
// rpDiffuseModifier contains light color multiplier
float3 lightColor = sRGBToLinearRGB( lightProj.xyz * lightFalloff.xyz );
@ -149,8 +158,8 @@ void main( PS_IN fragment, out PS_OUT result )
float vdotH = clamp( dot3( viewVector, halfAngleVector ), 0.0, 1.0 );
float ldotH = clamp( dot3( lightVector, halfAngleVector ), 0.0, 1.0 );
// compensate r_lightScale 3 * 2
float3 reflectColor = specularColor * rpSpecularModifier.rgb * 1.0;// * 0.5;
// keep in mind this is r_lightScale 3 * 2
float3 reflectColor = specularColor * rpSpecularModifier.rgb;
// cheap approximation by ARM with only one division
// http://community.arm.com/servlet/JiveServlet/download/96891546-19496/siggraph2015-mmg-renaldas-slides.pdf
@ -162,17 +171,19 @@ void main( PS_IN fragment, out PS_OUT result )
// disney GGX
float D = ( hdotN * hdotN ) * ( rrrr - 1.0 ) + 1.0;
float VFapprox = ( ldotH * ldotH ) * ( roughness + 0.5 );
#if KENNY_PBR
float3 specularLight = ( rrrr / ( 4.0 * D * D * VFapprox ) ) * ldotN * reflectColor;
#else
float3 specularLight = ( rrrr / ( 4.0 * PI * D * D * VFapprox ) ) * ldotN * reflectColor;
//specularLight = float3( 0.0 );
#endif
#if 0
result.color = float4( _float3( VFapprox ), 1.0 );
return;
#endif
// see http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
//lambert /= PI;
//float3 diffuseColor = mix( diffuseMap, F0, metal ) * rpDiffuseModifier.xyz;
float3 diffuseLight = diffuseColor * lambert * ( rpDiffuseModifier.xyz );

View file

@ -474,9 +474,9 @@ void main( PS_IN fragment, out PS_OUT result )
float hdotN = clamp( dot3( halfAngleVector, localNormal ), 0.0, 1.0 );
#if USE_PBR
// RB: roughness 0 somehow is not shiny so we clamp it
float roughness = max( 0.05, specMapSRGB.r );
const float metallic = specMapSRGB.g;
const float roughness = specMapSRGB.r;
const float glossiness = 1.0 - roughness;
// the vast majority of real-world materials (anything not metal or gems) have F(0)
// values in a very narrow range (~0.02 - 0.08)
@ -489,16 +489,34 @@ void main( PS_IN fragment, out PS_OUT result )
float3 diffuseColor = baseColor * ( 1.0 - metallic );
float3 specularColor = lerp( dielectricColor, baseColor, metallic );
#elif KENNY_PBR
float3 diffuseColor = diffuseMap;
float3 specularColor;
float roughness;
PBRFromSpecmap( specMapSRGB.rgb, specularColor, roughness );
#else
const float roughness = EstimateLegacyRoughness( specMapSRGB.rgb );
float roughness = EstimateLegacyRoughness( specMapSRGB.rgb );
float3 diffuseColor = diffuseMap;
float3 specularColor = specMapSRGB.rgb; // RB: should be linear but it looks too flat
#endif
#if 0
// specular AA - https://yusuketokuyoshi.com/papers/2021/Tokuyoshi2021SAA.pdf
// RB: compensate r_lightScale 3 and the division of Pi
//roughness = IsotropicNDFFiltering( localNormal, roughness * roughness );
float r2 = roughness * roughness;
roughness = AxisAlignedNDFFiltering( halfAngleVector, float2( r2, r2 ) ).x;
#endif
// RB FIXME or not: compensate r_lightScale 3 and the division of Pi
//lambert *= 1.3;
// see http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
//lambert /= PI;
// rpDiffuseModifier contains light color multiplier
float3 lightColor = sRGBToLinearRGB( lightProj.xyz * lightFalloff.xyz );
@ -507,8 +525,8 @@ void main( PS_IN fragment, out PS_OUT result )
float vdotH = clamp( dot3( viewVector, halfAngleVector ), 0.0, 1.0 );
float ldotH = clamp( dot3( lightVector, halfAngleVector ), 0.0, 1.0 );
// compensate r_lightScale 3 * 2
float3 reflectColor = specularColor * rpSpecularModifier.rgb * 1.0;// * 0.5;
// keep in mind this is r_lightScale 3 * 2
float3 reflectColor = specularColor * rpSpecularModifier.rgb;
// cheap approximation by ARM with only one division
// http://community.arm.com/servlet/JiveServlet/download/96891546-19496/siggraph2015-mmg-renaldas-slides.pdf
@ -520,17 +538,25 @@ void main( PS_IN fragment, out PS_OUT result )
// disney GGX
float D = ( hdotN * hdotN ) * ( rrrr - 1.0 ) + 1.0;
float VFapprox = ( ldotH * ldotH ) * ( roughness + 0.5 );
#if KENNY_PBR
// specular cook-torrance brdf (visibility, geo and denom in one)
//float D = Distribution_GGX_Disney( hdotN, rr );
//float Vis = Visibility_Schlick( vdotN, lambert, rr );
//float3 F = Fresnel_Schlick( reflectColor, vdotH );
//float3 specularLight = D * Vis * F;
float3 specularLight = ( rrrr / ( 4.0 * D * D * VFapprox ) ) * ldotN * reflectColor;
#else
float3 specularLight = ( rrrr / ( 4.0 * PI * D * D * VFapprox ) ) * ldotN * reflectColor;
//specularLight = float3( 0.0 );
#endif
#if 0
result.color = float4( _float3( VFapprox ), 1.0 );
return;
#endif
// see http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
//lambert /= PI;
//float3 diffuseColor = mix( diffuseMap, F0, metal ) * rpDiffuseModifier.xyz;
float3 diffuseLight = diffuseColor * lambert * ( rpDiffuseModifier.xyz );

View file

@ -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-2020 Robert Beckebans
Copyright (C) 2013-2024 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@ -492,6 +492,10 @@ static float2 vposToScreenPosTexCoord( float2 vpos )
return vpos.xy * rpWindowCoord.xy;
}
// ----------------------
// PSX rendering
// ----------------------
// a very useful resource with many examples about the PS1 look:
// https://www.david-colson.com/2021/11/30/ps1-style-renderer.html
@ -532,6 +536,12 @@ static float psxAffineWarp( float distance )
#define BRANCH
#define IFANY
// ----------------------
// Noise tricks
// ----------------------
//note: works for structured patterns too
// [0;1[
float RemapNoiseTriErp( const float v )
@ -604,3 +614,63 @@ float DitherArray8x8Anim( float2 pos, int frameIndexMod4 )
}
// ----------------------
// COLLISION DETECTION
// ----------------------
// RB: TODO OPTIMIZE
// this is a straight port of idBounds::RayIntersection
bool AABBRayIntersection( float3 b[2], float3 start, float3 dir, out float scale )
{
int i, ax0, ax1, ax2, side, inside;
float f;
float3 hit;
ax0 = -1;
inside = 0;
for( i = 0; i < 3; i++ )
{
if( start[i] < b[0][i] )
{
side = 0;
}
else if( start[i] > b[1][i] )
{
side = 1;
}
else
{
inside++;
continue;
}
if( dir[i] == 0.0f )
{
continue;
}
f = ( start[i] - b[side][i] );
if( ax0 < 0 || abs( f ) > abs( scale * dir[i] ) )
{
scale = - ( f / dir[i] );
ax0 = i;
}
}
if( ax0 < 0 )
{
scale = 0.0f;
// return true if the start point is inside the bounds
return ( inside == 3 );
}
ax1 = ( ax0 + 1 ) % 3;
ax2 = ( ax0 + 2 ) % 3;
hit[ax1] = start[ax1] + scale * dir[ax1];
hit[ax2] = start[ax2] + scale * dir[ax2];
return ( hit[ax1] >= b[0][ax1] && hit[ax1] <= b[1][ax1] &&
hit[ax2] >= b[0][ax2] && hit[ax2] <= b[1][ax2] );
}

View file

@ -34,6 +34,8 @@ builtin/legacy/environment.vs.hlsl -T vs -D USE_GPU_SKINNING={0,1}
builtin/legacy/environment.ps.hlsl -T ps -D USE_GPU_SKINNING={0,1}
builtin/legacy/bumpyenvironment.vs.hlsl -T vs -D USE_GPU_SKINNING={0,1}
builtin/legacy/bumpyenvironment.ps.hlsl -T ps -D USE_GPU_SKINNING={0,1}
builtin/legacy/bumpyenvironment2.vs.hlsl -T vs -D USE_GPU_SKINNING={0,1}
builtin/legacy/bumpyenvironment2.ps.hlsl -T ps -D USE_GPU_SKINNING={0,1}
builtin/legacy/skybox.vs.hlsl -T vs
builtin/legacy/skybox.ps.hlsl -T ps
builtin/legacy/wobblesky.vs.hlsl -T vs