Move Static Model rasterization into Global SDF code

This commit is contained in:
Wojciech Figat
2022-03-22 17:15:21 +01:00
parent 18321937e4
commit 885d2f0771
3 changed files with 133 additions and 126 deletions

View File

@@ -9,6 +9,7 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Level/Prefabs/PrefabManager.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
#include "Engine/Utilities/Encryption.h"
#if USE_EDITOR
#include "Editor/Editor.h"
@@ -209,62 +210,66 @@ bool StaticModel::HasContentLoaded() const
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)
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);
const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass);
if (Model && Model->IsLoaded() && drawModes != DrawPass::None)
// Flush vertex colors if need to
if (_vertexColorsDirty)
{
// Flush vertex colors if need to
if (_vertexColorsDirty)
for (int32 lodIndex = 0; lodIndex < _vertexColorsCount; lodIndex++)
{
for (int32 lodIndex = 0; lodIndex < _vertexColorsCount; lodIndex++)
auto& vertexColorsData = _vertexColorsData[lodIndex];
auto& vertexColorsBuffer = _vertexColorsBuffer[lodIndex];
if (vertexColorsData.HasItems())
{
auto& vertexColorsData = _vertexColorsData[lodIndex];
auto& vertexColorsBuffer = _vertexColorsBuffer[lodIndex];
if (vertexColorsData.HasItems())
const uint32 size = vertexColorsData.Count() * sizeof(Color32);
if (!vertexColorsBuffer)
vertexColorsBuffer = GPUDevice::Instance->CreateBuffer(TEXT("VertexColors"));
if (vertexColorsBuffer->GetSize() != size)
{
const uint32 size = vertexColorsData.Count() * sizeof(Color32);
if (!vertexColorsBuffer)
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);
if (vertexColorsBuffer->Init(GPUBufferDescription::Vertex(sizeof(Color32), vertexColorsData.Count())))
return;
}
GPUDevice::Instance->GetMainContext()->UpdateBuffer(vertexColorsBuffer, vertexColorsData.Get(), size);
}
else
{
SAFE_DELETE_GPU_RESOURCE(vertexColorsBuffer);
}
_vertexColorsDirty = false;
}
_vertexColorsDirty = false;
}
#if USE_EDITOR
// Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving
if (!Editor::IsPlayMode)
_drawState.PrevWorld = _world;
// Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving
if (!Editor::IsPlayMode)
_drawState.PrevWorld = _world;
#endif
Mesh::DrawInfo draw;
draw.Buffer = &Entries;
draw.World = &_world;
draw.DrawState = &_drawState;
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
draw.LightmapUVs = &Lightmap.UVsArea;
draw.Flags = _staticFlags;
draw.DrawModes = drawModes;
draw.Bounds = _sphere;
draw.PerInstanceRandom = GetPerInstanceRandom();
draw.LODBias = _lodBias;
draw.ForcedLOD = _forcedLod;
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
Mesh::DrawInfo draw;
draw.Buffer = &Entries;
draw.World = &_world;
draw.DrawState = &_drawState;
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
draw.LightmapUVs = &Lightmap.UVsArea;
draw.Flags = _staticFlags;
draw.DrawModes = drawModes;
draw.Bounds = _sphere;
draw.PerInstanceRandom = GetPerInstanceRandom();
draw.LODBias = _lodBias;
draw.ForcedLOD = _forcedLod;
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
Model->Draw(renderContext, draw);
}
Model->Draw(renderContext, draw);
GEOMETRY_DRAW_STATE_EVENT_END(_drawState, _world);
}

View File

@@ -314,9 +314,11 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
_modelsBuffer->Clear();
_modelsTextures.Clear();
}
int32 modelsBufferCount = 0;
// Draw all objects from all scenes into the cascade
_modelsBufferCount = 0;
_voxelSize = voxelSize;
_cascadeBounds = cascadeBounds;
for (auto* scene : renderContext.List->Scenes)
{
// 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))
{
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->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;
}
}
}
}

View File

@@ -37,8 +37,13 @@ private:
GPUShaderProgramCS* _csGenerateMip1 = nullptr;
GPUConstantBuffer* _cb0 = nullptr;
GPUConstantBuffer* _cb1 = nullptr;
// Rasterization cache
class DynamicStructuredBuffer* _modelsBuffer = nullptr;
Array<GPUTextureView*> _modelsTextures;
int32 _modelsBufferCount;
float _voxelSize;
BoundingBox _cascadeBounds;
public:
/// <summary>
@@ -58,6 +63,8 @@ public:
/// <param name="output">The output buffer.</param>
void RenderDebug(RenderContext& renderContext, GPUContext* context, GPUTexture* output);
void RasterizeModelSDF(const ModelBase::SDFData& sdf, const Matrix& localToWorld, const BoundingBox& objectBounds);
private:
#if COMPILE_WITH_DEV_ENV
void OnShaderReloading(Asset* obj);