Add direct lighting rendering into Global Surface Atlas
This commit is contained in:
BIN
Content/Shaders/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
BIN
Content/Shaders/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
Binary file not shown.
@@ -254,6 +254,7 @@
|
|||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=comperand/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=comperand/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=coord/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=coord/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=cubemap/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=cubemap/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=defragmentation/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Delaunay/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Delaunay/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Defocus/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Defocus/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Deinitialize/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Deinitialize/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "GlobalSurfaceAtlasPass.h"
|
#include "GlobalSurfaceAtlasPass.h"
|
||||||
#include "GlobalSignDistanceFieldPass.h"
|
#include "GlobalSignDistanceFieldPass.h"
|
||||||
#include "RenderList.h"
|
#include "RenderList.h"
|
||||||
|
#include "ShadowsPass.h"
|
||||||
#include "Engine/Core/Math/Matrix3x3.h"
|
#include "Engine/Core/Math/Matrix3x3.h"
|
||||||
#include "Engine/Core/Math/OrientedBoundingBox.h"
|
#include "Engine/Core/Math/OrientedBoundingBox.h"
|
||||||
#include "Engine/Engine/Engine.h"
|
#include "Engine/Engine/Engine.h"
|
||||||
@@ -19,10 +20,11 @@
|
|||||||
// This must match HLSL
|
// This must match HLSL
|
||||||
#define GLOBAL_SURFACE_ATLAS_OBJECT_SIZE (5 + 6 * 5) // Amount of float4s per-object
|
#define GLOBAL_SURFACE_ATLAS_OBJECT_SIZE (5 + 6 * 5) // Amount of float4s per-object
|
||||||
#define GLOBAL_SURFACE_ATLAS_TILE_PADDING 1 // 1px padding to prevent color bleeding between tiles
|
#define GLOBAL_SURFACE_ATLAS_TILE_PADDING 1 // 1px padding to prevent color bleeding between tiles
|
||||||
#define GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_TILES_REDRAW 0 // Forces to redraw all object tiles every frame
|
#define GLOBAL_SURFACE_ATLAS_TILE_PROJ_PLANE_OFFSET 0.1f // Small offset to prevent clipping with the closest triangles (shifts near and far planes)
|
||||||
#define GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_DRAW_OBJECTS 0 // Debug draws object bounds on redraw (and tile draw projection locations)
|
#define GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_TILES 0 // Forces to redraw all object tiles every frame
|
||||||
|
#define GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_OBJECTS 0 // Debug draws object bounds on redraw (and tile draw projection locations)
|
||||||
|
|
||||||
#if GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_DRAW_OBJECTS
|
#if GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_OBJECTS
|
||||||
#include "Engine/Debug/DebugDraw.h"
|
#include "Engine/Debug/DebugDraw.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -35,11 +37,15 @@ PACK_STRUCT(struct Data0
|
|||||||
Vector4 ViewFrustumWorldRays[4];
|
Vector4 ViewFrustumWorldRays[4];
|
||||||
GlobalSignDistanceFieldPass::GlobalSDFData GlobalSDF;
|
GlobalSignDistanceFieldPass::GlobalSDFData GlobalSDF;
|
||||||
GlobalSurfaceAtlasPass::GlobalSurfaceAtlasData GlobalSurfaceAtlas;
|
GlobalSurfaceAtlasPass::GlobalSurfaceAtlasData GlobalSurfaceAtlas;
|
||||||
|
LightData Light;
|
||||||
});
|
});
|
||||||
|
|
||||||
PACK_STRUCT(struct AtlasTileVertex
|
PACK_STRUCT(struct AtlasTileVertex
|
||||||
{
|
{
|
||||||
Half2 Position;
|
Half2 Position;
|
||||||
|
Half2 TileUV;
|
||||||
|
uint16 ObjectIndex;
|
||||||
|
uint16 TileIndex;
|
||||||
});
|
});
|
||||||
|
|
||||||
struct GlobalSurfaceAtlasTile : RectPack<GlobalSurfaceAtlasTile, uint16>
|
struct GlobalSurfaceAtlasTile : RectPack<GlobalSurfaceAtlasTile, uint16>
|
||||||
@@ -66,6 +72,8 @@ struct GlobalSurfaceAtlasObject
|
|||||||
uint64 LastFrameUsed;
|
uint64 LastFrameUsed;
|
||||||
uint64 LastFrameDirty;
|
uint64 LastFrameDirty;
|
||||||
GlobalSurfaceAtlasTile* Tiles[6];
|
GlobalSurfaceAtlasTile* Tiles[6];
|
||||||
|
uint32 Index;
|
||||||
|
float Radius;
|
||||||
OrientedBoundingBox Bounds;
|
OrientedBoundingBox Bounds;
|
||||||
|
|
||||||
GlobalSurfaceAtlasObject()
|
GlobalSurfaceAtlasObject()
|
||||||
@@ -103,11 +111,13 @@ public:
|
|||||||
uint64 LastFrameAtlasInsertFail = 0;
|
uint64 LastFrameAtlasInsertFail = 0;
|
||||||
uint64 LastFrameAtlasDefragmentation = 0;
|
uint64 LastFrameAtlasDefragmentation = 0;
|
||||||
GPUTexture* AtlasDepth = nullptr;
|
GPUTexture* AtlasDepth = nullptr;
|
||||||
|
GPUTexture* AtlasEmissive = nullptr;
|
||||||
GPUTexture* AtlasGBuffer0 = nullptr;
|
GPUTexture* AtlasGBuffer0 = nullptr;
|
||||||
GPUTexture* AtlasGBuffer1 = nullptr;
|
GPUTexture* AtlasGBuffer1 = nullptr;
|
||||||
GPUTexture* AtlasGBuffer2 = nullptr;
|
GPUTexture* AtlasGBuffer2 = nullptr;
|
||||||
GPUTexture* AtlasDirectLight = nullptr;
|
GPUTexture* AtlasDirectLight = nullptr;
|
||||||
DynamicTypedBuffer ObjectsBuffer;
|
DynamicTypedBuffer ObjectsBuffer;
|
||||||
|
uint32 ObjectIndexCounter;
|
||||||
GlobalSurfaceAtlasPass::BindingData Result;
|
GlobalSurfaceAtlasPass::BindingData Result;
|
||||||
GlobalSurfaceAtlasTile* AtlasTiles = nullptr; // TODO: optimize with a single allocation for atlas tiles
|
GlobalSurfaceAtlasTile* AtlasTiles = nullptr; // TODO: optimize with a single allocation for atlas tiles
|
||||||
Dictionary<Actor*, GlobalSurfaceAtlasObject> Objects;
|
Dictionary<Actor*, GlobalSurfaceAtlasObject> Objects;
|
||||||
@@ -128,6 +138,7 @@ public:
|
|||||||
FORCE_INLINE void Clear()
|
FORCE_INLINE void Clear()
|
||||||
{
|
{
|
||||||
RenderTargetPool::Release(AtlasDepth);
|
RenderTargetPool::Release(AtlasDepth);
|
||||||
|
RenderTargetPool::Release(AtlasEmissive);
|
||||||
RenderTargetPool::Release(AtlasGBuffer0);
|
RenderTargetPool::Release(AtlasGBuffer0);
|
||||||
RenderTargetPool::Release(AtlasGBuffer1);
|
RenderTargetPool::Release(AtlasGBuffer1);
|
||||||
RenderTargetPool::Release(AtlasGBuffer2);
|
RenderTargetPool::Release(AtlasGBuffer2);
|
||||||
@@ -198,11 +209,27 @@ bool GlobalSurfaceAtlasPass::setupResources()
|
|||||||
psDesc.DepthTestEnable = true;
|
psDesc.DepthTestEnable = true;
|
||||||
psDesc.DepthWriteEnable = true;
|
psDesc.DepthWriteEnable = true;
|
||||||
psDesc.DepthFunc = ComparisonFunc::Always;
|
psDesc.DepthFunc = ComparisonFunc::Always;
|
||||||
psDesc.VS = shader->GetVS("VS_Clear");
|
psDesc.VS = shader->GetVS("VS_Atlas");
|
||||||
psDesc.PS = shader->GetPS("PS_Clear");
|
psDesc.PS = shader->GetPS("PS_Clear");
|
||||||
if (_psClear->Init(psDesc))
|
if (_psClear->Init(psDesc))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!_psDirectLighting0)
|
||||||
|
{
|
||||||
|
_psDirectLighting0 = device->CreatePipelineState();
|
||||||
|
psDesc.DepthTestEnable = false;
|
||||||
|
psDesc.DepthWriteEnable = false;
|
||||||
|
psDesc.DepthFunc = ComparisonFunc::Never;
|
||||||
|
psDesc.BlendMode = BlendingMode::Add;
|
||||||
|
psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RGB;
|
||||||
|
psDesc.PS = shader->GetPS("PS_DirectLighting", 0);
|
||||||
|
if (_psDirectLighting0->Init(psDesc))
|
||||||
|
return true;
|
||||||
|
_psDirectLighting1 = device->CreatePipelineState();
|
||||||
|
psDesc.PS = shader->GetPS("PS_DirectLighting", 1);
|
||||||
|
if (_psDirectLighting1->Init(psDesc))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -212,6 +239,8 @@ bool GlobalSurfaceAtlasPass::setupResources()
|
|||||||
void GlobalSurfaceAtlasPass::OnShaderReloading(Asset* obj)
|
void GlobalSurfaceAtlasPass::OnShaderReloading(Asset* obj)
|
||||||
{
|
{
|
||||||
SAFE_DELETE_GPU_RESOURCE(_psClear);
|
SAFE_DELETE_GPU_RESOURCE(_psClear);
|
||||||
|
SAFE_DELETE_GPU_RESOURCE(_psDirectLighting0);
|
||||||
|
SAFE_DELETE_GPU_RESOURCE(_psDirectLighting1);
|
||||||
SAFE_DELETE_GPU_RESOURCE(_psDebug);
|
SAFE_DELETE_GPU_RESOURCE(_psDebug);
|
||||||
invalidateResources();
|
invalidateResources();
|
||||||
}
|
}
|
||||||
@@ -225,6 +254,8 @@ void GlobalSurfaceAtlasPass::Dispose()
|
|||||||
// Cleanup
|
// Cleanup
|
||||||
SAFE_DELETE(_vertexBuffer);
|
SAFE_DELETE(_vertexBuffer);
|
||||||
SAFE_DELETE_GPU_RESOURCE(_psClear);
|
SAFE_DELETE_GPU_RESOURCE(_psClear);
|
||||||
|
SAFE_DELETE_GPU_RESOURCE(_psDirectLighting0);
|
||||||
|
SAFE_DELETE_GPU_RESOURCE(_psDirectLighting1);
|
||||||
SAFE_DELETE_GPU_RESOURCE(_psDebug);
|
SAFE_DELETE_GPU_RESOURCE(_psDebug);
|
||||||
_cb0 = nullptr;
|
_cb0 = nullptr;
|
||||||
_shader = nullptr;
|
_shader = nullptr;
|
||||||
@@ -239,6 +270,11 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
return true;
|
return true;
|
||||||
auto& surfaceAtlasData = *renderContext.Buffers->GetCustomBuffer<GlobalSurfaceAtlasCustomBuffer>(TEXT("GlobalSurfaceAtlas"));
|
auto& surfaceAtlasData = *renderContext.Buffers->GetCustomBuffer<GlobalSurfaceAtlasCustomBuffer>(TEXT("GlobalSurfaceAtlas"));
|
||||||
|
|
||||||
|
// Render Global SDF (used for direct shadowing)
|
||||||
|
GlobalSignDistanceFieldPass::BindingData bindingDataSDF;
|
||||||
|
if (GlobalSignDistanceFieldPass::Instance()->Render(renderContext, context, bindingDataSDF))
|
||||||
|
return true;
|
||||||
|
|
||||||
// Skip if already done in the current frame
|
// Skip if already done in the current frame
|
||||||
const auto currentFrame = Engine::FrameCount;
|
const auto currentFrame = Engine::FrameCount;
|
||||||
if (surfaceAtlasData.LastFrameUsed == currentFrame)
|
if (surfaceAtlasData.LastFrameUsed == currentFrame)
|
||||||
@@ -265,6 +301,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
uint64 memUsage = 0;
|
uint64 memUsage = 0;
|
||||||
// TODO: try using BC4/BC5/BC7 block compression for Surface Atlas (eg. for Tiles material properties)
|
// TODO: try using BC4/BC5/BC7 block compression for Surface Atlas (eg. for Tiles material properties)
|
||||||
#define INIT_ATLAS_TEXTURE(texture, format) desc.Format = format; surfaceAtlasData.texture = RenderTargetPool::Get(desc); if (!surfaceAtlasData.texture) return true; memUsage += surfaceAtlasData.texture->GetMemoryUsage()
|
#define INIT_ATLAS_TEXTURE(texture, format) desc.Format = format; surfaceAtlasData.texture = RenderTargetPool::Get(desc); if (!surfaceAtlasData.texture) return true; memUsage += surfaceAtlasData.texture->GetMemoryUsage()
|
||||||
|
INIT_ATLAS_TEXTURE(AtlasEmissive, LIGHT_BUFFER_FORMAT);
|
||||||
INIT_ATLAS_TEXTURE(AtlasGBuffer0, GBUFFER0_FORMAT);
|
INIT_ATLAS_TEXTURE(AtlasGBuffer0, GBUFFER0_FORMAT);
|
||||||
INIT_ATLAS_TEXTURE(AtlasGBuffer1, GBUFFER1_FORMAT);
|
INIT_ATLAS_TEXTURE(AtlasGBuffer1, GBUFFER1_FORMAT);
|
||||||
INIT_ATLAS_TEXTURE(AtlasGBuffer2, GBUFFER2_FORMAT);
|
INIT_ATLAS_TEXTURE(AtlasGBuffer2, GBUFFER2_FORMAT);
|
||||||
@@ -289,9 +326,37 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
surfaceAtlasData.AtlasTiles = New<GlobalSurfaceAtlasTile>(0, 0, resolution, resolution);
|
surfaceAtlasData.AtlasTiles = New<GlobalSurfaceAtlasTile>(0, 0, resolution, resolution);
|
||||||
if (!_vertexBuffer)
|
if (!_vertexBuffer)
|
||||||
_vertexBuffer = New<DynamicVertexBuffer>(0u, (uint32)sizeof(AtlasTileVertex), TEXT("GlobalSurfaceAtlas.VertexBuffer"));
|
_vertexBuffer = New<DynamicVertexBuffer>(0u, (uint32)sizeof(AtlasTileVertex), TEXT("GlobalSurfaceAtlas.VertexBuffer"));
|
||||||
|
const Vector2 posToClipMul(2.0f * resolutionInv, -2.0f * resolutionInv);
|
||||||
|
const Vector2 posToClipAdd(-1.0f, 1.0f);
|
||||||
|
#define VB_WRITE_TILE_POS_ONLY(tile) \
|
||||||
|
Vector2 minPos((float)tile->X, (float)tile->Y), maxPos((float)(tile->X + tile->Width), (float)(tile->Y + tile->Height)); \
|
||||||
|
Half2 min(minPos * posToClipMul + posToClipAdd), max(maxPos * posToClipMul + posToClipAdd); \
|
||||||
|
auto* quad = _vertexBuffer->WriteReserve<AtlasTileVertex>(6); \
|
||||||
|
quad[0].Position = max; \
|
||||||
|
quad[1].Position = { min.X, max.Y }; \
|
||||||
|
quad[2].Position = min; \
|
||||||
|
quad[3].Position = quad[2].Position; \
|
||||||
|
quad[4].Position = { max.X, min.Y }; \
|
||||||
|
quad[5].Position = quad[0].Position
|
||||||
|
#define VB_WRITE_TILE(tile) \
|
||||||
|
Vector2 minPos((float)tile->X, (float)tile->Y), maxPos((float)(tile->X + tile->Width), (float)(tile->Y + tile->Height)); \
|
||||||
|
Half2 min(minPos * posToClipMul + posToClipAdd), max(maxPos * posToClipMul + posToClipAdd); \
|
||||||
|
Vector2 minUV(0, 0), maxUV(1, 1); \
|
||||||
|
auto* quad = _vertexBuffer->WriteReserve<AtlasTileVertex>(6); \
|
||||||
|
quad[0] = { { max }, { maxUV }, (uint16)object.Index, (uint16)tileIndex }; \
|
||||||
|
quad[1] = { { min.X, max.Y }, { minUV.X, maxUV.Y }, (uint16)object.Index, (uint16)tileIndex }; \
|
||||||
|
quad[2] = { { min }, { minUV }, (uint16)object.Index, (uint16)tileIndex }; \
|
||||||
|
quad[3] = quad[2]; \
|
||||||
|
quad[4] = { { max.X, min.Y }, { maxUV.X, minUV.Y }, (uint16)object.Index, (uint16)tileIndex }; \
|
||||||
|
quad[5] = quad[0]
|
||||||
|
#define VB_DRAW() \
|
||||||
|
_vertexBuffer->Flush(context); \
|
||||||
|
auto vb = _vertexBuffer->GetBuffer(); \
|
||||||
|
context->BindVB(ToSpan(&vb, 1)); \
|
||||||
|
context->DrawInstanced(_vertexBuffer->Data.Count() / sizeof(AtlasTileVertex), 1);
|
||||||
// Add objects into the atlas
|
// Add objects into the atlas
|
||||||
surfaceAtlasData.ObjectsBuffer.Clear();
|
surfaceAtlasData.ObjectsBuffer.Clear();
|
||||||
|
surfaceAtlasData.ObjectIndexCounter = 0;
|
||||||
_dirtyObjectsBuffer.Clear();
|
_dirtyObjectsBuffer.Clear();
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Draw");
|
PROFILE_CPU_NAMED("Draw");
|
||||||
@@ -390,7 +455,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
object->LastFrameUsed = currentFrame;
|
object->LastFrameUsed = currentFrame;
|
||||||
object->Bounds = OrientedBoundingBox(localBounds);
|
object->Bounds = OrientedBoundingBox(localBounds);
|
||||||
object->Bounds.Transform(localToWorld);
|
object->Bounds.Transform(localToWorld);
|
||||||
if (dirty || GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_TILES_REDRAW)
|
object->Radius = e.Bounds.Radius;
|
||||||
|
if (dirty || GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_TILES)
|
||||||
{
|
{
|
||||||
object->LastFrameDirty = currentFrame;
|
object->LastFrameDirty = currentFrame;
|
||||||
_dirtyObjectsBuffer.Add(e.Actor);
|
_dirtyObjectsBuffer.Add(e.Actor);
|
||||||
@@ -400,6 +466,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
Matrix worldToLocalBounds;
|
Matrix worldToLocalBounds;
|
||||||
Matrix::Invert(object->Bounds.Transformation, worldToLocalBounds);
|
Matrix::Invert(object->Bounds.Transformation, worldToLocalBounds);
|
||||||
// TODO: cache data for static objects to optimize CPU perf (move ObjectsBuffer into surfaceAtlasData)
|
// TODO: cache data for static objects to optimize CPU perf (move ObjectsBuffer into surfaceAtlasData)
|
||||||
|
object->Index = surfaceAtlasData.ObjectIndexCounter++;
|
||||||
auto* objectData = surfaceAtlasData.ObjectsBuffer.WriteReserve<Vector4>(GLOBAL_SURFACE_ATLAS_OBJECT_SIZE);
|
auto* objectData = surfaceAtlasData.ObjectsBuffer.WriteReserve<Vector4>(GLOBAL_SURFACE_ATLAS_OBJECT_SIZE);
|
||||||
objectData[0] = *(Vector4*)&e.Bounds;
|
objectData[0] = *(Vector4*)&e.Bounds;
|
||||||
objectData[1] = Vector4(worldToLocalBounds.M11, worldToLocalBounds.M12, worldToLocalBounds.M13, worldToLocalBounds.M41);
|
objectData[1] = Vector4(worldToLocalBounds.M11, worldToLocalBounds.M12, worldToLocalBounds.M13, worldToLocalBounds.M41);
|
||||||
@@ -500,7 +567,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
GPUTextureView* depthBuffer = surfaceAtlasData.AtlasDepth->View();
|
GPUTextureView* depthBuffer = surfaceAtlasData.AtlasDepth->View();
|
||||||
GPUTextureView* targetBuffers[4] =
|
GPUTextureView* targetBuffers[4] =
|
||||||
{
|
{
|
||||||
surfaceAtlasData.AtlasDirectLight->View(),
|
surfaceAtlasData.AtlasEmissive->View(),
|
||||||
surfaceAtlasData.AtlasGBuffer0->View(),
|
surfaceAtlasData.AtlasGBuffer0->View(),
|
||||||
surfaceAtlasData.AtlasGBuffer1->View(),
|
surfaceAtlasData.AtlasGBuffer1->View(),
|
||||||
surfaceAtlasData.AtlasGBuffer2->View(),
|
surfaceAtlasData.AtlasGBuffer2->View(),
|
||||||
@@ -508,7 +575,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
context->SetRenderTarget(depthBuffer, ToSpan(targetBuffers, ARRAY_COUNT(targetBuffers)));
|
context->SetRenderTarget(depthBuffer, ToSpan(targetBuffers, ARRAY_COUNT(targetBuffers)));
|
||||||
{
|
{
|
||||||
PROFILE_GPU_CPU("Clear");
|
PROFILE_GPU_CPU("Clear");
|
||||||
if (noCache || GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_TILES_REDRAW)
|
if (noCache || GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_TILES)
|
||||||
{
|
{
|
||||||
// Full-atlas hardware clear
|
// Full-atlas hardware clear
|
||||||
context->ClearDepth(depthBuffer);
|
context->ClearDepth(depthBuffer);
|
||||||
@@ -522,8 +589,6 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
// Per-tile clear (with a single draw call)
|
// Per-tile clear (with a single draw call)
|
||||||
_vertexBuffer->Clear();
|
_vertexBuffer->Clear();
|
||||||
_vertexBuffer->Data.EnsureCapacity(_dirtyObjectsBuffer.Count() * 6 * sizeof(AtlasTileVertex));
|
_vertexBuffer->Data.EnsureCapacity(_dirtyObjectsBuffer.Count() * 6 * sizeof(AtlasTileVertex));
|
||||||
const Vector2 posToClipMul(2.0f * resolutionInv, -2.0f * resolutionInv);
|
|
||||||
const Vector2 posToClipAdd(-1.0f, 1.0f);
|
|
||||||
for (Actor* actor : _dirtyObjectsBuffer)
|
for (Actor* actor : _dirtyObjectsBuffer)
|
||||||
{
|
{
|
||||||
const auto& object = ((const Dictionary<Actor*, GlobalSurfaceAtlasObject>&)surfaceAtlasData.Objects)[actor];
|
const auto& object = ((const Dictionary<Actor*, GlobalSurfaceAtlasObject>&)surfaceAtlasData.Objects)[actor];
|
||||||
@@ -532,23 +597,12 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
auto* tile = object.Tiles[tileIndex];
|
auto* tile = object.Tiles[tileIndex];
|
||||||
if (!tile)
|
if (!tile)
|
||||||
continue;
|
continue;
|
||||||
Vector2 minPos((float)tile->X, (float)tile->Y), maxPos((float)(tile->X + tile->Width), (float)(tile->Y + tile->Height));
|
VB_WRITE_TILE_POS_ONLY(tile);
|
||||||
Half2 min(minPos * posToClipMul + posToClipAdd), max(maxPos * posToClipMul + posToClipAdd);
|
|
||||||
auto* quad = _vertexBuffer->WriteReserve<AtlasTileVertex>(6);
|
|
||||||
quad[0] = { { max } };
|
|
||||||
quad[1] = { { min.X, max.Y } };
|
|
||||||
quad[2] = { { min } };
|
|
||||||
quad[3] = quad[2];
|
|
||||||
quad[4] = { { max.X, min.Y } };
|
|
||||||
quad[5] = quad[0];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_vertexBuffer->Flush(context);
|
|
||||||
auto vb = _vertexBuffer->GetBuffer();
|
|
||||||
context->SetState(_psClear);
|
context->SetState(_psClear);
|
||||||
context->SetViewportAndScissors(Viewport(0, 0, resolution, resolution));
|
context->SetViewportAndScissors(Viewport(0, 0, resolution, resolution));
|
||||||
context->BindVB(ToSpan(&vb, 1));
|
VB_DRAW();
|
||||||
context->DrawInstanced(_vertexBuffer->Data.Count() / sizeof(AtlasTileVertex), 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: limit dirty objects count on a first frame (eg. collect overflown objects to be redirty next frame)
|
// TODO: limit dirty objects count on a first frame (eg. collect overflown objects to be redirty next frame)
|
||||||
@@ -574,7 +628,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
|
|
||||||
// Render all tiles into the atlas
|
// Render all tiles into the atlas
|
||||||
const auto& object = ((const Dictionary<Actor*, GlobalSurfaceAtlasObject>&)surfaceAtlasData.Objects)[actor];
|
const auto& object = ((const Dictionary<Actor*, GlobalSurfaceAtlasObject>&)surfaceAtlasData.Objects)[actor];
|
||||||
#if GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_DRAW_OBJECTS
|
#if GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_OBJECTS
|
||||||
DebugDraw::DrawBox(object.Bounds, Color::Red.AlphaMultiplied(0.4f));
|
DebugDraw::DrawBox(object.Bounds, Color::Red.AlphaMultiplied(0.4f));
|
||||||
#endif
|
#endif
|
||||||
for (int32 tileIndex = 0; tileIndex < 6; tileIndex++)
|
for (int32 tileIndex = 0; tileIndex < 6; tileIndex++)
|
||||||
@@ -588,12 +642,12 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
// Setup projection to capture object from the side
|
// Setup projection to capture object from the side
|
||||||
renderContextTiles.View.Position = tile->ViewPosition;
|
renderContextTiles.View.Position = tile->ViewPosition;
|
||||||
renderContextTiles.View.Direction = tile->ViewDirection;
|
renderContextTiles.View.Direction = tile->ViewDirection;
|
||||||
renderContextTiles.View.Near = -0.1f; // Small offset to prevent clipping with the closest triangles
|
renderContextTiles.View.Near = -GLOBAL_SURFACE_ATLAS_TILE_PROJ_PLANE_OFFSET;
|
||||||
renderContextTiles.View.Far = tile->ViewBoundsSize.Z + 0.2f;
|
renderContextTiles.View.Far = tile->ViewBoundsSize.Z + 2 * GLOBAL_SURFACE_ATLAS_TILE_PROJ_PLANE_OFFSET;
|
||||||
Matrix projectionMatrix;
|
Matrix projectionMatrix;
|
||||||
Matrix::Ortho(tile->ViewBoundsSize.X, tile->ViewBoundsSize.Y, renderContextTiles.View.Near, renderContextTiles.View.Far, projectionMatrix);
|
Matrix::Ortho(tile->ViewBoundsSize.X, tile->ViewBoundsSize.Y, renderContextTiles.View.Near, renderContextTiles.View.Far, projectionMatrix);
|
||||||
renderContextTiles.View.SetUp(tile->ViewMatrix, projectionMatrix);
|
renderContextTiles.View.SetUp(tile->ViewMatrix, projectionMatrix);
|
||||||
#if GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_DRAW_OBJECTS
|
#if GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_OBJECTS
|
||||||
DebugDraw::DrawLine(renderContextTiles.View.Position, renderContextTiles.View.Position + renderContextTiles.View.Direction * 20.0f, Color::Orange);
|
DebugDraw::DrawLine(renderContextTiles.View.Position, renderContextTiles.View.Position + renderContextTiles.View.Direction * 20.0f, Color::Orange);
|
||||||
DebugDraw::DrawWireSphere(BoundingSphere(renderContextTiles.View.Position, 10.0f), Color::Green);
|
DebugDraw::DrawWireSphere(BoundingSphere(renderContextTiles.View.Position, 10.0f), Color::Green);
|
||||||
#endif
|
#endif
|
||||||
@@ -608,12 +662,6 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
RenderList::ReturnToPool(renderContextTiles.List);
|
RenderList::ReturnToPool(renderContextTiles.List);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: update direct lighting atlas (for modified tiles and lights)
|
|
||||||
// TODO: update static lights only for dirty tiles (dynamic lights every X frames)
|
|
||||||
// TODO: use custom dynamic vertex buffer to decide which atlas tiles to shade with a light
|
|
||||||
|
|
||||||
// TODO: indirect lighting apply to get infinite bounces for GI
|
|
||||||
|
|
||||||
// Copy results
|
// Copy results
|
||||||
result.Atlas[0] = surfaceAtlasData.AtlasDepth;
|
result.Atlas[0] = surfaceAtlasData.AtlasDepth;
|
||||||
result.Atlas[1] = surfaceAtlasData.AtlasGBuffer0;
|
result.Atlas[1] = surfaceAtlasData.AtlasGBuffer0;
|
||||||
@@ -624,6 +672,114 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
|||||||
result.GlobalSurfaceAtlas.Resolution = (float)resolution;
|
result.GlobalSurfaceAtlas.Resolution = (float)resolution;
|
||||||
result.GlobalSurfaceAtlas.ObjectsCount = surfaceAtlasData.Objects.Count();
|
result.GlobalSurfaceAtlas.ObjectsCount = surfaceAtlasData.Objects.Count();
|
||||||
surfaceAtlasData.Result = result;
|
surfaceAtlasData.Result = result;
|
||||||
|
|
||||||
|
// Render direct lighting into atlas
|
||||||
|
if (surfaceAtlasData.Objects.Count() != 0)
|
||||||
|
{
|
||||||
|
PROFILE_GPU_CPU("Direct Lighting");
|
||||||
|
|
||||||
|
// Copy emissive light into the final direct lighting atlas
|
||||||
|
// TODO: test perf diff when manually copying only dirty object tiles and dirty light tiles
|
||||||
|
context->CopyTexture(surfaceAtlasData.AtlasDirectLight, 0, 0, 0, 0, surfaceAtlasData.AtlasEmissive, 0);
|
||||||
|
|
||||||
|
context->SetViewportAndScissors(Viewport(0, 0, resolution, resolution));
|
||||||
|
context->SetRenderTarget(surfaceAtlasData.AtlasDirectLight->View());
|
||||||
|
context->BindSR(0, surfaceAtlasData.AtlasGBuffer0->View());
|
||||||
|
context->BindSR(1, surfaceAtlasData.AtlasGBuffer1->View());
|
||||||
|
context->BindSR(2, surfaceAtlasData.AtlasGBuffer2->View());
|
||||||
|
context->BindSR(3, surfaceAtlasData.AtlasDepth->View());
|
||||||
|
context->BindSR(4, surfaceAtlasData.ObjectsBuffer.GetBuffer()->View());
|
||||||
|
context->BindCB(0, _cb0);
|
||||||
|
Data0 data;
|
||||||
|
data.ViewWorldPos = renderContext.View.Position;
|
||||||
|
data.GlobalSDF = bindingDataSDF.GlobalSDF;
|
||||||
|
data.GlobalSurfaceAtlas = result.GlobalSurfaceAtlas;
|
||||||
|
|
||||||
|
// Shade object tiles influenced by lights to calculate direct lighting
|
||||||
|
// TODO: reduce redraw frequency for static lights (StaticFlags::Lightmap)
|
||||||
|
for (auto& light : renderContext.List->DirectionalLights)
|
||||||
|
{
|
||||||
|
// Collect tiles to shade
|
||||||
|
_vertexBuffer->Clear();
|
||||||
|
for (const auto& e : surfaceAtlasData.Objects)
|
||||||
|
{
|
||||||
|
const auto& object = e.Value;
|
||||||
|
for (int32 tileIndex = 0; tileIndex < 6; tileIndex++)
|
||||||
|
{
|
||||||
|
auto* tile = object.Tiles[tileIndex];
|
||||||
|
if (!tile || Vector3::Dot(tile->ViewDirection, light.Direction) < ZeroTolerance)
|
||||||
|
continue;
|
||||||
|
VB_WRITE_TILE(tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw draw light
|
||||||
|
const bool useShadow = CanRenderShadow(renderContext.View, light);
|
||||||
|
// TODO: test perf/quality when using Shadow Map for directional light (ShadowsPass::Instance()->LastDirLightShadowMap) instead of Global SDF trace
|
||||||
|
light.SetupLightData(&data.Light, useShadow);
|
||||||
|
context->UpdateCB(_cb0, &data);
|
||||||
|
context->SetState(_psDirectLighting0);
|
||||||
|
VB_DRAW();
|
||||||
|
}
|
||||||
|
for (auto& light : renderContext.List->PointLights)
|
||||||
|
{
|
||||||
|
// Collect tiles to shade
|
||||||
|
_vertexBuffer->Clear();
|
||||||
|
for (const auto& e : surfaceAtlasData.Objects)
|
||||||
|
{
|
||||||
|
const auto& object = e.Value;
|
||||||
|
Vector3 lightToObject = object.Bounds.GetCenter() - light.Position;
|
||||||
|
if (lightToObject.LengthSquared() >= Math::Square(object.Radius + light.Radius))
|
||||||
|
continue;
|
||||||
|
for (int32 tileIndex = 0; tileIndex < 6; tileIndex++)
|
||||||
|
{
|
||||||
|
auto* tile = object.Tiles[tileIndex];
|
||||||
|
if (!tile)
|
||||||
|
continue;
|
||||||
|
VB_WRITE_TILE(tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw draw light
|
||||||
|
const bool useShadow = CanRenderShadow(renderContext.View, light);
|
||||||
|
light.SetupLightData(&data.Light, useShadow);
|
||||||
|
context->UpdateCB(_cb0, &data);
|
||||||
|
context->SetState(_psDirectLighting1);
|
||||||
|
VB_DRAW();
|
||||||
|
}
|
||||||
|
for (auto& light : renderContext.List->SpotLights)
|
||||||
|
{
|
||||||
|
// Collect tiles to shade
|
||||||
|
_vertexBuffer->Clear();
|
||||||
|
for (const auto& e : surfaceAtlasData.Objects)
|
||||||
|
{
|
||||||
|
const auto& object = e.Value;
|
||||||
|
Vector3 lightToObject = object.Bounds.GetCenter() - light.Position;
|
||||||
|
if (lightToObject.LengthSquared() >= Math::Square(object.Radius + light.Radius))
|
||||||
|
continue;
|
||||||
|
for (int32 tileIndex = 0; tileIndex < 6; tileIndex++)
|
||||||
|
{
|
||||||
|
auto* tile = object.Tiles[tileIndex];
|
||||||
|
if (!tile || Vector3::Dot(tile->ViewDirection, light.Direction) < ZeroTolerance)
|
||||||
|
continue;
|
||||||
|
VB_WRITE_TILE(tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw draw light
|
||||||
|
const bool useShadow = CanRenderShadow(renderContext.View, light);
|
||||||
|
light.SetupLightData(&data.Light, useShadow);
|
||||||
|
context->UpdateCB(_cb0, &data);
|
||||||
|
context->SetState(_psDirectLighting1);
|
||||||
|
VB_DRAW();
|
||||||
|
}
|
||||||
|
|
||||||
|
context->ResetRenderTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: indirect lighting apply to get infinite bounces for GI
|
||||||
|
|
||||||
|
#undef WRITE_TILE
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -639,7 +795,6 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex
|
|||||||
|
|
||||||
PROFILE_GPU_CPU("Global Surface Atlas Debug");
|
PROFILE_GPU_CPU("Global Surface Atlas Debug");
|
||||||
const Vector2 outputSize(output->Size());
|
const Vector2 outputSize(output->Size());
|
||||||
if (_cb0)
|
|
||||||
{
|
{
|
||||||
Data0 data;
|
Data0 data;
|
||||||
data.ViewWorldPos = renderContext.View.Position;
|
data.ViewWorldPos = renderContext.View.Position;
|
||||||
@@ -660,10 +815,10 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex
|
|||||||
context->BindSR(8, bindingData.Objects ? bindingData.Objects->View() : nullptr);
|
context->BindSR(8, bindingData.Objects ? bindingData.Objects->View() : nullptr);
|
||||||
context->BindSR(9, bindingData.Atlas[0]->View());
|
context->BindSR(9, bindingData.Atlas[0]->View());
|
||||||
{
|
{
|
||||||
GPUTexture* tex = bindingData.Atlas[1]; // Preview diffuse
|
//GPUTexture* tex = bindingData.Atlas[1]; // Preview diffuse
|
||||||
//GPUTexture* tex = bindingData.Atlas[2]; // Preview normals
|
//GPUTexture* tex = bindingData.Atlas[2]; // Preview normals
|
||||||
//GPUTexture* tex = bindingData.Atlas[3]; // Preview roughness/metalness/ao
|
//GPUTexture* tex = bindingData.Atlas[3]; // Preview roughness/metalness/ao
|
||||||
//GPUTexture* tex = bindingData.Atlas[4]; // Preview direct light
|
GPUTexture* tex = bindingData.Atlas[4]; // Preview direct light
|
||||||
context->BindSR(10, tex->View());
|
context->BindSR(10, tex->View());
|
||||||
}
|
}
|
||||||
context->SetState(_psDebug);
|
context->SetState(_psDebug);
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ private:
|
|||||||
bool _supported = false;
|
bool _supported = false;
|
||||||
AssetReference<Shader> _shader;
|
AssetReference<Shader> _shader;
|
||||||
GPUPipelineState* _psClear = nullptr;
|
GPUPipelineState* _psClear = nullptr;
|
||||||
|
GPUPipelineState* _psDirectLighting0 = nullptr;
|
||||||
|
GPUPipelineState* _psDirectLighting1 = nullptr;
|
||||||
GPUPipelineState* _psDebug = nullptr;
|
GPUPipelineState* _psDebug = nullptr;
|
||||||
GPUConstantBuffer* _cb0 = nullptr;
|
GPUConstantBuffer* _cb0 = nullptr;
|
||||||
|
|
||||||
|
|||||||
@@ -141,27 +141,6 @@ bool LightPass::setupResources()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
bool CanRenderShadow(RenderView& view, const T& light)
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
switch ((ShadowsCastingMode)light.ShadowsMode)
|
|
||||||
{
|
|
||||||
case ShadowsCastingMode::StaticOnly:
|
|
||||||
result = view.IsOfflinePass;
|
|
||||||
break;
|
|
||||||
case ShadowsCastingMode::DynamicOnly:
|
|
||||||
result = !view.IsOfflinePass;
|
|
||||||
break;
|
|
||||||
case ShadowsCastingMode::All:
|
|
||||||
result = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result && light.ShadowsStrength > ZeroTolerance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightPass::Dispose()
|
void LightPass::Dispose()
|
||||||
{
|
{
|
||||||
// Base
|
// Base
|
||||||
|
|||||||
@@ -14,6 +14,27 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
#define SHADOWS_PASS_SS_RR_FORMAT PixelFormat::R11G11B10_Float
|
#define SHADOWS_PASS_SS_RR_FORMAT PixelFormat::R11G11B10_Float
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool CanRenderShadow(RenderView& view, const T& light)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
switch ((ShadowsCastingMode)light.ShadowsMode)
|
||||||
|
{
|
||||||
|
case ShadowsCastingMode::StaticOnly:
|
||||||
|
result = view.IsOfflinePass;
|
||||||
|
break;
|
||||||
|
case ShadowsCastingMode::DynamicOnly:
|
||||||
|
result = !view.IsOfflinePass;
|
||||||
|
break;
|
||||||
|
case ShadowsCastingMode::All:
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result && light.ShadowsStrength > ZeroTolerance;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shadows rendering service.
|
/// Shadows rendering service.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -144,24 +144,6 @@ GBufferSample SampleGBufferFast(GBufferData gBuffer, float2 uv)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sample GBuffer normal vector, shading model and view space position
|
|
||||||
GBufferSample SampleGBufferNormalVPos(GBufferData gBuffer, float2 uv)
|
|
||||||
{
|
|
||||||
GBufferSample result;
|
|
||||||
|
|
||||||
// Sample GBuffer
|
|
||||||
float4 gBuffer1 = SAMPLE_RT(GBuffer1, uv);
|
|
||||||
|
|
||||||
// Decode normal and shading model
|
|
||||||
result.Normal = DecodeNormal(gBuffer1.rgb);
|
|
||||||
result.ShadingModel = (int)(gBuffer1.a * 3.999);
|
|
||||||
|
|
||||||
// Calculate view space position
|
|
||||||
result.ViewPos = GetViewPos(gBuffer, uv);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||||
|
|
||||||
// Sample GBuffer custom data only
|
// Sample GBuffer custom data only
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
// This must match C++
|
// This must match C++
|
||||||
#define GLOBAL_SURFACE_ATLAS_OBJECT_SIZE (5 + 6 * 5) // Amount of float4s per-object
|
#define GLOBAL_SURFACE_ATLAS_OBJECT_SIZE (5 + 6 * 5) // Amount of float4s per-object
|
||||||
#define GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD 0.1f // Cut-off value for tiles transitions blending during sampling
|
#define GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD 0.1f // Cut-off value for tiles transitions blending during sampling
|
||||||
|
#define GLOBAL_SURFACE_ATLAS_TILE_PROJ_PLANE_OFFSET 0.1f // Small offset to prevent clipping with the closest triangles (shifts near and far planes)
|
||||||
|
|
||||||
struct GlobalSurfaceTile
|
struct GlobalSurfaceTile
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
// Diffuse-only lighting
|
||||||
|
#define NO_SPECULAR
|
||||||
|
|
||||||
#include "./Flax/Common.hlsl"
|
#include "./Flax/Common.hlsl"
|
||||||
#include "./Flax/Math.hlsl"
|
#include "./Flax/Math.hlsl"
|
||||||
#include "./Flax/GlobalSurfaceAtlas.hlsl"
|
#include "./Flax/GlobalSurfaceAtlas.hlsl"
|
||||||
#include "./Flax/GlobalSignDistanceField.hlsl"
|
#include "./Flax/GlobalSignDistanceField.hlsl"
|
||||||
|
#include "./Flax/LightingCommon.hlsl"
|
||||||
|
|
||||||
META_CB_BEGIN(0, Data)
|
META_CB_BEGIN(0, Data)
|
||||||
float3 ViewWorldPos;
|
float3 ViewWorldPos;
|
||||||
@@ -13,14 +17,35 @@ float ViewFarPlane;
|
|||||||
float4 ViewFrustumWorldRays[4];
|
float4 ViewFrustumWorldRays[4];
|
||||||
GlobalSDFData GlobalSDF;
|
GlobalSDFData GlobalSDF;
|
||||||
GlobalSurfaceAtlasData GlobalSurfaceAtlas;
|
GlobalSurfaceAtlasData GlobalSurfaceAtlas;
|
||||||
|
LightData Light;
|
||||||
META_CB_END
|
META_CB_END
|
||||||
|
|
||||||
// Vertex shader for Global Surface Atlas software clearing
|
struct AtlasVertexIput
|
||||||
|
{
|
||||||
|
float2 Position : POSITION0;
|
||||||
|
float2 TileUV : TEXCOORD0;
|
||||||
|
uint2 Index : TEXCOORD1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AtlasVertexOutput
|
||||||
|
{
|
||||||
|
float4 Position : SV_Position;
|
||||||
|
float2 TileUV : TEXCOORD0;
|
||||||
|
nointerpolation uint2 Index : TEXCOORD1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Vertex shader for Global Surface Atlas rendering (custom vertex buffer to render per-tile)
|
||||||
META_VS(true, FEATURE_LEVEL_SM5)
|
META_VS(true, FEATURE_LEVEL_SM5)
|
||||||
META_VS_IN_ELEMENT(POSITION, 0, R16G16_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
|
META_VS_IN_ELEMENT(POSITION, 0, R16G16_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
|
||||||
float4 VS_Clear(float2 Position : POSITION0) : SV_Position
|
META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
|
||||||
|
META_VS_IN_ELEMENT(TEXCOORD, 1, R16G16_UINT, 0, ALIGN, PER_VERTEX, 0, true)
|
||||||
|
AtlasVertexOutput VS_Atlas(AtlasVertexIput input)
|
||||||
{
|
{
|
||||||
return float4(Position, 1, 1);
|
AtlasVertexOutput output;
|
||||||
|
output.Position = float4(input.Position, 1, 1);
|
||||||
|
output.TileUV = input.TileUV;
|
||||||
|
output.Index = input.Index;
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pixel shader for Global Surface Atlas software clearing
|
// Pixel shader for Global Surface Atlas software clearing
|
||||||
@@ -33,6 +58,70 @@ void PS_Clear(out float4 Light : SV_Target0, out float4 RT0 : SV_Target1, out fl
|
|||||||
RT2 = float4(1, 0, 0, 0);
|
RT2 = float4(1, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _PS_DirectLighting
|
||||||
|
|
||||||
|
#include "./Flax/GBuffer.hlsl"
|
||||||
|
#include "./Flax/Matrix.hlsl"
|
||||||
|
#include "./Flax/Lighting.hlsl"
|
||||||
|
|
||||||
|
// GBuffer+Depth at 0-3 slots
|
||||||
|
Buffer<float4> GlobalSurfaceAtlasObjects : register(t4);
|
||||||
|
|
||||||
|
// Pixel shader for Global Surface Atlas shading with direct light contribution
|
||||||
|
META_PS(true, FEATURE_LEVEL_SM5)
|
||||||
|
META_PERMUTATION_1(RADIAL_LIGHT=0)
|
||||||
|
META_PERMUTATION_1(RADIAL_LIGHT=1)
|
||||||
|
float4 PS_DirectLighting(AtlasVertexOutput input) : SV_Target
|
||||||
|
{
|
||||||
|
// Load current tile info
|
||||||
|
//GlobalSurfaceObject object = LoadGlobalSurfaceAtlasObject(GlobalSurfaceAtlasObjects, input.Index.x);
|
||||||
|
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(GlobalSurfaceAtlasObjects, input.Index.x, input.Index.y);
|
||||||
|
float2 atlasUV = input.TileUV * tile.AtlasRectUV.zw + tile.AtlasRectUV.xy;
|
||||||
|
|
||||||
|
// Load GBuffer sample from atlas
|
||||||
|
GBufferData gBufferData = (GBufferData)0;
|
||||||
|
GBufferSample gBuffer = SampleGBuffer(gBufferData, atlasUV);
|
||||||
|
|
||||||
|
// Skip unlit pixels
|
||||||
|
BRANCH
|
||||||
|
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT)
|
||||||
|
{
|
||||||
|
discard;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconstruct world-space position manually (from uv+depth within a tile)
|
||||||
|
float tileDepth = SampleZ(atlasUV);
|
||||||
|
//float tileNear = -GLOBAL_SURFACE_ATLAS_TILE_PROJ_PLANE_OFFSET;
|
||||||
|
//float tileFar = tile.ViewBoundsSize.z + 2 * GLOBAL_SURFACE_ATLAS_TILE_PROJ_PLANE_OFFSET;
|
||||||
|
//gBufferData.ViewInfo.zw = float2(tileFar / (tileFar - tileNear), (-tileFar * tileNear) / (tileFar - tileNear) / tileFar);
|
||||||
|
//gBufferData.ViewInfo.zw = float2(1, 0);
|
||||||
|
//float tileLinearDepth = LinearizeZ(gBufferData, tileDepth);
|
||||||
|
float3 tileSpacePos = float3(input.TileUV.x - 0.5f, 0.5f - input.TileUV.y, tileDepth);
|
||||||
|
float3 gBufferTilePos = tileSpacePos * tile.ViewBoundsSize;
|
||||||
|
float4x4 tileLocalToWorld = Inverse(tile.WorldToLocal);
|
||||||
|
gBuffer.WorldPos = mul(float4(gBufferTilePos, 1), tileLocalToWorld).xyz;
|
||||||
|
|
||||||
|
float4 shadowMask = 1;
|
||||||
|
BRANCH
|
||||||
|
if (Light.CastShadows > 0)
|
||||||
|
{
|
||||||
|
// TODO: calculate shadow for the light (use Global SDF)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate lighting
|
||||||
|
#if RADIAL_LIGHT
|
||||||
|
bool isSpotLight = Light.SpotAngles.x > -2.0f;
|
||||||
|
#else
|
||||||
|
bool isSpotLight = false;
|
||||||
|
#endif
|
||||||
|
float4 light = GetLighting(ViewWorldPos, Light, gBuffer, shadowMask, RADIAL_LIGHT, isSpotLight);
|
||||||
|
|
||||||
|
return light;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _PS_Debug
|
#ifdef _PS_Debug
|
||||||
|
|
||||||
Texture3D<float> GlobalSDFTex[4] : register(t0);
|
Texture3D<float> GlobalSDFTex[4] : register(t0);
|
||||||
|
|||||||
@@ -3,10 +3,6 @@
|
|||||||
#ifndef __LIGHTING__
|
#ifndef __LIGHTING__
|
||||||
#define __LIGHTING__
|
#define __LIGHTING__
|
||||||
|
|
||||||
#if !defined(USE_GBUFFER_CUSTOM_DATA)
|
|
||||||
#error "Cannot calculate lighting without custom data in GBuffer. Define USE_GBUFFER_CUSTOM_DATA."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "./Flax/LightingCommon.hlsl"
|
#include "./Flax/LightingCommon.hlsl"
|
||||||
|
|
||||||
ShadowData GetShadow(LightData lightData, GBufferSample gBuffer, float4 shadowMask)
|
ShadowData GetShadow(LightData lightData, GBufferSample gBuffer, float4 shadowMask)
|
||||||
@@ -20,21 +16,23 @@ ShadowData GetShadow(LightData lightData, GBufferSample gBuffer, float4 shadowMa
|
|||||||
LightingData StandardShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
LightingData StandardShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||||
{
|
{
|
||||||
float3 diffuseColor = GetDiffuseColor(gBuffer);
|
float3 diffuseColor = GetDiffuseColor(gBuffer);
|
||||||
float3 specularColor = GetSpecularColor(gBuffer);
|
|
||||||
|
|
||||||
float3 H = normalize(V + L);
|
float3 H = normalize(V + L);
|
||||||
float NoL = saturate(dot(N, L));
|
float NoL = saturate(dot(N, L));
|
||||||
float NoV = max(dot(N, V), 1e-5);
|
float NoV = max(dot(N, V), 1e-5);
|
||||||
float NoH = saturate(dot(N, H));
|
float NoH = saturate(dot(N, H));
|
||||||
float VoH = saturate(dot(V, H));
|
float VoH = saturate(dot(V, H));
|
||||||
|
|
||||||
float D = D_GGX(gBuffer.Roughness, NoH) * energy;
|
|
||||||
float Vis = Vis_SmithJointApprox(gBuffer.Roughness, NoV, NoL);
|
|
||||||
float3 F = F_Schlick(specularColor, VoH);
|
|
||||||
|
|
||||||
LightingData lighting;
|
LightingData lighting;
|
||||||
lighting.Diffuse = Diffuse_Lambert(diffuseColor);
|
lighting.Diffuse = Diffuse_Lambert(diffuseColor);
|
||||||
|
#if defined(NO_SPECULAR)
|
||||||
|
lighting.Specular = 0;
|
||||||
|
#else
|
||||||
|
float3 specularColor = GetSpecularColor(gBuffer);
|
||||||
|
float3 F = F_Schlick(specularColor, VoH);
|
||||||
|
float D = D_GGX(gBuffer.Roughness, NoH) * energy;
|
||||||
|
float Vis = Vis_SmithJointApprox(gBuffer.Roughness, NoV, NoL);
|
||||||
lighting.Specular = (D * Vis) * F;
|
lighting.Specular = (D * Vis) * F;
|
||||||
|
#endif
|
||||||
lighting.Transmission = 0;
|
lighting.Transmission = 0;
|
||||||
return lighting;
|
return lighting;
|
||||||
}
|
}
|
||||||
@@ -42,7 +40,7 @@ LightingData StandardShading(GBufferSample gBuffer, float energy, float3 L, floa
|
|||||||
LightingData SubsurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
LightingData SubsurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||||
{
|
{
|
||||||
LightingData lighting = StandardShading(gBuffer, energy, L, V, N);
|
LightingData lighting = StandardShading(gBuffer, energy, L, V, N);
|
||||||
|
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||||
// Fake effect of the light going through the material
|
// Fake effect of the light going through the material
|
||||||
float3 subsurfaceColor = gBuffer.CustomData.rgb;
|
float3 subsurfaceColor = gBuffer.CustomData.rgb;
|
||||||
float opacity = gBuffer.CustomData.a;
|
float opacity = gBuffer.CustomData.a;
|
||||||
@@ -51,21 +49,21 @@ LightingData SubsurfaceShading(GBufferSample gBuffer, float energy, float3 L, fl
|
|||||||
float normalContribution = saturate(dot(N, H) * opacity + 1.0f - opacity);
|
float normalContribution = saturate(dot(N, H) * opacity + 1.0f - opacity);
|
||||||
float backScatter = gBuffer.AO * normalContribution / (PI * 2.0f);
|
float backScatter = gBuffer.AO * normalContribution / (PI * 2.0f);
|
||||||
lighting.Transmission = lerp(backScatter, 1, inscatter) * subsurfaceColor;
|
lighting.Transmission = lerp(backScatter, 1, inscatter) * subsurfaceColor;
|
||||||
|
#endif
|
||||||
return lighting;
|
return lighting;
|
||||||
}
|
}
|
||||||
|
|
||||||
LightingData FoliageShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
LightingData FoliageShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||||
{
|
{
|
||||||
LightingData lighting = StandardShading(gBuffer, energy, L, V, N);
|
LightingData lighting = StandardShading(gBuffer, energy, L, V, N);
|
||||||
|
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||||
// Fake effect of the light going through the thin foliage
|
// Fake effect of the light going through the thin foliage
|
||||||
float3 subsurfaceColor = gBuffer.CustomData.rgb;
|
float3 subsurfaceColor = gBuffer.CustomData.rgb;
|
||||||
float wrapNoL = saturate((-dot(N, L) + 0.5f) / 2.25);
|
float wrapNoL = saturate((-dot(N, L) + 0.5f) / 2.25);
|
||||||
float VoL = dot(V, L);
|
float VoL = dot(V, L);
|
||||||
float scatter = D_GGX(0.36, saturate(-VoL));
|
float scatter = D_GGX(0.36, saturate(-VoL));
|
||||||
lighting.Transmission = subsurfaceColor * (wrapNoL * scatter);
|
lighting.Transmission = subsurfaceColor * (wrapNoL * scatter);
|
||||||
|
#endif
|
||||||
return lighting;
|
return lighting;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,9 +142,6 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f
|
|||||||
|
|
||||||
// Calculate direct lighting
|
// Calculate direct lighting
|
||||||
LightingData lighting = SurfaceShading(gBuffer, energy, L, V, N);
|
LightingData lighting = SurfaceShading(gBuffer, energy, L, V, N);
|
||||||
#if NO_SPECULAR
|
|
||||||
lighting.Specular = float3(0, 0, 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Calculate final light color
|
// Calculate final light color
|
||||||
float3 surfaceLight = (lighting.Diffuse + lighting.Specular) * (NoL * attenuation * shadow.SurfaceShadow);
|
float3 surfaceLight = (lighting.Diffuse + lighting.Specular) * (NoL * attenuation * shadow.SurfaceShadow);
|
||||||
|
|||||||
Reference in New Issue
Block a user