Add stencil buffer usage with object layer information

#3080 #967
This commit is contained in:
Wojtek Figat
2025-10-07 18:07:23 +02:00
parent 823ed247d2
commit 361fc3ecfb
36 changed files with 291 additions and 65 deletions

View File

@@ -70,7 +70,7 @@
// Default depth buffer pixel format
#ifndef GPU_DEPTH_BUFFER_PIXEL_FORMAT
#define GPU_DEPTH_BUFFER_PIXEL_FORMAT PixelFormat::D32_Float
#define GPU_DEPTH_BUFFER_PIXEL_FORMAT PixelFormat::D24_UNorm_S8_UInt
#endif
// Enable/disable gpu resources naming

View File

@@ -85,13 +85,10 @@ void DeferredMaterialShader::Bind(BindParameters& params)
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant)
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)
cullMode = CullMode::Inverted;
else
cullMode = CullMode::Normal;
cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal;
}
ASSERT_LOW_LAYER(!(useSkinning && params.Instanced)); // No support for instancing skinned meshes
const auto cache = params.Instanced ? &_cacheInstanced : &_cache;
@@ -101,6 +98,7 @@ void DeferredMaterialShader::Bind(BindParameters& params)
// Bind pipeline
context->SetState(state);
context->SetStencilRef(drawCall.StencilValue);
}
void DeferredMaterialShader::Unload()
@@ -137,6 +135,10 @@ bool DeferredMaterialShader::Load()
}
#endif
psDesc.StencilEnable = true;
psDesc.StencilReadMask = 0;
psDesc.StencilPassOp = StencilOperation::Replace;
// GBuffer Pass
psDesc.VS = _shader->GetVS("VS");
failed |= psDesc.VS == nullptr;
@@ -160,6 +162,9 @@ bool DeferredMaterialShader::Load()
psDesc.PS = _shader->GetPS("PS_GBuffer");
_cache.DefaultSkinned.Init(psDesc);
psDesc.StencilEnable = false;
psDesc.StencilPassOp = StencilOperation::Keep;
#if USE_EDITOR
if (_shader->HasShader("PS_QuadOverdraw"))
{

View File

@@ -62,7 +62,7 @@ void DeformableMaterialShader::Bind(BindParameters& params)
{
Matrix::Transpose(drawCall.World, materialData->WorldMatrix);
Matrix::Transpose(drawCall.Deformable.LocalMatrix, materialData->LocalMatrix);
materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign;
materialData->WorldDeterminantSign = drawCall.WorldDeterminant ? -1.0f : 1.0f;
materialData->Segment = drawCall.Deformable.Segment;
materialData->ChunksPerSegment = drawCall.Deformable.ChunksPerSegment;
materialData->MeshMinZ = drawCall.Deformable.MeshMinZ;
@@ -84,13 +84,10 @@ void DeformableMaterialShader::Bind(BindParameters& params)
// Select pipeline state based on current pass and render mode
const bool wireframe = (_info.FeaturesFlags & MaterialFeaturesFlags::Wireframe) != MaterialFeaturesFlags::None || view.Mode == ViewMode::Wireframe;
CullMode cullMode = view.Pass == DrawPass::Depth ? CullMode::TwoSided : _info.CullMode;
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant)
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)
cullMode = CullMode::Inverted;
else
cullMode = CullMode::Normal;
cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal;
}
PipelineStateCache* psCache = _cache.GetPS(view.Pass);
ASSERT(psCache);
@@ -98,6 +95,7 @@ void DeformableMaterialShader::Bind(BindParameters& params)
// Bind pipeline
context->SetState(state);
context->SetStencilRef(drawCall.StencilValue);
}
void DeformableMaterialShader::Unload()
@@ -139,10 +137,17 @@ bool DeformableMaterialShader::Load()
{
_drawModes |= DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas;
psDesc.StencilEnable = true;
psDesc.StencilReadMask = 0;
psDesc.StencilPassOp = StencilOperation::Replace;
// GBuffer Pass
psDesc.VS = _shader->GetVS("VS_SplineModel");
psDesc.PS = _shader->GetPS("PS_GBuffer");
_cache.Default.Init(psDesc);
psDesc.StencilEnable = false;
psDesc.StencilPassOp = StencilOperation::Keep;
}
else
{

View File

@@ -80,13 +80,10 @@ void ForwardMaterialShader::Bind(BindParameters& params)
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant)
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)
cullMode = CullMode::Inverted;
else
cullMode = CullMode::Normal;
cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal;
}
ASSERT_LOW_LAYER(!(useSkinning && params.Instanced)); // No support for instancing skinned meshes
const auto cacheObj = params.Instanced ? &_cacheInstanced : &_cache;

View File

@@ -76,7 +76,7 @@ void TerrainMaterialShader::Bind(BindParameters& params)
scaleX > 0.00001f ? 1.0f / scaleX : 0.0f,
scaleY > 0.00001f ? 1.0f / scaleY : 0.0f,
scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f);
materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign;
materialData->WorldDeterminantSign = drawCall.WorldDeterminant ? -1.0f : 1.0f;
materialData->PerInstanceRandom = drawCall.PerInstanceRandom;
materialData->CurrentLOD = drawCall.Terrain.CurrentLOD;
materialData->ChunkSizeNextLOD = drawCall.Terrain.ChunkSizeNextLOD;
@@ -109,13 +109,10 @@ void TerrainMaterialShader::Bind(BindParameters& params)
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant)
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)
cullMode = CullMode::Inverted;
else
cullMode = CullMode::Normal;
cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal;
}
const PipelineStateCache* psCache = _cache.GetPS(view.Pass, useLightmap);
ASSERT(psCache);
@@ -123,6 +120,7 @@ void TerrainMaterialShader::Bind(BindParameters& params)
// Bind pipeline
context->SetState(state);
context->SetStencilRef(drawCall.StencilValue);
}
void TerrainMaterialShader::Unload()
@@ -139,6 +137,10 @@ bool TerrainMaterialShader::Load()
psDesc.DepthEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthTest) == MaterialFeaturesFlags::None;
psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == MaterialFeaturesFlags::None;
psDesc.StencilEnable = true;
psDesc.StencilReadMask = 0;
psDesc.StencilPassOp = StencilOperation::Replace;
#if GPU_ALLOW_TESSELLATION_SHADERS
// Check if use tessellation (both material and runtime supports it)
const bool useTess = _info.TessellationMode != TessellationMethod::None && GPUDevice::Instance->Limits.HasTessellation;

View File

@@ -218,7 +218,7 @@ bool Mesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const void*
return Init(vertices, triangles, vbData, ib, use16BitIndexBuffer, vbLayout);
}
void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int8 sortOrder) const
void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int8 sortOrder, uint8 stencilValue) const
{
if (!material || !material->IsSurface() || !IsInitialized())
return;
@@ -240,8 +240,8 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons
drawCall.ObjectRadius = (float)_sphere.Radius * drawCall.World.GetScaleVector().GetAbsolute().MaxValue();
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.Surface.PrevWorld = world;
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = perInstanceRandom;
drawCall.StencilValue = stencilValue;
#if USE_EDITOR
const ViewMode viewMode = renderContext.View.Mode;
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)
@@ -307,8 +307,8 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr;
drawCall.Surface.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
drawCall.StencilValue = info.StencilValue;
#if USE_EDITOR
const ViewMode viewMode = renderContext.View.Mode;
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)
@@ -370,8 +370,8 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in
drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr;
drawCall.Surface.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
drawCall.StencilValue = info.StencilValue;
#if USE_EDITOR
const ViewMode viewMode = renderContextBatch.GetMainContext().View.Mode;
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)

View File

@@ -142,7 +142,8 @@ public:
/// <param name="drawModes">The draw passes to use for rendering this object.</param>
/// <param name="perInstanceRandom">The random per-instance value (normalized to range 0-1).</param>
/// <param name="sortOrder">Object sorting key.</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int8 sortOrder = 0) const;
/// <param name="stencilValue">Object stencil value.</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int8 sortOrder = 0, uint8 stencilValue = 0) const;
/// <summary>
/// Draws the mesh.

View File

@@ -404,6 +404,11 @@ public:
/// </summary>
float PerInstanceRandom;
/// <summary>
/// The 8-bit stencil value to write into Depth-Stencil Buffer.
/// </summary>
uint8 StencilValue;
/// <summary>
/// The LOD bias value.
/// </summary>
@@ -422,6 +427,12 @@ public:
#if USE_EDITOR
float LightmapScale = -1.0f;
#endif
// Packs object layer into the stencil bits.
FORCE_INLINE void SetStencilValue(int32 layer)
{
StencilValue = uint8(layer & 0x1f);
}
};
/// <summary>

View File

@@ -314,8 +314,8 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info,
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Skinning = info.Skinning;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
drawCall.StencilValue = info.StencilValue;
// Push draw call to the render list
renderContext.List->AddDrawCall(renderContext, drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals, info.SortOrder);
@@ -355,8 +355,8 @@ void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawI
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Skinning = info.Skinning;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
drawCall.StencilValue = info.StencilValue;
// Push draw call to the render lists
const auto shadowsMode = entry.ShadowsMode & slot.ShadowsMode;

View File

@@ -13,6 +13,17 @@
#define GBUFFER2_FORMAT PixelFormat::R8G8B8A8_UNorm
#define GBUFFER3_FORMAT PixelFormat::R8G8B8A8_UNorm
// Stencil bits usage (must match GBuffer.hlsl)
// [0] | Object Layer
// [1] | Object Layer
// [2] | Object Layer
// [3] | Object Layer
// [4] | Object Layer
// [5] | <unsued>
// [6] | <unsued>
// [7] | <unsued>
#define STENCIL_BUFFER_OBJECT_LAYER(value) uint8(value & 0x1f)
/// <summary>
/// The scene rendering buffers container.
/// </summary>

View File

@@ -437,6 +437,12 @@ public:
/// <returns>The view to the depth-stencil resource descriptor as read-only depth.</returns>
API_FUNCTION() virtual GPUTextureView* ViewReadOnlyDepth() const = 0;
/// <summary>
/// Gets the view to the texture as stencil buffer.
/// </summary>
/// <returns>The view to the stencil resource descriptor.</returns>
API_FUNCTION() virtual GPUTextureView* ViewStencil() const = 0;
/// <summary>
/// Implicit conversion to the first surface (only for 2D textures).
/// </summary>