Add Volumetric Fog support for particles to modify local fog
This commit is contained in:
@@ -44,6 +44,11 @@ API_ENUM() enum class MaterialDomain : byte
|
||||
/// The deformable shader. Can be used only with objects that can be deformed (spline models).
|
||||
/// </summary>
|
||||
Deformable = 6,
|
||||
|
||||
/// <summary>
|
||||
/// The particle shader used for volumetric effects rendering such as Volumetric Fog.
|
||||
/// </summary>
|
||||
VolumeParticle = 7,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "TerrainMaterialShader.h"
|
||||
#include "ParticleMaterialShader.h"
|
||||
#include "DeformableMaterialShader.h"
|
||||
#include "VolumeParticleMaterialShader.h"
|
||||
|
||||
GPUPipelineState* MaterialShader::PipelineStateCache::InitPS(CullMode mode, bool wireframe)
|
||||
{
|
||||
@@ -65,8 +66,11 @@ MaterialShader* MaterialShader::Create(const String& name, MemoryReadStream& sha
|
||||
case MaterialDomain::Deformable:
|
||||
material = New<DeformableMaterialShader>(name);
|
||||
break;
|
||||
case MaterialDomain::VolumeParticle:
|
||||
material = New<VolumeParticleMaterialShader>(name);
|
||||
break;
|
||||
default:
|
||||
LOG(Fatal, "Unknown material type.");
|
||||
LOG(Error, "Unknown material type.");
|
||||
return nullptr;
|
||||
}
|
||||
if (material->Load(shaderCacheStream, info))
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
|
||||
#include "Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h"
|
||||
|
||||
#define MAX_LOCAL_LIGHTS 4
|
||||
|
||||
PACK_STRUCT(struct ParticleMaterialShaderData {
|
||||
Matrix ViewProjectionMatrix;
|
||||
Matrix WorldMatrix;
|
||||
@@ -98,53 +96,6 @@ void ParticleMaterialShader::Bind(BindParameters& params)
|
||||
}
|
||||
}
|
||||
|
||||
// Select pipeline state based on current pass and render mode
|
||||
const bool wireframe = (_info.FeaturesFlags & MaterialFeaturesFlags::Wireframe) != 0 || view.Mode == ViewMode::Wireframe;
|
||||
CullMode cullMode = view.Pass == DrawPass::Depth ? CullMode::TwoSided : _info.CullMode;
|
||||
PipelineStateCache* psCache = nullptr;
|
||||
switch (drawCall.Particle.Module->TypeID)
|
||||
{
|
||||
// Sprite Rendering
|
||||
case 400:
|
||||
{
|
||||
psCache = (PipelineStateCache*)_cacheSprite.GetPS(view.Pass);
|
||||
break;
|
||||
}
|
||||
// Model Rendering
|
||||
case 403:
|
||||
{
|
||||
psCache = (PipelineStateCache*)_cacheModel.GetPS(view.Pass);
|
||||
break;
|
||||
}
|
||||
// Ribbon Rendering
|
||||
case 404:
|
||||
{
|
||||
psCache = (PipelineStateCache*)_cacheRibbon.GetPS(view.Pass);
|
||||
|
||||
static StringView ParticleRibbonWidth(TEXT("RibbonWidth"));
|
||||
static StringView ParticleRibbonTwist(TEXT("RibbonTwist"));
|
||||
static StringView ParticleRibbonFacingVector(TEXT("RibbonFacingVector"));
|
||||
|
||||
materialData->RibbonWidthOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonWidth, ParticleAttribute::ValueTypes::Float, -1);
|
||||
materialData->RibbonTwistOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonTwist, ParticleAttribute::ValueTypes::Float, -1);
|
||||
materialData->RibbonFacingVectorOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonFacingVector, ParticleAttribute::ValueTypes::Vector3, -1);
|
||||
|
||||
materialData->RibbonUVTilingDistance = drawCall.Particle.Ribbon.UVTilingDistance;
|
||||
materialData->RibbonUVScale.X = drawCall.Particle.Ribbon.UVScaleX;
|
||||
materialData->RibbonUVScale.Y = drawCall.Particle.Ribbon.UVScaleY;
|
||||
materialData->RibbonUVOffset.X = drawCall.Particle.Ribbon.UVOffsetX;
|
||||
materialData->RibbonUVOffset.Y = drawCall.Particle.Ribbon.UVOffsetY;
|
||||
materialData->RibbonSegmentCount = drawCall.Particle.Ribbon.SegmentCount;
|
||||
|
||||
if (drawCall.Particle.Ribbon.SegmentDistances)
|
||||
context->BindSR(1, drawCall.Particle.Ribbon.SegmentDistances->View());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT(psCache);
|
||||
GPUPipelineState* state = psCache->GetPS(cullMode, wireframe);
|
||||
|
||||
// Setup material constants
|
||||
{
|
||||
static StringView ParticlePosition(TEXT("Position"));
|
||||
@@ -179,6 +130,53 @@ void ParticleMaterialShader::Bind(BindParameters& params)
|
||||
Matrix::Invert(drawCall.World, materialData->WorldMatrixInverseTransposed);
|
||||
}
|
||||
|
||||
// Select pipeline state based on current pass and render mode
|
||||
bool wireframe = (_info.FeaturesFlags & MaterialFeaturesFlags::Wireframe) != 0 || view.Mode == ViewMode::Wireframe;
|
||||
CullMode cullMode = view.Pass == DrawPass::Depth ? CullMode::TwoSided : _info.CullMode;
|
||||
PipelineStateCache* psCache = nullptr;
|
||||
switch (drawCall.Particle.Module->TypeID)
|
||||
{
|
||||
// Sprite Rendering
|
||||
case 400:
|
||||
{
|
||||
psCache = _cacheSprite.GetPS(view.Pass);
|
||||
break;
|
||||
}
|
||||
// Model Rendering
|
||||
case 403:
|
||||
{
|
||||
psCache = _cacheModel.GetPS(view.Pass);
|
||||
break;
|
||||
}
|
||||
// Ribbon Rendering
|
||||
case 404:
|
||||
{
|
||||
psCache = _cacheRibbon.GetPS(view.Pass);
|
||||
|
||||
static StringView ParticleRibbonWidth(TEXT("RibbonWidth"));
|
||||
static StringView ParticleRibbonTwist(TEXT("RibbonTwist"));
|
||||
static StringView ParticleRibbonFacingVector(TEXT("RibbonFacingVector"));
|
||||
|
||||
materialData->RibbonWidthOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonWidth, ParticleAttribute::ValueTypes::Float, -1);
|
||||
materialData->RibbonTwistOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonTwist, ParticleAttribute::ValueTypes::Float, -1);
|
||||
materialData->RibbonFacingVectorOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonFacingVector, ParticleAttribute::ValueTypes::Vector3, -1);
|
||||
|
||||
materialData->RibbonUVTilingDistance = drawCall.Particle.Ribbon.UVTilingDistance;
|
||||
materialData->RibbonUVScale.X = drawCall.Particle.Ribbon.UVScaleX;
|
||||
materialData->RibbonUVScale.Y = drawCall.Particle.Ribbon.UVScaleY;
|
||||
materialData->RibbonUVOffset.X = drawCall.Particle.Ribbon.UVOffsetX;
|
||||
materialData->RibbonUVOffset.Y = drawCall.Particle.Ribbon.UVOffsetY;
|
||||
materialData->RibbonSegmentCount = drawCall.Particle.Ribbon.SegmentCount;
|
||||
|
||||
if (drawCall.Particle.Ribbon.SegmentDistances)
|
||||
context->BindSR(1, drawCall.Particle.Ribbon.SegmentDistances->View());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT(psCache);
|
||||
GPUPipelineState* state = psCache->GetPS(cullMode, wireframe);
|
||||
|
||||
// Bind constants
|
||||
if (_cb)
|
||||
{
|
||||
@@ -198,6 +196,7 @@ void ParticleMaterialShader::Unload()
|
||||
_cacheSprite.Release();
|
||||
_cacheModel.Release();
|
||||
_cacheRibbon.Release();
|
||||
_cacheVolumetricFog.Release();
|
||||
}
|
||||
|
||||
bool ParticleMaterialShader::Load()
|
||||
@@ -263,5 +262,8 @@ bool ParticleMaterialShader::Load()
|
||||
psDesc.VS = vsRibbon;
|
||||
_cacheRibbon.Depth.Init(psDesc);
|
||||
|
||||
// Lazy initialization
|
||||
_cacheVolumetricFog.Desc.PS = nullptr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ private:
|
||||
Cache _cacheSprite;
|
||||
Cache _cacheModel;
|
||||
Cache _cacheRibbon;
|
||||
PipelineStateCache _cacheVolumetricFog;
|
||||
DrawPass _drawModes = DrawPass::None;
|
||||
|
||||
public:
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "VolumeParticleMaterialShader.h"
|
||||
#include "MaterialShaderFeatures.h"
|
||||
#include "MaterialParams.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Renderer/DrawCall.h"
|
||||
#include "Engine/Renderer/VolumetricFogPass.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Graphics/RenderView.h"
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/Shaders/GPUShader.h"
|
||||
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
|
||||
#include "Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h"
|
||||
|
||||
PACK_STRUCT(struct VolumeParticleMaterialShaderData {
|
||||
Matrix ViewProjectionMatrix;
|
||||
Matrix InverseViewProjectionMatrix;
|
||||
Matrix ViewMatrix;
|
||||
Matrix WorldMatrix;
|
||||
Matrix WorldMatrixInverseTransposed;
|
||||
Vector3 ViewPos;
|
||||
float ViewFar;
|
||||
Vector3 ViewDir;
|
||||
float TimeParam;
|
||||
Vector4 ViewInfo;
|
||||
Vector4 ScreenSize;
|
||||
Vector3 GridSize;
|
||||
float PerInstanceRandom;
|
||||
float Dummy0;
|
||||
float VolumetricFogMaxDistance;
|
||||
int ParticleStride;
|
||||
int ParticleIndex;
|
||||
});
|
||||
|
||||
DrawPass VolumeParticleMaterialShader::GetDrawModes() const
|
||||
{
|
||||
return DrawPass::None;
|
||||
}
|
||||
|
||||
void VolumeParticleMaterialShader::Bind(BindParameters& params)
|
||||
{
|
||||
// Prepare
|
||||
auto context = params.GPUContext;
|
||||
auto& view = params.RenderContext.View;
|
||||
auto& drawCall = *params.FirstDrawCall;
|
||||
byte* cb = _cbData.Get();
|
||||
auto materialData = reinterpret_cast<VolumeParticleMaterialShaderData*>(cb);
|
||||
cb += sizeof(VolumeParticleMaterialShaderData);
|
||||
int32 srv = 1;
|
||||
auto customData = (VolumetricFogPass::CustomData*)params.CustomData;
|
||||
|
||||
// Setup parameters
|
||||
MaterialParameter::BindMeta bindMeta;
|
||||
bindMeta.Context = context;
|
||||
bindMeta.Constants = cb;
|
||||
bindMeta.Input = nullptr;
|
||||
bindMeta.Buffers = params.RenderContext.Buffers;
|
||||
bindMeta.CanSampleDepth = true;
|
||||
bindMeta.CanSampleGBuffer = true;
|
||||
MaterialParams::Bind(params.ParamsLink, bindMeta);
|
||||
|
||||
// Setup particles data
|
||||
context->BindSR(0, drawCall.Particle.Particles->GPU.Buffer->View());
|
||||
|
||||
// Setup particles attributes binding info
|
||||
{
|
||||
const auto& p = *params.ParamsLink->This;
|
||||
for (int32 i = 0; i < p.Count(); i++)
|
||||
{
|
||||
const auto& param = p.At(i);
|
||||
if (param.GetParameterType() == MaterialParameterType::Integer && param.GetName().StartsWith(TEXT("Particle.")))
|
||||
{
|
||||
const StringView name(param.GetName().Get() + 9, param.GetName().Length() - 9);
|
||||
const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name);
|
||||
*((int32*)(bindMeta.Constants + param.GetBindOffset())) = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup material constants
|
||||
{
|
||||
Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix);
|
||||
Matrix::Transpose(view.IVP, materialData->InverseViewProjectionMatrix);
|
||||
Matrix::Transpose(view.View, materialData->ViewMatrix);
|
||||
Matrix::Transpose(drawCall.World, materialData->WorldMatrix);
|
||||
Matrix::Invert(drawCall.World, materialData->WorldMatrixInverseTransposed);
|
||||
materialData->ViewPos = view.Position;
|
||||
materialData->ViewFar = view.Far;
|
||||
materialData->ViewDir = view.Direction;
|
||||
materialData->TimeParam = Time::Draw.UnscaledTime.GetTotalSeconds();
|
||||
materialData->ViewInfo = view.ViewInfo;
|
||||
materialData->ScreenSize = view.ScreenSize;
|
||||
materialData->GridSize = customData->GridSize;
|
||||
materialData->PerInstanceRandom = drawCall.PerInstanceRandom;
|
||||
materialData->VolumetricFogMaxDistance = customData->VolumetricFogMaxDistance;
|
||||
materialData->ParticleStride = drawCall.Particle.Particles->Stride;
|
||||
materialData->ParticleIndex = customData->ParticleIndex;
|
||||
}
|
||||
|
||||
// Bind constants
|
||||
if (_cb)
|
||||
{
|
||||
context->UpdateCB(_cb, _cbData.Get());
|
||||
context->BindCB(0, _cb);
|
||||
}
|
||||
|
||||
// Bind pipeline
|
||||
if (_psVolumetricFog == nullptr)
|
||||
{
|
||||
auto psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
|
||||
psDesc.BlendMode = BlendingMode::Add;
|
||||
psDesc.VS = customData->Shader->GetVS("VS_WriteToSlice");
|
||||
psDesc.GS = customData->Shader->GetGS("GS_WriteToSlice");
|
||||
psDesc.PS = _shader->GetPS("PS_VolumetricFog");
|
||||
_psVolumetricFog = GPUDevice::Instance->CreatePipelineState();
|
||||
_psVolumetricFog->Init(psDesc);
|
||||
}
|
||||
context->SetState(_psVolumetricFog);
|
||||
}
|
||||
|
||||
void VolumeParticleMaterialShader::Unload()
|
||||
{
|
||||
// Base
|
||||
MaterialShader::Unload();
|
||||
|
||||
SAFE_DELETE_GPU_RESOURCE(_psVolumetricFog);
|
||||
}
|
||||
|
||||
bool VolumeParticleMaterialShader::Load()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MaterialShader.h"
|
||||
|
||||
/// <summary>
|
||||
/// Represents material that can be used to render particles.
|
||||
/// </summary>
|
||||
class VolumeParticleMaterialShader : public MaterialShader
|
||||
{
|
||||
private:
|
||||
|
||||
GPUPipelineState* _psVolumetricFog = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="name">Material resource name</param>
|
||||
VolumeParticleMaterialShader(const String& name)
|
||||
: MaterialShader(name)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [MaterialShader]
|
||||
DrawPass GetDrawModes() const override;
|
||||
void Bind(BindParameters& params) override;
|
||||
void Unload() override;
|
||||
|
||||
protected:
|
||||
|
||||
// [MaterialShader]
|
||||
bool Load() override;
|
||||
};
|
||||
Reference in New Issue
Block a user