// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #include "DeformableMaterialShader.h" #include "MaterialShaderFeatures.h" #include "MaterialParams.h" #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/RenderView.h" #include "Engine/Renderer/DrawCall.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/Shaders/GPUConstantBuffer.h" #include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Graphics/GPULimits.h" #include "Engine/Graphics/RenderTask.h" PACK_STRUCT(struct DeformableMaterialShaderData { Matrix WorldMatrix; Matrix LocalMatrix; Float3 Dummy0; float WorldDeterminantSign; float MeshMinZ; float Segment; float ChunksPerSegment; float PerInstanceRandom; Float3 GeometrySize; float MeshMaxZ; }); DrawPass DeformableMaterialShader::GetDrawModes() const { return _drawModes; } void DeformableMaterialShader::Bind(BindParameters& params) { // Prepare auto context = params.GPUContext; auto& view = params.RenderContext.View; auto& drawCall = *params.FirstDrawCall; Span cb(_cbData.Get(), _cbData.Count()); ASSERT_LOW_LAYER(cb.Length() >= sizeof(DeformableMaterialShaderData)); auto materialData = reinterpret_cast(cb.Get()); cb = Span(cb.Get() + sizeof(DeformableMaterialShaderData), cb.Length() - sizeof(DeformableMaterialShaderData)); int32 srv = 1; // Setup features if (_info.BlendMode != MaterialBlendMode::Opaque) ForwardShadingFeature::Bind(params, cb, srv); // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; bindMeta.Constants = cb; bindMeta.Input = nullptr; bindMeta.Buffers = params.RenderContext.Buffers; bindMeta.CanSampleDepth = false; bindMeta.CanSampleGBuffer = false; MaterialParams::Bind(params.ParamsLink, bindMeta); // Setup material constants { Matrix::Transpose(drawCall.World, materialData->WorldMatrix); Matrix::Transpose(drawCall.Deformable.LocalMatrix, materialData->LocalMatrix); materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign; materialData->Segment = drawCall.Deformable.Segment; materialData->ChunksPerSegment = drawCall.Deformable.ChunksPerSegment; materialData->MeshMinZ = drawCall.Deformable.MeshMinZ; materialData->MeshMaxZ = drawCall.Deformable.MeshMaxZ; materialData->PerInstanceRandom = drawCall.PerInstanceRandom; materialData->GeometrySize = drawCall.Deformable.GeometrySize; } // Bind spline deformation buffer context->BindSR(0, drawCall.Deformable.SplineDeformation->View()); // Bind constants if (_cb) { context->UpdateCB(_cb, _cbData.Get()); context->BindCB(0, _cb); } // Select pipeline state based on current pass and render mode const bool wireframe = (_info.FeaturesFlags & MaterialFeaturesFlags::Wireframe) != MaterialFeaturesFlags::None || view.Mode == ViewMode::Wireframe; CullMode cullMode = view.Pass == DrawPass::Depth ? CullMode::TwoSided : _info.CullMode; if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0) { // Invert culling when scale is negative if (cullMode == CullMode::Normal) cullMode = CullMode::Inverted; else cullMode = CullMode::Normal; } PipelineStateCache* psCache = _cache.GetPS(view.Pass); ASSERT(psCache); GPUPipelineState* state = psCache->GetPS(cullMode, wireframe); // Bind pipeline context->SetState(state); } void DeformableMaterialShader::Unload() { // Base MaterialShader::Unload(); _cache.Release(); } bool DeformableMaterialShader::Load() { _drawModes = DrawPass::Depth | DrawPass::QuadOverdraw; auto psDesc = GPUPipelineState::Description::Default; psDesc.DepthEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthTest) == MaterialFeaturesFlags::None; psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == MaterialFeaturesFlags::None; #if GPU_ALLOW_TESSELLATION_SHADERS // Check if use tessellation (both material and runtime supports it) const bool useTess = _info.TessellationMode != TessellationMethod::None && GPUDevice::Instance->Limits.HasTessellation; if (useTess) { psDesc.HS = _shader->GetHS("HS"); psDesc.DS = _shader->GetDS("DS"); } #endif #if USE_EDITOR if (_shader->HasShader("PS_QuadOverdraw")) { // Quad Overdraw psDesc.VS = _shader->GetVS("VS_SplineModel"); psDesc.PS = _shader->GetPS("PS_QuadOverdraw"); _cache.QuadOverdraw.Init(psDesc); } #endif if (_info.BlendMode == MaterialBlendMode::Opaque) { _drawModes |= DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas; // GBuffer Pass psDesc.VS = _shader->GetVS("VS_SplineModel"); psDesc.PS = _shader->GetPS("PS_GBuffer"); _cache.Default.Init(psDesc); } else { _drawModes |= DrawPass::Forward; // Forward Pass psDesc.VS = _shader->GetVS("VS_SplineModel"); psDesc.PS = _shader->GetPS("PS_Forward"); psDesc.DepthWriteEnable = false; psDesc.BlendMode = BlendingMode::AlphaBlend; switch (_info.BlendMode) { case MaterialBlendMode::Transparent: psDesc.BlendMode = BlendingMode::AlphaBlend; break; case MaterialBlendMode::Additive: psDesc.BlendMode = BlendingMode::Additive; break; case MaterialBlendMode::Multiply: psDesc.BlendMode = BlendingMode::Multiply; break; } _cache.Default.Init(psDesc); } // Depth Pass psDesc.CullMode = CullMode::TwoSided; psDesc.DepthClipEnable = false; psDesc.DepthWriteEnable = true; psDesc.DepthEnable = true; psDesc.DepthFunc = ComparisonFunc::Less; psDesc.HS = nullptr; psDesc.DS = nullptr; _cache.Depth.Init(psDesc); return false; }