From 3dff9196c210d2a591cae77370b437c616e63144 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 30 Sep 2021 23:48:48 +0200 Subject: [PATCH] Add LOD Preview debug view mode --- Content/Shaders/Editor/LODPreview.flax | 3 + Source/Editor/Viewport/EditorViewport.cs | 1 + Source/Engine/Graphics/Enums.h | 5 + Source/Engine/Renderer/Editor/LODPreview.cpp | 127 +++++++++++++++++++ Source/Engine/Renderer/Editor/LODPreview.h | 47 +++++++ Source/Engine/Renderer/GBufferPass.cpp | 83 ++++++------ Source/Engine/Renderer/GBufferPass.h | 5 +- Source/Shaders/Editor/LODPreview.shader | 89 +++++++++++++ 8 files changed, 318 insertions(+), 42 deletions(-) create mode 100644 Content/Shaders/Editor/LODPreview.flax create mode 100644 Source/Engine/Renderer/Editor/LODPreview.cpp create mode 100644 Source/Engine/Renderer/Editor/LODPreview.h create mode 100644 Source/Shaders/Editor/LODPreview.shader diff --git a/Content/Shaders/Editor/LODPreview.flax b/Content/Shaders/Editor/LODPreview.flax new file mode 100644 index 000000000..13f4d05e1 --- /dev/null +++ b/Content/Shaders/Editor/LODPreview.flax @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b320422e51839935dc81d80b83de54aa5200d27ba97a90f49f84e4c99fe8b97d +size 3404 diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index bae4d31a8..fa35eda94 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1397,6 +1397,7 @@ namespace FlaxEditor.Viewport new ViewModeOptions(ViewMode.LightmapUVsDensity, "Lightmap UVs Density"), new ViewModeOptions(ViewMode.VertexColors, "Vertex Colors"), new ViewModeOptions(ViewMode.PhysicsColliders, "Physics Colliders"), + new ViewModeOptions(ViewMode.LODPreview, "LOD Preview"), }; private void WidgetCamSpeedShowHide(Control cm) diff --git a/Source/Engine/Graphics/Enums.h b/Source/Engine/Graphics/Enums.h index af03678e9..6ab7abdf7 100644 --- a/Source/Engine/Graphics/Enums.h +++ b/Source/Engine/Graphics/Enums.h @@ -813,6 +813,11 @@ API_ENUM() enum class ViewMode /// Draw physics colliders debug view /// PhysicsColliders = 20, + + /// + /// Draw Level Of Detail number as colors to debug LOD switches. + /// + LODPreview = 21, }; /// diff --git a/Source/Engine/Renderer/Editor/LODPreview.cpp b/Source/Engine/Renderer/Editor/LODPreview.cpp new file mode 100644 index 000000000..cdbbc4135 --- /dev/null +++ b/Source/Engine/Renderer/Editor/LODPreview.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#if USE_EDITOR + +#include "LODPreview.h" +#include "Engine/Content/Content.h" +#include "Engine/Content/Assets/Model.h" +#include "Engine/Graphics/GPUDevice.h" +#include "Engine/Graphics/GPUPipelineState.h" +#include "Engine/Graphics/Shaders/GPUShader.h" +#include "Engine/Graphics/Shaders/GPUConstantBuffer.h" +#include "Engine/Graphics/RenderTask.h" +#include "Engine/Renderer/DrawCall.h" + +PACK_STRUCT(struct LODPreviewMaterialShaderData { + Matrix ViewProjectionMatrix; + Matrix WorldMatrix; + Color Color; + Vector3 WorldInvScale; + float LODDitherFactor; + }); + +LODPreviewMaterialShader::LODPreviewMaterialShader() +{ + _ps = GPUDevice::Instance->CreatePipelineState(); + _shader = Content::LoadAsyncInternal(TEXT("Shaders/Editor/LODPreview")); + if (!_shader) + return; +#if COMPILE_WITH_DEV_ENV + _shader.Get()->OnReloading.Bind(this); +#endif +} + +#if COMPILE_WITH_DEV_ENV + +void LODPreviewMaterialShader::OnShaderReloading(Asset* obj) +{ + _ps->ReleaseGPU(); +} + +#endif + +const MaterialInfo& LODPreviewMaterialShader::GetInfo() const +{ + return _info; +} + +bool LODPreviewMaterialShader::IsReady() const +{ + return _shader && _shader->IsLoaded(); +} + +DrawPass LODPreviewMaterialShader::GetDrawModes() const +{ + return DrawPass::GBuffer; +} + +void LODPreviewMaterialShader::Bind(BindParameters& params) +{ + auto context = params.GPUContext; + auto& drawCall = *params.FirstDrawCall; + auto shader = _shader->GetShader(); + auto cb = shader->GetCB(0); + if (!_ps->IsValid()) + { + auto psDesc = GPUPipelineState::Description::Default; + psDesc.VS = shader->GetVS("VS"); + psDesc.PS = shader->GetPS("PS"); + _ps->Init(psDesc); + } + + // Find the LOD that produced this draw call + int32 lodIndex = 0; + for (auto& e : Content::GetAssetsRaw()) + { + auto model = ScriptingObject::Cast(e.Value); + if (!model) + continue; + bool found = false; + for (const auto& lod : model->LODs) + { + for (const auto& mesh : lod.Meshes) + { + if (mesh.GetIndexBuffer() == drawCall.Geometry.IndexBuffer) + { + lodIndex = mesh.GetLODIndex(); + found = true; + break; + } + } + } + if (found) + break; + } + + // Bind + if (cb && cb->GetSize()) + { + ASSERT(cb->GetSize() == sizeof(LODPreviewMaterialShaderData)); + LODPreviewMaterialShaderData data; + Matrix::Transpose(params.RenderContext.View.Frustum.GetMatrix(), data.ViewProjectionMatrix); + Matrix::Transpose(drawCall.World, data.WorldMatrix); + const float scaleX = Vector3(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13).Length(); + const float scaleY = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23).Length(); + const float scaleZ = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33).Length(); + data.WorldInvScale = Vector3( + scaleX > 0.00001f ? 1.0f / scaleX : 0.0f, + scaleY > 0.00001f ? 1.0f / scaleY : 0.0f, + scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f); + const Color colors[MODEL_MAX_LODS] = { + Color::White, + Color::Red, + Color::Orange, + Color::Yellow, + Color::Green, + Color::Blue, + }; + ASSERT(lodIndex < MODEL_MAX_LODS); + data.Color = colors[lodIndex]; + data.LODDitherFactor = drawCall.Surface.LODDitherFactor; + context->UpdateCB(cb, &data); + context->BindCB(0, cb); + } + context->SetState(_ps); +} + +#endif diff --git a/Source/Engine/Renderer/Editor/LODPreview.h b/Source/Engine/Renderer/Editor/LODPreview.h new file mode 100644 index 000000000..c5ab05f1e --- /dev/null +++ b/Source/Engine/Renderer/Editor/LODPreview.h @@ -0,0 +1,47 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if USE_EDITOR + +#include "Engine/Content/AssetReference.h" +#include "Engine/Content/Assets/Shader.h" +#include "Engine/Content/Assets/Texture.h" +#include "Engine/Graphics/Materials/IMaterial.h" + +class GPUPipelineState; + +/// +/// Rendering Level Of Detail number as colors to debug LOD switches in editor. +/// +class LODPreviewMaterialShader : public IMaterial +{ +private: + + AssetReference _shader; + GPUPipelineState* _ps = nullptr; + MaterialInfo _info; + +public: + + LODPreviewMaterialShader(); + virtual ~LODPreviewMaterialShader() + { + } + +private: + +#if COMPILE_WITH_DEV_ENV + void OnShaderReloading(Asset* obj); +#endif + +public: + + // [IMaterial] + const MaterialInfo& GetInfo() const override; + bool IsReady() const override; + DrawPass GetDrawModes() const override; + void Bind(BindParameters& params) override; +}; + +#endif diff --git a/Source/Engine/Renderer/GBufferPass.cpp b/Source/Engine/Renderer/GBufferPass.cpp index 079ac9bbe..fdefb237f 100644 --- a/Source/Engine/Renderer/GBufferPass.cpp +++ b/Source/Engine/Renderer/GBufferPass.cpp @@ -2,8 +2,11 @@ #include "GBufferPass.h" #include "RenderList.h" +#if USE_EDITOR #include "Engine/Renderer/Editor/VertexColors.h" #include "Engine/Renderer/Editor/LightmapUVsDensity.h" +#include "Engine/Renderer/Editor/LODPreview.h" +#endif #include "Engine/Core/Collections/Sorting.h" #include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUContext.h" @@ -87,11 +90,37 @@ void GBufferPass::Dispose() _skyModel = nullptr; _boxModel = nullptr; #if USE_EDITOR - SAFE_DELETE(_lightmapUVsDensityMaterialShader); - SAFE_DELETE(_vertexColorsMaterialShader); + SAFE_DELETE(_lightmapUVsDensity); + SAFE_DELETE(_vertexColors); + SAFE_DELETE(_lodPreview); #endif } +#if USE_EDITOR + +void DebugOverrideDrawCallsMaterial(RenderContext& renderContext, IMaterial* material) +{ + if (material->IsReady()) + { + auto& drawCallsList = renderContext.List->DrawCallsLists[(int32)DrawCallsListType::GBuffer]; + for (int32 i : drawCallsList.Indices) + { + auto& drawCall = renderContext.List->DrawCalls[i]; + if (drawCall.Material->IsSurface()) + { + drawCall.Material = material; + } + } + IMaterial::InstancingHandler handler; + if (!material->CanUseInstancing(handler)) + { + drawCallsList.CanUseInstancing = false; + } + } +} + +#endif + void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer) { PROFILE_GPU_CPU("GBuffer"); @@ -133,47 +162,21 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer // Override draw calls material to use material debug shader if (renderContext.View.Mode == ViewMode::LightmapUVsDensity) { - if (!_lightmapUVsDensityMaterialShader) - _lightmapUVsDensityMaterialShader = New(); - if (_lightmapUVsDensityMaterialShader->IsReady()) - { - auto& drawCallsList = renderContext.List->DrawCallsLists[(int32)DrawCallsListType::GBuffer]; - for (int32 i : drawCallsList.Indices) - { - auto& drawCall = renderContext.List->DrawCalls[i]; - if (drawCall.Material->IsSurface()) - { - drawCall.Material = _lightmapUVsDensityMaterialShader; - } - } - IMaterial::InstancingHandler handler; - if (!_lightmapUVsDensityMaterialShader->CanUseInstancing(handler)) - { - drawCallsList.CanUseInstancing = false; - } - } + if (!_lightmapUVsDensity) + _lightmapUVsDensity = New(); + DebugOverrideDrawCallsMaterial(renderContext, _lightmapUVsDensity); } else if (renderContext.View.Mode == ViewMode::VertexColors) { - if (!_vertexColorsMaterialShader) - _vertexColorsMaterialShader = New(); - if (_vertexColorsMaterialShader->IsReady()) - { - auto& drawCallsList = renderContext.List->DrawCallsLists[(int32)DrawCallsListType::GBuffer]; - for (int32 i : drawCallsList.Indices) - { - auto& drawCall = renderContext.List->DrawCalls[i]; - if (drawCall.Material->IsSurface()) - { - drawCall.Material = _vertexColorsMaterialShader; - } - } - IMaterial::InstancingHandler handler; - if (!_vertexColorsMaterialShader->CanUseInstancing(handler)) - { - drawCallsList.CanUseInstancing = false; - } - } + if (!_vertexColors) + _vertexColors = New(); + DebugOverrideDrawCallsMaterial(renderContext, _vertexColors); + } + else if (renderContext.View.Mode == ViewMode::LODPreview) + { + if (!_lodPreview) + _lodPreview = New(); + DebugOverrideDrawCallsMaterial(renderContext, _lodPreview); } if (renderContext.View.Mode == ViewMode::PhysicsColliders) { diff --git a/Source/Engine/Renderer/GBufferPass.h b/Source/Engine/Renderer/GBufferPass.h index dce5d3ee7..7bb1c1eaf 100644 --- a/Source/Engine/Renderer/GBufferPass.h +++ b/Source/Engine/Renderer/GBufferPass.h @@ -16,8 +16,9 @@ private: AssetReference _skyModel; AssetReference _boxModel; #if USE_EDITOR - class LightmapUVsDensityMaterialShader* _lightmapUVsDensityMaterialShader = nullptr; - class VertexColorsMaterialShader* _vertexColorsMaterialShader = nullptr; + class LightmapUVsDensityMaterialShader* _lightmapUVsDensity = nullptr; + class VertexColorsMaterialShader* _vertexColors = nullptr; + class LODPreviewMaterialShader* _lodPreview = nullptr; #endif public: diff --git a/Source/Shaders/Editor/LODPreview.shader b/Source/Shaders/Editor/LODPreview.shader new file mode 100644 index 000000000..26b4114bd --- /dev/null +++ b/Source/Shaders/Editor/LODPreview.shader @@ -0,0 +1,89 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#define MATERIAL 1 + +#include "./Flax/Common.hlsl" +#include "./Flax/MaterialCommon.hlsl" + +META_CB_BEGIN(0, Data) +float4x4 ViewProjectionMatrix; +float4x4 WorldMatrix; +float4 Color; +float3 WorldInvScale; +float LODDitherFactor; +META_CB_END + +struct VertexOutput +{ + float4 Position : SV_Position; + float3 WorldNormal : TEXCOORD0; +}; + +struct PixelInput +{ + float4 Position : SV_Position; + float3 WorldNormal : TEXCOORD0; +}; + +float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld) +{ + localToWorld[0] *= WorldInvScale.x; + localToWorld[1] *= WorldInvScale.y; + localToWorld[2] *= WorldInvScale.z; + return localToWorld; +} + +float3x3 CalcTangentToWorld(float4x4 world, float3x3 tangentToLocal) +{ + float3x3 localToWorld = RemoveScaleFromLocalToWorld((float3x3)world); + return mul(tangentToLocal, localToWorld); +} + +float4 PerformFakeLighting(float3 n, float4 c) +{ + c.rgb *= saturate(abs(dot(n, float3(0, 1, 0))) + 0.5f); + return c; +} + +void ClipLODTransition(float4 svPosition, float ditherFactor) +{ + if (abs(ditherFactor) > 0.001) + { + float randGrid = cos(dot(floor(svPosition.xy), float2(347.83452793, 3343.28371863))); + float randGridFrac = frac(randGrid * 1000.0); + half mask = (ditherFactor < 0.0) ? (ditherFactor + 1.0 > randGridFrac) : (ditherFactor < randGridFrac); + clip(mask - 0.001); + } +} + +META_VS(true, FEATURE_LEVEL_ES2) +META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true) +META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 1, 0, PER_VERTEX, 0, true) +META_VS_IN_ELEMENT(NORMAL, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true) +META_VS_IN_ELEMENT(TANGENT, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true) +META_VS_IN_ELEMENT(TEXCOORD, 1, R16G16_FLOAT, 1, ALIGN, PER_VERTEX, 0, true) +VertexOutput VS(ModelInput input) +{ + float bitangentSign = input.Tangent.w ? -1.0f : +1.0f; + float3 normal = input.Normal.xyz * 2.0 - 1.0; + float3 tangent = input.Tangent.xyz * 2.0 - 1.0; + float3 bitangent = cross(normal, tangent) * bitangentSign; + float3x3 tangentToLocal = float3x3(tangent, bitangent, normal); + float3x3 tangentToWorld = CalcTangentToWorld(WorldMatrix, tangentToLocal); + float3 worldPosition = mul(float4(input.Position.xyz, 1), WorldMatrix).xyz; + + VertexOutput output; + output.Position = mul(float4(worldPosition, 1), ViewProjectionMatrix); + output.WorldNormal = tangentToWorld[2]; + return output; +} + +META_PS(true, FEATURE_LEVEL_ES2) +void PS(in PixelInput input, out float4 Light : SV_Target0, out float4 RT0 : SV_Target1, out float4 RT1 : SV_Target2, out float4 RT2 : SV_Target3) +{ + ClipLODTransition(input.Position, LODDitherFactor); + Light = PerformFakeLighting(input.WorldNormal, Color); + RT0 = float4(0, 0, 0, 0); + RT1 = float4(input.WorldNormal * 0.5 + 0.5, SHADING_MODEL_LIT * (1.0 / 3.0)); + RT2 = float4(0.4, 0, 0.5, 0); +}