UE5.5正向渲染:如何扩展HLSL自定义节点实现方向光阴影采样
解决UE5.5材质自定义HLSL节点访问Uniform结构体的问题
以下是针对你的需求,一步步实现可被材质自定义HLSL节点访问的Uniform结构体的具体操作:
1. 用引擎宏正确定义并注册Uniform Buffer结构体
你之前仅在HLSL中定义结构体是不够的,必须在C++层面用UE的UBO宏声明并注册,才能让引擎生成正确的绑定代码。
比如在Engine/Source/Runtime/Engine/Public/MaterialShared.h(或自定义的头文件)中添加:
BEGIN_UNIFORM_BUFFER_STRUCT(FMaterialAccessibleShadowData, ENGINE_API) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_ARRAY(FMatrix, CascadeViewProjMatrices, [4]) // 按实际级联数调整 DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(FVector, LightWorldDirection) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(FTexture2DArray*, DirectionalShadowAtlas) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(FVector4, CascadeSplitDistances) END_UNIFORM_BUFFER_STRUCT(FMaterialAccessibleShadowData) // 在模块启动代码中注册UBO,比如Engine模块的StartupModule里 IMPLEMENT_UNIFORM_BUFFER_STRUCT(FMaterialAccessibleShadowData, TEXT("/Engine/Generated/UniformBuffers/MaterialAccessibleShadowData"))
这样引擎会自动生成对应的.ush文件,无需手动编写HLSL结构体。
2. 将UBO绑定到材质着色器上下文
需要修改正向渲染材质Shader的C++代码,把UBO绑定到着色器参数槽中:
在ForwardMaterialShader.h中添加UBO声明:
class FForwardMaterialShader : public FMaterialShader { // ... 原有代码 DECLARE_UNIFORM_BUFFER_BINDING(FMaterialAccessibleShadowData, MaterialAccessibleShadowData) };
然后在Shader的Setup方法中填充并绑定数据:
void FForwardMaterialShader::SetupUniformBuffers(FRHICommandList& RHICmdList, const FSceneView& View, const FMeshMaterialShaderElementData& MaterialShaderElementData) const { // ... 原有代码 FMaterialAccessibleShadowData ShadowData; FMemory::Memzero(&ShadowData, sizeof(ShadowData)); // 收集主方向光阴影数据 if (const FDirectionalLightSceneProxy* DirLightProxy = View.GetPrimaryDirectionalLight()) { ShadowData.LightWorldDirection = DirLightProxy->GetDirection(); const FCascadeShadowSettings& CascadeSettings = DirLightProxy->GetCascadeSettings(); // 填充级联矩阵 for (int32 CascadeIdx = 0; CascadeIdx < CascadeSettings.NumCascades; ++CascadeIdx) { ShadowData.CascadeViewProjMatrices[CascadeIdx] = CascadeSettings.Cascades[CascadeIdx].ViewProjectionMatrix; } // 填充级联分割距离 for (int32 i = 0; i < 4; ++i) { ShadowData.CascadeSplitDistances[i] = i < CascadeSettings.NumCascades ? CascadeSettings.CascadeSplitDistances[i] : 0.0f; } // 绑定阴影图集 ShadowData.DirectionalShadowAtlas = DirLightProxy->GetShadowAtlasTexture(); } // 将UBO绑定到着色器 SetUniformBufferParameter(RHICmdList, GetVertexShader(), GetPixelShader(), MaterialAccessibleShadowData, ShadowData); }
注意:移动端和PC的阴影实现有差异,可通过PLATFORM_MOBILE宏做分支处理,比如移动端可能不使用级联阴影,直接用单矩阵。
3. 在材质模板中正确引用UBO
在MaterialTemplate.ush中,除了include生成的ush文件,还要用CBUFFER_START/END包裹,确保HLSL能识别:
CBUFFER_START(MaterialAccessibleShadowData) #include "/Engine/Generated/UniformBuffers/MaterialAccessibleShadowData.ush" CBUFFER_END // 声明阴影采样器(按需调整过滤模式) SAMPLER(SamplerDirectionalShadow, FILTER_BILINEAR, ADDRESS_CLAMP, ADDRESS_CLAMP, ADDRESS_CLAMP);
4. 自定义HLSL节点中安全访问
在你的isInDirectionalLightShadow节点中,先做有效性判断再访问字段,避免崩溃:
bool isInDirectionalLightShadow(float3 WorldPosition, float3 WorldNormal) { // 先判断阴影数据是否有效 if (MaterialAccessibleShadowData.DirectionalShadowAtlas == nullptr) { return false; } // 找到当前位置所属的阴影级联 int32 CascadeIndex = 0; float ViewDepth = FSceneDepth(WorldPosition, View.WorldToView); for (int32 i = 1; i < 4; ++i) { if (ViewDepth < MaterialAccessibleShadowData.CascadeSplitDistances[i]) { break; } CascadeIndex = i; } // 转换到阴影裁剪空间 float4 ShadowClipPos = mul(float4(WorldPosition, 1.0), MaterialAccessibleShadowData.CascadeViewProjMatrices[CascadeIndex]); float2 ShadowUV = ShadowClipPos.xy / ShadowClipPos.w * 0.5 + 0.5; // 采样阴影图并做深度对比 float ShadowDepth = Texture2DArraySample(MaterialAccessibleShadowData.DirectionalShadowAtlas, SamplerDirectionalShadow, ShadowUV, CascadeIndex).r; float CurrentDepth = ShadowClipPos.z / ShadowClipPos.w; // 加入偏移避免阴影 acne return CurrentDepth > ShadowDepth + 0.001f; }
内容的提问来源于stack exchange,提问作者wild-ptr
相关产品推荐
相关产品推荐

