Add direct lighting rendering into Global Surface Atlas

This commit is contained in:
Wojciech Figat
2022-04-13 16:26:19 +02:00
parent 0a27d277a0
commit 0668a23167
10 changed files with 322 additions and 97 deletions

BIN
Content/Shaders/GlobalSurfaceAtlas.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -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>

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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
{ {

View File

@@ -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);

View File

@@ -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);