Move Static Model rasterization into Global SDF code
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user