0. 画多个RT

根据AI之前给到的路线,下面是要让PS输出到多个RT
在这里插入图片描述
AI也很快实现了代码。然后在RenderDoc中截帧,可以看到结果输出到了多个RT中:

在这里插入图片描述
相比之前几篇,本篇涉及的新内容很少,下面快速过下代码修改:

1. 着色器修改

着色器代码修改如下:
在这里插入图片描述
对于顶点着色器:

  • 多了一个输入 in float3 InNormal : ATTRIBUTE2,,可以留意之后传入的顶点数据也会多一个Normal。
  • 多了一个输出 out float3 OutNormal : TEXCOORD1,,可以留意之后传入像素着色器中的数据多了一个Normal

对于像素着色器:

  • 多了一个输入 in float3 InNormal : TEXCOORD1,,这对应于顶点着色器。
  • 多了一个输出 out float4 OutNormalEncoded : SV_Target1,可以留意在C++代码中像素着色器的绑定。

这里语法上我有些不确定的,所以问了下AI:

对于像素着色器的语法我有些想问的。
我留意到改之前像素着色器返回值是float4,然后函数声明的末尾是: SV_Target0。而现在像素着色器是void类型,SV_Target0和SV_Target1都变成了out 参数。所以返回值和out参数是等效的吗?你也可以再补充一些关于像素着色器与RT对应关系的一些知识。

AI回答:(仅供参考)

可以这么理解:在只写一路颜色时,return float4(...) : SV_Target0void + out float4 X : SV_Target0 在语义上等价——都是给 RT 槽位 0 提供最终颜色。


单路输出:return 与 out

写法 含义
float4 MainPS(...) : SV_Target0 { return c; } 单返回值,绑定到 SV_Target0
void MainPS(..., out float4 O : SV_Target0) { O = c; } 通过 out 参数 写出 SV_Target0

编译器都会生成「PS 向第 0 个渲染目标写 float4」的接口。选哪种多半是风格和是否需要多路输出


多路输出(MRT)为什么常用 void + 多个 out

HLSL 里一个函数不能 return 两个独立的 float4(不像有些语言的多返回值)。MRT 需要 多个独立语义,常见写法就是:

void MainPS(...,
    out float4 Out0 : SV_Target0,
    out float4 Out1 : SV_Target1)

也可以用 带语义的结构体 作为输出,本质一样,仍是多个 SV_TargetN 槽位。

所以:不是「return 和 out 在所有情况下都等价」,而是——单路时 return 可以看成 out 的语法糖;多路时通常用 void + 多个 out(或 struct)


像素着色器与 RT 的对应关系(补充)

  1. SV_TargetN 里的 N 对应 PSO / 绑定里的 第 N 个颜色渲染目标(从 0 开始)。你在 C++ 里 RenderTargets[0]RenderTargets[1] 就要和 SV_Target0SV_Target1 顺序一致

  2. 格式要匹配:每个 RT 的格式(如 PF_FloatRGBA)必须和 PS 里该路输出的类型兼容(一般是 float4 或可归约到 4 通道的向量)。

  3. 未声明的槽位:若 PSO 只绑了 2 张 RT,PS 里就不应再写 SV_Target2,否则未定义/编译错误。

  4. 深度:深度缓冲不是 SV_Target,而是 深度/模板子通道;深度来自 SV_Position 算出的深度,或由 PS 写 SV_Depth(覆盖)。你当前学习路径里用的是默认深度,没有额外 SV_Depth

  5. discard / clip:若像素被丢弃,所有 RT 都不会写入该像素(和深度规则一起由硬件处理)。


一句话:单路时 return ... : SV_Target0out ... : SV_Target0;多路时必须用多个输出(多个 out 或带语义的 struct),这和 C++ 里 RenderTargets[0..1] 一一对应。

2. 顶点数据多Normal

不需要什么解释,之前已学习过相关概念
在这里插入图片描述

3. 创建法线的RT纹理

不需要什么解释,之前已学习过相关概念:
在这里插入图片描述

4. 绑定法线RT

这里的代码是:
在这里插入图片描述
这里问了AI俩问题:

  1. RenderTargets[0]和[1]是否完全对应于像素着色器里的SV_Target0和SV_Target1
  2. 我没有在我的像素着色器中定义RenderTargets这个变量,这个变量是哪来的?

AI回答:(仅供参考)

1. RenderTargets[0] / [1]SV_Target0 / SV_Target1 是否一一对应?

是的。 在 UE / D3D 这条管线上,颜色 RT 绑定的顺序PS 里 SV_TargetN 的 N 一致:

  • RenderTargets[0] → 绑定到 第 0 路颜色输出 → 对应 SV_Target0
  • RenderTargets[1] → 绑定到 第 1 路 → 对应 SV_Target1

深度/模板不占用这些下标,它们走的是 RenderTargets.DepthStencil,对应的是 DSV,不是 SV_Target*


2. 没在 .usf 里写 RenderTargets,它从哪来?

RenderTargets 根本不在 HLSL 里,只在 C++ 的参数结构体里。

你在 FSceneLearningPS 里写了 RENDER_TARGET_BINDING_SLOTS(),宏会展开成固定名字的成员,类型是 FRenderTargetBindingSlots

/** Adds bindings slots for render targets on the structure. This is important for rasterizer based pass bind the
 * render target at the RHI pass creation. The name of the struct member will forced to be RenderTargets, and
 * its type is a FRenderTargetBindingSlots.
 * ...
 */
#define RENDER_TARGET_BINDING_SLOTS() \
	INTERNAL_SHADER_PARAMETER_EXPLICIT(UBMT_RENDER_TARGET_BINDING_SLOTS, FRenderTargetBindingSlots::FTypeInfo, FRenderTargetBindingSlots,RenderTargets,,,EShaderPrecisionModifier::Float,TEXT(""),false)

也就是说:

  • C++PSParameters->RenderTargets[0] 告诉 RDG / RHI「这个 pass 要把第 0 路颜色写到哪张纹理、LoadAction 是什么」。
  • HLSL:只声明 out float4 ... : SV_Target0/1,表示「PS 往第几路颜色输出写数据」。

两边通过 槽位编号 对上,而不是通过同名变量。RenderTargetsRDG 资源依赖 + RHI 绑 RT 用的;shader 里永远不会出现一个叫 RenderTargets 的变量。

5. SV_Target的SV是啥意思?

AI回答:
SVSystem Value(系统值语义)的缩写。

在 HLSL 里,带 SV_ 前缀的语义表示:这个输入/输出由图形管线约定含义,由硬件或驱动按固定规则解释,而不是普通用户自定义的插值数据。

例如:

  • SV_Target0:第 0 路渲染目标(颜色输出)
  • SV_Position:顶点阶段是裁剪空间位置;像素阶段常表示像素位置
  • SV_Depth:可写深度(若使用)

所以 SV_Target = “系统约定的渲染目标输出槽位”


AI这个解释得到了官方文档的证实:
在这里插入图片描述

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐