mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-03-14 06:34:10 +00:00
Point lights can be culled. #756
This commit is contained in:
parent
5107d12cc5
commit
e121472661
8 changed files with 161 additions and 114 deletions
|
@ -501,9 +501,10 @@ float idConsoleLocal::DrawFPS( float y )
|
|||
//ImGui::Text( "Cull: %i box in %i box out\n",
|
||||
// commonLocal.stats_frontend.c_box_cull_in, commonLocal.stats_frontend.c_box_cull_out );
|
||||
|
||||
ImGui::TextColored( colorLtGrey, "MASKCULL: tests:%-3i culls:%i maskVerts:%i maskTris:%i",
|
||||
ImGui::TextColored( colorLtGrey, "MASKCULL: tests:%-3i lightCulls:%i surfCulls:%i verts:%i tris:%i",
|
||||
commonLocal.stats_frontend.c_mocTests,
|
||||
commonLocal.stats_frontend.c_mocCulls,
|
||||
commonLocal.stats_frontend.c_mocCulledLights,
|
||||
commonLocal.stats_frontend.c_mocCulledSurfaces,
|
||||
commonLocal.stats_frontend.c_mocVerts,
|
||||
commonLocal.stats_frontend.c_mocIndexes );
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#
|
||||
set(CMAKE_SUPPRESS_REGENERATION true)
|
||||
option(USE_AVX512 "Enable experimental AVX-512 support" OFF)
|
||||
set(CMAKE_CONFIGURATION_TYPES Debug Release)
|
||||
|
||||
#
|
||||
# Lists of all files included in the library
|
||||
|
|
|
@ -1046,6 +1046,7 @@ public:
|
|||
idRenderBackend backend;
|
||||
|
||||
MaskedOcclusionCulling* maskedOcclusionCulling;
|
||||
idVec4 maskedUnitCubeVerts[8];
|
||||
idVec4 maskedZeroOneCubeVerts[8];
|
||||
unsigned int maskedZeroOneCubeIndexes[36];
|
||||
|
||||
|
|
|
@ -129,7 +129,8 @@ struct performanceCounters_t
|
|||
int c_mocVerts;
|
||||
int c_mocIndexes;
|
||||
int c_mocTests;
|
||||
int c_mocCulls;
|
||||
int c_mocCulledSurfaces;
|
||||
int c_mocCulledLights;
|
||||
|
||||
uint64 frontEndMicroSec; // sum of time in all RE_RenderScene's in a frame
|
||||
};
|
||||
|
|
|
@ -1945,7 +1945,7 @@ static srfTriangles_t* R_MakeZeroOneCubeTris()
|
|||
// RB begin
|
||||
static void R_MakeZeroOneCubeTrisForMaskedOcclusionCulling()
|
||||
{
|
||||
const float low = -1.0f;
|
||||
const float low = 0.0f;
|
||||
const float high = 1.0f;
|
||||
|
||||
idVec3 center( 0.0f );
|
||||
|
@ -1975,6 +1975,40 @@ static void R_MakeZeroOneCubeTrisForMaskedOcclusionCulling()
|
|||
verts[5].w = 1;
|
||||
verts[6].w = 1;
|
||||
verts[7].w = 1;
|
||||
}
|
||||
|
||||
static void R_MakeUnitCubeTrisForMaskedOcclusionCulling()
|
||||
{
|
||||
const float low = -1.0f;
|
||||
const float high = 1.0f;
|
||||
|
||||
idVec3 center( 0.0f );
|
||||
idVec3 mx( low, 0.0f, 0.0f );
|
||||
idVec3 px( high, 0.0f, 0.0f );
|
||||
idVec3 my( 0.0f, low, 0.0f );
|
||||
idVec3 py( 0.0f, high, 0.0f );
|
||||
idVec3 mz( 0.0f, 0.0f, low );
|
||||
idVec3 pz( 0.0f, 0.0f, high );
|
||||
|
||||
idVec4* verts = tr.maskedUnitCubeVerts;
|
||||
|
||||
verts[0].ToVec3() = center + mx + my + mz;
|
||||
verts[1].ToVec3() = center + px + my + mz;
|
||||
verts[2].ToVec3() = center + px + py + mz;
|
||||
verts[3].ToVec3() = center + mx + py + mz;
|
||||
verts[4].ToVec3() = center + mx + my + pz;
|
||||
verts[5].ToVec3() = center + px + my + pz;
|
||||
verts[6].ToVec3() = center + px + py + pz;
|
||||
verts[7].ToVec3() = center + mx + py + pz;
|
||||
|
||||
verts[0].w = 1;
|
||||
verts[1].w = 1;
|
||||
verts[2].w = 1;
|
||||
verts[3].w = 1;
|
||||
verts[4].w = 1;
|
||||
verts[5].w = 1;
|
||||
verts[6].w = 1;
|
||||
verts[7].w = 1;
|
||||
|
||||
unsigned int* indexes = tr.maskedZeroOneCubeIndexes;
|
||||
|
||||
|
@ -2262,6 +2296,7 @@ void idRenderSystemLocal::Init()
|
|||
|
||||
maskedOcclusionCulling = MaskedOcclusionCulling::Create();
|
||||
R_MakeZeroOneCubeTrisForMaskedOcclusionCulling();
|
||||
R_MakeUnitCubeTrisForMaskedOcclusionCulling();
|
||||
|
||||
// make sure the command buffers are ready to accept the first screen update
|
||||
SwapCommandBuffers( NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
|
|
|
@ -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-2014 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").
|
||||
|
||||
|
@ -30,6 +30,8 @@ If you have questions concerning this license or the applicable additional terms
|
|||
#include "precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "../libs/moc/MaskedOcclusionCulling.h"
|
||||
|
||||
#include "RenderCommon.h"
|
||||
|
||||
extern idCVar r_useAreasConnectedForShadowCulling;
|
||||
|
@ -264,6 +266,86 @@ static void R_AddSingleLight( viewLight_t* vLight )
|
|||
vLight->scissorRect.zmin = projected[0][2];
|
||||
vLight->scissorRect.zmax = projected[1][2];
|
||||
|
||||
const bool viewInsideLight = !idRenderMatrix::CullPointToMVP( light->baseLightProject, viewDef->renderView.vieworg, true );
|
||||
|
||||
// RB: test surface visibility by drawing the triangles of the bounds
|
||||
|
||||
// FIXME spot light projections are too short
|
||||
if( r_useMaskedOcclusionCulling.GetBool() && !viewInsideLight && vLight->pointLight )
|
||||
{
|
||||
#if 1
|
||||
idVec4 triVerts[3];
|
||||
unsigned int triIndices[] = { 0, 1, 2 };
|
||||
|
||||
tr.pc.c_mocIndexes += 36;
|
||||
tr.pc.c_mocVerts += 8;
|
||||
|
||||
idRenderMatrix invProjectMVPMatrix;
|
||||
idRenderMatrix::Multiply( viewDef->worldSpace.unjitteredMVP, light->inverseBaseLightProject, invProjectMVPMatrix );
|
||||
|
||||
tr.pc.c_mocTests += 1;
|
||||
|
||||
bool maskVisible = false;
|
||||
// NOTE: zeroToOne cube is only for lights and models need the unit cube
|
||||
idVec4* verts = tr.maskedZeroOneCubeVerts;
|
||||
unsigned int* indexes = tr.maskedZeroOneCubeIndexes;
|
||||
for( int i = 0, face = 0; i < 36; i += 3, face++ )
|
||||
{
|
||||
const idVec4& v0 = verts[indexes[i + 0]];
|
||||
const idVec4& v1 = verts[indexes[i + 1]];
|
||||
const idVec4& v2 = verts[indexes[i + 2]];
|
||||
|
||||
// transform to clip space
|
||||
invProjectMVPMatrix.TransformPoint( v0, triVerts[0] );
|
||||
invProjectMVPMatrix.TransformPoint( v1, triVerts[1] );
|
||||
invProjectMVPMatrix.TransformPoint( v2, triVerts[2] );
|
||||
|
||||
#if 1
|
||||
// backface none so objects are still visible where we run into
|
||||
MaskedOcclusionCulling::CullingResult result = tr.maskedOcclusionCulling->TestTriangles( ( float* )triVerts, triIndices, 1, NULL, MaskedOcclusionCulling::BACKFACE_NONE );
|
||||
if( result == MaskedOcclusionCulling::VISIBLE )
|
||||
{
|
||||
maskVisible = true;
|
||||
}
|
||||
#else
|
||||
// draw for debugging
|
||||
tr.maskedOcclusionCulling->RenderTriangles( ( float* )triVerts, triIndices, 1, NULL, MaskedOcclusionCulling::BACKFACE_NONE );
|
||||
maskVisible = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
if( !maskVisible )
|
||||
{
|
||||
tr.pc.c_mocCulledLights += 1;
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// scissor test alternative
|
||||
// I would prefer this method however lights become visible again when the distance increases to the occluder surface
|
||||
|
||||
// source scissor rectangle has GL convention and starts in the lower left corner
|
||||
// convert to NDC values
|
||||
float x1 = -1.0f + ( float( vLight->scissorRect.x1 ) / screenWidth ) * 2.0f;
|
||||
float x2 = -1.0f + ( float( vLight->scissorRect.x2 ) / screenWidth ) * 2.0f;
|
||||
float y1 = -1.0f + ( float( vLight->scissorRect.y1 ) / screenHeight ) * 2.0f;
|
||||
float y2 = -1.0f + ( float( vLight->scissorRect.y2 ) / screenHeight ) * 2.0f;
|
||||
|
||||
//float y2 = -1.0f + ( float( screenHeight - vLight->scissorRect.y1 ) / screenHeight ) * 2.0f;
|
||||
//float y1 = -1.0f + ( float( screenHeight - vLight->scissorRect.y2 ) / screenHeight ) * 2.0f;
|
||||
|
||||
double zmin = 1.0 - vLight->scissorRect.zmin; // reverse depth
|
||||
double wmin = 1.0 / zmin;
|
||||
|
||||
MaskedOcclusionCulling::CullingResult result = tr.maskedOcclusionCulling->TestRect( x1, y1, x2, y2, wmin );
|
||||
if( result != MaskedOcclusionCulling::VISIBLE )
|
||||
{
|
||||
tr.pc.c_mocCulledLights += 1;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// RB end
|
||||
|
||||
// RB: calculate shadow LOD similar to Q3A .md3 LOD code
|
||||
|
||||
// -1 means no shadows
|
||||
|
|
|
@ -675,8 +675,13 @@ void R_AddSingleModel( viewEntity_t* vEntity )
|
|||
// RB: added check wether GPU skinning is available at all
|
||||
const bool gpuSkinned = ( tri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() );
|
||||
|
||||
idRenderMatrix cullSurfaceProject;
|
||||
idRenderMatrix::InverseOffsetScaleForBounds( renderMatrix_identity, tri->bounds, cullSurfaceProject );
|
||||
|
||||
const bool viewInsideSurface = !idRenderMatrix::CullPointToMVP( cullSurfaceProject, localViewOrigin, false );
|
||||
|
||||
// RB: test surface visibility by drawing the triangles of the bounds
|
||||
if( r_useMaskedOcclusionCulling.GetBool() )
|
||||
if( r_useMaskedOcclusionCulling.GetBool() && !viewInsideSurface )
|
||||
{
|
||||
#if 1
|
||||
if( !model->IsStaticWorldModel() && !renderEntity->weaponDepthHack && renderEntity->modelDepthHack == 0.0f )
|
||||
|
@ -687,9 +692,6 @@ void R_AddSingleModel( viewEntity_t* vEntity )
|
|||
tr.pc.c_mocIndexes += 36;
|
||||
tr.pc.c_mocVerts += 8;
|
||||
|
||||
idRenderMatrix modelRenderMatrix;
|
||||
idRenderMatrix::CreateFromOriginAxis( renderEntity->origin, renderEntity->axis, modelRenderMatrix );
|
||||
|
||||
const float size = 16.0f;
|
||||
idBounds debugBounds( idVec3( -size ), idVec3( size ) );
|
||||
//debugBounds = vEntity->entityDef->localReferenceBounds;
|
||||
|
@ -705,6 +707,9 @@ void R_AddSingleModel( viewEntity_t* vEntity )
|
|||
debugBounds = tri->bounds;
|
||||
}
|
||||
|
||||
idRenderMatrix modelRenderMatrix;
|
||||
idRenderMatrix::CreateFromOriginAxis( renderEntity->origin, renderEntity->axis, modelRenderMatrix );
|
||||
|
||||
idRenderMatrix inverseBaseModelProject;
|
||||
idRenderMatrix::OffsetScaleForBounds( modelRenderMatrix, debugBounds, inverseBaseModelProject );
|
||||
|
||||
|
@ -714,7 +719,8 @@ void R_AddSingleModel( viewEntity_t* vEntity )
|
|||
tr.pc.c_mocTests += 1;
|
||||
|
||||
bool maskVisible = false;
|
||||
idVec4* verts = tr.maskedZeroOneCubeVerts;
|
||||
// NOTE: unit cube instead of zeroToOne cube
|
||||
idVec4* verts = tr.maskedUnitCubeVerts;
|
||||
unsigned int* indexes = tr.maskedZeroOneCubeIndexes;
|
||||
for( int i = 0, face = 0; i < 36; i += 3, face++ )
|
||||
{
|
||||
|
@ -737,7 +743,7 @@ void R_AddSingleModel( viewEntity_t* vEntity )
|
|||
|
||||
if( !maskVisible )
|
||||
{
|
||||
tr.pc.c_mocCulls += 1;
|
||||
tr.pc.c_mocCulledSurfaces += 1;
|
||||
surfaceDirectlyVisible = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,105 +140,8 @@ void R_RenderSingleModel( viewEntity_t* vEntity )
|
|||
const bool addInteractions = modelIsVisible && ( !viewDef->isXraySubview || entityDef->parms.xrayIndex == 2 );
|
||||
const int entityIndex = entityDef->index;
|
||||
|
||||
extern idCVar r_lodMaterialDistance;
|
||||
|
||||
//---------------------------
|
||||
// Find which of the visible lights contact this entity
|
||||
//
|
||||
// If the entity doesn't accept light or cast shadows from any surface,
|
||||
// this can be skipped.
|
||||
//
|
||||
// OPTIMIZE: world areas can assume all referenced lights are used
|
||||
//---------------------------
|
||||
int numContactedLights = 0;
|
||||
static const int MAX_CONTACTED_LIGHTS = 128;
|
||||
viewLight_t* contactedLights[MAX_CONTACTED_LIGHTS];
|
||||
idInteraction* staticInteractions[MAX_CONTACTED_LIGHTS];
|
||||
|
||||
if( renderEntity->hModel == NULL ||
|
||||
renderEntity->hModel->ModelHasInteractingSurfaces() ||
|
||||
renderEntity->hModel->ModelHasShadowCastingSurfaces() )
|
||||
{
|
||||
SCOPED_PROFILE_EVENT( "Find lights" );
|
||||
for( viewLight_t* vLight = viewDef->viewLights; vLight != NULL; vLight = vLight->next )
|
||||
{
|
||||
if( vLight->scissorRect.IsEmpty() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if( vLight->entityInteractionState != NULL )
|
||||
{
|
||||
// new code path, everything was done in AddLight
|
||||
if( vLight->entityInteractionState[entityIndex] == viewLight_t::INTERACTION_YES )
|
||||
{
|
||||
contactedLights[numContactedLights] = vLight;
|
||||
staticInteractions[numContactedLights] = world->interactionTable[vLight->lightDef->index * world->interactionTableWidth + entityIndex];
|
||||
if( ++numContactedLights == MAX_CONTACTED_LIGHTS )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const idRenderLightLocal* lightDef = vLight->lightDef;
|
||||
|
||||
if( !lightDef->globalLightBounds.IntersectsBounds( entityDef->globalReferenceBounds ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( R_CullModelBoundsToLight( lightDef, entityDef->localReferenceBounds, entityDef->modelRenderMatrix ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !modelIsVisible )
|
||||
{
|
||||
// some lights have their center of projection outside the world
|
||||
if( lightDef->areaNum != -1 )
|
||||
{
|
||||
// if no part of the model is in an area that is connected to
|
||||
// the light center (it is behind a solid, closed door), we can ignore it
|
||||
bool areasConnected = false;
|
||||
for( areaReference_t* ref = entityDef->entityRefs; ref != NULL; ref = ref->ownerNext )
|
||||
{
|
||||
if( world->AreasAreConnected( lightDef->areaNum, ref->area->areaNum, PS_BLOCK_VIEW ) )
|
||||
{
|
||||
areasConnected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( areasConnected == false )
|
||||
{
|
||||
// can't possibly be seen or shadowed
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// check more precisely for shadow visibility
|
||||
idBounds shadowBounds;
|
||||
R_ShadowBounds( entityDef->globalReferenceBounds, lightDef->globalLightBounds, lightDef->globalLightOrigin, shadowBounds );
|
||||
|
||||
// this doesn't say that the shadow can't effect anything, only that it can't
|
||||
// effect anything in the view
|
||||
if( idRenderMatrix::CullBoundsToMVP( viewDef->worldSpace.mvp, shadowBounds ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
contactedLights[numContactedLights] = vLight;
|
||||
staticInteractions[numContactedLights] = world->interactionTable[vLight->lightDef->index * world->interactionTableWidth + entityIndex];
|
||||
if( ++numContactedLights == MAX_CONTACTED_LIGHTS )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we aren't visible and none of the shadows stretch into the view,
|
||||
// we don't need to do anything else
|
||||
if( !modelIsVisible && numContactedLights == 0 )
|
||||
// if we aren't visible we don't need to do anything else
|
||||
if( !modelIsVisible )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -281,6 +184,8 @@ void R_RenderSingleModel( viewEntity_t* vEntity )
|
|||
idVec3 localViewOrigin;
|
||||
R_GlobalPointToLocal( vEntity->modelMatrix, viewDef->renderView.vieworg, localViewOrigin );
|
||||
|
||||
extern idCVar r_lodMaterialDistance;
|
||||
|
||||
//---------------------------
|
||||
// add all the model surfaces
|
||||
//---------------------------
|
||||
|
@ -422,7 +327,8 @@ void R_RenderSingleModel( viewEntity_t* vEntity )
|
|||
{
|
||||
// render to masked occlusion buffer
|
||||
|
||||
if( !gpuSkinned ) //model->IsStaticWorldModel() )
|
||||
if( !gpuSkinned )
|
||||
//if( model->IsStaticWorldModel() )
|
||||
{
|
||||
// super simple bruteforce
|
||||
idVec4 triVerts[3];
|
||||
|
@ -484,7 +390,8 @@ void R_RenderSingleModel( viewEntity_t* vEntity )
|
|||
idRenderMatrix::Multiply( viewDef->worldSpace.unjitteredMVP, inverseBaseModelProject, invProjectMVPMatrix );
|
||||
|
||||
#if 1
|
||||
idVec4* verts = tr.maskedZeroOneCubeVerts;
|
||||
// NOTE: unit cube instead of zeroToOne cube
|
||||
idVec4* verts = tr.maskedUnitCubeVerts;
|
||||
unsigned int* indexes = tr.maskedZeroOneCubeIndexes;
|
||||
for( int i = 0, face = 0; i < 36; i += 3, face++ )
|
||||
{
|
||||
|
@ -671,7 +578,22 @@ CONSOLE_COMMAND( maskShot, "Dumping masked occlusion culling buffer", NULL )
|
|||
float* perPixelZBuffer = new float[width * height];
|
||||
tr.maskedOcclusionCulling->ComputePixelDepthBuffer( perPixelZBuffer, false );
|
||||
|
||||
// Tonemap the image
|
||||
halfFloat_t* halfImage = new halfFloat_t[width * height * 3];
|
||||
|
||||
for( int i = 0; i < ( width * height ); i++ )
|
||||
{
|
||||
float depth = perPixelZBuffer[i];
|
||||
halfFloat_t f16Depth = F32toF16( depth );
|
||||
|
||||
halfImage[ i * 3 + 0 ] = f16Depth;
|
||||
halfImage[ i * 3 + 1 ] = f16Depth;
|
||||
halfImage[ i * 3 + 2 ] = f16Depth;
|
||||
}
|
||||
|
||||
// write raw values
|
||||
R_WriteEXR( "screenshots/soft_occlusion_buffer.exr", halfImage, 3, width, height, "fs_basepath" );
|
||||
|
||||
// tonemap the image
|
||||
unsigned char* image = new unsigned char[width * height * 3];
|
||||
TonemapDepth( perPixelZBuffer, image, width, height );
|
||||
|
||||
|
|
Loading…
Reference in a new issue