Move Static Model rasterization into Global SDF code
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
#include "Engine/Serialization/Serialization.h"
|
#include "Engine/Serialization/Serialization.h"
|
||||||
#include "Engine/Level/Prefabs/PrefabManager.h"
|
#include "Engine/Level/Prefabs/PrefabManager.h"
|
||||||
#include "Engine/Level/Scene/Scene.h"
|
#include "Engine/Level/Scene/Scene.h"
|
||||||
|
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
|
||||||
#include "Engine/Utilities/Encryption.h"
|
#include "Engine/Utilities/Encryption.h"
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
#include "Editor/Editor.h"
|
#include "Editor/Editor.h"
|
||||||
@@ -209,62 +210,66 @@ bool StaticModel::HasContentLoaded() const
|
|||||||
|
|
||||||
void StaticModel::Draw(RenderContext& renderContext)
|
void StaticModel::Draw(RenderContext& renderContext)
|
||||||
{
|
{
|
||||||
|
const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass);
|
||||||
|
if (!Model || !Model->IsLoaded() || !Model->CanBeRendered() || drawModes == DrawPass::None)
|
||||||
|
return;
|
||||||
if (renderContext.View.Pass == DrawPass::GlobalSDF)
|
if (renderContext.View.Pass == DrawPass::GlobalSDF)
|
||||||
return; // TODO: Static Model rendering to Global SDF
|
{
|
||||||
|
if (!Model->SDF.Texture)
|
||||||
|
Model->GenerateSDF();
|
||||||
|
GlobalSignDistanceFieldPass::Instance()->RasterizeModelSDF(Model->SDF, _world, _box);
|
||||||
|
return;
|
||||||
|
}
|
||||||
GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, _world);
|
GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, _world);
|
||||||
|
|
||||||
const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass);
|
// Flush vertex colors if need to
|
||||||
if (Model && Model->IsLoaded() && drawModes != DrawPass::None)
|
if (_vertexColorsDirty)
|
||||||
{
|
{
|
||||||
// Flush vertex colors if need to
|
for (int32 lodIndex = 0; lodIndex < _vertexColorsCount; lodIndex++)
|
||||||
if (_vertexColorsDirty)
|
|
||||||
{
|
{
|
||||||
for (int32 lodIndex = 0; lodIndex < _vertexColorsCount; lodIndex++)
|
auto& vertexColorsData = _vertexColorsData[lodIndex];
|
||||||
|
auto& vertexColorsBuffer = _vertexColorsBuffer[lodIndex];
|
||||||
|
if (vertexColorsData.HasItems())
|
||||||
{
|
{
|
||||||
auto& vertexColorsData = _vertexColorsData[lodIndex];
|
const uint32 size = vertexColorsData.Count() * sizeof(Color32);
|
||||||
auto& vertexColorsBuffer = _vertexColorsBuffer[lodIndex];
|
if (!vertexColorsBuffer)
|
||||||
if (vertexColorsData.HasItems())
|
vertexColorsBuffer = GPUDevice::Instance->CreateBuffer(TEXT("VertexColors"));
|
||||||
|
if (vertexColorsBuffer->GetSize() != size)
|
||||||
{
|
{
|
||||||
const uint32 size = vertexColorsData.Count() * sizeof(Color32);
|
if (vertexColorsBuffer->Init(GPUBufferDescription::Vertex(sizeof(Color32), vertexColorsData.Count())))
|
||||||
if (!vertexColorsBuffer)
|
return;
|
||||||
vertexColorsBuffer = GPUDevice::Instance->CreateBuffer(TEXT("VertexColors"));
|
|
||||||
if (vertexColorsBuffer->GetSize() != size)
|
|
||||||
{
|
|
||||||
if (vertexColorsBuffer->Init(GPUBufferDescription::Vertex(sizeof(Color32), vertexColorsData.Count())))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
GPUDevice::Instance->GetMainContext()->UpdateBuffer(vertexColorsBuffer, vertexColorsData.Get(), size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SAFE_DELETE_GPU_RESOURCE(vertexColorsBuffer);
|
|
||||||
}
|
}
|
||||||
|
GPUDevice::Instance->GetMainContext()->UpdateBuffer(vertexColorsBuffer, vertexColorsData.Get(), size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SAFE_DELETE_GPU_RESOURCE(vertexColorsBuffer);
|
||||||
}
|
}
|
||||||
_vertexColorsDirty = false;
|
|
||||||
}
|
}
|
||||||
|
_vertexColorsDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
// Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving
|
// Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving
|
||||||
if (!Editor::IsPlayMode)
|
if (!Editor::IsPlayMode)
|
||||||
_drawState.PrevWorld = _world;
|
_drawState.PrevWorld = _world;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Mesh::DrawInfo draw;
|
Mesh::DrawInfo draw;
|
||||||
draw.Buffer = &Entries;
|
draw.Buffer = &Entries;
|
||||||
draw.World = &_world;
|
draw.World = &_world;
|
||||||
draw.DrawState = &_drawState;
|
draw.DrawState = &_drawState;
|
||||||
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
|
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
|
||||||
draw.LightmapUVs = &Lightmap.UVsArea;
|
draw.LightmapUVs = &Lightmap.UVsArea;
|
||||||
draw.Flags = _staticFlags;
|
draw.Flags = _staticFlags;
|
||||||
draw.DrawModes = drawModes;
|
draw.DrawModes = drawModes;
|
||||||
draw.Bounds = _sphere;
|
draw.Bounds = _sphere;
|
||||||
draw.PerInstanceRandom = GetPerInstanceRandom();
|
draw.PerInstanceRandom = GetPerInstanceRandom();
|
||||||
draw.LODBias = _lodBias;
|
draw.LODBias = _lodBias;
|
||||||
draw.ForcedLOD = _forcedLod;
|
draw.ForcedLOD = _forcedLod;
|
||||||
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
|
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
|
||||||
|
|
||||||
Model->Draw(renderContext, draw);
|
Model->Draw(renderContext, draw);
|
||||||
}
|
|
||||||
|
|
||||||
GEOMETRY_DRAW_STATE_EVENT_END(_drawState, _world);
|
GEOMETRY_DRAW_STATE_EVENT_END(_drawState, _world);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -314,9 +314,11 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
|
|||||||
_modelsBuffer->Clear();
|
_modelsBuffer->Clear();
|
||||||
_modelsTextures.Clear();
|
_modelsTextures.Clear();
|
||||||
}
|
}
|
||||||
int32 modelsBufferCount = 0;
|
|
||||||
|
|
||||||
// Draw all objects from all scenes into the cascade
|
// Draw all objects from all scenes into the cascade
|
||||||
|
_modelsBufferCount = 0;
|
||||||
|
_voxelSize = voxelSize;
|
||||||
|
_cascadeBounds = cascadeBounds;
|
||||||
for (auto* scene : renderContext.List->Scenes)
|
for (auto* scene : renderContext.List->Scenes)
|
||||||
{
|
{
|
||||||
// TODO: optimize for moving camera (copy sdf)
|
// TODO: optimize for moving camera (copy sdf)
|
||||||
@@ -326,90 +328,6 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
|
|||||||
if (viewMask & e.LayerMask && e.Bounds.Radius >= minObjectRadius && CollisionsHelper::BoxIntersectsSphere(cascadeBounds, e.Bounds))
|
if (viewMask & e.LayerMask && e.Bounds.Radius >= minObjectRadius && CollisionsHelper::BoxIntersectsSphere(cascadeBounds, e.Bounds))
|
||||||
{
|
{
|
||||||
e.Actor->Draw(renderContext);
|
e.Actor->Draw(renderContext);
|
||||||
|
|
||||||
// TODO: move to be properly implemented per object type
|
|
||||||
auto staticModel = ScriptingObject::Cast<StaticModel>(e.Actor);
|
|
||||||
if (staticModel && staticModel->Model && staticModel->Model->IsLoaded() && staticModel->Model->CanBeRendered() && staticModel->DrawModes & DrawPass::GlobalSDF)
|
|
||||||
{
|
|
||||||
// okay so firstly we need SDF for this model
|
|
||||||
// TODO: implement SDF generation on model import
|
|
||||||
if (!staticModel->Model->SDF.Texture)
|
|
||||||
staticModel->Model->GenerateSDF();
|
|
||||||
ModelBase::SDFData& sdf = staticModel->Model->SDF;
|
|
||||||
if (sdf.Texture)
|
|
||||||
{
|
|
||||||
// Setup object data
|
|
||||||
BoundingBox objectBounds = staticModel->GetBox();
|
|
||||||
BoundingBox objectBoundsCascade;
|
|
||||||
const float objectMargin = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN;
|
|
||||||
Vector3::Clamp(objectBounds.Minimum - objectMargin, cascadeBounds.Minimum, cascadeBounds.Maximum, objectBoundsCascade.Minimum);
|
|
||||||
Vector3::Subtract(objectBoundsCascade.Minimum, cascadeBounds.Minimum, objectBoundsCascade.Minimum);
|
|
||||||
Vector3::Clamp(objectBounds.Maximum + objectMargin, cascadeBounds.Minimum, cascadeBounds.Maximum, objectBoundsCascade.Maximum);
|
|
||||||
Vector3::Subtract(objectBoundsCascade.Maximum, cascadeBounds.Minimum, objectBoundsCascade.Maximum);
|
|
||||||
Int3 objectChunkMin(objectBoundsCascade.Minimum / chunkSize);
|
|
||||||
Int3 objectChunkMax(objectBoundsCascade.Maximum / chunkSize);
|
|
||||||
Matrix localToWorld, worldToLocal, volumeToWorld;
|
|
||||||
staticModel->GetWorld(&localToWorld);
|
|
||||||
Matrix::Invert(localToWorld, worldToLocal);
|
|
||||||
BoundingBox localVolumeBounds(sdf.LocalBoundsMin, sdf.LocalBoundsMax);
|
|
||||||
Vector3 volumeLocalBoundsExtent = localVolumeBounds.GetSize() * 0.5f;
|
|
||||||
Matrix worldToVolume = worldToLocal * Matrix::Translation(-(localVolumeBounds.Minimum + volumeLocalBoundsExtent));
|
|
||||||
Matrix::Invert(worldToVolume, volumeToWorld);
|
|
||||||
|
|
||||||
// Pick the SDF mip for the cascade
|
|
||||||
int32 mipLevelIndex = 1;
|
|
||||||
float worldUnitsPerVoxel = sdf.WorldUnitsPerVoxel * localToWorld.GetScaleVector().MaxValue() * 2;
|
|
||||||
while (voxelSize > worldUnitsPerVoxel && mipLevelIndex < sdf.Texture->MipLevels())
|
|
||||||
{
|
|
||||||
mipLevelIndex++;
|
|
||||||
worldUnitsPerVoxel *= 2.0f;
|
|
||||||
}
|
|
||||||
mipLevelIndex--;
|
|
||||||
|
|
||||||
// Volume -> Local -> UVW
|
|
||||||
Vector3 volumeToUVWMul = sdf.LocalToUVWMul;
|
|
||||||
Vector3 volumeToUVWAdd = sdf.LocalToUVWAdd + (localVolumeBounds.Minimum + volumeLocalBoundsExtent) * sdf.LocalToUVWMul;
|
|
||||||
|
|
||||||
// Add model data for the GPU buffer
|
|
||||||
int32 modelIndex = modelsBufferCount++;
|
|
||||||
ModelRasterizeData modelData;
|
|
||||||
Matrix::Transpose(worldToVolume, modelData.WorldToVolume);
|
|
||||||
Matrix::Transpose(volumeToWorld, modelData.VolumeToWorld);
|
|
||||||
modelData.VolumeLocalBoundsExtent = volumeLocalBoundsExtent;
|
|
||||||
modelData.VolumeToUVWMul = volumeToUVWMul;
|
|
||||||
modelData.VolumeToUVWAdd = volumeToUVWAdd;
|
|
||||||
modelData.MipOffset = (float)mipLevelIndex;
|
|
||||||
modelData.DecodeMul = 2.0f * sdf.MaxDistance;
|
|
||||||
modelData.DecodeAdd = -sdf.MaxDistance;
|
|
||||||
_modelsBuffer->Write(modelData);
|
|
||||||
_modelsTextures.Add(sdf.Texture->ViewVolume());
|
|
||||||
|
|
||||||
// Inject object into the intersecting cascade chunks
|
|
||||||
RasterizeChunkKey key;
|
|
||||||
for (key.Coord.Z = objectChunkMin.Z; key.Coord.Z <= objectChunkMax.Z; key.Coord.Z++)
|
|
||||||
{
|
|
||||||
for (key.Coord.Y = objectChunkMin.Y; key.Coord.Y <= objectChunkMax.Y; key.Coord.Y++)
|
|
||||||
{
|
|
||||||
for (key.Coord.X = objectChunkMin.X; key.Coord.X <= objectChunkMax.X; key.Coord.X++)
|
|
||||||
{
|
|
||||||
key.Layer = 0;
|
|
||||||
key.Hash = key.Coord.Z * (RasterizeChunkKeyHashResolution * RasterizeChunkKeyHashResolution) + key.Coord.Y * RasterizeChunkKeyHashResolution + key.Coord.X;
|
|
||||||
RasterizeChunk* chunk = &chunks[key];
|
|
||||||
|
|
||||||
// Move to the next layer if chunk has overflown
|
|
||||||
while (chunk->ModelsCount == GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT)
|
|
||||||
{
|
|
||||||
key.Layer++;
|
|
||||||
key.Hash += RasterizeChunkKeyHashResolution * RasterizeChunkKeyHashResolution * RasterizeChunkKeyHashResolution;
|
|
||||||
chunk = &chunks[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk->Models[chunk->ModelsCount++] = modelIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -594,3 +512,80 @@ void GlobalSignDistanceFieldPass::RenderDebug(RenderContext& renderContext, GPUC
|
|||||||
context->SetViewportAndScissors(outputSize.X, outputSize.Y);
|
context->SetViewportAndScissors(outputSize.X, outputSize.Y);
|
||||||
context->DrawFullscreenTriangle();
|
context->DrawFullscreenTriangle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GlobalSignDistanceFieldPass::RasterizeModelSDF(const ModelBase::SDFData& sdf, const Matrix& localToWorld, const BoundingBox& objectBounds)
|
||||||
|
{
|
||||||
|
if (!sdf.Texture)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Setup object data
|
||||||
|
BoundingBox objectBoundsCascade;
|
||||||
|
const float objectMargin = _voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN;
|
||||||
|
Vector3::Clamp(objectBounds.Minimum - objectMargin, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Minimum);
|
||||||
|
Vector3::Subtract(objectBoundsCascade.Minimum, _cascadeBounds.Minimum, objectBoundsCascade.Minimum);
|
||||||
|
Vector3::Clamp(objectBounds.Maximum + objectMargin, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Maximum);
|
||||||
|
Vector3::Subtract(objectBoundsCascade.Maximum, _cascadeBounds.Minimum, objectBoundsCascade.Maximum);
|
||||||
|
const float chunkSize = _voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
|
||||||
|
Int3 objectChunkMin(objectBoundsCascade.Minimum / chunkSize);
|
||||||
|
Int3 objectChunkMax(objectBoundsCascade.Maximum / chunkSize);
|
||||||
|
Matrix worldToLocal, volumeToWorld;
|
||||||
|
Matrix::Invert(localToWorld, worldToLocal);
|
||||||
|
BoundingBox localVolumeBounds(sdf.LocalBoundsMin, sdf.LocalBoundsMax);
|
||||||
|
Vector3 volumeLocalBoundsExtent = localVolumeBounds.GetSize() * 0.5f;
|
||||||
|
Matrix worldToVolume = worldToLocal * Matrix::Translation(-(localVolumeBounds.Minimum + volumeLocalBoundsExtent));
|
||||||
|
Matrix::Invert(worldToVolume, volumeToWorld);
|
||||||
|
|
||||||
|
// Pick the SDF mip for the cascade
|
||||||
|
int32 mipLevelIndex = 1;
|
||||||
|
float worldUnitsPerVoxel = sdf.WorldUnitsPerVoxel * localToWorld.GetScaleVector().MaxValue() * 2;
|
||||||
|
while (_voxelSize > worldUnitsPerVoxel && mipLevelIndex < sdf.Texture->MipLevels())
|
||||||
|
{
|
||||||
|
mipLevelIndex++;
|
||||||
|
worldUnitsPerVoxel *= 2.0f;
|
||||||
|
}
|
||||||
|
mipLevelIndex--;
|
||||||
|
|
||||||
|
// Volume -> Local -> UVW
|
||||||
|
Vector3 volumeToUVWMul = sdf.LocalToUVWMul;
|
||||||
|
Vector3 volumeToUVWAdd = sdf.LocalToUVWAdd + (localVolumeBounds.Minimum + volumeLocalBoundsExtent) * sdf.LocalToUVWMul;
|
||||||
|
|
||||||
|
// Add model data for the GPU buffer
|
||||||
|
int32 modelIndex = _modelsBufferCount++;
|
||||||
|
ModelRasterizeData modelData;
|
||||||
|
Matrix::Transpose(worldToVolume, modelData.WorldToVolume);
|
||||||
|
Matrix::Transpose(volumeToWorld, modelData.VolumeToWorld);
|
||||||
|
modelData.VolumeLocalBoundsExtent = volumeLocalBoundsExtent;
|
||||||
|
modelData.VolumeToUVWMul = volumeToUVWMul;
|
||||||
|
modelData.VolumeToUVWAdd = volumeToUVWAdd;
|
||||||
|
modelData.MipOffset = (float)mipLevelIndex;
|
||||||
|
modelData.DecodeMul = 2.0f * sdf.MaxDistance;
|
||||||
|
modelData.DecodeAdd = -sdf.MaxDistance;
|
||||||
|
_modelsBuffer->Write(modelData);
|
||||||
|
_modelsTextures.Add(sdf.Texture->ViewVolume());
|
||||||
|
|
||||||
|
// Inject object into the intersecting cascade chunks
|
||||||
|
RasterizeChunkKey key;
|
||||||
|
auto& chunks = ChunksCache;
|
||||||
|
for (key.Coord.Z = objectChunkMin.Z; key.Coord.Z <= objectChunkMax.Z; key.Coord.Z++)
|
||||||
|
{
|
||||||
|
for (key.Coord.Y = objectChunkMin.Y; key.Coord.Y <= objectChunkMax.Y; key.Coord.Y++)
|
||||||
|
{
|
||||||
|
for (key.Coord.X = objectChunkMin.X; key.Coord.X <= objectChunkMax.X; key.Coord.X++)
|
||||||
|
{
|
||||||
|
key.Layer = 0;
|
||||||
|
key.Hash = key.Coord.Z * (RasterizeChunkKeyHashResolution * RasterizeChunkKeyHashResolution) + key.Coord.Y * RasterizeChunkKeyHashResolution + key.Coord.X;
|
||||||
|
RasterizeChunk* chunk = &chunks[key];
|
||||||
|
|
||||||
|
// Move to the next layer if chunk has overflown
|
||||||
|
while (chunk->ModelsCount == GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT)
|
||||||
|
{
|
||||||
|
key.Layer++;
|
||||||
|
key.Hash += RasterizeChunkKeyHashResolution * RasterizeChunkKeyHashResolution * RasterizeChunkKeyHashResolution;
|
||||||
|
chunk = &chunks[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk->Models[chunk->ModelsCount++] = modelIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,8 +37,13 @@ private:
|
|||||||
GPUShaderProgramCS* _csGenerateMip1 = nullptr;
|
GPUShaderProgramCS* _csGenerateMip1 = nullptr;
|
||||||
GPUConstantBuffer* _cb0 = nullptr;
|
GPUConstantBuffer* _cb0 = nullptr;
|
||||||
GPUConstantBuffer* _cb1 = nullptr;
|
GPUConstantBuffer* _cb1 = nullptr;
|
||||||
|
|
||||||
|
// Rasterization cache
|
||||||
class DynamicStructuredBuffer* _modelsBuffer = nullptr;
|
class DynamicStructuredBuffer* _modelsBuffer = nullptr;
|
||||||
Array<GPUTextureView*> _modelsTextures;
|
Array<GPUTextureView*> _modelsTextures;
|
||||||
|
int32 _modelsBufferCount;
|
||||||
|
float _voxelSize;
|
||||||
|
BoundingBox _cascadeBounds;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -58,6 +63,8 @@ public:
|
|||||||
/// <param name="output">The output buffer.</param>
|
/// <param name="output">The output buffer.</param>
|
||||||
void RenderDebug(RenderContext& renderContext, GPUContext* context, GPUTexture* output);
|
void RenderDebug(RenderContext& renderContext, GPUContext* context, GPUTexture* output);
|
||||||
|
|
||||||
|
void RasterizeModelSDF(const ModelBase::SDFData& sdf, const Matrix& localToWorld, const BoundingBox& objectBounds);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if COMPILE_WITH_DEV_ENV
|
#if COMPILE_WITH_DEV_ENV
|
||||||
void OnShaderReloading(Asset* obj);
|
void OnShaderReloading(Asset* obj);
|
||||||
|
|||||||
Reference in New Issue
Block a user