UE5 如何在延时渲染在材质编辑器提前使用到多灯光的数据

1、首先我们要去了解custom hlsl的include file paths

  • Include File Paths 和 C/C++ 的 #include 指令功能相同,用于在编译当前材质的 HLSL 代码时,将指定的文件内容"粘贴"进来。

include file 的内容:

复制代码
#pragma once

#ifndef IS_BASE_PASS
	#define IS_BASE_PASS 0
#endif

#ifndef SHADING_PATH_DEFERRED
	#define SHADING_PATH_DEFERRED 0
#endif

#ifndef MATERIALBLENDING_ANY_TRANSLUCENT
	#define MATERIALBLENDING_ANY_TRANSLUCENT 0
#endif

#define GM_BASEPASS_FORWARD_LIGHT_ACCESS (IS_BASE_PASS && SHADING_PATH_DEFERRED)

#if GM_BASEPASS_FORWARD_LIGHT_ACCESS

	#ifndef ForwardLightStruct
		#define GM_DEFINED_FORWARD_LIGHT_STRUCT 1
		#if MATERIALBLENDING_ANY_TRANSLUCENT
			#define ForwardLightStruct TranslucentBasePass.Shared.Forward
		#else
			#define ForwardLightStruct OpaqueBasePass.Shared.Forward
		#endif
	#else
		#define GM_DEFINED_FORWARD_LIGHT_STRUCT 0
	#endif

	#include "/Engine/Private/LightGridCommon.ush"

#endif

uint GM_GetBasePassForwardEyeIndex(FMaterialPixelParameters Parameters)
{
#if MOBILE_MULTI_VIEW
	return GetViewId(Parameters);
#else
	// 当前材质参数里没有直接等价于 BasePassPixelShader.usf 中 EyeIndex 的通用字段。
	// 非 multiview / 非 instanced stereo 路径用 0。
	return 0;
#endif
}

uint GM_GetForwardLightGridIndex(FMaterialPixelParameters Parameters)
{
#if GM_BASEPASS_FORWARD_LIGHT_ACCESS && FEATURE_LEVEL >= FEATURE_LEVEL_SM5
	const uint EyeIndex = GM_GetBasePassForwardEyeIndex(Parameters);
	const uint2 PixelPos = (uint2)((Parameters.SvPosition.xy - ResolvedView.ViewRectMin.xy) * View.LightProbeSizeRatioAndInvSizeRatio.zw);
	return ComputeLightGridCellIndex(PixelPos, Parameters.SvPosition.w, EyeIndex);
#else
	return 0;
#endif
}

#if GM_BASEPASS_FORWARD_LIGHT_ACCESS && defined(GM_DEFINED_FORWARD_LIGHT_STRUCT) && GM_DEFINED_FORWARD_LIGHT_STRUCT
	#undef ForwardLightStruct
#endif

#ifdef GM_DEFINED_FORWARD_LIGHT_STRUCT
	#undef GM_DEFINED_FORWARD_LIGHT_STRUCT
#endif

写入gbuffer的流程就是BasePass

|------------------------------------|------------------------------------------------|
| IS_BASE_PASS | 当前是否在 BasePass 中渲染 |
| SHADING_PATH_DEFERRED | 当前是否使用延迟渲染路径 |
| MATERIALBLENDING_ANY_TRANSLUCENT | 当前材质是否是透明材质 |
| GM_BASEPASS_FORWARD_LIGHT_ACCESS | 最终开关:是否在延迟渲染的 BasePass 中允许访问 Forward+ 灯光网格 |

复制代码
#if GM_BASEPASS_FORWARD_LIGHT_ACCESS

    #ifndef ForwardLightStruct
        #define GM_DEFINED_FORWARD_LIGHT_STRUCT 1
        #if MATERIALBLENDING_ANY_TRANSLUCENT
            #define ForwardLightStruct TranslucentBasePass.Shared.Forward
        #else
            #define ForwardLightStruct OpaqueBasePass.Shared.Forward
        #endif
    #else
        #define GM_DEFINED_FORWARD_LIGHT_STRUCT 0
    #endif

    #include "/Engine/Private/LightGridCommon.ush"

#endif
  • 如果开关开启,且 ForwardLightStruct 没有被外部定义过,就定义它

  • 根据材质类型(透明 / 不透明),指向不同的 Uniform Buffer

  • Uniform Buffer 是 CPU 传递给 GPU 的数据块,这里包含灯光网格的索引、光源列表等

  • 然后包含 LightGridCommon.ush------这个文件里定义了 ComputeLightGridCellIndex、GetForwardLightingDataFromGrid 等核心函数


在材质编辑器的 Custom 节点中,HLSL 代码会被封装在一个函数里。这种"隔离"环境限制了你直接访问很多引擎内部功能,而这些功能通过 #include 相应的头文件就能解锁。

当你通过 Include File Paths 引入一个头文件时,就相当于把那个文件的全部内容"粘贴"到了 Custom 节点的代码中,从而获得了访问其中所有声明的能力。这正是你突破 Custom 节点限制的关键。例如,通过包含 /Engine/Private/LightGridCommon.ush,你可以:

  • 调用内部函数 :你可以使用 ComputeLightGridCellIndex() 来获取当前像素的灯光网格索引,或者使用 GetLocalLightDataFromGrid() 来获取光源数据。这些函数是引擎内部多光源算法的核心,通常无法直接调用。

  • 获取核心数据结构 :可以访问 ForwardLightStruct 这样的 Uniform Buffer。这个结构体里存储着所有经过剔除(Culling)的光源列表,是 Forward+ 渲染的核心数据。

  • 引用全局变量 :除了 View.XXX,还能访问 ResolvedView 等更丰富的渲染状态变量。


复制代码
float3 Sum = 0.0.xxx;

#if GM_BASEPASS_FORWARD_LIGHT_ACCESS && FEATURE_LEVEL >= FEATURE_LEVEL_SM5
	const uint GridIndex = GM_GetForwardLightGridIndex(Parameters);
	const FCulledLightsGridHeader Header = GetCulledLightsGridHeader(GridIndex);
	const uint NumLights = min(Header.NumLights, GetMaxLightsPerCell());
	const uint EyeIndex = GM_GetBasePassForwardEyeIndex(Parameters);

	LOOP
	for (uint LightIndex = 0; LightIndex < NumLights; ++LightIndex)
	{
		const FLocalLightData LocalLight = GetLocalLightDataFromGrid(Header.DataStartIndex + LightIndex, EyeIndex);
		float3 LightPos = UnpackLightTranslatedWorldPosition(LocalLight);
        float3 LightDir = normalize(LightPos - GetTranslatedWorldPosition(Parameters));
        Sum += saturate(dot(LightDir,WorldNormal));
	}
#endif

return Sum;
  1. 判断是否可用 :首先检查 GM_BASEPASS_FORWARD_LIGHT_ACCESS 和 FEATURE_LEVEL >= FEATURE_LEVEL_SM5,确保当前渲染环境支持访问 Forward+ 灯光网格数据。

  2. 获取当前像素的光源列表:

    • GridIndex = GM_GetForwardLightGridIndex(Parameters):根据像素位置计算它在 3D 灯光网格中的格子索引

    • Header = GetCulledLightsGridHeader(GridIndex):获取该格子包含的光源列表元数据(起始位置、数量等)

    • NumLights = min(Header.NumLights, GetMaxLightsPerCell()):获取实际需要处理的光源数量

  3. 遍历所有影响该像素的光源:

    • 对于每个光源,获取其完整属性数据(位置、颜色、半径等)

    • 将光源位置从编码格式解包到世界空间

    • 计算从像素指向光源的方向向量

    • 通过 saturate(dot(LightDir, WorldNormal)) 计算兰伯特漫反射强度(光线与法线的夹角余弦值,限制在 0-1 范围)

  4. 输出结果:将所有光源的漫反射强度累加后返回,这个值可以连接到材质的自发光或基础颜色等引脚。


以上是HLSL代码,其中GM_BASEPASS_FORWARD_LIGHT_ACCESS是include file path的文件里面定义的宏


FEATURE_LEVEL 是 UE5 着色器系统中的一个全局宏 。它并不是一个普通的变量,而是在着色器编译时,由引擎根据当前渲染平台的功能级别预定义并注入到代码中的。


GM_GetForwardLightGridIndex(Parameters);

是include file path的文件BasePassForwardLightGrid.ush声明的自定义函数


GetCulledLightsGridHeader

GetCulledLightsGridHeader和 FCulledLightsGridHeader 在BasePassForwardLightGrid.ush文件中的LightGridCommon.ush文件下:


GM_GetBasePassForwardEyeIndex(Parameters);

是include file path的文件BasePassForwardLightGrid.ush声明的自定义函数


FLocalLightData

是include file path的文件BasePassForwardLightGrid.ush里面包含了的头文件:

#include "/Engine/Private/LightGridCommon.ush"

头文件包含了LightData.ush

LightData.ush里面定义了FLocalLightData


0.0.xxx 是 HLSL 中的一种 swizzle(分量重组) 写法,意思是:将标量 0.0 当作一个只有 x 分量的向量,然后通过 .xxx 将其复制三份,构成一个 float3 向量,效果等同于 float3(0.0, 0.0, 0.0) 或 0.0.xxx。

在 HLSL 中,任何标量(如 0.0)都可以隐式地当作一个只有 .x 分量的向量,因此可以像向量一样使用 .x、.y、.z、.w 等 swizzle 操作。例如:

  • (0.0).xxx → float3(0,0,0)

  • (1.0).xxy → float3(1,1,0)

  • (2.5).x → 还是 2.5(标量)

所以 0.0.xxx 实际上等价于 (0.0).xxx。由于编译器允许省略括号,就写成了 0.0.xxx。


复制代码
struct FCulledLightsGridHeader
{
	uint NumLights;
	uint NumMegaLights;
	uint DataStartIndex;
	uint MegaLightsDataStartIndex;
	bool bHasRectLight;
	bool bHasTexturedLight;
};
  • bHasTexturedLight:"纹理光源"广义上指其光照属性由纹理决定的光源。在UE中,这通常指使用了光源函数(Light Function) 的光源。通过将材质应用到光源上,可以创造出类似水底焦散、云层阴影或霓虹灯图案等丰富的光照效果。此外,它也可以指使用IES描述文件的光源,以模拟真实灯光的分布曲线。
相关推荐
directx3d_beginner5 小时前
41,从UE5.1迁移到UE5.8排坑全记录
ue5
广东帝工14 小时前
智能防撞进入高增长周期:多技术融合推动桥梁防护从被动硬抗迈向主动预警时代
编辑器
HwJack201 天前
【共创稿事节】HarmonyOS 7材质与光照:真实感的营造与性能的平衡
pytorch·harmonyos·材质
NailiConveyor2 天前
输送机皮带材质选型指南:PVC、PU、橡胶、模块化塑料各适用什么场景?
大数据·运维·自动化·制造·业界资讯·材质
星辰徐哥3 天前
鸿蒙平台 KDevelop 集成开发环境适配实战:基于 Electron 壳方案的跨平台多语言代码编辑器开发
electron·编辑器·harmonyos
风华同学3 天前
vscode远程连接
ide·vscode·编辑器
Sunny_GMF4 天前
所见即所得编辑器原理实战:源码与渲染永不失真的三层一致性设计(Markdown/CodeMirror 装饰)
microsoft·编辑器·ai编程·harmonyos
落魄大学生之流水线上谋生计4 天前
BLACK TIDE · 黑潮
ue5
一路向北he4 天前
trae或者vscode定义了文件,但是文件不亮是灰的
ide·vscode·编辑器
bro_Java6664 天前
《栈与队列:数据结构的“双生花”》
java·数据结构·编辑器