-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
345 lines (182 loc) · 300 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>风中追风</title>
<link href="http://example.com/atom.xml" rel="self"/>
<link href="http://example.com/"/>
<updated>2024-08-04T01:34:58.944Z</updated>
<id>http://example.com/</id>
<author>
<name>NeoZheng</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Lighting Channels 使用及原理</title>
<link href="http://example.com/2024/08/04/Lighting%20Channels%20%E4%BD%BF%E7%94%A8%E5%8F%8A%E5%8E%9F%E7%90%86/"/>
<id>http://example.com/2024/08/04/Lighting%20Channels%20%E4%BD%BF%E7%94%A8%E5%8F%8A%E5%8E%9F%E7%90%86/</id>
<published>2024-08-04T01:15:09.012Z</published>
<updated>2024-08-04T01:34:58.944Z</updated>
<content type="html"><![CDATA[<p>#Blog</p><h1 id="Lighting-Channels-使用及原理"><a href="#Lighting-Channels-使用及原理" class="headerlink" title="Lighting Channels 使用及原理"></a>Lighting Channels 使用及原理</h1><h1 id="字面解释"><a href="#字面解释" class="headerlink" title="字面解释"></a>字面解释</h1><p>通过light channel设置可以实现灯光分层对不同层物体的影响<br>只影响对不透明物体的光照,不影响半透 or Mask材质</p><p><img src="Pasted%20image%2020240804092202.png"></p><p>开启light channels后在光照计算前多一步CopyStencilToLightingChannelTexture,大致计算时间在0.08ms<br><img src="Pasted%20image%2020240804092256.png"></p><h1 id="内部解析"><a href="#内部解析" class="headerlink" title="内部解析"></a>内部解析</h1><p>通过截帧可以看到采样光照的时候采样一张uint 的Texture作为LightingChannelsMask进行计算:<br><img src="Pasted%20image%2020240804092352.png"><br>通过查阅代码可知道:<br>DeferredShadingRenderer.cpp 中 通过 CopyStencilToLightingChannelTexture 函数初始化LightingChannelsTexture。</p><p>通过 FCopyStencilToLightingChannelsPS shader实现从stencil贴图中读取到LightingChannels</p><p>通过查阅源码可以得知UE对Lighting ChannelsMask进行位运算压缩,将多种情况压缩到3位存储到LightingChannelTexture:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// EngineTypes.h</span></span><br><span class="line"><span class="comment">/** Converts lighting channels into a bitfield */</span> </span><br><span class="line"><span class="keyword">inline</span> uint8 <span class="title function_">GetLightingChannelMaskForStruct</span><span class="params">(FLightingChannels Value)</span> </span><br><span class="line">{ </span><br><span class="line"> <span class="comment">// Note: this is packed into 3 bits of a stencil channel </span></span><br><span class="line"> <span class="keyword">return</span> (uint8)((Value.bChannel0 ? <span class="number">1</span> : <span class="number">0</span>) | (Value.bChannel1 << <span class="number">1</span>) | (Value.bChannel2 << <span class="number">2</span>)); </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>具体示例我用下表展示:<br><img src="Pasted%20image%2020240804092543.png"><br>lightingChannelsMask == 0 -> 0001 -> 1<br>lightingChannelsMask == 1 -> 0010 -> 2<br>lightingChannelsMask == 2 -> 0100 -> 4<br>lightingChannelsMask == 0,1 -> 0011 -> 3<br>lightingChannelsMask == 0,2 -> 0101 -> 5<br>lightingChannelsMask == 1,2 -> 0110 -> 6<br>lightingChannelsMask == 0,1,2 -> 0111 -> 7</p><p>Lighting Channels 统一合并在 stencil Texture,以下是SceneRenderTarget中对Lighting channels 进行的划分:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// SceneRenderTargets.h</span></span><br><span class="line"></span><br><span class="line">* Stencil layout during basepass / deferred decals: </span><br><span class="line">* BIT ID | USE </span><br><span class="line">* [<span class="number">0</span>] | sandbox <span class="title function_">bit</span> <span class="params">(bit to be use by any rendering passes, but must be properly reset to <span class="number">0</span> after using)</span> </span><br><span class="line">* [1] | unallocated </span><br><span class="line">* [2] | Distance Field Representation </span><br><span class="line">* [3] | Temporal AA mask <span class="keyword">for</span> translucent object. </span><br><span class="line">* [4] | Lighting channels </span><br><span class="line">* [5] | Lighting channels </span><br><span class="line">* [6] | Lighting channels </span><br><span class="line">* [7] | primitive receive decal bit</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>在写入DeferredLightUniforms前,UE已进行位移运算将stencil值调整到lighting channel对应的位数(具体可看:STENCIL_LIGHTING_CHANNELS_MASK)<br><img src="Pasted%20image%2020240804093457.png"></p><p>在shader中通过位运算可计算出通道<br>light channel == 1 : DeferredLightUniforms.LightingChannelMask & 0x6</p><p>Notes: 在UE延迟渲染中,会将stencil值在Decal之前将stencil值拷贝到LightingChannelsTexture , 因为后面延迟贴花会重写这些值</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://docs.unrealengine.com/4.27/en-US/BuildingWorlds/LightingAndShadows/LightingChannels/">https://docs.unrealengine.com/4.27/en-US/BuildingWorlds/LightingAndShadows/LightingChannels/</a></p>]]></content>
<summary type="html">本文主要是近期对lighting Channels 源码学习记录</summary>
<category term="UnrealEngine" scheme="http://example.com/categories/UnrealEngine/"/>
<category term="Engine" scheme="http://example.com/tags/Engine/"/>
</entry>
<entry>
<title>自然颜色混合与机器学习实践</title>
<link href="http://example.com/2023/12/28/%E8%87%AA%E7%84%B6%E9%A2%9C%E8%89%B2%E6%B7%B7%E5%90%88%E4%B8%8E%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%AE%9E%E8%B7%B5/"/>
<id>http://example.com/2023/12/28/%E8%87%AA%E7%84%B6%E9%A2%9C%E8%89%B2%E6%B7%B7%E5%90%88%E4%B8%8E%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%AE%9E%E8%B7%B5/</id>
<published>2023-12-28T14:24:14.000Z</published>
<updated>2023-12-28T15:21:52.390Z</updated>
<content type="html"><![CDATA[<p>在目前计算机中,主流的颜色混合方案为线性相加,线性减弱。近期正好在研究一些水彩渲染相关,探索发现到2021年已有前人在siggraph上实现了水彩渲染,于是乎结合AI 做了次机器学习自然颜色混合渲染测试。.</p><p><img src="AI_1.png"></p><h1 id="一、颜色混合"><a href="#一、颜色混合" class="headerlink" title="一、颜色混合"></a>一、颜色混合</h1><h2 id="1-1-常见的颜色混合方案"><a href="#1-1-常见的颜色混合方案" class="headerlink" title="1.1 常见的颜色混合方案"></a>1.1 常见的颜色混合方案</h2><p>常见的颜色混合在计算机中主要是线性叠加和线性相减的方法进行颜色混合。<br><img src="2_1.png"><br>其中线性叠加代表物理意义上光的相加,线性减弱代表着物理意义上光的吸收。但在现实生活中,蓝色和黄色的颜料混合会变成绿色,而使用线性叠加,相减等方法均不能很好的表示该效果。</p><p>为了实现水彩混合的效果,2021年的一篇siggraph使用离线烘培的技术初步实现了计算机中模拟水彩混合的效果mixbox。但在开始介绍mixbox之前,还需要介绍下mixbox的前身和相关技术发展</p><h2 id="1-2-Mixbox-方案之前"><a href="#1-2-Mixbox-方案之前" class="headerlink" title="1.2 Mixbox 方案之前"></a>1.2 Mixbox 方案之前</h2><p>早在1931年,就已经有学者意识到颜料混合后产生的颜色效果,当时Kubelka-Munk 学者提出了Kubelka-Munk 模型,就初步解释了蓝色与黄色混合后出现绿色的现象:<br><img src="AI_2.png"><br>人眼能看到混合后为绿色,是因为光照射到混合颜料后发生光线散射与吸收,从而实现了反射为绿色的效果。但在之后的计算机中却一直未能完成落地,原因其实有一下三点:</p><ol><li>Kubelka-Munk模型(以下简称KM模型)需要对入射到颜料中的光线进行实时次表面散射的模拟,较难做到实时效果。</li><li>KM模型需要多通道进行颜色模拟才能得到效果,而主流软件都是使用RGB三色通道来进行颜色混合,较难兼容。<br><img src="3.png"><br>正因为如此,该效果一直未能在计算机中模拟实现。直到2021年,mixbox团队创新性的使用了半离线的方法解决了该问题。</li></ol><h2 id="1-3-Mixbox-方案"><a href="#1-3-Mixbox-方案" class="headerlink" title="1.3 Mixbox 方案"></a>1.3 Mixbox 方案</h2><p>Mixbox团队 在 KM模型 模型的基础上,通过分析多通道的方式,设计了以下方法实现:</p><ol><li><p>将RGB颜色转换到由四种颜色组成的更大色域空间<br><img src="4.png"></p></li><li><p>通过算法在更大的色彩空间中进行颜色混合并对<br><img src="5.png"></p></li><li><p>将混合后的颜色转换回RGB的颜色通道中。</p></li></ol><p>由于实时计算性能消耗较大, mixbox团队使用预烘焙lut图的方式,查表优化计算速度,最终实现了在计算机中实现自然颜色混合的效果。</p><p><img src="6.png"><br>上图为经过mixbox团队混合后的颜色过渡曲线。<br><img src="7.png"><br>上图为mixbox预烘焙后的lut图<br>Mixbox团队在使用lut图等预烘焙技术实现了一个表现优秀的自然颜料混合效果。<br>可以看到Mixbox团队使用大量真实数据,基于KM模型,拟合出自然颜色混合后的颜色过渡曲线,同时使用预烘培技术,将复杂的拟合过程改用采样的方式实现,最终达到实时的效果。<br>不过该技术目前也存在一定的带宽压力,lut图大小为48M,对于多平台的表现来讲可能会涉及到性能优化的一定问题,故在分析思考后针对mixbox的实现原理分析后发现可以使用机器学习的方式优化颜色混合曲线从而实现该效果。</p><h1 id="二、自然颜色混合的机器学习拟合方案"><a href="#二、自然颜色混合的机器学习拟合方案" class="headerlink" title="二、自然颜色混合的机器学习拟合方案"></a>二、自然颜色混合的机器学习拟合方案</h1><p>通过分析可以看到,mixbox团队通过lut图及相关算法实现了对两个颜色混合过渡的曲线拟合。通过机器学习的方式利用游戏引擎制作数据集后做监督学习算法,从而实现多项式拟合色彩混合曲线,实现自然颜色混合的效果。</p><h2 id="2-1-数据集准备"><a href="#2-1-数据集准备" class="headerlink" title="2.1 数据集准备"></a>2.1 数据集准备</h2><p>由于mixbox官方lut图存在版权限制,不能正常在外部调用,但官方在提供的github工程中分享了Unity解码工程,可以在Unity中调用mixbox官方库实现自然颜色混合的shader效果。故这边通过调用unity的示例工程获取机器学习所需数据集:<br><img src="8.png"><br><img src="9.png"></p><h2 id="2-2-算法部分"><a href="#2-2-算法部分" class="headerlink" title="2.2 算法部分"></a>2.2 算法部分</h2><p>算法部分使用sklearn 包,通过chatgpt直接生成多项式回归的算法来实现。</p><h2 id="2-3-训练"><a href="#2-3-训练" class="headerlink" title="2.3 训练"></a>2.3 训练</h2><p>训练部分通过前面生成的算法直接到python中进行训练,得到多项式的系数与表达式。<br><img src="11.png"><br><img src="10.png"></p><h2 id="2-4-调优"><a href="#2-4-调优" class="headerlink" title="2.4 调优"></a>2.4 调优</h2><p>由于Unity中默认工程用的是Linear空间的颜色,在UE中把颜色从gamma 转回 Linear 还原回原来的颜色,最终将前面计算得到的多项式方程通过Unreal 的custom node节点写入到材质中:<br><img src="12.png"></p><p><img src="13.png"></p><h2 id="2-5-引擎内效果对比"><a href="#2-5-引擎内效果对比" class="headerlink" title="2.5 引擎内效果对比"></a>2.5 引擎内效果对比</h2><p>给定相同颜色进行混合后,对比传统的线性混合,经过机器学习拟合后的颜色混合曲线更好地展现出了水彩混合的效果</p><p><img src="14.png"></p><p><img src="15.png"></p><p>演示视频:[<a href="https://www.zhihu.com/zvideo/1722285976427261952]https://www.zhihu.com/zvideo/1722285976427261952">https://www.zhihu.com/zvideo/1722285976427261952]https://www.zhihu.com/zvideo/1722285976427261952</a></p><p><img src="16.png">•左为机器学习后混合效果,右为传统lerp混合效果</p><h1 id="三、总结"><a href="#三、总结" class="headerlink" title="三、总结"></a>三、总结</h1><p>从实验结果上看,虽有初步接近自然颜料混合的效果,但仍存在色相饱和度未完全匹配的问题。后续可再通过优化算法,增加数据集等方式迭代优化。</p><h1 id="四、参考"><a href="#四、参考" class="headerlink" title="四、参考"></a>四、参考</h1><p>•ChatGPT <a href="https://openai.com/">https://openai.com/</a></p><p>•官方论文 <a href="https://scrtwpns.com/mixbox.pdf">https://scrtwpns.com/mixbox.pdf</a></p><p>•Mixbox 网址 <a href="https://scrtwpns.com/mixbox/">https://scrtwpns.com/mixbox/</a></p><p>•Siggraph Talk <a href="https://www.youtube.com/watch?v=_qa5iWdfNKg">https://www.youtube.com/watch?v=_qa5iWdfNKg</a></p>]]></content>
<summary type="html">本文分享了个人通过机器学习的方式实现实时自然颜色混合效果流程。</summary>
<category term="UnrealEngine" scheme="http://example.com/categories/UnrealEngine/"/>
<category term="Python" scheme="http://example.com/categories/UnrealEngine/Python/"/>
<category term="AIGC" scheme="http://example.com/categories/UnrealEngine/Python/AIGC/"/>
<category term="#Unreal" scheme="http://example.com/tags/Unreal/"/>
<category term="#渲染" scheme="http://example.com/tags/%E6%B8%B2%E6%9F%93/"/>
<category term="AIGC" scheme="http://example.com/tags/AIGC/"/>
</entry>
<entry>
<title>Niagara 粒子排序探索</title>
<link href="http://example.com/2023/11/19/Niagara%E7%B2%92%E5%AD%90%E6%8E%92%E5%BA%8F%E6%8E%A2%E7%B4%A2/"/>
<id>http://example.com/2023/11/19/Niagara%E7%B2%92%E5%AD%90%E6%8E%92%E5%BA%8F%E6%8E%A2%E7%B4%A2/</id>
<published>2023-11-19T02:15:55.000Z</published>
<updated>2023-11-19T02:35:11.166Z</updated>
<content type="html"><![CDATA[<h1 id="Niagara粒子排序探索"><a href="#Niagara粒子排序探索" class="headerlink" title="Niagara粒子排序探索"></a>Niagara粒子排序探索</h1><p>本文主要介绍下UE5 niagara 系统中粒子的排序顺序,可以解决粒子重叠和透明度问题。niagara 主要有三层排序: Particle, Emitter and Component.<br>粒子可以在自己的Emitter内进行排序,不可跨Emitter排序。<br><img src="Pasted%20image%2020231102172316.png"></p><h1 id="Particle-排序"><a href="#Particle-排序" class="headerlink" title="Particle 排序"></a>Particle 排序</h1><p>View Depth :基于到相机平面的距离<br>View Distance : 基于到相机位置的距离<br>Custom Ascending /Descending: 按照自己设定值进行排序(升降序)</p><p><img src="Pasted%20image%2020231102172417.png"><br>单个粒子内部可以通过设置custom sorting binding设置排序顺序</p><h1 id="Emitter-排序"><a href="#Emitter-排序" class="headerlink" title="Emitter 排序"></a>Emitter 排序</h1><p>(Sort Order Hint)</p><p>默认发射器将按照创建顺序(不是激活顺序,而是添加到系统中的顺序)绘制</p><p><img src="Pasted%20image%2020231102173023.png"><br>可以通过设置Sort Order Hint 设置Emitter 发射顺序</p><p>顺序大的在后面发射,所以sort order hint 越大,越后渲染,<br>这个排序只会在component内排序</p><h1 id="Component-排序"><a href="#Component-排序" class="headerlink" title="Component 排序"></a>Component 排序</h1><p>在虚拟引擎中,所有具有半透明元素的元件都将从后向前呈现其半透明元素。这意味着只要物体的位置与像素的实际位置有一定的相关性,更近的半透明物体就会在更远的半透明物体前面绘制。</p><p> Translucency Sort Distance Offset 和 Translucency Sort Priority 参数可以调节 </p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>制作特效时可以根据设计层级提前规划特效排序。如无法调整,可以使用camera offset做最后世界空间偏移。</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://realtimevfx.com/t/niagara-5-3-sorting-mini-tutorial/24380">https://realtimevfx.com/t/niagara-5-3-sorting-mini-tutorial/24380</a></p>]]></content>
<summary type="html">本文主要介绍Unreal5引擎中Niagara粒子排序相关</summary>
<category term="UnrealEngine" scheme="http://example.com/categories/UnrealEngine/"/>
<category term="UE" scheme="http://example.com/tags/UE/"/>
<category term="Niagara" scheme="http://example.com/tags/Niagara/"/>
</entry>
<entry>
<title>UE5 Nanite 原理</title>
<link href="http://example.com/2023/08/24/UE5%20Nanite%20%E5%8E%9F%E7%90%86/"/>
<id>http://example.com/2023/08/24/UE5%20Nanite%20%E5%8E%9F%E7%90%86/</id>
<published>2023-08-23T16:11:36.228Z</published>
<updated>2023-08-23T16:14:29.032Z</updated>
<content type="html"><![CDATA[<p>#Blog</p><h1 id="Nanite原理"><a href="#Nanite原理" class="headerlink" title="Nanite原理"></a>Nanite原理</h1><h2 id="前提"><a href="#前提" class="headerlink" title="前提"></a>前提</h2><p>复杂场景绘制的瓶颈通常有两个:(1)每次Draw Call带来的CPU端验证及CPU-GPU之间的通信开销;(2)由于剔除不够精确导致的overdraw和由此带来的GPU计算资源的浪费;近年来渲染技术优化往往也都是围绕这两个难题:</p><p>针对第一个:新一代的图形API,要求开发者自行处理CPU和GPU之间的同步;充分利用多核CPU的优势多线程向GPU提交命令。<br>针对第二个:减少CPU和GPU之间的数据通讯,诞生了<strong>GPU Driven Pipeline</strong>。把模型的顶点数据进一步切分为更细粒度的<strong>Cluster</strong>(或者叫做<strong>Meshlet</strong>),让每个Cluster的粒度能够更好地适应Vertex Processing阶段的Cache大小,并以Cluster为单位进行各类剔除(<strong>Frustum Culling,Occulsion Culling,Backface Culling</strong>)</p><p><img src="Pasted%20image%2020230502144437.png"><br>基于Mesh Shader的Pipeline,Cluster剔除成为了顶点处理阶段的一部分,减少没必要的Vertex Buffer Load/Store</p><h2 id="硬件光栅化和软件光栅化"><a href="#硬件光栅化和软件光栅化" class="headerlink" title="硬件光栅化和软件光栅化"></a>硬件光栅化和软件光栅化</h2><p><strong>传统光栅化硬件设计之初,设想的输入三角形大小是远大于一个像素的</strong>。基于这样的设想,硬件光栅化的过程通常是<strong>层次式的</strong>。以N卡的光栅器为例,一个三角形通常会经历两个阶段的光栅化:<strong>Coarse Raster</strong>和<strong>Fine Raster</strong> , 前者以一个三角形作为输入,以8x8像素为一个块,将三角形光栅化为若干块(你也可以理解成在尺寸为原始FrameBuffer 1/8*1/8大小的FrameBuffer上做了一次粗光栅化)。在这个阶段,借由低分辨率的Z-Buffer,被遮挡的块会被整个剔除,N卡上称之为<strong>Z Cull</strong>;在Coarse Raster之后,通过Z Cull的块会被送到下一阶段做Fine Raster,最终生成用于着色计算的像素。在Fine Raster阶段,有我们熟悉的<strong>Early Z</strong>。由于mip-map采样的计算需要,我们必须知道每个像素相邻像素的信息,并利用采样UV的差分作为mip-map采样层级的计算依据。为此,Fine Raster最终输出的并不是一个个像素,而是2x2的小像素块(<strong>Pixel Quad</strong>)。</p><p>对于接近像素大小的三角形来说,硬件光栅化的浪费就很明显了<br><img src="Pasted%20image%2020230502144619.png"><br>小三角形由于Pixel Quad造成的光栅化浪费</p><p><strong>软光栅化(基于Compute Shader)的确有机会打败硬光栅化</strong>。这也正是Nanite的核心优化之一,这一优化使得UE5在小三角形光栅化的效率上<strong>提升了3倍</strong>。</p><h2 id="Visibility-Buffer的新渲染管线"><a href="#Visibility-Buffer的新渲染管线" class="headerlink" title="Visibility Buffer的新渲染管线"></a><strong>Visibility Buffer</strong>的新渲染管线</h2><p>基于Visibility Buffer的算法不再单独产生臃肿的G-Buffer,而是以带宽开销更低的Visibility Buffer作为替代,Visibility Buffer通常需要这些信息:</p><p>(1)<strong>InstanceID</strong>,表示当前像素属于哪个Instance(16~24 bits);</p><p>(2)<strong>PrimitiveID</strong>,表示当前像素属于Instance的哪个三角形(8~16 bits);</p><p>(3)<strong>Barycentric Coord</strong>,代表当前像素位于三角形内的位置,用重心坐标表示(16 bits);</p><p>(4)<strong>Depth Buffer</strong>,代表当前像素的深度(16~24 bits);</p><p>(5)<strong>MaterialID</strong>,表示当前像素属于哪个材质(8~16 bits);</p><p>我们只需要存储大约<strong>8~12 Bytes/Pixel</strong>即可表示场景中所有几何体的材质信息,同时,我们需要维护一个<strong>全局的顶点数据和材质贴图表</strong>,表中存储了当前帧所有几何体的顶点数据,以及材质参数和贴图。在光照着色阶段,只需要根据InstanceID和PrimitiveID从全局的Vertex Buffer中索引到相关三角形的信息;进一步地,根据像该素的重心坐标,对Vertex Buffer内的顶点信息(UV,Tangent Space等)进行插值得到逐像素信息;再进一步地,根据MaterialID去索引相关的材质信息,执行贴图采样等操作,并输入到光照计算环节最终完成着色,有时这类方法也被称为<strong>Deferred Texturing</strong>。</p><p>下面是基于G-Buffer的渲染管线流程:</p><p><img src="Pasted%20image%2020230502144730.png"></p><p>这是基于Visibility-Buffer的渲染管线流程:<br><img src="Pasted%20image%2020230502144733.png"></p><h1 id="Nanite"><a href="#Nanite" class="headerlink" title="Nanite"></a>Nanite</h1><p><img src="NaniteGPUWorkflow.png"></p><p>它的核心思想可以简单拆解为两大部分:<strong>顶点处理的优化</strong>和<strong>像素处理的优化</strong>。<strong>其中顶点处理的优化主要是GPU Driven Pipeline的思想;像素处理的优化,是在Visibility Buffer思想的基础上,结合软光栅化完成的</strong>。</p><p><img src="Pasted%20image%2020230502143913.png"></p><h2 id="Geometry-Representation"><a href="#Geometry-Representation" class="headerlink" title="Geometry Representation"></a>Geometry Representation</h2><h3 id="View-Dependent-LOD-Transitions"><a href="#View-Dependent-LOD-Transitions" class="headerlink" title="View Dependent LOD Transitions"></a>View Dependent LOD Transitions</h3><p><img src="Pasted%20image%2020230503174932.png"></p><p>解决LOD Cracks 方法: 将边进行锁住,永远锁住LOD0的边,但是会在变换时候边缘产生大量碎面,造成高频问题。<br><img src="Pasted%20image%2020230503175311.png"><br>nanite会生成cluster group ,然后只锁住cluster group的边<br>多个cluster 生成一个 cluster group ,build的时候重新计算cluster group中的cluster分布,从而保证锁边和优化三角面<br><img src="Pasted%20image%2020230503175434.png"><br><img src="Pasted%20image%2020230503175522.png"><br>如下图所示,多个cluster组成一个cluster group,再重新拆分为多个cluster。<br><img src="Pasted%20image%2020230503175822.png"><br>可以看到下面,第一个cluster group在变换到第二个cluster group 后锁边范围均发生改变。<br>cluster group 在变换LOD时锁边范围会变换掉, 这样避免掉前面LOD变化时候锁边边缘的碎面高频问题。</p><p><img src="Pasted%20image%2020230503175900.png"><br><img src="Pasted%20image%2020230503180849.png"></p><p><img src="Pasted%20image%2020230503181157.png"><br><img src="Pasted%20image%2020230503181237.png"><br><img src="Pasted%20image%2020230508103802.png"><br>对于一些微小物体离得很远以后的情况,我们减到最后一级cluster,其实它还是有128个面,还有Imposter atlas的方式处理</p><h3 id="Detail-of-Simplification-QEM"><a href="#Detail-of-Simplification-QEM" class="headerlink" title="Detail of Simplification - QEM"></a>Detail of Simplification - QEM</h3><p><img src="Pasted%20image%2020230503181616.png"></p><p>解析下图,多个clusters 通过 METIS库 组合成 cluster group, 通过QEM算法进行减面优化后,重新通过METIS库组合成新的cluster group<br><img src="Pasted%20image%2020230503181629.png"></p><h2 id="Instance-Cull-amp-amp-Persistent-Cull"><a href="#Instance-Cull-amp-amp-Persistent-Cull" class="headerlink" title="Instance Cull && Persistent Cull"></a>Instance Cull && Persistent Cull</h2><p><img src="Pasted%20image%2020230506103816.png"><br>每个Nanite Mesh在预处理阶段,会被切成若干Cluster,每个Cluster包含128个三角形,整个Mesh以<strong>BVH(Bounding Volume Hierarchy)</strong>的形式组织成树状结构,每个叶节点代表一个Cluster。</p><p>剔除分两步,包含了<strong>视锥剔除</strong>和<strong>基于HZB的遮挡剔除</strong>。其中Instance Cull以Mesh为单位,通过Instance Cull的Mesh会将其BVH的根节点送到Persistent Cull阶段进行层次式的剔除(若某个BVH节点被剔除,则不再处理其子节点)。</p><p><strong>每棵BVH树一个单独的线程,也就是一个线程负责一个Nanite Mesh</strong>。从而实现<strong>Persistent Cull阶段的剔除任务数量映射到Compute Shader的线程数量</strong><br>由于每个Mesh的复杂度不同,其BVH树的节点数、深度差异很大,并行性很差,Nanite解决这个问题的思路是:设置固定数量的线程,每个线程通过一个全局的FIFO任务队列去取BVH节点进行剔除,若该节点通过了剔除,则把该节点的所有子节点也放进任务队列尾部,然后继续循环从全局队列中取新的节点,直到整个队列为空且不再产生新的节点。<strong>这其实是一个多线程并发的经典生产-消费者模式,不同的是,这里的每个线程既充当生产者,又充当消费者</strong>。通过这样的模式,Nanite就保证了各个线程之间的处理时长大致相同。</p><p><img src="Pasted%20image%2020230502160720.png"></p><p>整个剔除阶段分为两个Pass:<strong>Main Pass</strong>和<strong>Post Pass</strong>(可以通过控制台变量设置为只有Main Pass)。这两个Pass的逻辑基本是一致的,区别仅仅在于Main Pass遮挡剔除使用的HZB是基于<strong>上一帧数据</strong>构造的,而Post Pass则是使用Main Pass结束后构建的<strong>当前帧的HZB</strong>,这样是为了防止上一帧的HZB错误地剔除了某些可见的Mesh。<br><img src="Pasted%20image%2020230503174034.png"></p><p>需要注意的是,<strong>Nanite并未使用Mesh Shader</strong>,究其原因,一方面是因为Mesh Shader的支持尚未普及;另一方面是由于Nanite使用软光栅化,Mesh Shader的输出仍要写回GPU Buffer再用于软光栅化输入,因此相较于CS的方案并没有太多带宽的节省。</p><p><img src="Pasted%20image%2020230508103913.png"></p><h2 id="Runtime-LOD-Selection"><a href="#Runtime-LOD-Selection" class="headerlink" title="Runtime LOD Selection"></a>Runtime LOD Selection</h2><h3 id="LOD-Selection-Parallel"><a href="#LOD-Selection-Parallel" class="headerlink" title="LOD Selection Parallel"></a>LOD Selection Parallel</h3><p><img src="Pasted%20image%2020230506101552.png"><br><img src="Pasted%20image%2020230506101611.png"><br>整个树状的数据结构,error从下往上逐步递增。</p><p><img src="Pasted%20image%2020230503181957.png"></p><p>内部对整个cluster group 作为一个虚拟节点,整个节点有个error值。</p><p><img src="Pasted%20image%2020230506001731.png"></p><p><img src="Pasted%20image%2020230506001504.png"></p><p>每个cluster group 中的cluster存着自己的error,每个cluster group存该cluster group的error,最底层的cluster的error统一设置为-1,树的全部节点丢到GPU里面坐并行化运算。(空间换时间)</p><h3 id="Build-BVH-for-Acceleration-of-LOD-Selection"><a href="#Build-BVH-for-Acceleration-of-LOD-Selection" class="headerlink" title="Build BVH for Acceleration of LOD Selection"></a>Build BVH for Acceleration of LOD Selection</h3><p><img src="Pasted%20image%2020230506003315.png"><br>把每个LOD当作节点组建成新的BVH Tree<br><img src="Pasted%20image%2020230506003713.png"></p><p>对于遍历上述bvh树,官方相当于再开一个线程池,做一个MPMC 进程队列来快速处理<br><img src="Pasted%20image%2020230506003951.png"></p><h3 id="Screen-Pixels-and-Target"><a href="#Screen-Pixels-and-Target" class="headerlink" title="Screen Pixels and Target"></a>Screen Pixels and Target</h3><p><img src="Pasted%20image%2020230503174727.png"><br>根据屏幕比例控制三角形面数<strong>(这一步需要TA与美术确定屏幕比与模型面数关系)</strong></p><h2 id="Rasterization"><a href="#Rasterization" class="headerlink" title="Rasterization"></a>Rasterization</h2><p>在剔除结束之后,每个Cluster会根据其屏幕空间的大小送至不同的光栅器,<strong>大三角形和非Nanite Mesh仍然基于硬件光栅化,小三角形基于Compute Shader写成的软光栅化</strong>。<br><img src="Pasted%20image%2020230506104053.png"></p><ul><li>渲染 3333 个实例,每个实例包含 384 个顶点</li><li>运行 34821 组compute shader<br><img src="Pasted%20image%2020230508103934.png"></li></ul><p>Nanite的Visibility Buffer为一张R32G32_UINT的贴图(8 Bytes/Pixel),其中R通道的0<del>6 bit存储Triangle ID,7</del>31 bit存储Cluster ID,G通道存储32 bit深度:</p><p><img src="Pasted%20image%2020230502160853.png"><br>Cluster ID<br><img src="Pasted%20image%2020230502160901.png"><br>Triangle ID<br><img src="Pasted%20image%2020230502160909.png"><br>Depth</p><p>整个软光栅化的逻辑比较简单:基于扫描线算法,每个Cluster启动一个单独的Compute Shader,在Compute Shader初始阶段计算并缓存所有Clip Space Vertex Positon到shared memory,而后CS中的每个线程读取对应三角形的Index Buffer和变换后的Vertex Position,根据Vertex Position计算出三角形的边,执行背面剔除和小三角形(小于一个像素)剔除,然后利用原子操作完成Z-Test,并将数据写进Visibility Buffer。<strong>值得一提的是,为了保证整个软光栅化逻辑的简洁高效,Nanite Mesh不支持带有骨骼动画、材质中包含顶点变换或者Mask的模型</strong>。</p><h3 id="Emit-Targets"><a href="#Emit-Targets" class="headerlink" title="Emit Targets"></a>Emit Targets</h3><p>为了保证数据结构尽量紧凑,减少读写带宽,所有软光栅化需要的数据都存进了一张Visibility Buffer,但是为了与场景中基于硬件光栅化生成的像素混合,我们最终还是需要将Visibility Buffer中的额外信息写入到统一的Depth/Stencil Buffer以及Motion Vector Buffer当中。这个阶段通常由几个全屏Pass组成:</p><p>(1)<strong>Emit Scene Depth/Stencil/Nanite Mask/Velocity Buffer</strong>,这一步根据最终场景需要的RenderTarget数据,最多输出四个Buffer,其中Nanite Mask用0/1表示当前像素是普通Mesh还是Nanite Mesh(根据Visibility Buffer对应位置的ClusterID得到),对于Nanite Mesh Pixel,将Visibility Buffer中的Depth由UINT转为float写入Scene Depth Buffer,并根据Nanite Mesh是否接受贴花,将贴花对应的Stencil Value写入Scene Stencil Buffer,并根据上一帧位置计算当前像素的Motion Vector写入Velocity Buffer,非Nanite Mesh则直接discard跳过。</p><p><img src="Pasted%20image%2020230502161016.png"></p><p>Nanite Mask</p><p><img src="Pasted%20image%2020230502161023.png"></p><p>Velocity Buffer</p><p><img src="Pasted%20image%2020230502161030.png"></p><p>Scene Depth/Stencil Buffer</p><p>(2)<strong>Emit Material Depth</strong>,这一步将生成一张Material ID Buffer,稍有不同的是,它并未存储在一张UINT类型的贴图,而是将UINT类型的Material ID转为float存储在一张格式为D32S8的Depth/Stencil Target上(稍后我们会解释这么做的理由),理论上最多支持2^32种材质(实际上只有14 bits用于存储Material ID),而Nanite Mask会被写入Stencil Buffer中。</p><p><img src="Pasted%20image%2020230502161049.png"></p><p>Material Depth Buffer</p><h2 id="Classify-Materials-amp-amp-Emit-G-Buffer"><a href="#Classify-Materials-amp-amp-Emit-G-Buffer" class="headerlink" title="Classify Materials && Emit G-Buffer"></a>Classify Materials && Emit G-Buffer</h2><p><img src="Pasted%20image%2020230508103947.png"></p><p><strong>Nanite的材质Shader是在Screen Space执行</strong></p><p>Nanite在Base Pass绘制阶段并不是每种材质一个全屏Pass,而是将屏幕空间分成若干8x8的块,比如屏幕大小为800x600,则每种材质绘制时生成100x75个块,每块对应屏幕位置。为了能够整块地剔除,在Emit Targets之后,Nanite会启动一个CS用于统计每个块内包含的Material ID的种类。由于Material ID对应的Depth值预先是经过排序的,所以这个CS会统计每个8x8的块内Material Depth的最大最小值作为Material ID Range存储在一张R32G32_UINT的贴图中:</p><p><img src="Pasted%20image%2020230502161156.png"><br>Material ID Range</p><p>有了这张图之后,每种材质在其VS阶段,都会根据自身块的位置去采样这张贴图对应位置的Material ID Range,若当前材质的Material ID处于Range内,则继续执行材质的PS;否则表示当前块内没有像素使用该材质,则整块可以剔除,此时只需将VS的顶点位置设置为NaN,GPU就会将对应的三角形剔除。由于通常一个块内的材质种类不会太多,这种方法可以有效地减少不必要的overdraw。</p><p><strong>所以一种材质一个drawcall</strong></p><p>在完成了逐块的剔除后,Material Depth Buffer就派上了用场。在Base Pass PS阶段,Material Depth Buffer被设置为Depth/Stencil Target,同时Depth/Stencil Test被打开,Compare Function设置为Equal。只有当前像素的Material ID和待绘制的材质ID相同(Depth Test Pass)且该像素为Nanite Mesh(Stencil Test Pass)时才会真正执行PS,于是借助硬件的<strong>Early Z/Stencil我们完成了逐像素的材质ID剔除</strong>,整个绘制和剔除的原理见下图:</p><p><img src="Pasted%20image%2020230502161708.png"></p><p><img src="Pasted%20image%2020230502161711.png"><br>整个Base Pass分为两部分,首先绘制非Nanite Mesh的G-Buffer,这部分仍然在Object Space执行,和UE4的逻辑一致;之后按照上述流程绘制Nanite Mesh的G-Buffer,其中材质需要的额外VS信息(UV,Normal,Vertex Color等)通过像素的Cluster ID和Triangle ID索引到相应的Vertex Position,并变换到Clip Space,根据Clip Space Vertex Position和当前像素的深度值求出当前像素的重心坐标以及Clip Space Position的梯度(DDX/DDY),将重心坐标和梯度代入各类Vertex Attributes中插值即可得到所有的Vertex Attributes及其梯度(梯度可用于计算采样的Mip Map层级)。</p><p><img src="Pasted%20image%2020230503174341.png"></p><p>MaterialLayers虽然在MaterialInstances里面,但是不同的Layers组合会产生新的Shader,也就是说继承母材质后只要MaterialInstance修改过MaterialLayer栈,包括MaterialLayer和MaterialLayerBlend,会导致这个材质产生新的Shader。在执行Nanite最后的Emit GBuffer的时候每种材质都会执行一次屏幕空间的后处理 </p><p>MaterialInstance是可能产生新shader的。出来MaterialLayer,还有一些控制静态分支的参数。比如你在节点连线的时候加了一个静态分支节点,然后用静态参数来控制编译时的分支走向。那在MaterialInstance里修改参数就会导致新的Shader生成</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://zhuanlan.zhihu.com/p/382687738">https://zhuanlan.zhihu.com/p/382687738</a></p><p><a href="https://www.bilibili.com/video/BV1Et4y1P7ro/?spm_id_from=333.788&vd_source=a3c39604e9bbc2eac4a6998a43108c9b">https://www.bilibili.com/video/BV1Et4y1P7ro/?spm_id_from=333.788&vd_source=a3c39604e9bbc2eac4a6998a43108c9b</a></p><p><a href="https://www.elopezr.com/a-macro-view-of-nanite/">https://www.elopezr.com/a-macro-view-of-nanite/</a></p><p><a href="https://www.twitch.tv/videos/1044652371">https://www.twitch.tv/videos/1044652371</a></p><p>知乎从越大佬</p>]]></content>
<summary type="html">本文为参考相关资料后总结整理的Nanite原理个人理解,引用其他大佬的文章初步分析nanite在底层的实现,并为后续生产做相关思考与研究探索。</summary>
<category term="UnrealEngine" scheme="http://example.com/categories/UnrealEngine/"/>
<category term="UE" scheme="http://example.com/tags/UE/"/>
<category term="Nanite" scheme="http://example.com/tags/Nanite/"/>
</entry>
<entry>
<title>UE 破碎方案</title>
<link href="http://example.com/2023/05/15/UE%20%E7%A0%B4%E7%A2%8E%E6%96%B9%E6%A1%88/"/>
<id>http://example.com/2023/05/15/UE%20%E7%A0%B4%E7%A2%8E%E6%96%B9%E6%A1%88/</id>
<published>2023-05-15T14:15:13.731Z</published>
<updated>2023-05-15T14:48:55.250Z</updated>
<content type="html"><![CDATA[<p>#Blog</p><h1 id="UE-破碎方案"><a href="#UE-破碎方案" class="headerlink" title="UE 破碎方案"></a>UE 破碎方案</h1><p>破碎效果是游戏中的常见效果,但由于较多复杂的物理计算,通常游戏中都会做离线预先bake的方法来实现破碎,像彩虹六号,Control 这种以破碎为核心体验的游戏,则有专业引擎团队进行大量的物理优化后实现。本文尝试总结了已有一些破碎实现方案,并参考黑客帝国demo后设计的一个半实时模拟的破碎效果,实现实时模拟破碎。</p><p>先介绍下已有的方案:(此处参考:<a href="https://zhuanlan.zhihu.com/p/608906269%EF%BC%89">https://zhuanlan.zhihu.com/p/608906269)</a></p><h1 id="已有方案"><a href="#已有方案" class="headerlink" title="已有方案"></a>已有方案</h1><h2 id="骨骼动画"><a href="#骨骼动画" class="headerlink" title="骨骼动画"></a>骨骼动画</h2><p>houdini中用Voronoi fracture制作破碎后通过rbd bullet solver结算动画数据导出为fbx,并在引擎中作骨骼动画播放<br><img src="Pasted%20image%2020230510221059.png"><br><img src="310579ef77a041486bf3ce128e6e69d.png"></p><h2 id="顶点动画"><a href="#顶点动画" class="headerlink" title="顶点动画"></a>顶点动画</h2><p>houdini中用Voronoi fracture制作破碎后通过filecache节点,Labs vertex animation textures生成vat数据,在houdini中讲VAT材质function迁移到项目中使用。</p><p><img src="Pasted%20image%2020230510220957.png"></p><p><img src="Pasted%20image%2020230426201239.png"><br>并将position 和 rotation 两张贴图导入后设置相关材质后,查看破碎效果。</p><h2 id="Alembic"><a href="#Alembic" class="headerlink" title="Alembic"></a>Alembic</h2><p>使用ROP Alembic Output 将破碎动画导出为abc格式(数据量大)<br>导入到引擎中,变成几何体缓存,并通过sequence进行播放<br><img src="Pasted%20image%2020230510221209.png"></p><h2 id="chaos"><a href="#chaos" class="headerlink" title="chaos"></a>chaos</h2><p>UE5自带破碎系统</p><h2 id="前置方案对比"><a href="#前置方案对比" class="headerlink" title="前置方案对比"></a>前置方案对比</h2><table><thead><tr><th align="left">破碎方案</th><th>优点</th><th>缺点</th></tr></thead><tbody><tr><td align="left">骨骼动画</td><td>骨骼支持二次修改效果可控</td><td>碎块越多骨骼数量越多性能消耗越大</td></tr><tr><td align="left">顶点动画</td><td>基于顶点着色器性能消耗最小</td><td>贴图的大小约束了动画数据量的上限</td></tr><tr><td align="left">alembic</td><td>精度高,兼容性强</td><td>体积大</td></tr><tr><td align="left">Chaos</td><td>碎块与环境的碰撞</td><td>碰撞计算量大性能消耗大</td></tr></tbody></table><p>可以看到前置方案基本都是预先计算好破碎,并在相应情况下播放相应动画。其中骨骼动画、顶点动画、alembic方案均为离线破碎,无法做好很好的交互效果。chaos虽然是实时物理计算的破碎,但存在消耗计算量大,性能消耗高的问题。<br>考虑到场景中可能有大量实时玻璃破碎,参考了其他方案后,可以发现:在顶点动画中,可以使用顶点偏移的方式实现mesh的位移,而由compute shader并行计算的Niagara有不少节点可以实现物理模拟,所以可以尝试将Niagara模拟的信息传递到材质的顶点偏移中,实现实时破碎模拟。</p><h1 id="Niagara-WorldPositionOffset-实时模拟"><a href="#Niagara-WorldPositionOffset-实时模拟" class="headerlink" title="Niagara + WorldPositionOffset 实时模拟"></a>Niagara + WorldPositionOffset 实时模拟</h1><h2 id="技术剖析"><a href="#技术剖析" class="headerlink" title="技术剖析"></a>技术剖析</h2><p>破碎部分任然使用预处理的方法提前将碎块碎号,避免Gameplay中大量的实时计算。这个破碎我使用houdini先做默认破碎,并导出pivot painter图到UE引擎。<br>在Niagara中,我使用pivot painter图的采样,将生成的粒子放置到每个碎片处。然后通过Niagara中的物理模拟,将粒子的物理模拟位置,旋转等信息bake到Pivot painter图上,并传递给Material 做 world position offset 的偏移。</p><h2 id="前期处理"><a href="#前期处理" class="headerlink" title="前期处理"></a>前期处理</h2><p>houdini处理思路流程图</p><ol><li>导入模型(box位置) </li><li>通过scatter散点,并通过attributewrangle 设置散点的root。 </li><li>通过rbd material fracture 对模型根据输入的散点进行破碎。</li><li>对每个碎片进行单独设置root命名设置。</li><li>根据每个片的内外设置顶点色(内切缝处顶点色设置为1)。</li></ol><p><img src="Pasted%20image%2020230510203342.png"></p><ol start="6"><li>通过houdini自带的pivotpainter工具导出即可。</li></ol><h2 id="引擎处理"><a href="#引擎处理" class="headerlink" title="引擎处理"></a>引擎处理</h2><p>将处理后的mesh和原mesh及生成的3张PP2贴图导入引擎<br><img src="Pasted%20image%2020230510203722.png"></p><h2 id="Niagara-处理"><a href="#Niagara-处理" class="headerlink" title="Niagara 处理"></a>Niagara 处理</h2><p><img src="Pasted%20image%2020230510203759.png"></p><h3 id="采样贴图"><a href="#采样贴图" class="headerlink" title="采样贴图"></a>采样贴图</h3><ul><li><p>Initialize Component Variables : 采样PP2贴图生成粒子信息及scale </p></li><li><p>Calculate Per Particle Render Target UV Location:根据每个粒子的index信息找到其在PivotPainter2贴图中的UV位置,方便下一步读取。</p></li><li><p>Initialize Component Variables:根据上一步的UV信息读取PP2贴图,并将粒子位置、朝向等设置到对应的世界空间位置,朝向等。(其中读取使用了ms_PivotPainter2_DecodePostion_NF函数)<br><img src="Pasted%20image%2020230513135440.png"></p></li><li><p>Calculate Mass and Rotational Inertia by Volume : 根据scale设置碎片的mass </p></li></ul><h3 id="设置物理模拟"><a href="#设置物理模拟" class="headerlink" title="设置物理模拟"></a>设置物理模拟</h3><p><img src="Pasted%20image%2020230513215245.png"></p><h3 id="写回材质"><a href="#写回材质" class="headerlink" title="写回材质"></a>写回材质</h3><ul><li>Control PP2 Mesh Elements Via Niagara: 通过Niagara将粒子位置信息烘焙回Texture,内部通过将位置差、旋转信息组合成LinearColor后,根据Calculate Per Particle Render Target UV Location计算到的UV信息,将颜色写入到前者的UV上。</li><li>并在niagara材质输出部分将相关的RT和参数传递给mesh的material。<br><img src="Pasted%20image%2020230510204140.png"></li></ul><h2 id="Material-解析"><a href="#Material-解析" class="headerlink" title="Material 解析"></a>Material 解析</h2><h3 id="WPO解析"><a href="#WPO解析" class="headerlink" title="WPO解析"></a>WPO解析</h3><p><img src="Pasted%20image%2020230510204203.png"><br>材质核心部分在SkinPivotPainter2MeshesViaNiagaraWAccurateVelocity,里面做了pingpong 采样贴图,更好优化卡顿效果。 </p><p><img src="Pasted%20image%2020230510204210.png"></p><p>通过实时计算出来的PP2 采样将位置信息从世界空间转换为MeshParticle空间,与原来的PP2位置计算差值后转到世界空间得到实时的偏移。</p><p><img src="Pasted%20image%2020230510204217.png"></p><p>加上原来初始偏移在旋转后的位移差与绝对世界位置(包含WPO)相加,最终减掉实时PP2采样的位置,得到实时PP2的位移,乘上缩放量后再叠加原来的偏移即可达到效果.</p><p><img src="Pasted%20image%2020230510204224.png"></p><p><img src="Pasted%20image%2020230513230834.png"></p><h3 id="其他项解析"><a href="#其他项解析" class="headerlink" title="其他项解析"></a>其他项解析</h3><p><img src="Pasted%20image%2020230510204235.png"></p><p>直接采样模型顶点色做其他项的lerp实现破碎效果。</p><p><img src="Pasted%20image%2020230510204244.png"><br>最终实现效果:<br>可以控制玻璃破碎方向及破碎力度等。</p><p><img src="1.gif"></p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><table><thead><tr><th align="left">破碎方案</th><th>优点</th><th>缺点</th></tr></thead><tbody><tr><td align="left">骨骼动画</td><td>骨骼支持二次修改效果可控</td><td>碎块越多骨骼数量越多性能消耗越大</td></tr><tr><td align="left">顶点动画</td><td>基于顶点着色器性能消耗最小</td><td>贴图的大小约束了动画数据量的上限</td></tr><tr><td align="left">alembic</td><td>精度高,兼容性强</td><td>体积大</td></tr><tr><td align="left">Chaos</td><td>碎块与环境的碰撞</td><td>碰撞计算量大性能消耗大</td></tr><tr><td align="left">Niagara+PP2 实时</td><td>碎块和粒子近似模拟,效率高</td><td>贴图较多,需要较好项目管理</td></tr></tbody></table><h1 id="3A游戏制作破碎参考"><a href="#3A游戏制作破碎参考" class="headerlink" title="3A游戏制作破碎参考"></a>3A游戏制作破碎参考</h1><h2 id="Control"><a href="#Control" class="headerlink" title="Control"></a>Control</h2><p><a href="https://www.youtube.com/watch?v=kODJsQGXanU">https://www.youtube.com/watch?v=kODJsQGXanU</a> </p><p><a href="https://www.gdcvault.com/play/1026820/Destructible-Environments-in-Control-Lessons">https://www.gdcvault.com/play/1026820/Destructible-Environments-in-Control-Lessons</a></p><h2 id="RainBow-6"><a href="#RainBow-6" class="headerlink" title="RainBow 6"></a>RainBow 6</h2><p><a href="https://www.gdcvault.com/play/1023003/The-Art-of-Destruction-in">https://www.gdcvault.com/play/1023003/The-Art-of-Destruction-in</a> </p><p><a href="https://www.gdcvault.com/play/1022990/Rendering-Rainbow-Six-Siege">https://www.gdcvault.com/play/1022990/Rendering-Rainbow-Six-Siege</a></p><h2 id="fortnites"><a href="#fortnites" class="headerlink" title="fortnites"></a>fortnites</h2><p><a href="https://www.gdcvault.com/play/1018192/The-Inner-Workings-of-Fortnite">https://www.gdcvault.com/play/1018192/The-Inner-Workings-of-Fortnite</a></p><h2 id="Uncharted-4-A-Thief’s-End"><a href="#Uncharted-4-A-Thief’s-End" class="headerlink" title="Uncharted 4: A Thief’s End"></a>Uncharted 4: A Thief’s End</h2><p><a href="https://www.sidefx.com/community/fx-adventures-in-uncharted-4-a-thiefs-end/">https://www.sidefx.com/community/fx-adventures-in-uncharted-4-a-thiefs-end/</a></p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><ul><li><a href="https://zhuanlan.zhihu.com/p/608906269">https://zhuanlan.zhihu.com/p/608906269</a></li><li><a href="https://www.youtube.com/watch?v=alQEf454PjU&t=1058s">https://www.youtube.com/watch?v=alQEf454PjU&t=1058s</a></li><li><a href="https://papalqi.cn/%E7%A0%B4%E7%A2%8E/">https://papalqi.cn/%E7%A0%B4%E7%A2%8E/</a></li></ul>]]></content>
<summary type="html">本文为参考相关破碎方案后,探索设计的一套半实时模拟破碎效果的方案总结</summary>
<category term="UnrealEngine" scheme="http://example.com/categories/UnrealEngine/"/>
<category term="UE" scheme="http://example.com/tags/UE/"/>
<category term="Niagara" scheme="http://example.com/tags/Niagara/"/>
<category term="Projects" scheme="http://example.com/tags/Projects/"/>
</entry>
<entry>
<title>UE5.1 Two Side Foliage材质改进及优化</title>
<link href="http://example.com/2023/03/12/UE5.1%20%E6%A0%91%E5%8F%B6%E6%9D%90%E8%B4%A8%E5%AF%B9%E6%AF%94/"/>
<id>http://example.com/2023/03/12/UE5.1%20%E6%A0%91%E5%8F%B6%E6%9D%90%E8%B4%A8%E5%AF%B9%E6%AF%94/</id>
<published>2023-03-12T07:40:00.000Z</published>
<updated>2023-03-12T07:41:38.113Z</updated>
<content type="html"><![CDATA[<h1 id="UE5-1-Two-Side-Foliage材质改进及优化"><a href="#UE5-1-Two-Side-Foliage材质改进及优化" class="headerlink" title="UE5.1 Two Side Foliage材质改进及优化"></a>UE5.1 Two Side Foliage材质改进及优化</h1><p><img src="lumen-foliage-2.png" alt="Lumen Two-Sided Foliage Example 2"></p><p>本文在于项目升级5.1后做的横向测试对比,写做笔记记录下。</p><h1 id="改动"><a href="#改动" class="headerlink" title="改动"></a>改动</h1><ul><li><code>Screen probe importance sampling</code>在两侧的树叶上被禁用,因此一半的光线用于收集背面照明</li><li><code>Screen traces</code> 过背面光线以避免立即自相交。其他跟踪方法使用背面法线进行偏置。</li><li>Two Sided Foliage Shading 用 <code>DiffuseLighting * DiffuseColor + BackfaceDiffuseLighting * PreintegratedTwoSidedBxDF * SubsurfaceColor</code></li><li><code>r.Lumen.ScreenProbeGather.TwoSidedFoliageBackfaceDiffuse</code> 关闭后会将次表面的颜色加到DiffuseColor(没有backface lighting)</li><li><code>The High GI scalability level disables TwoSidedFoliageBackfaceDiffuse to avoid overhead</code></li></ul><h1 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h1><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// DiffuseIndirectComposite.usf</span></span><br><span class="line"><span class="keyword">if</span> (GBuffer.ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE)</span><br><span class="line">{</span><br><span class="line">float3 SubsurfaceColor = <span class="built_in">ExtractSubsurfaceColor</span>(GBuffer);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (bLumenSupportBackfaceDiffuse > <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line">BackfaceDiffuseIndirectLighting += SubsurfaceColor * DiffuseIndirect_Textures_1.<span class="built_in">SampleLevel</span>(GlobalPointClampedSampler, SceneBufferUV, <span class="number">0</span>).rgb;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">{</span><br><span class="line"><span class="comment">// Adding Subsurface energy to the diffuse lobe is a poor approximation when DiffuseColor is small and SubsurfaceColor is large</span></span><br><span class="line"><span class="comment">// Reduce the error by attenuating SubsurfaceColor, even though diffuse already has the 1/PI for Lambert.</span></span><br><span class="line"><span class="type">const</span> <span class="type">float</span> PreintegratedTwoSidedBxDF = <span class="number">1.0f</span> / PI;</span><br><span class="line">DiffuseColor += SubsurfaceColor * PreintegratedTwoSidedBxDF;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">IndirectLighting.Diffuse = (DiffuseIndirectLighting * DiffuseColor + BackfaceDiffuseIndirectLighting) * Occlusion.DiffuseOcclusion;</span><br><span class="line">IndirectLighting.Transmission = <span class="number">0</span>;</span><br></pre></td></tr></table></figure><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><ul><li>UE 在5.1中对two side foliage类型的材质进行背部次表面散射补偿。通过叠加次表面散射的颜色与间接光的乘积得到背部补偿。但会增加16mb的RT来累积光照</li><li>可用点乘的方式在低版本的植被上添加自发光实现补偿trick效果。</li></ul><p><img src="Untitled_1.png"></p><p><img src="Untitled_2.png"></p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://github.com/EpicGames/UnrealEngine/commit/3ecab7b620fe7c74c0427f5dc24cc32528eba301">https://github.com/EpicGames/UnrealEngine/commit/3ecab7b620fe7c74c0427f5dc24cc32528eba301</a></p>]]></content>
<summary type="html">本文主要记录下UE5.1引擎升级后对TwoSideFoliage材质的优化</summary>
<category term="UnrealEngine" scheme="http://example.com/categories/UnrealEngine/"/>
<category term="UE" scheme="http://example.com/tags/UE/"/>
<category term="Shading" scheme="http://example.com/tags/Shading/"/>
</entry>
<entry>
<title>使用插件修改UE5渲染管线</title>
<link href="http://example.com/2022/10/01/Modify%20Render%20Pipeline%20By%20Plugin%20in%20UE5/"/>
<id>http://example.com/2022/10/01/Modify%20Render%20Pipeline%20By%20Plugin%20in%20UE5/</id>
<published>2022-10-01T03:50:00.000Z</published>
<updated>2023-11-19T02:06:42.629Z</updated>
<content type="html"><![CDATA[<h1 id="使用插件修改UE5渲染管线"><a href="#使用插件修改UE5渲染管线" class="headerlink" title="使用插件修改UE5渲染管线"></a>使用插件修改UE5渲染管线</h1><p>笔者最近需要在渲染管线中做相关GBuffer的修改处理,故笔者尝试使用UE的plugin插件来修改渲染管线,经过一周多的折腾研究,初步完成了拓展,故写下此篇笔记,希望对您有帮助。</p><p>在修改前我们需要先对引擎做一定拓展才方便我们读取到GBuffer相关数据。</p><h1 id="扩展-Render-Pipeline"><a href="#扩展-Render-Pipeline" class="headerlink" title="扩展 Render Pipeline"></a>扩展 Render Pipeline</h1><h2 id="打开ViewExtension-的-RDG-接口"><a href="#打开ViewExtension-的-RDG-接口" class="headerlink" title="打开ViewExtension 的 RDG 接口"></a>打开ViewExtension 的 RDG 接口</h2><p>需要修改引擎,开放GraphBuilder接口(此处需要手动添加)</p><p><img src="Blog/source/_posts/UnityPBR/Untitled.png" alt="开放GraphBuilder接口"></p><h2 id="渲染管线中添加对应RDG的渲染流程"><a href="#渲染管线中添加对应RDG的渲染流程" class="headerlink" title="渲染管线中添加对应RDG的渲染流程"></a>渲染管线中添加对应RDG的渲染流程</h2><p><img src="Untitled_1.png"></p><h2 id="修改GBuffer的SceneTexture-Flag-以便Compute-Shader使用"><a href="#修改GBuffer的SceneTexture-Flag-以便Compute-Shader使用" class="headerlink" title="修改GBuffer的SceneTexture Flag 以便Compute Shader使用"></a>修改GBuffer的SceneTexture Flag 以便Compute Shader使用</h2><p><img src="Untitled_2.png"></p><h1 id="暴露引擎内部函数(可选)"><a href="#暴露引擎内部函数(可选)" class="headerlink" title="暴露引擎内部函数(可选)"></a>暴露引擎内部函数(可选)</h1><p>在引擎中需要自己创建RENDERER_API类,并在该类中声明相关函数来调用引擎中的相关接口,从而实现在插件中调用引擎内部函数。(笔者需要调用到SceneView,所以创建了相应类后直接return)</p><p><img src="Untitled_3.png"></p><p>至此,引擎侧的修改就OK了,接下来就是到Plugin侧做开发</p><h1 id="逻辑层面"><a href="#逻辑层面" class="headerlink" title="逻辑层面"></a>逻辑层面</h1><h2 id="SceneViewExtensionBase"><a href="#SceneViewExtensionBase" class="headerlink" title="SceneViewExtensionBase"></a>SceneViewExtensionBase</h2><p>拓展UE底层渲染,笔者使用了FSceneViewExtensionBase这个类进行拓展。Scene View Extension是引擎提供的一个接口,原本设计是可以在后处理的某个pass后插入一个pass,其中就包括:</p><ul><li>MotionBlur</li><li>Tonemap</li><li>FXAA</li><li>VisualizeDepthOfField</li></ul><p>这四个阶段后插入pass。该接口只支持Deferred Shading Path。</p><p>在Deferred Shading Path中调用如下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (int32 ViewExt = <span class="number">0</span>; ViewExt < ViewFamily.ViewExtensions.<span class="built_in">Num</span>(); ++ViewExt)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">for</span> (int32 ViewIndex = <span class="number">0</span>; ViewIndex < ViewFamily.Views.<span class="built_in">Num</span>(); ++ViewIndex)</span><br><span class="line">{</span><br><span class="line">FViewInfo& View = Views[ViewIndex];</span><br><span class="line"><span class="built_in">RDG_GPU_MASK_SCOPE</span>(GraphBuilder, View.GPUMask);</span><br><span class="line">PostProcessingInputs.TranslucencyViewResourcesMap = <span class="built_in">FTranslucencyViewResourcesMap</span>(TranslucencyResourceMap, ViewIndex);</span><br><span class="line">ViewFamily.ViewExtensions[ViewExt]-><span class="built_in">PrePostProcessPass_RenderThread</span>(GraphBuilder, View, PostProcessingInputs);</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在源码的<code>Engine\Source\Runtime\Engine\Public\SceneViewExtension.h</code>文件里,Epic在注释里给出了SceneViewExtension的推荐用法。这里我直接代入自己的代码做展示</p><p>首先要创建一个类,继承自FSceneViewExtensionBase。Note:第一个参数一定要是 FAutoRegister ,且要传递给 FSceneViewExtensionBase</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">FWetnessWaterSceneViewExtension</span> : <span class="keyword">public</span> FSceneViewExtensionBase</span><br><span class="line">{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"></span><br><span class="line"><span class="built_in">FWetnessWaterSceneViewExtension</span>(<span class="type">const</span> FAutoRegister& AutoRegister, UWetnessWaterSubsystem* InWorldSubsystem);</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">FWetnessWaterSceneViewExtension::<span class="built_in">FWetnessWaterSceneViewExtension</span>(<span class="type">const</span> FAutoRegister& AutoRegister, UWetnessWaterSubsystem* InWorldSubsystem) </span><br><span class="line">: <span class="built_in">FSceneViewExtensionBase</span>(AutoRegister), <span class="built_in">WetnessWaterSubsystem</span>(InWorldSubsystem)</span><br><span class="line">{</span><br><span class="line">OwnerWorld = InWorldSubsystem-><span class="built_in">GetWorld</span>();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然后继承<code>ISceneViewExtension</code>的5个纯虚函数。这里我创建多一个虚函数,方便后续操作及对应前面引擎侧的修改:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//~ Begin FSceneViewExtensionBase Interface</span></span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">SetupViewFamily</span><span class="params">(FSceneViewFamily& InViewFamily)</span> <span class="keyword">override</span> </span>{};</span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">SetupView</span><span class="params">(FSceneViewFamily& InViewFamily, FSceneView& InView)</span> <span class="keyword">override</span> </span>{};</span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">BeginRenderViewFamily</span><span class="params">(FSceneViewFamily& InViewFamily)</span> <span class="keyword">override</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">PreRenderViewFamily_RenderThread</span><span class="params">(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& InViewFamily)</span> <span class="keyword">override</span> </span>{};</span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">PreRenderView_RenderThread</span><span class="params">(FRHICommandListImmediate& RHICmdList, FSceneView& InView)</span> <span class="keyword">override</span> </span>{};</span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">PostRenderBasePass_RenderThread</span><span class="params">(FRDGBuilder& GraphBuilder, FSceneView& InView)</span> <span class="keyword">override</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">PostRenderBasePass_RenderThread</span><span class="params">(FRHICommandListImmediate& RHICmdList, FSceneView& InView)</span> <span class="keyword">override</span> </span>{};</span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">PrePostProcessPass_RenderThread</span><span class="params">(FRDGBuilder& GraphBuilder, <span class="type">const</span> FSceneView& View, <span class="type">const</span> FPostProcessingInputs& Inputs)</span> <span class="keyword">override</span> </span>{};</span><br><span class="line"><span class="comment">//~ End FSceneViewExtensionBase Interface</span></span><br></pre></td></tr></table></figure><p>接下来是实现相关函数。需要具体实现 BeginRenderViewFamily 和 PostRenderBasePass_RenderThread(GraphBuilder)</p><p>BeginRenderViewFamily 函数主要负责SceneView相关初始化的参数设置。</p><p>PostRenderBasePass_RenderThread 主要负责SceneView的相关渲染逻辑,其中包括AddPass也在此实现</p><h2 id="Shader处理"><a href="#Shader处理" class="headerlink" title="Shader处理"></a>Shader处理</h2><p>这里直接使用了ComputeShader进行处理,所以只讲如何从Gbuffer中读取出相关数据。</p><p>首先我们需要声明创建Global Shader,并在Shader Parameter 中预留给传入shader的GBuffer的RWTexture2D。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">BEGIN_SHADER_PARAMETER_STRUCT</span>(FParameters, )</span><br><span class="line"></span><br><span class="line"><span class="built_in">SHADER_PARAMETER_RDG_TEXTURE_UAV</span>(RWTexture2D<float4>, GBufferBRWTexture)</span><br><span class="line"><span class="built_in">SHADER_PARAMETER_RDG_TEXTURE_UAV</span>(RWTexture2D<float4>, GBufferCRWTexture)</span><br><span class="line">(...)</span><br><span class="line"><span class="built_in">END_SHADER_PARAMETER_STRUCT</span>()</span><br></pre></td></tr></table></figure><p>然后在前面提到的PostRenderBasePass_RenderThread 实现函数中,由GBuffer中创建对应的UAV来进行后续Compute Shader的处理。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">PassParameters->GBufferBRWTexture = GraphBuilder.<span class="built_in">CreateUAV</span>(<span class="built_in">FRDGTextureUAVDesc</span>(SceneTextures.GBufferB,<span class="number">0</span>));</span><br></pre></td></tr></table></figure><p>并执行调用FComputeShaderUtils::AddPass操作将Compute Shader进行处理。</p><p>在Shader中执行相应的Encode和Decode操作及GBuffer修改后即可实现相关效果。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">ApplyRainWetness</span><span class="params">(inout FRainWetnessModifiedParams ModifiedParams, FGBufferData GBuffer, <span class="type">float</span> Porosity, <span class="type">float</span> Wetness, <span class="type">float</span> WaterSpecular)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="comment">//Derken the albedo base on porosity, except if metallic</span></span><br><span class="line"> ModifiedParams.BaseColor = GBuffer.BaseColor * <span class="built_in">lerp</span>(<span class="number">1.0</span>, <span class="number">0.25</span>, Wetness * Porosity * (<span class="number">1.0</span> - GBuffer.Metallic));</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Generate a wet version of smoothness map using porosity and original smoothness</span></span><br><span class="line"> <span class="type">float</span> Smoothness = <span class="number">1.0</span> - GBuffer.Roughness;</span><br><span class="line"> <span class="type">float</span> BaseSmoothness = <span class="built_in">lerp</span>(<span class="number">0.9</span>, <span class="number">0.6</span>, Porosity);</span><br><span class="line"> <span class="type">float</span> SmoothnessAdjustment = <span class="built_in">lerp</span>(<span class="number">-0.3</span>, <span class="number">0.4</span>, <span class="built_in">sqrt</span>(Smoothness));</span><br><span class="line"> <span class="type">float</span> FinalSmoothness = <span class="built_in">saturate</span>(BaseSmoothness + SmoothnessAdjustment);</span><br><span class="line"> <span class="comment">// The new smoothness should not be lower than before or higher than max of 0.9</span></span><br><span class="line"> FinalSmoothness = <span class="built_in">clamp</span>(FinalSmoothness, Smoothness, <span class="number">0.9</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Apply new smoothness and reflectance</span></span><br><span class="line"> Smoothness = <span class="built_in">lerp</span>(Smoothness, FinalSmoothness, Wetness);</span><br><span class="line"> ModifiedParams.Roughness = <span class="number">1.0</span> - Smoothness;</span><br><span class="line"> ModifiedParams.Specular = <span class="built_in">lerp</span>(GBuffer.Specular, WaterSpecular, Wetness);</span><br><span class="line">ModifiedParams.WetnessShadow = Wetness;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>此处为Shader中修改的函数。</p><p>修改结果如下:</p><p><img src="Untitled_5.jpg"></p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>本文只做指引,希望对各位有所帮助~</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://zhuanlan.zhihu.com/p/554298261">分享UE4虚幻引擎中通过材质球修改管线GBuffer的插件</a></p><p><a href="https://blog.csdn.net/u010281174/article/details/123806725">UE源码剖析 - Scene View Extension__子宽的博客-CSDN博客_ue源码剖析</a></p><p>ColorCorrectRegions插件</p>]]></content>
<summary type="html">本文主要记录自己通过UE5 Plugin修改渲染管线的流程</summary>
<category term="UnrealEngine" scheme="http://example.com/categories/UnrealEngine/"/>
<category term="UE" scheme="http://example.com/tags/UE/"/>
<category term="Shading" scheme="http://example.com/tags/Shading/"/>
</entry>
<entry>
<title>Creating a new GBuffer in Unreal5</title>
<link href="http://example.com/2022/09/19/Creating%20a%20new%20GBuffer%20in%20Unreal5/"/>
<id>http://example.com/2022/09/19/Creating%20a%20new%20GBuffer%20in%20Unreal5/</id>
<published>2022-09-19T14:36:00.000Z</published>
<updated>2023-06-01T16:26:40.860Z</updated>
<content type="html"><![CDATA[<h1 id="Creating-a-new-GBuffer-in-Unreal5"><a href="#Creating-a-new-GBuffer-in-Unreal5" class="headerlink" title="Creating a new GBuffer in Unreal5"></a>Creating a new GBuffer in Unreal5</h1><p>新增一个GBuffer在UE来说较为复杂,主要需要修改以下文件,这里我先列举方便后期校对</p><ol><li>SceneTextures.h/.cpp :负责声明创建SceneTexture及GBuffer相关Texture</li><li>GBufferInfo.h / .cpp :负责声明GBuffer相关属性 </li><li>DeferredShadingCommon.ush : 负责DecodeShaderingCommon</li><li>SceneTextureParameters.h/.cpp :负责SceneTexture Parameter 绑定等</li><li>ShaderGenerationUtil.cpp : 负责生成Shader</li><li>SceneTexturesCommon.ush : SceneTextures 相关Common 函数</li><li>MaterialTemplate.ush : hlsl函数模板</li><li>ShaderCompiler.h : 负责Shader编译</li><li>PixelShaderOutputCommon : PixelShaderOutput相关定义</li></ol><h1 id="1-什么是GBuffer?"><a href="#1-什么是GBuffer?" class="headerlink" title="1. 什么是GBuffer?"></a>1. 什么是GBuffer?</h1><p>延迟渲染管线不了解的话可以直接参考 0向往0 dalao的文章了解</p><p>The G-buffer is the collective term of all textures used to store lighting-relevant data for the final lighting pass. (定义来自<a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">here</a>)</p><p>了解了GBuffer的定义后我们来了解下UE对GBuffer的相关定义和处理:</p><h1 id="2-UE中的GBuffer解析"><a href="#2-UE中的GBuffer解析" class="headerlink" title="2.UE中的GBuffer解析"></a>2.UE中的GBuffer解析</h1><h2 id="2-1-初步了解GBuffer"><a href="#2-1-初步了解GBuffer" class="headerlink" title="2.1 初步了解GBuffer"></a>2.1 初步了解GBuffer</h2><p>首先我们在延迟渲染中可以通过Renderdoc 截取看到 在渲染流程中会在不同时期对 GBuffer 进行读取/写入。</p><p><img src="Blog/source/_posts/Creating%20a%20new%20GBuffer%20in%20Unreal5/Untitled.png"></p><p>UE会在渲染的BasePass阶段将场景相关信息写入存储到GBuffer中。并在后续Lighting阶段将GBuffer中的相关信息进行读取后参与计算光照结果。</p><p><img src="Blog/source/_posts/Adding%20a%20new%20Shading%20Model/Untitled%201.png"></p><p><em>这里可以看到在直接光的计算中使用的相关Gbuffer的读取。</em></p><p>既然UE在基础的Deferred Rendering中使用到GBuffer,那我们先直接定位到 DeferredShadingRenderer.cpp 查看 Render 函数,这里由于本文篇幅原因不做详细解析,后续有时间笔者再补充下FDeferredShadingSceneRenderer::Render的相关细节流程,这里我直接使用总结的流程图:</p><p><img src="Blog/source/_posts/Adding%20a%20new%20Shading%20Model/Untitled%202.png"></p><p>在渲染流程中,与GBuffer相关的处理主要是由FSceneTextures及其相关类进行处理。</p><p>主要是通过最开始根据 View 相关设置获得 SceneTexture 相关设置</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> FSceneTexturesConfig SceneTexturesConfig = FSceneTexturesConfig::<span class="built_in">Create</span>(ViewFamily);</span><br><span class="line">FSceneTexturesConfig::<span class="built_in">Set</span>(SceneTexturesConfig);</span><br></pre></td></tr></table></figure><p>之后通过FSceneTextures::Create 方法进行创建相关的 SceneTexture。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">FSceneTextures& SceneTextures = FSceneTextures::<span class="built_in">Create</span>(GraphBuilder, SceneTexturesConfig);</span><br></pre></td></tr></table></figure><p>并在相关BasePass Lighting 计算前后进行调用来将结果输出到GBuffer中。</p><p><img src="Blog/source/_posts/Adding%20a%20new%20Shading%20Model/Untitled%203.png"></p><p>找到Render中对Gbuffer写入、读写位置后,继续深入FSceneTextures中进行研究。首先从Create函数开始,该函数主要是用于创建GBuffer对应的RT,在创建时会使用RDG的形式创建相应的2D Render Target</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// SceneTextures.cpp</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (Config.GBufferA.Index >= <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line"><span class="function"><span class="type">const</span> FRDGTextureDesc <span class="title">Desc</span><span class="params">(FRDGTextureDesc::Create2D(Config.Extent, Config.GBufferA.Format, FClearValueBinding::Transparent, Config.GBufferA.Flags | FlagsToAdd | GFastVRamConfig.GBufferA))</span></span>;</span><br><span class="line">SceneTextures.GBufferA = GraphBuilder.<span class="built_in">CreateTexture</span>(Desc, <span class="built_in">TEXT</span>(<span class="string">"GBufferA"</span>));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (Config.GBufferB.Index >= <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line"><span class="function"><span class="type">const</span> FRDGTextureDesc <span class="title">Desc</span><span class="params">(FRDGTextureDesc::Create2D(Config.Extent, Config.GBufferB.Format, FClearValueBinding::Transparent, Config.GBufferB.Flags | FlagsToAdd | GFastVRamConfig.GBufferB))</span></span>;</span><br><span class="line">SceneTextures.GBufferB = GraphBuilder.<span class="built_in">CreateTexture</span>(Desc, <span class="built_in">TEXT</span>(<span class="string">"GBufferB"</span>));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (Config.GBufferC.Index >= <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line"><span class="function"><span class="type">const</span> FRDGTextureDesc <span class="title">Desc</span><span class="params">(FRDGTextureDesc::Create2D(Config.Extent, Config.GBufferC.Format, FClearValueBinding::Transparent, Config.GBufferC.Flags | FlagsToAdd | GFastVRamConfig.GBufferC))</span></span>;</span><br><span class="line">SceneTextures.GBufferC = GraphBuilder.<span class="built_in">CreateTexture</span>(Desc, <span class="built_in">TEXT</span>(<span class="string">"GBufferC"</span>));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (Config.GBufferD.Index >= <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line"><span class="function"><span class="type">const</span> FRDGTextureDesc <span class="title">Desc</span><span class="params">(FRDGTextureDesc::Create2D(Config.Extent, Config.GBufferD.Format, FClearValueBinding::Transparent, Config.GBufferD.Flags | FlagsToAdd | GFastVRamConfig.GBufferD))</span></span>;</span><br><span class="line">SceneTextures.GBufferD = GraphBuilder.<span class="built_in">CreateTexture</span>(Desc, <span class="built_in">TEXT</span>(<span class="string">"GBufferD"</span>));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (Config.GBufferE.Index >= <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line"><span class="function"><span class="type">const</span> FRDGTextureDesc <span class="title">Desc</span><span class="params">(FRDGTextureDesc::Create2D(Config.Extent, Config.GBufferE.Format, FClearValueBinding::Transparent, Config.GBufferE.Flags | FlagsToAdd | GFastVRamConfig.GBufferE))</span></span>;</span><br><span class="line">SceneTextures.GBufferE = GraphBuilder.<span class="built_in">CreateTexture</span>(Desc, <span class="built_in">TEXT</span>(<span class="string">"GBufferE"</span>));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>CreateSceneTextureUniformBuffer 函数会根据SetupMode进行初始化相应RT,其中最关键的就是通过EnumHasAnyFlags判断传入的SetupMode来进行RT的复制和修改</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="built_in">EnumHasAnyFlags</span>(SetupMode, ESceneTextureSetupMode::GBufferA) && <span class="built_in">HasBeenProduced</span>(SceneTextures->GBufferA))</span><br><span class="line">{</span><br><span class="line">SceneTextureParameters.GBufferATexture = SceneTextures->GBufferA;</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>同时在 SceneTextureParameters.h 中 定义了SceneTexture中相关RDG Texture的文件及相关方法,以辅助将GBuffer中的数据传递到相应的RT上进行绘制。</p><p>在 SceneTexturesCommon.ush 中定义了 RT 的采样器等来实现在GPU中进行读取。</p><p>至此我们基本了解UE在延迟渲染管线中对GBuffer对应RT的操作流程。</p><h2 id="2-2-UE如何定义GBuffer"><a href="#2-2-UE如何定义GBuffer" class="headerlink" title="2.2 UE如何定义GBuffer"></a>2.2 UE如何定义GBuffer</h2><p>UE中使用Encode和Decode机制来实现对GBuffer的写/读。并通过GBUFFER_REFACTOR宏来区别</p><p><img src="Blog/source/_posts/Adding%20a%20new%20Shading%20Model/Untitled%204.png"></p><p><em>此处参考YivanLee大佬的文章</em></p><p>使用GBUFFER_REFACTOR宏来生成的是由C++部分生成 Encode 和 Decode 代码,负责生成代码的文件为 ShaderGenerationUtil.cpp ,可以在文件中查看</p><p><img src="Blog/source/_posts/Adding%20a%20new%20Shading%20Model/Untitled%205.png"></p><p><em>(无力吐槽UE5使用FString拼接的方式实现C++控制HLSL的方法…debug起来十分不友好)</em></p><p>ShaderGenerationUtil 文件主要负责在C++层面写入HLSL的相关逻辑。这里会实现GBuffer 的 Decode(写入) 和 Encode(读取)方法。这些后续在添加自己的GBuffer的时候都是需要进行修改的。</p><p>另一种就是在ush中直接写好的。具体可查看 DeferredShadingCommon.ush ~</p><p><img src="Blog/source/_posts/Adding%20a%20new%20Shading%20Model/Untitled%206.png"></p><p>在FShaderCompileUtilities::ApplyDerivedDefines函数中把GBUFFER_REFACTOR宏加入实现区分。</p><p>在Decode Encode过程中,有一个结构特别关键:FGBufferData。</p><p>FGbufferData主要负责存储写入读出GBuffer的相关数据,在UE的PS阶段渲染函数BasePassPixelShader.usf 中看到调用 SetGBufferForShadingModel 来将相关 Material Input的数据传递给GBuffer。FGbufferData定义可以在DeferredShadingCommon.ush中查看。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// all values that are output by the forward rendering pass</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">FGBufferData</span></span><br><span class="line">{</span><br><span class="line"><span class="comment">// normalized</span></span><br><span class="line">float3 WorldNormal;</span><br><span class="line"><span class="comment">// normalized, only valid if HAS_ANISOTROPY_MASK in SelectiveOutputMask</span></span><br><span class="line">float3 WorldTangent;</span><br><span class="line"><span class="comment">// 0..1 (derived from BaseColor, Metalness, Specular)</span></span><br><span class="line">float3 DiffuseColor;</span><br><span class="line"><span class="comment">// 0..1 (derived from BaseColor, Metalness, Specular)</span></span><br><span class="line">float3 SpecularColor;</span><br><span class="line"><span class="comment">// 0..1, white for SHADINGMODELID_SUBSURFACE_PROFILE and SHADINGMODELID_EYE (apply BaseColor after scattering is more correct and less blurry)</span></span><br><span class="line">float3 BaseColor;</span><br><span class="line"><span class="comment">// 0..1</span></span><br><span class="line"><span class="type">float</span> Metallic;</span><br><span class="line"><span class="comment">// 0..1</span></span><br><span class="line"><span class="type">float</span> Specular;</span><br><span class="line"><span class="comment">// 0..1</span></span><br><span class="line">float4 CustomData;</span><br><span class="line"><span class="comment">// AO utility value</span></span><br><span class="line"><span class="type">float</span> GenericAO;</span><br><span class="line"><span class="comment">// Indirect irradiance luma</span></span><br><span class="line"><span class="type">float</span> IndirectIrradiance;</span><br><span class="line"><span class="comment">// Static shadow factors for channels assigned by Lightmass</span></span><br><span class="line"><span class="comment">// Lights using static shadowing will pick up the appropriate channel in their deferred pass</span></span><br><span class="line">float4 PrecomputedShadowFactors;</span><br><span class="line"><span class="comment">// 0..1</span></span><br><span class="line"><span class="type">float</span> Roughness;</span><br><span class="line"><span class="comment">// -1..1, only valid if only valid if HAS_ANISOTROPY_MASK in SelectiveOutputMask</span></span><br><span class="line"><span class="type">float</span> Anisotropy;</span><br><span class="line"><span class="comment">// 0..1 ambient occlusion e.g.SSAO, wet surface mask, skylight mask, ...</span></span><br><span class="line"><span class="type">float</span> GBufferAO;</span><br><span class="line"><span class="comment">// Bit mask for occlusion of the diffuse indirect samples</span></span><br><span class="line">uint DiffuseIndirectSampleOcclusion;</span><br><span class="line"><span class="comment">// 0..255 </span></span><br><span class="line">uint ShadingModelID;</span><br><span class="line"><span class="comment">// 0..255 </span></span><br><span class="line">uint SelectiveOutputMask;</span><br><span class="line"><span class="comment">// 0..1, 2 bits, use CastContactShadow(GBuffer) or HasDynamicIndirectShadowCasterRepresentation(GBuffer) to extract</span></span><br><span class="line"><span class="type">float</span> PerObjectGBufferData;</span><br><span class="line"><span class="comment">// in world units</span></span><br><span class="line"><span class="type">float</span> CustomDepth;</span><br><span class="line"><span class="comment">// Custom depth stencil value</span></span><br><span class="line">uint CustomStencil;</span><br><span class="line"><span class="comment">// in unreal units (linear), can be used to reconstruct world position,</span></span><br><span class="line"><span class="comment">// only valid when decoding the GBuffer as the value gets reconstructed from the Z buffer</span></span><br><span class="line"><span class="type">float</span> Depth;</span><br><span class="line"><span class="comment">// Velocity for motion blur (only used when WRITES_VELOCITY_TO_GBUFFER is enabled)</span></span><br><span class="line">float4 Velocity;</span><br><span class="line"></span><br><span class="line"><span class="comment">// My Custom Depth </span></span><br><span class="line">float4 MyCustomDepth;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 0..1, only needed by SHADINGMODELID_SUBSURFACE_PROFILE and SHADINGMODELID_EYE which apply BaseColor later</span></span><br><span class="line">float3 StoredBaseColor;</span><br><span class="line"><span class="comment">// 0..1, only needed by SHADINGMODELID_SUBSURFACE_PROFILE and SHADINGMODELID_EYE which apply Specular later</span></span><br><span class="line"><span class="type">float</span> StoredSpecular;</span><br><span class="line"><span class="comment">// 0..1, only needed by SHADINGMODELID_EYE which encodes Iris Distance inside Metallic</span></span><br><span class="line"><span class="type">float</span> StoredMetallic;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>此处只是定义好Gbuffer的Encode及Decode的方式,于是笔者继续深入,找到UE对GBuffer在C++侧定义的位置(GBufferInfo.h/.cpp),该文件定义了Gbuffer相关属性:</p><ul><li>EGBufferSlot:GBuffer相关内容接口</li><li>EGBufferCompression:GBuffer相关压缩格式</li><li>EGBufferType:GBuffer输出到Texture时Texture的格式</li><li>FGBufferItem:在GBuffer中Texture的位置</li><li>FGBufferBinding:GBuffer相关绑定信息,负责绑定GBuffer相关Format及CreateFlags</li></ul><p>同时也定义了Gbuffer相关函数:</p><ul><li>FindGBufferTargetByName:通过name来查询对应的Gbuffer的RenderTarget</li><li>FindGBufferBindingByName:通过name来找到GBuffer绑定的信息,内部会调用FindGBufferTargetByName</li><li>FetchFullGBufferInfo/FetchLegacyGBufferInfo:负责绑定GBuffer的相关信息,这个函数会设置FGBufferInfo所有信息,包括初始化GBuffer的格式及各个通道的数据类型绑定情况</li><li>FetchGBufferSlots:负责设置GBufferSlots,该步骤会在将需要写入GBuffer的EGBufferSlot存放成一个TArray,方便后续绑定使用。</li></ul><p>至此GBuffer的相关定义也已完成,接下来笔者继续研究了UE中GBuffer中的组成部分。</p><h2 id="2-3-UE-中-GBuffer-的组成"><a href="#2-3-UE-中-GBuffer-的组成" class="headerlink" title="2.3 UE 中 GBuffer 的组成"></a>2.3 UE 中 GBuffer 的组成</h2><p>从前面的解析,可以了解到GBuffer的读取主要是通过Decode来进行,所以我们直接定位到延迟渲染中的EncodeGBuffer函数。该函数主要用于将 FGBufferData 中的相关数据解析后输出给 各个buffer。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// DeferredShadingCommon.ush</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/** Populates OutGBufferA, B and C */</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">EncodeGBuffer</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">FGBufferData GBuffer,</span></span></span><br><span class="line"><span class="params"><span class="function">out float4 OutGBufferA,</span></span></span><br><span class="line"><span class="params"><span class="function">out float4 OutGBufferB,</span></span></span><br><span class="line"><span class="params"><span class="function">out float4 OutGBufferC,</span></span></span><br><span class="line"><span class="params"><span class="function">out float4 OutGBufferD,</span></span></span><br><span class="line"><span class="params"><span class="function">out float4 OutGBufferE,</span></span></span><br><span class="line"><span class="params"><span class="function">out float4 OutGBufferVelocity,</span></span></span><br><span class="line"><span class="params"><span class="function"><span class="comment">/*out float4 OutMyCustomDepth,*/</span> <span class="comment">//注释的是我自己加入的GBuffer</span></span></span></span><br><span class="line"><span class="params"><span class="function"><span class="type">float</span> QuantizationBias = <span class="number">0</span><span class="comment">// -0.5 to 0.5 random float. Used to bias quantization.</span></span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (GBuffer.ShadingModelID == SHADINGMODELID_UNLIT)</span><br><span class="line">{</span><br><span class="line">OutGBufferA = <span class="number">0</span>;</span><br><span class="line"><span class="built_in">SetGBufferForUnlit</span>(OutGBufferB);</span><br><span class="line">OutGBufferC = <span class="number">0</span>;</span><br><span class="line">OutGBufferD = <span class="number">0</span>;</span><br><span class="line">OutGBufferE = <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">{</span><br><span class="line"><span class="meta">#<span class="keyword">if</span> MOBILE_DEFERRED_SHADING</span></span><br><span class="line">OutGBufferA.rg = <span class="built_in">UnitVectorToOctahedron</span>( <span class="built_in">normalize</span>(GBuffer.WorldNormal) ) * <span class="number">0.5f</span> + <span class="number">0.5f</span>;</span><br><span class="line">OutGBufferA.b = GBuffer.PrecomputedShadowFactors.x;</span><br><span class="line">OutGBufferA.a = GBuffer.PerObjectGBufferData;</span><br><span class="line"><span class="meta">#<span class="keyword">elif</span> 1</span></span><br><span class="line">OutGBufferA.rgb = <span class="built_in">EncodeNormal</span>( GBuffer.WorldNormal );</span><br><span class="line">OutGBufferA.a = GBuffer.PerObjectGBufferData;</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">float3 Normal = GBuffer.WorldNormal;</span><br><span class="line">uint NormalFace = <span class="number">0</span>;</span><br><span class="line"><span class="built_in">EncodeNormal</span>( Normal, NormalFace );</span><br><span class="line"></span><br><span class="line">OutGBufferA.rg = Normal.xy;</span><br><span class="line">OutGBufferA.b = <span class="number">0</span>;</span><br><span class="line">OutGBufferA.a = GBuffer.PerObjectGBufferData;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">OutGBufferB.r = GBuffer.Metallic;</span><br><span class="line">OutGBufferB.g = GBuffer.Specular;</span><br><span class="line">OutGBufferB.b = GBuffer.Roughness;</span><br><span class="line">OutGBufferB.a = <span class="built_in">EncodeShadingModelIdAndSelectiveOutputMask</span>(GBuffer.ShadingModelID, GBuffer.SelectiveOutputMask);</span><br><span class="line"></span><br><span class="line">OutGBufferC.rgb = <span class="built_in">EncodeBaseColor</span>( GBuffer.BaseColor );</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> GBUFFER_HAS_DIFFUSE_SAMPLE_OCCLUSION</span></span><br><span class="line">OutGBufferC.a = <span class="built_in">float</span>(GBuffer.DiffuseIndirectSampleOcclusion) * <span class="built_in">rcp</span>(<span class="number">255</span>) + (<span class="number">0.5</span> / <span class="number">255.0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">elif</span> ALLOW_STATIC_LIGHTING</span></span><br><span class="line"><span class="comment">// No space for AO. Multiply IndirectIrradiance by AO instead of storing.</span></span><br><span class="line">OutGBufferC.a = <span class="built_in">EncodeIndirectIrradiance</span>(GBuffer.IndirectIrradiance * GBuffer.GBufferAO) + QuantizationBias * (<span class="number">1.0</span> / <span class="number">255.0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">OutGBufferC.a = GBuffer.GBufferAO;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">OutGBufferD = GBuffer.CustomData;</span><br><span class="line">OutGBufferE = GBuffer.PrecomputedShadowFactors;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> WRITES_VELOCITY_TO_GBUFFER</span></span><br><span class="line">GBufferF= GBuffer.Velocity;</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">OutGBufferVelocity = <span class="number">0</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="comment">/*OutMyCustomDepth = GBuffer.MyCustomDepth;*/</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>该函数会在FPixelShaderInOut_MainPS中进行最后调用,将FGbufferData中的数据输出到GBuffer中。</p><p>通过分析可以看到UE5中对GBuffer做了一下分配:</p><ul><li>手机端延迟渲染管线</li></ul><table><thead><tr><th></th><th>R</th><th>G</th><th>B</th><th>A</th></tr></thead><tbody><tr><td>GBufferA</td><td>Normal</td><td>Normal</td><td>PrecomputedShadowFactors.x</td><td>PerObjectGBufferData</td></tr><tr><td>GBufferB</td><td>Metallic</td><td>Specular</td><td>Roughness</td><td>ShadingModelID+SelectiveOutputMask(各占4bit,Shading Mode最大值16)</td></tr><tr><td>GBufferC</td><td>BaseColor</td><td>BaseColor</td><td>BaseColor</td><td>见注释</td></tr><tr><td>GBufferD</td><td>CustomData</td><td>CustomData</td><td>CustomData</td><td>CustomData</td></tr><tr><td>OutGBufferE</td><td>PrecomputedShadowFactors</td><td>PrecomputedShadowFactors</td><td>PrecomputedShadowFactors</td><td>PrecomputedShadowFactors</td></tr><tr><td>GBufferF</td><td>Velocity</td><td>Velocity</td><td>Velocity</td><td>各向异性强度</td></tr></tbody></table><ul><li>延迟渲染管线</li></ul><table><thead><tr><th></th><th>R</th><th>G</th><th>B</th><th>A</th></tr></thead><tbody><tr><td>GBufferA</td><td>Normal</td><td>Normal</td><td>Normal</td><td>PerObjectGBufferData</td></tr><tr><td>GBufferB</td><td>Metallic</td><td>Specular</td><td>Roughness</td><td>ShadingModelID+SelectiveOutputMask(各占4bit,Shading Mode最大值16)</td></tr><tr><td>GBufferC</td><td>BaseColor</td><td>BaseColor</td><td>BaseColor</td><td>见注释</td></tr><tr><td>GBufferD</td><td>CustomData</td><td>CustomData</td><td>CustomData</td><td>CustomData</td></tr><tr><td>OutGBufferE</td><td>PrecomputedShadowFactors</td><td>PrecomputedShadowFactors</td><td>PrecomputedShadowFactors</td><td>PrecomputedShadowFactors</td></tr><tr><td>GBufferF</td><td>Velocity</td><td>Velocity</td><td>Velocity</td><td>各向异性强度</td></tr></tbody></table><ul><li>在GBufferA禁用的分支里可以Encode法线到RG,如果这样做了B可以空出一个10bit,但移动没法使用。</li><li>GBufferC的Alpha通道在有静态光照时候储存随机抖动过的IndirectIrradiance*Material AO,否则直接储存Material AO。</li></ul><h2 id="2-4-UE中GBuffer光照相关计算"><a href="#2-4-UE中GBuffer光照相关计算" class="headerlink" title="2.4 UE中GBuffer光照相关计算"></a>2.4 UE中GBuffer光照相关计算</h2><h3 id="2-4-1-延迟渲染管线"><a href="#2-4-1-延迟渲染管线" class="headerlink" title="2.4.1 延迟渲染管线"></a>2.4.1 延迟渲染管线</h3><p>在延迟管线BasePass 的 GBuffer 数据填充后,会在 DiffuseIndirectAndAO 阶段中将间接光计算的漫反射 和 镜面反射 叠加到 GBuffer 中的BaseColor部分。(Note:不同方案会进行不同处理)</p><p>DiffuseIndirectAndAO 中包含了Lumen 相关 Radiosity 计算(感兴趣的朋友可以阅读 丛越dalao 文章),在完成间接光照计算后,GBuffer中的 BaseColor 将GI 计算结果 和 AO 叠加到 GBuffer 的 BaseColor 的过程叫 DiffuseIndirectComposite 。</p><p>DiffuseIndirectComposite 会将前面生成的DiffuseIndirect、RoughSpecularIndirect、SpecularIndirect 等与场景GBuffer 组合生成最终场景颜色 (同时包括bentNormal)。具体实现代码可以在IndirectLightRendering.cpp 中查看。组合过程可以在DiffuseIndirectComposite.usf中查看。</p><p>首先我们可以看下IndirectLightRendering.cpp 中,在该文件中会通过DIM_APPLY_DIFFUSE_INDIRECT宏来区分不同间接光方案。不同间接光方案在后续计算间接光对BaseColor的方式会有所不同。</p><p><img src="Untitled%207.png"></p><p>后续在DiffuseIndirectComposite.usf 中 </p><p><strong>在Lumen的间接光环境下,UE默认材质不会计算间接光的高光遮蔽计算(BentNormal除外) ,只会将漫反射间接光的遮蔽信息相乘到BaseColor上。</strong></p><p><img src="Untitled%208.png"></p><p>其他间接光模式后续再做进一步补充。</p><p>在后处理中 SSGI 也会调用到 GBuffer 进行渲染。主要是对AO进行相关处理</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// DiffuseIndirectComposite.usf</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> DIM_APPLY_DIFFUSE_INDIRECT</span></span><br><span class="line"> {</span><br><span class="line"> float3 DiffuseColor = GBuffer.DiffuseColor;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">UseSubsurfaceProfile</span>(GBuffer.ShadingModelID))</span><br><span class="line"> {</span><br><span class="line"> DiffuseColor = GBuffer.StoredBaseColor;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> OutColor.rgb += DiffuseColor * DiffuseIndirectTexture.<span class="built_in">SampleLevel</span>(DiffuseIndirectSampler, BufferUV, <span class="number">0</span>).rgb;</span><br><span class="line"> }</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 应用AO到场景颜色. 因为在延迟直接照明之前,假设SceneColor中的所有照明都是间接照明.</span></span><br><span class="line"> {</span><br><span class="line"> <span class="type">float</span> AOMask = (GBuffer.ShadingModelID != SHADINGMODELID_UNLIT);</span><br><span class="line"> OutColor.a = <span class="built_in">lerp</span>(<span class="number">1.0f</span>, FinalAmbientOcclusion, AOMask * AmbientOcclusionStaticFraction);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h3 id="2-4-2-移动延迟渲染管线"><a href="#2-4-2-移动延迟渲染管线" class="headerlink" title="2.4.2 移动延迟渲染管线"></a>2.4.2 移动延迟渲染管线</h3><p>开启方法 : Mobile默认还是会使用forward shading。也可以手动设置,使得mobile使用pc renderer(“平台”->”项目设置”。默认为deferred,也可以使用forward)。但总的来说在mobile使用pc renderer不合适。</p><p>所以,引入了新的针对mobile GPU优化过的deferred shading。启用它的方法是在DefaultEngine.ini中添加r.Mobile.ShadingPath=1。</p><p>Mobile Deferred Shading 通过断点可一查看到在 MobileShadingRenderer.cpp 中 调用。</p><p><strong>在移动延迟渲染管线MobileBasePass的GBuffer数据填充后,不存在DiffuseIndirectAndAO的叠加过程,而是通过与间接光编码存在GBufferC.a 中 ,后续用于Diffuse IBL 的遮蔽计算,Specular没有进行遮蔽计算。</strong></p><p><img src="Untitled%209.png"></p><p>在MobileDeferredShading.usf 中设置读取GBufferAO进行叠加</p><p><img src="Untitled%2010.png"></p><p><img src="Untitled%2011.png"></p><h1 id="3-实践:如何在UE5中添加自己的GBuffer"><a href="#3-实践:如何在UE5中添加自己的GBuffer" class="headerlink" title="3. 实践:如何在UE5中添加自己的GBuffer"></a>3. 实践:如何在UE5中添加自己的GBuffer</h1><p>介绍完GBuffer相关细节后,笔者开始尝试添加自己的GBuffer,这里感谢下 yivanlee dalao文章做的指导orz~</p><p>本人将基于上一篇文章的思路进行修改~</p><h2 id="3-1-声明GBuffer-Slot"><a href="#3-1-声明GBuffer-Slot" class="headerlink" title="3.1 声明GBuffer Slot"></a>3.1 声明GBuffer Slot</h2><p>GBufferInfo 用于声明设置 GBuffer 相关信息,包括GBuffer的名字、格式、相关可读性</p><p>首先我们需要在GBufferInfo文件中声明相关GBuffer声明。</p><p>在EGBufferSlot中添加写入GBuffer的类型,在FetchGBufferSlots函数中的EGBufferSlot数组中添加对应类型</p><p><img src="Untitled%2012.png"></p><p>并在 FetchLegacyGBufferInfo 函数中新增新GBuffer的Target及在Slot的绑定信息</p><p><img src="Untitled%2013.png"></p><p>Note需要在NumTargets中新增1(因为增加了自己的Buffer)</p><p><img src="Untitled%2014.png"></p><p><img src="Untitled%2015.png"></p><p><img src="Untitled%2016.png"></p><p>进入到 GBufferInfo.h 的头文件中,修改FGBufferInfo的MaxTargets</p><p><img src="Untitled%2017.png"></p><h2 id="3-2-添加FGBufferData"><a href="#3-2-添加FGBufferData" class="headerlink" title="3.2 添加FGBufferData"></a>3.2 添加FGBufferData</h2><p>找到 DeferredShadingCommon.ush ,并在 struct FGBufferData 中添加新的数据格式</p><p><img src="Untitled%2018.png"></p><h2 id="3-3-创建对应RT并绑定"><a href="#3-3-创建对应RT并绑定" class="headerlink" title="3.3 创建对应RT并绑定"></a>3.3 创建对应RT并绑定</h2><p>首先定位到SceneTextures.h文件,并在FSceneTexturesConfig结构定义FGBufferBinding 的地方添加自己的 GBuffer Binding</p><p><img src="Untitled%2019.png"></p><p>同时在FSceneTextures结构中添加相应RT的RDGTextureRef</p><p><img src="Untitled%2020.png"></p><p>接下来来到 SceneTextures.cpp 中,在FSceneTexturesConfig::Create函数中将Config中的Buffer与名字进行绑定:</p><p><img src="Untitled%2021.png"></p><p>然后在FSceneTextures::Create函数中添加逻辑判断Config是否使用到相应GBuffer,并创建相应的RT,Note:这里可以设置RT的相关格式~</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (Config.MyCustomDepth.Index >= <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line"><span class="function"><span class="type">const</span> FRDGTextureDesc <span class="title">Desc</span><span class="params">(FRDGTextureDesc::Create2D(Config.Extent, Config.MyCustomDepth.Format, FClearValueBinding({ <span class="number">0.0f</span>, <span class="number">0.0f</span>, <span class="number">0.0f</span>, <span class="number">0.0f</span> }), Config.MyCustomDepth.Flags | FlagsToAdd | GFastVRamConfig.MyCustomDepth))</span></span>;</span><br><span class="line">SceneTextures.MyCustomDepthTexture = GraphBuilder.<span class="built_in">CreateTexture</span>(Desc, <span class="built_in">TEXT</span>(<span class="string">"MyCustomDepth"</span>));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>同时在FSceneTextures::GetGBufferRenderTargets函数中添加相关GBuffer</p><p><img src="Untitled%2022.png"></p><p>同时在同文件中的SetupSceneTextureUniformParameters函数中修改将RT与GBuffer进行绑定。这里需要调用EnumHasAnyFlags函数进行判断是否需要进行复制。</p><p><img src="Untitled%2023.png"></p><p>(同理移动端在下方的SetupMobileSceneTextureUniformParameters函数也可进行设置,由于笔者实现的PC的逻辑,移动端可自行拓展)</p><h2 id="3-4-Encode-And-Decode"><a href="#3-4-Encode-And-Decode" class="headerlink" title="3.4 Encode And Decode"></a>3.4 Encode And Decode</h2><p>接下来修改GBuffer的Encode和Decode机制,打开ShaderGenerationUtil.cpp,首页需要在GetSlotTextName函数中创建对应的接口名称:</p><p><img src="Untitled%2024.png"></p><p>然后在 SetSharedGBufferSlots 函数中将对应的Slots解析接口打开(这里打开的话是默认全部打开,也可以在SetSlotsForShadingModelType 中对单独材质类型进行打开)</p><p><img src="Untitled%2025.png"></p><p>然后在 SetStandardGBufferSlots 中 将对应接口在不同条件下判断是否开启进行添加。</p><p><img src="Untitled%2026.png"></p><p>该函数主要会用在DetermineUsedMaterialSlots函数,DetermineUsedMaterialSlots会设置不同shadingmodel所需要的GBuffer情况及是否使用相关CustomData。不了解ShadingModel的朋友可以查看我上一篇文章。这里我们需要为我们上一篇自定义的Shadingmodel添加相关GBuffer设置</p><p><img src="Untitled%2027.png"></p><h2 id="3-5-写入GBuffer"><a href="#3-5-写入GBuffer" class="headerlink" title="3.5 写入GBuffer"></a>3.5 写入GBuffer</h2><p>写入GBuffer部分需要到Shader中进行设置。首先我们打开 DeferredShadingCommon.ush ,并在相应的encode区域添加 新增 GBuffer的绑定。</p><p>Note:需要通过#ifndef MOBILE_DEFERRED_SHADING 的方式来绕开 MOBILE_DEFERRED_SHADING</p><p><img src="Untitled%2028.png"></p><p>然后在默认的Decode函数中添加相关Decode</p><p><img src="Untitled%2029.png"></p><p>GetGBufferDataUint 函数中添加采样</p><p><img src="Untitled%2030.png"></p><p>GetGBufferDataFromSceneTextures 函数中添加采样</p><p><img src="Untitled%2031.png"></p><p>GetGBufferData 函数中添加采样</p><p><img src="Untitled%2032.png"></p><p>接下来打开 BasePassPixelShader.usf ,在FPixelShaderInOut_MainPS中添加HLSL中默认的绑定。</p><p><img src="Untitled%2033.png"></p><p>最后在ShaderGenerationUtil.cpp 文件的 SetSlotsForShadingModelType中添加默认GBufferSlots(Note:可见3.4节笔者的注释) </p><p><img src="Untitled%2034.png"></p><h2 id="3-3-调试GBuffer"><a href="#3-3-调试GBuffer" class="headerlink" title="3.3 调试GBuffer"></a>3.3 调试GBuffer</h2><p>编译后进行调试,通过打断点检查OutputData的方式查看相关Encode及Decode函数的正确性</p><p>最后结果为:</p><p><img src="Untitled%2035.jpg"></p><h1 id="4-总结"><a href="#4-总结" class="headerlink" title="4. 总结"></a>4. 总结</h1><p>在UE中新增GBuffer的步骤有些过于复杂,同时UE使用C++控制HLSL的过程在Debug过程中比较有难度。同时新增的GBuffer也会增加相关带宽消耗(移动端几乎可以放弃)。</p><p>不过新增GBuffer可以将BasePass阶段相关数据存储为RT后传递给后续流程进行计算。写下这边笔记记录下过程,希望对大家有帮助~</p><h1 id="5-参考"><a href="#5-参考" class="headerlink" title="5. 参考"></a>5. 参考</h1><p><a href="https://zhuanlan.zhihu.com/p/562673914"></a></p><p><a href="https://zhuanlan.zhihu.com/p/521681785">虚幻五渲染编程(Graphic篇)【第六卷: Customize GBuffer of UnrealEngine5】</a></p><p><a href="https://www.cnblogs.com/timlly/p/15092257.html">https://www.cnblogs.com/timlly/p/15092257.html</a></p>]]></content>
<summary type="html">本文主要记录了UE5如何通过修改源代码添加扩充引擎GBuffer,同时对相关GBuffer进行细分学习过程。</summary>
<category term="UnrealEngine" scheme="http://example.com/categories/UnrealEngine/"/>
<category term="UE" scheme="http://example.com/tags/UE/"/>
<category term="Shading" scheme="http://example.com/tags/Shading/"/>
</entry>
<entry>
<title>Adding a new Shading Model in Unreal5</title>
<link href="http://example.com/2022/08/14/Adding%20a%20new%20Shading%20Model/"/>
<id>http://example.com/2022/08/14/Adding%20a%20new%20Shading%20Model/</id>
<published>2022-08-14T02:57:00.000Z</published>
<updated>2023-06-01T16:26:21.098Z</updated>
<content type="html"><![CDATA[<h1 id="Adding-a-new-Shading-Model"><a href="#Adding-a-new-Shading-Model" class="headerlink" title="Adding a new Shading Model"></a>Adding a new Shading Model</h1><p>本文主要记录了UE5如何通过修改源代码添加自己的shadingmodel~本文会先梳理下添加ShadingModel所需文件及其相关用途再进行相关介绍,了解过的朋友可直接跳转第二章开始。</p><h1 id="1-文件梳理"><a href="#1-文件梳理" class="headerlink" title="1.文件梳理"></a>1.文件梳理</h1><ol><li>EngineTypes.h : 存储引擎中类型集合,在这里需要添加ShaderingModel</li><li>MaterialShader.cpp : 负责材质相关属性声明</li><li>HLSLMaterialTranslator.cpp : 负责控制材质C++侧到hlsl侧的数据传递</li><li>ShaderMaterial.h : 声明材质相关定义</li><li>Material.cpp :Material 相关设置</li><li>ShaderMaterialDerivedHelpers.cpp :声明处理相关材质parameter</li><li>ShadingCommon.ush : Shading Common 数据 包括共用的ShadingModel 和 相关函数</li><li>BasePassPixelShader.usf :Base Pass Pixel Shader 相关</li><li>ShadingModelsMaterial.ush : 对GBuffer相关设置</li><li>BasePassCommon.ush :BasePass相关共用数据声明</li><li>DeferredShadingCommon.ush :声明GBuffer数据及相关DeferredRendering数据函数</li><li>Definitions.usf : 定义各种材质的宏</li><li>ClusteredDeferredShadingPixelShader.usf :Defer Rendering Pixel Shader相关光照处理 </li><li>ShadingModels.ush : 负责处理不同ShadingModel分类</li></ol><h1 id="2-新增-MaterialShadingModel"><a href="#2-新增-MaterialShadingModel" class="headerlink" title="2.新增 MaterialShadingModel"></a>2.新增 MaterialShadingModel</h1><p>在 <em>EngineTypes.h</em> 中新增自己的ShadingModel类型</p><p><img src="Blog/source/_posts/Adding%20a%20new%20Shading%20Model/Untitled.png"></p><p>添加后编译即可在ShadingModel中找到自己的ShadingModel</p><p><strong>Note : 这里需要在MSM_NUM 之前进行添加,否则会报check(InShadingModel < MSM_NUM)检查导致没法Hack</strong></p><p>然后在 <em>MaterialShader.cpp</em> 文件中GetShadingModelString中添加相关声明,Note:名字必须相同</p><p><img src="Blog/source/_posts/Adding%20a%20new%20Shading%20Model/Untitled%201.png"></p><p>在 <em>HLSLMaterialTranslator.cpp</em> 文件,这里需要找到FHLSLMaterialTranslator::GetMaterialEnvironment 函数并为shadingmodel添加相关defined</p><p><img src="Blog/source/_posts/Adding%20a%20new%20Shading%20Model/Untitled%202.png"></p><p>在 <em>ShaderMaterial.h</em> 中 FShaderMaterialPropertyDefines 声明映射:</p><p><img src="Blog/source/_posts/Adding%20a%20new%20Shading%20Model/Untitled%203.png"></p><h1 id="3-开启CustomData-接口"><a href="#3-开启CustomData-接口" class="headerlink" title="3.开启CustomData 接口"></a>3.开启CustomData 接口</h1><p>在 Material.cpp 中找到IsPropertyActive_Internal函数,在switch中找到相关接口,并输入自己新shadingModel。</p><p><img src="Blog/source/_posts/Adding%20a%20new%20Shading%20Model/Untitled%204.png"></p><p>这一步是为了激活接口在不同ShadingModel中的使用情况。</p><h2 id="2-1-设置CustomData接口名字"><a href="#2-1-设置CustomData接口名字" class="headerlink" title="2.1 设置CustomData接口名字"></a>2.1 设置CustomData接口名字</h2><p>打开 <em>MaterialShared.cpp</em> ,在GetAttributeOverrideForMaterial函数中case MP_CustomData0中添加CustomPinNames。该函数控制在Editor中切换不同模式下Pin接口的可用情况。 这里Add第一个是ShadingModel , 第二个是接口展示的名字。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">case</span> MP_CustomData0:</span><br><span class="line">CustomPinNames.<span class="built_in">Add</span>({ MSM_ClearCoat, <span class="string">"Clear Coat"</span> });</span><br><span class="line">CustomPinNames.<span class="built_in">Add</span>({MSM_Hair, <span class="string">"Backlit"</span>});</span><br><span class="line">CustomPinNames.<span class="built_in">Add</span>({MSM_Cloth, <span class="string">"Cloth"</span>});</span><br><span class="line">CustomPinNames.<span class="built_in">Add</span>({MSM_Eye, <span class="string">"Iris Mask"</span>});</span><br><span class="line">CustomPinNames.<span class="built_in">Add</span>({MSM_SubsurfaceProfile, <span class="string">"Curvature"</span> });</span><br><span class="line">CustomPinNames.<span class="built_in">Add</span>({MSM_StylizedShadow, <span class="string">"MyStylizedShadow"</span> });</span><br><span class="line"><span class="keyword">return</span> FText::<span class="built_in">FromString</span>(<span class="built_in">GetPinNameFromShadingModelField</span>(Material-><span class="built_in">GetShadingModels</span>(), CustomPinNames, <span class="string">"Custom Data 0"</span>));</span><br></pre></td></tr></table></figure><p>在 <em>ShaderMaterialDerivedHelpers.cpp</em> 文件中修改Custom Node 相关映射。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Only some shader models actually need custom data.</span></span><br><span class="line">Dst.WRITES_CUSTOMDATA_TO_GBUFFER = (Dst.USES_GBUFFER && (Mat.MATERIAL_SHADINGMODEL_SUBSURFACE || Mat.MATERIAL_SHADINGMODEL_PREINTEGRATED_SKIN || Mat.MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE || Mat.MATERIAL_SHADINGMODEL_CLEAR_COAT || Mat.MATERIAL_SHADINGMODEL_TWOSIDED_FOLIAGE || Mat.MATERIAL_SHADINGMODEL_HAIR || Mat.MATERIAL_SHADINGMODEL_CLOTH || Mat.MATERIAL_SHADINGMODEL_EYE || Mat.MATERIAL_SHADINGMODEL_STYLIZED_SHAODW));</span><br></pre></td></tr></table></figure><h1 id="3-设置-GBuffer-Shading-Model-ID"><a href="#3-设置-GBuffer-Shading-Model-ID" class="headerlink" title="3.设置 GBuffer Shading Model ID"></a>3.设置 GBuffer Shading Model ID</h1><p>打开 <em>ShadingCommone.ush</em> 文件在Shading Model的定义中添加自己的定义</p><p><img src="Blog/source/_posts/Adding%20a%20new%20Shading%20Model/Untitled%205.png"></p><p>然后在GetShadingModelColor(uint ShadingModelID) 中修改 ShadingModel ID的颜色</p><p><img src="Blog/source/_posts/Adding%20a%20new%20Shading%20Model/Untitled%206.png"></p><p><img src="Untitled%207.png"></p><p>然后在 BasePassPixelShader.usf 中找到GetMaterialShadingModel 并进行声明 ShadingModel</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">if</span> MATERIAL_SHADINGMODEL_STYLIZED_SHADOW</span></span><br><span class="line">uint ShadingModel = SHADINGMODELID_STYLIZED_SHADOW;</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">uint ShadingModel = <span class="built_in">GetMaterialShadingModel</span>(PixelMaterialInputs);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><h2 id="3-1将CustomData数据写入GBuffer"><a href="#3-1将CustomData数据写入GBuffer" class="headerlink" title="3.1将CustomData数据写入GBuffer"></a>3.1将CustomData数据写入GBuffer</h2><p>接下来打开 <em>ShadingModelsMaterial.ush</em> 。查看<code>SetGBufferForShadingModel</code>函数。该函数控制不同shadingModel写入FGbufferData的数据。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">if</span> MATERIAL_SHADINGMODEL_STYLIZED_SHADOW</span></span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (ShadingModel == SHADINGMODEL_STYLIZED_SHADOW)</span><br><span class="line">{</span><br><span class="line">GBuffer.ShadingModelID = SHADINGMODELID_STYLIZED_SHADOW;</span><br><span class="line">GBuffer.CustomData.x = <span class="built_in">GetMaterialCustomData0</span>(MaterialParameters);</span><br><span class="line">}</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>这里使用GetMaterialCustomData0来获取CustomData0的输入,并将其存放到GBuffer.CustomData中</p><p>然后在 <em>BasePassCommon.ush</em> 中的#define WRITES_CUSTOMDATA_TO_GBUFFER 添加 <code>MATERIAL_SHADINGMODEL_STYLIZED_SHADOW</code>。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#define WRITES_CUSTOMDATA_TO_GBUFFER (USES_GBUFFER && (MATERIAL_SHADINGMODEL_SUBSURFACE || MATERIAL_SHADINGMODEL_PREINTEGRATED_SKIN || MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE || MATERIAL_SHADINGMODEL_CLEAR_COAT || MATERIAL_SHADINGMODEL_TWOSIDED_FOLIAGE || MATERIAL_SHADINGMODEL_HAIR || MATERIAL_SHADINGMODEL_CLOTH || MATERIAL_SHADINGMODEL_EYE || SHADINGMODELID_STYLIZED_SHADOW))</span></span><br></pre></td></tr></table></figure><h1 id="4-修改-CustomGBufferData-数据"><a href="#4-修改-CustomGBufferData-数据" class="headerlink" title="4.修改 CustomGBufferData 数据"></a>4.修改 CustomGBufferData 数据</h1><p><strong>DeferredShadingCommon.ush</strong> 修改<strong>HasCustomGBufferData</strong>函数,支持<strong>SHADINGMODELID_STYLIZED_SHADOW</strong>写入</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">HasCustomGBufferData</span><span class="params">(<span class="type">int</span> ShadingModelID)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">return</span> ShadingModelID == SHADINGMODELID_SUBSURFACE</span><br><span class="line"> || ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN</span><br><span class="line"> || ShadingModelID == SHADINGMODELID_CLEAR_COAT</span><br><span class="line"> || ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE</span><br><span class="line"> || ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE</span><br><span class="line"> || ShadingModelID == SHADINGMODELID_HAIR</span><br><span class="line"> || ShadingModelID == SHADINGMODELID_CLOTH</span><br><span class="line"> || ShadingModelID == SHADINGMODELID_EYE</span><br><span class="line"> || ShadingModelID == SHADINGMODELID_STYLIZED_SHADOW;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="5-其他声明"><a href="#5-其他声明" class="headerlink" title="5.其他声明"></a>5.其他声明</h1><p>打开 ShaderGenerationUtil.cpp 文件,找到ApplyFetchEnvironment 并在其中声明 Shader Compile </p><p><img src="Untitled%208.png"></p><p>同时到 DetermineUsedMaterialSlots 函数中添加对象slot</p><p><img src="Untitled%209.png"></p><p>然后在 Definitions.usf 中声明</p><p><img src="Untitled%2010.png"></p><h1 id="6-光照修改"><a href="#6-光照修改" class="headerlink" title="6.光照修改"></a>6.光照修改</h1><p>从这里开始计算光照部分的修改:</p><h2 id="6-1获取光源数据"><a href="#6-1获取光源数据" class="headerlink" title="6.1获取光源数据"></a>6.1获取光源数据</h2><p>在 <code>Engine\Shaders\Private\ClusteredDeferredShadingPixelShader.usf</code> 的 <code>ClusteredShadingPixelShader</code> 函数中添加我们的着色模型。</p><p><img src="Untitled%2011.png"></p><h2 id="6-2着色计算"><a href="#6-2着色计算" class="headerlink" title="6.2着色计算"></a>6.2着色计算</h2><p>打开 ShadingModels.ush 在其中的IntegrateBxDF 函数中添加相关着色类型:</p><p><img src="Untitled%2012.png"></p><p>接下来是StylizedShadowBxDF 相关渲染代码,这里直接参考dalao文章</p><ul><li>code <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">FDirectLighting <span class="title">StylizedShadowBxDF</span><span class="params">(FGBufferData GBuffer, half3 N, half3 V, half3 L, <span class="type">float</span> Falloff, <span class="type">float</span> NoL, FAreaLight AreaLight, FShadowTerms Shadow)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="meta">#<span class="keyword">if</span> GBUFFER_HAS_TANGENT</span></span><br><span class="line">half3 X = GBuffer.WorldTangent;</span><br><span class="line">half3 Y = <span class="built_in">normalize</span>(<span class="built_in">cross</span>(N, X));</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">half3 X = <span class="number">0</span>;</span><br><span class="line">half3 Y = <span class="number">0</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">BxDFContext Context;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Init</span>(Context, N, X, Y, V, L);</span><br><span class="line"></span><br><span class="line"><span class="built_in">SphereMaxNoH</span>(Context, AreaLight.SphereSinAlpha, <span class="literal">true</span>);</span><br><span class="line">Context.NoV = <span class="built_in">saturate</span>(<span class="built_in">abs</span>(Context.NoV) + <span class="number">0.00001</span>);</span><br><span class="line"></span><br><span class="line"><span class="type">float</span> SpecularOffset = <span class="number">0.5</span>;</span><br><span class="line"><span class="type">float</span> SpecularRange = GBuffer.CustomData.x;</span><br><span class="line"></span><br><span class="line">float3 ShadowColor = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">ShadowColor = GBuffer.DiffuseColor * ShadowColor;</span><br><span class="line"><span class="type">float</span> offset = GBuffer.CustomData.y;</span><br><span class="line"><span class="type">float</span> SoftScatterStrength = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">offset = offset * <span class="number">2</span> - <span class="number">1</span>;</span><br><span class="line">half3 H = <span class="built_in">normalize</span>(V + L);</span><br><span class="line"><span class="type">float</span> NoH = <span class="built_in">saturate</span>(<span class="built_in">dot</span>(N, H));</span><br><span class="line">NoL = (<span class="built_in">dot</span>(N, L) + <span class="number">1</span>) / <span class="number">2</span>; <span class="comment">// overwrite NoL To Get more range out Of it</span></span><br><span class="line">half NoLOffset = <span class="built_in">saturate</span>(NoL + offset);</span><br><span class="line"></span><br><span class="line">FDirectLighting Lighting;</span><br><span class="line">Lighting.Diffuse = AreaLight.FalloffColor * (<span class="built_in">smoothstep</span>(<span class="number">0</span>, <span class="number">1</span>, NoLOffset) * Falloff) * <span class="built_in">Diffuse_Lambert</span>(GBuffer.DiffuseColor) * <span class="number">2.2</span>;</span><br><span class="line"><span class="type">float</span> InScatter = <span class="built_in">pow</span>(<span class="built_in">saturate</span>(<span class="built_in">dot</span>(L, -V)), <span class="number">12</span>) * <span class="built_in">lerp</span>(<span class="number">3</span>, <span class="number">0.1F</span>, <span class="number">1</span>);</span><br><span class="line"><span class="type">float</span> NormalContribution = <span class="built_in">saturate</span>(<span class="built_in">dot</span>(N, H));</span><br><span class="line"><span class="type">float</span> BackScatter = GBuffer.GBufferAO * NormalContribution / (PI * <span class="number">2</span>);</span><br><span class="line">Lighting.Specular = <span class="built_in">ToonStep</span>(SpecularRange, (<span class="built_in">saturate</span>(<span class="built_in">D_GGX</span>(SpecularOffset, NoH)))) * (AreaLight.FalloffColor * GBuffer.SpecularColor * Falloff * <span class="number">8</span>);</span><br><span class="line">float3 TransmissionSoft = AreaLight.FalloffColor * (Falloff * <span class="built_in">lerp</span>(BackScatter, <span class="number">1</span>, InScatter)) * ShadowColor * SoftScatterStrength;</span><br><span class="line">float3 ShadowLightener = <span class="number">0</span>;</span><br><span class="line">ShadowLightener = (<span class="built_in">saturate</span>(<span class="built_in">smoothstep</span>(<span class="number">0</span>, <span class="number">1</span>, <span class="built_in">saturate</span>(<span class="number">1</span> - NoLOffset))) * ShadowColor * <span class="number">0.1</span>);</span><br><span class="line"></span><br><span class="line">Lighting.Transmission = (ShadowLightener + TransmissionSoft) * Falloff;</span><br><span class="line"><span class="keyword">return</span> Lighting;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>打开<em>DeferredLightingCommon.ush</em> ,并找到<code>GetDynamicLighting</code>函数,在其中的 GetDynamicLightingSplit,添加相关case 进行分类</p><ul><li>code <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** Calculates lighting for a given position, normal, etc with a fully featured lighting model designed for quality. */</span></span><br><span class="line"><span class="function">FDeferredLightingSplit <span class="title">GetDynamicLightingSplit</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">float3 TranslatedWorldPosition, float3 CameraVector, FGBufferData GBuffer, <span class="type">float</span> AmbientOcclusion, uint ShadingModelID, </span></span></span><br><span class="line"><span class="params"><span class="function">FDeferredLightData LightData, float4 LightAttenuation, <span class="type">float</span> Dither, uint2 SVPos, FRectTexture SourceTexture,</span></span></span><br><span class="line"><span class="params"><span class="function">inout <span class="type">float</span> SurfaceShadow)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">FLightAccumulator LightAccumulator = (FLightAccumulator)<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">float3 V = -CameraVector;</span><br><span class="line">float3 N = GBuffer.WorldNormal;</span><br><span class="line"><span class="function">BRANCH <span class="title">if</span><span class="params">( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT && CLEAR_COAT_BOTTOM_NORMAL)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="type">const</span> float2 oct1 = ((<span class="built_in">float2</span>(GBuffer.CustomData.a, GBuffer.CustomData.z) * <span class="number">2</span>) - (<span class="number">256.0</span>/<span class="number">255.0</span>)) + <span class="built_in">UnitVectorToOctahedron</span>(GBuffer.WorldNormal);</span><br><span class="line">N = <span class="built_in">OctahedronToUnitVector</span>(oct1);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">float3 L = LightData.Direction;<span class="comment">// Already normalized</span></span><br><span class="line">float3 ToLight = L;</span><br><span class="line"></span><br><span class="line"><span class="type">float</span> LightMask = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span> (LightData.bRadialLight)</span><br><span class="line">{</span><br><span class="line">LightMask = <span class="built_in">GetLocalLightAttenuation</span>( TranslatedWorldPosition, LightData, ToLight, L );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">LightAccumulator.EstimatedCost += <span class="number">0.3f</span>;<span class="comment">// running the PixelShader at all has a cost</span></span><br><span class="line"></span><br><span class="line"><span class="function">BRANCH</span></span><br><span class="line"><span class="function"><span class="title">if</span><span class="params">( LightMask > <span class="number">0</span> )</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">FShadowTerms Shadow;</span><br><span class="line">Shadow.SurfaceShadow = AmbientOcclusion;</span><br><span class="line">Shadow.TransmissionShadow = <span class="number">1</span>;</span><br><span class="line">Shadow.TransmissionThickness = <span class="number">1</span>;</span><br><span class="line">Shadow.HairTransmittance.OpaqueVisibility = <span class="number">1</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">float</span> ContactShadowOpacity = GBuffer.CustomData.a;</span><br><span class="line"><span class="built_in">GetShadowTerms</span>(GBuffer.Depth, GBuffer.PrecomputedShadowFactors, GBuffer.ShadingModelID, ContactShadowOpacity,</span><br><span class="line">LightData, TranslatedWorldPosition, L, LightAttenuation, Dither, Shadow);</span><br><span class="line">SurfaceShadow = Shadow.SurfaceShadow;</span><br><span class="line"></span><br><span class="line">LightAccumulator.EstimatedCost += <span class="number">0.3f</span>;<span class="comment">// add the cost of getting the shadow terms</span></span><br><span class="line"></span><br><span class="line"><span class="function">BRANCH</span></span><br><span class="line"><span class="function"><span class="title">if</span><span class="params">( Shadow.SurfaceShadow + Shadow.TransmissionShadow > <span class="number">0</span> )</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="type">const</span> <span class="type">bool</span> bNeedsSeparateSubsurfaceLightAccumulation = <span class="built_in">UseSubsurfaceProfile</span>(GBuffer.ShadingModelID);</span><br><span class="line">float3 LightColor = LightData.Color;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ****************** start here ********************* //</span></span><br><span class="line"><span class="comment">// STYLIZEDSHADOW SHADING </span></span><br><span class="line">float3 Attenuation = <span class="number">1</span>;</span><br><span class="line"><span class="function">BRANCH</span></span><br><span class="line"><span class="function"><span class="title">if</span> <span class="params">(GBuffer.ShadingModelID == SHADINGMODELID_STYLIZED_SHADOW)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> </span><br><span class="line"> <span class="type">float</span> offset = GBuffer.CustomData.x;</span><br><span class="line"> <span class="type">float</span> TerminatorRange = <span class="built_in">saturate</span>(GBuffer.Roughness - <span class="number">0.5</span>);</span><br><span class="line"> </span><br><span class="line"> offset = offset * <span class="number">2</span> - <span class="number">1</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="function">BRANCH</span></span><br><span class="line"><span class="function"> <span class="title">if</span> <span class="params">(offset >= <span class="number">1</span>)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line">Attenuation = <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"><span class="type">float</span> NoL = (<span class="built_in">dot</span>(N, L) + <span class="number">1</span>) / <span class="number">2</span>;</span><br><span class="line"><span class="type">float</span> NoLOffset = <span class="built_in">saturate</span>(NoL + offset);</span><br><span class="line"><span class="type">float</span> LightAttenuationOffset = <span class="built_in">saturate</span>( Shadow.SurfaceShadow + offset);</span><br><span class="line"><span class="type">float</span> ToonSurfaceShadow = <span class="built_in">smoothstep</span>(<span class="number">0.5</span> - TerminatorRange, <span class="number">0.5</span> + TerminatorRange, LightAttenuationOffset);</span><br><span class="line"></span><br><span class="line">Attenuation = <span class="built_in">smoothstep</span>(<span class="number">0.5</span> - TerminatorRange, <span class="number">0.5</span> + TerminatorRange, NoLOffset) * ToonSurfaceShadow;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// ****************** end here ********************* //</span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> NON_DIRECTIONAL_DIRECT_LIGHTING</span></span><br><span class="line"><span class="type">float</span> Lighting;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( LightData.bRectLight )</span><br><span class="line">{</span><br><span class="line">FRect Rect = <span class="built_in">GetRect</span>( ToLight, LightData );</span><br><span class="line"></span><br><span class="line">Lighting = <span class="built_in">IntegrateLight</span>( Rect, SourceTexture);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">{</span><br><span class="line">FCapsuleLight Capsule = <span class="built_in">GetCapsule</span>( ToLight, LightData );</span><br><span class="line"></span><br><span class="line">Lighting = <span class="built_in">IntegrateLight</span>( Capsule, LightData.bInverseSquared );</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">float3 LightingDiffuse = <span class="built_in">Diffuse_Lambert</span>( GBuffer.DiffuseColor ) * Lighting;</span><br><span class="line"><span class="comment">// ****************** start here ********************* //</span></span><br><span class="line"><span class="built_in">LightAccumulator_AddSplit</span>(LightAccumulator, LightingDiffuse, <span class="number">0.0f</span>, <span class="number">0</span>, LightColor * LightMask * Shadow.SurfaceShadow * Attenuation, bNeedsSeparateSubsurfaceLightAccumulation);</span><br><span class="line"><span class="comment">// ****************** end here ********************* //</span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">FDirectLighting Lighting;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (LightData.bRectLight)</span><br><span class="line">{</span><br><span class="line">FRect Rect = <span class="built_in">GetRect</span>( ToLight, LightData );</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> REFERENCE_QUALITY</span></span><br><span class="line">Lighting = <span class="built_in">IntegrateBxDF</span>( GBuffer, N, V, Rect, Shadow, SourceTexture, SVPos );</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">Lighting = <span class="built_in">IntegrateBxDF</span>( GBuffer, N, V, Rect, Shadow, SourceTexture);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">{</span><br><span class="line">FCapsuleLight Capsule = <span class="built_in">GetCapsule</span>( ToLight, LightData );</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> REFERENCE_QUALITY</span></span><br><span class="line">Lighting = <span class="built_in">IntegrateBxDF</span>( GBuffer, N, V, Capsule, Shadow, SVPos );</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">Lighting = <span class="built_in">IntegrateBxDF</span>( GBuffer, N, V, Capsule, Shadow, LightData.bInverseSquared );</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">Lighting.Specular *= LightData.SpecularScale;</span><br><span class="line"><span class="comment">// ****************** start here ********************* //</span></span><br><span class="line"><span class="built_in">LightAccumulator_AddSplit</span>( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, LightColor * LightMask * Shadow.SurfaceShadow * Attenuation, bNeedsSeparateSubsurfaceLightAccumulation );</span><br><span class="line"></span><br><span class="line"><span class="built_in">LightAccumulator_AddSplit</span>( LightAccumulator, Lighting.Transmission, <span class="number">0.0f</span>, Lighting.Transmission, LightColor * LightMask * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation );</span><br><span class="line"><span class="comment">// ****************** end here ********************* //</span></span><br><span class="line"></span><br><span class="line">LightAccumulator.EstimatedCost += <span class="number">0.4f</span>;<span class="comment">// add the cost of the lighting computations (should sum up to 1 form one light)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="built_in">LightAccumulator_GetResultSplit</span>(LightAccumulator);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>编译后即可看到效果</p><p><img src="Untitled%2013.png"></p><h1 id="7-总结"><a href="#7-总结" class="headerlink" title="7. 总结"></a>7. 总结</h1><p>初步实现自定义ShadingModel材质渲染。后续可做进一步开发~</p><h1 id="8-参考"><a href="#8-参考" class="headerlink" title="8.参考"></a>8.参考</h1><p><a href="https://medium.com/@lordned/ue4-rendering-part-6-adding-a-new-shading-model-e2972b40d72d">Unreal Engine 4 Rendering Part 6: Adding a new Shading Model</a></p><p><a href="https://github.com/Eragon-Brisingr/ToonShader">GitHub - Eragon-Brisingr/ToonShader: cartoon plugins for unreal engine</a></p><p><a href="https://blog.csdn.net/qq_33967521/article/details/106949986">在UE4中创建新的Shading Model_「已注销」的博客-CSDN博客</a></p><p><a href="https://zhuanlan.zhihu.com/p/404857208">UE5自定义着色模型 Unreal Engine 5 custom Shading Model</a></p>]]></content>
<summary type="html">本文主要记录了UE5如何通过修改源代码添加自己的shadingmodel~</summary>
<category term="UnrealEngine" scheme="http://example.com/categories/UnrealEngine/"/>
<category term="UE" scheme="http://example.com/tags/UE/"/>
<category term="Shading" scheme="http://example.com/tags/Shading/"/>
</entry>
<entry>
<title>pycharm 配置unreal python 环境</title>
<link href="http://example.com/2022/07/07/pycharm%20%E9%85%8D%E7%BD%AEunreal%20python%20%E7%8E%AF%E5%A2%83/"/>
<id>http://example.com/2022/07/07/pycharm%20%E9%85%8D%E7%BD%AEunreal%20python%20%E7%8E%AF%E5%A2%83/</id>
<published>2022-07-07T13:40:00.000Z</published>
<updated>2023-12-28T15:12:09.681Z</updated>
<content type="html"><![CDATA[<h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><p>正好需要使用Unreal Python开发相关功能,又逢换了新电脑,写篇记录记载下环境配置过程。希望能给到您帮助~</p><h1 id="1-开启python-plugin"><a href="#1-开启python-plugin" class="headerlink" title="1.开启python plugin"></a>1.<strong><strong>开启python plugin</strong></strong></h1><p><img src="Blog/source/_posts/pycharm/Untitled.png"></p><h1 id="2-生成unreal-py文件"><a href="#2-生成unreal-py文件" class="headerlink" title="2.生成unreal.py文件"></a>2.<strong><strong>生成unreal.py文件</strong></strong></h1><p><img src="Blog/source/_posts/pycharm/Untitled1.png"></p><p>重启引擎,可以在\your_unreal_project\Intermediate\PythonStub文件夹下生成unreal.py文件</p><h1 id="3-设置pycharm编译路径"><a href="#3-设置pycharm编译路径" class="headerlink" title="3.设置pycharm编译路径"></a>3.<strong>设置pycharm编译路径</strong></h1><p><img src="Blog/source/_posts/pycharm/Untitled2.png"></p><h1 id="4-调整最大文件大小"><a href="#4-调整最大文件大小" class="headerlink" title="4.调整最大文件大小"></a>4.<strong>调整最大文件大小</strong></h1><p>由于文件过大,导致pycharm不去扫描该文件。打开 help→ Edit Custom Properties</p><p><img src="Blog/source/_posts/pycharm/Untitled3.png"></p><p>在其中输入:</p><p><code>idea.max.intellisense.filesize=500000</code></p><h1 id="5-添加UE插件位置"><a href="#5-添加UE插件位置" class="headerlink" title="5. 添加UE插件位置"></a>5. 添加UE插件位置</h1><h2 id="添加脚本位置"><a href="#添加脚本位置" class="headerlink" title="添加脚本位置"></a>添加脚本位置</h2><p>虚幻编辑器会自动添加多条路径 <code>sys.path</code> 中的列表:</p><ul><li><strong>工程目录中 Content/Python</strong> 子文件夹下的项目。</li><li>主虚幻引擎安装目录中 <strong>Content/Python</strong> 子文件夹下的项目。</li><li>每个插件启用的目录中 <strong>Content/Python</strong> 子文件夹下的项目</li><li>…</li></ul><p>可以在<strong>Edit >Project Setting>Plugins>Python</strong> 中添加一个Python文件夹<br><img src="Blog/source/_posts/pycharm/Untitled4.png"></p><p>然后在文件夹中创建main.py的文件,写入以下代码:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> unreal</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">my_function</span>():</span><br><span class="line"> unreal.log(<span class="string">'This is an Unreal log'</span>)</span><br><span class="line"> unreal.log_error(<span class="string">'This is an Unreal error'</span>)</span><br></pre></td></tr></table></figure><p>重启编辑器后,在Out outlog窗口中输入:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> main</span><br><span class="line"><span class="comment"># reload(main)</span></span><br><span class="line">main.my_function()</span><br></pre></td></tr></table></figure><p>修改后需要调用 reload进行重新加载</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> importlib <span class="keyword">import</span> *</span><br><span class="line">reload(main)</span><br><span class="line">main.my_function()</span><br></pre></td></tr></table></figure><h2 id="Execute-Python-Script"><a href="#Execute-Python-Script" class="headerlink" title="Execute Python Script"></a>Execute Python Script</h2><p><img src="Blog/source/_posts/pycharm/Untitled5.png"></p><p>直接选中脚本进行运行</p><h2 id="直接py调用"><a href="#直接py调用" class="headerlink" title="直接py调用"></a>直接py调用</h2><p><img src="Blog/source/_posts/pycharm/Untitled6.png"></p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://sondreutheim.com/post/getting_started_with_python_in_ue4">Getting started with Python in UE4</a></p><p><a href="https://forums.unrealengine.com/t/how-do-you-get-auto-completion-and-stuff/118114">How do you get auto completion and stuff?</a></p>]]></content>
<summary type="html">本文主要记录自己unreal python环境配置过程.</summary>
<category term="UnrealEngine" scheme="http://example.com/categories/UnrealEngine/"/>
<category term="Python" scheme="http://example.com/categories/UnrealEngine/Python/"/>
<category term="Python" scheme="http://example.com/tags/Python/"/>
<category term="工具" scheme="http://example.com/tags/%E5%B7%A5%E5%85%B7/"/>
</entry>
<entry>
<title>MaliCompiler</title>
<link href="http://example.com/2021/12/22/MaliCompiler/"/>
<id>http://example.com/2021/12/22/MaliCompiler/</id>
<published>2021-12-22T11:59:48.000Z</published>
<updated>2023-12-28T15:11:14.692Z</updated>
<content type="html"><![CDATA[<h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><p>在智能手机专用的SoC(处理器)领域,最常见的GPU品牌就是高通Adreno、Imagination PowerVR以及ARM Mali。对于目前市面上主流手机中华为,三星,谷歌等手机较多均使用Mali GPU架构进行研发。</p><p><img src="Blog/source/_posts/UnityPBR/Untitled.png"></p><p><em>图片来源:<a href="https://www.eet-china.com/news/202112160927.html">https://www.eet-china.com/news/202112160927.html</a></em></p><p>所以在手游研发中,TA更加关注在Mali GPU架构下材质的性能消耗。</p><p>所以本文主要记录自己使用mali compiler 检测 Unity shader的代码逻辑复杂度及mali compiler的相关指数及shader优化建议。</p><h1 id="下载arm-Developer"><a href="#下载arm-Developer" class="headerlink" title="下载arm Developer"></a>下载arm Developer</h1><p><a href="https://developer.arm.com/tools-and-software/graphics-and-gaming/mali-offline-compiler/downloads">Mali Offline Compiler | Mali Offline Compiler Legacy Downloads - Arm Developer</a></p><h1 id="在Unity中导出shader"><a href="#在Unity中导出shader" class="headerlink" title="在Unity中导出shader"></a>在Unity中导出shader</h1><p>选择shader,设置GLES3x的格式进行编译</p><p><img src="Blog/source/_posts/UnityPBR/Untitled1.png"></p><h1 id="将shader进行拆分为vert-frag"><a href="#将shader进行拆分为vert-frag" class="headerlink" title="将shader进行拆分为vert frag"></a>将shader进行拆分为vert frag</h1><p>将#ifdef VERTEX到#endif之间的代码复制出来保存到vertex,Note:#version 300 es 必须放在第一行。(不要带上def)</p><h2 id="vert"><a href="#vert" class="headerlink" title="vert"></a>vert</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#version 300 es</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> HLSLCC_ENABLE_UNIFORM_BUFFERS 1</span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> HLSLCC_ENABLE_UNIFORM_BUFFERS</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_UNIFORM</span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_UNIFORM uniform</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_SUPPORTS_UNIFORM_LOCATION 1</span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> UNITY_SUPPORTS_UNIFORM_LOCATION</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_LOCATION(x) layout(location = x)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_BINDING(x) layout(binding = x, std140)</span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_LOCATION(x)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_BINDING(x) layout(std140)</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">uniform vec3 _WorldSpaceCameraPos;</span><br><span class="line">uniform vec4 hlslcc_mtx4x4unity_ObjectToWorld[<span class="number">4</span>];</span><br><span class="line">uniform vec4 hlslcc_mtx4x4unity_WorldToObject[<span class="number">4</span>];</span><br><span class="line">uniform vec4 hlslcc_mtx4x4unity_MatrixVP[<span class="number">4</span>];</span><br><span class="line">uniform vec4 _MainTex_ST;</span><br><span class="line">uniform vec4 _DetailAlbedoMap_ST;</span><br><span class="line">uniform mediump <span class="type">float</span> _UVSec;</span><br><span class="line">in highp vec4 in_POSITION0;</span><br><span class="line">in mediump vec3 in_NORMAL0;</span><br><span class="line">in highp vec2 in_TEXCOORD0;</span><br><span class="line">in highp vec2 in_TEXCOORD1;</span><br><span class="line">out highp vec4 vs_TEXCOORD0;</span><br><span class="line">out highp vec4 vs_TEXCOORD1;</span><br><span class="line">out highp vec4 vs_TEXCOORD2;</span><br><span class="line">out highp vec4 vs_TEXCOORD3;</span><br><span class="line">out highp vec4 vs_TEXCOORD4;</span><br><span class="line">out mediump vec4 vs_TEXCOORD5;</span><br><span class="line">out highp vec4 vs_TEXCOORD7;</span><br><span class="line">out highp vec3 vs_TEXCOORD8;</span><br><span class="line">vec4 u_xlat0;</span><br><span class="line"><span class="type">bool</span> u_xlatb0;</span><br><span class="line">vec4 u_xlat1;</span><br><span class="line"><span class="type">float</span> u_xlat6;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> u_xlat0 = in_POSITION0.yyyy * hlslcc_mtx4x4unity_ObjectToWorld[<span class="number">1</span>];</span><br><span class="line"> u_xlat0 = hlslcc_mtx4x4unity_ObjectToWorld[<span class="number">0</span>] * in_POSITION0.xxxx + u_xlat0;</span><br><span class="line"> u_xlat0 = hlslcc_mtx4x4unity_ObjectToWorld[<span class="number">2</span>] * in_POSITION0.zzzz + u_xlat0;</span><br><span class="line"> u_xlat0 = u_xlat0 + hlslcc_mtx4x4unity_ObjectToWorld[<span class="number">3</span>];</span><br><span class="line"> u_xlat1 = u_xlat0.yyyy * hlslcc_mtx4x4unity_MatrixVP[<span class="number">1</span>];</span><br><span class="line"> u_xlat1 = hlslcc_mtx4x4unity_MatrixVP[<span class="number">0</span>] * u_xlat0.xxxx + u_xlat1;</span><br><span class="line"> u_xlat1 = hlslcc_mtx4x4unity_MatrixVP[<span class="number">2</span>] * u_xlat0.zzzz + u_xlat1;</span><br><span class="line"> gl_Position = hlslcc_mtx4x4unity_MatrixVP[<span class="number">3</span>] * u_xlat0.wwww + u_xlat1;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> UNITY_ADRENO_ES3</span></span><br><span class="line"> u_xlatb0 = !!(_UVSec==<span class="number">0.0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"> u_xlatb0 = _UVSec==<span class="number">0.0</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"> u_xlat0.xy = (<span class="built_in">bool</span>(u_xlatb0)) ? in_TEXCOORD0.xy : in_TEXCOORD1.xy;</span><br><span class="line"> vs_TEXCOORD0.zw = u_xlat0.xy * _DetailAlbedoMap_ST.xy + _DetailAlbedoMap_ST.zw;</span><br><span class="line"> vs_TEXCOORD0.xy = in_TEXCOORD0.xy * _MainTex_ST.xy + _MainTex_ST.zw;</span><br><span class="line"> u_xlat0.xyz = in_POSITION0.yyy * hlslcc_mtx4x4unity_ObjectToWorld[<span class="number">1</span>].xyz;</span><br><span class="line"> u_xlat0.xyz = hlslcc_mtx4x4unity_ObjectToWorld[<span class="number">0</span>].xyz * in_POSITION0.xxx + u_xlat0.xyz;</span><br><span class="line"> u_xlat0.xyz = hlslcc_mtx4x4unity_ObjectToWorld[<span class="number">2</span>].xyz * in_POSITION0.zzz + u_xlat0.xyz;</span><br><span class="line"> u_xlat0.xyz = hlslcc_mtx4x4unity_ObjectToWorld[<span class="number">3</span>].xyz * in_POSITION0.www + u_xlat0.xyz;</span><br><span class="line"> vs_TEXCOORD1.xyz = u_xlat0.xyz + (-_WorldSpaceCameraPos.xyz);</span><br><span class="line"> vs_TEXCOORD8.xyz = u_xlat0.xyz;</span><br><span class="line"> vs_TEXCOORD1.w = <span class="number">0.0</span>;</span><br><span class="line"> vs_TEXCOORD2 = <span class="built_in">vec4</span>(<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>);</span><br><span class="line"> vs_TEXCOORD3 = <span class="built_in">vec4</span>(<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>);</span><br><span class="line"> u_xlat0.x = <span class="built_in">dot</span>(in_NORMAL0.xyz, hlslcc_mtx4x4unity_WorldToObject[<span class="number">0</span>].xyz);</span><br><span class="line"> u_xlat0.y = <span class="built_in">dot</span>(in_NORMAL0.xyz, hlslcc_mtx4x4unity_WorldToObject[<span class="number">1</span>].xyz);</span><br><span class="line"> u_xlat0.z = <span class="built_in">dot</span>(in_NORMAL0.xyz, hlslcc_mtx4x4unity_WorldToObject[<span class="number">2</span>].xyz);</span><br><span class="line"> u_xlat6 = <span class="built_in">dot</span>(u_xlat0.xyz, u_xlat0.xyz);</span><br><span class="line"> u_xlat6 = <span class="built_in">inversesqrt</span>(u_xlat6);</span><br><span class="line"> vs_TEXCOORD4.xyz = <span class="built_in">vec3</span>(u_xlat6) * u_xlat0.xyz;</span><br><span class="line"> vs_TEXCOORD4.w = <span class="number">0.0</span>;</span><br><span class="line"> vs_TEXCOORD5 = <span class="built_in">vec4</span>(<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>);</span><br><span class="line"> vs_TEXCOORD7 = <span class="built_in">vec4</span>(<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="frag"><a href="#frag" class="headerlink" title="frag"></a>frag</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#version 300 es</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> GL_EXT_shader_texture_lod</span></span><br><span class="line"><span class="meta">#extension GL_EXT_shader_texture_lod : enable</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">precision highp <span class="type">float</span>;</span><br><span class="line">precision highp <span class="type">int</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">define</span> HLSLCC_ENABLE_UNIFORM_BUFFERS 1</span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> HLSLCC_ENABLE_UNIFORM_BUFFERS</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_UNIFORM</span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_UNIFORM uniform</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_SUPPORTS_UNIFORM_LOCATION 1</span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> UNITY_SUPPORTS_UNIFORM_LOCATION</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_LOCATION(x) layout(location = x)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_BINDING(x) layout(binding = x, std140)</span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_LOCATION(x)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNITY_BINDING(x) layout(std140)</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">uniform mediump vec4 _WorldSpaceLightPos0;</span><br><span class="line">uniform vec4 unity_SpecCube0_BoxMax;</span><br><span class="line">uniform vec4 unity_SpecCube0_BoxMin;</span><br><span class="line">uniform vec4 unity_SpecCube0_ProbePosition;</span><br><span class="line">uniform mediump vec4 unity_SpecCube0_HDR;</span><br><span class="line">uniform vec4 unity_SpecCube1_BoxMax;</span><br><span class="line">uniform vec4 unity_SpecCube1_BoxMin;</span><br><span class="line">uniform vec4 unity_SpecCube1_ProbePosition;</span><br><span class="line">uniform mediump vec4 unity_SpecCube1_HDR;</span><br><span class="line">uniform mediump vec4 _LightColor0;</span><br><span class="line">uniform mediump vec4 _Color;</span><br><span class="line">uniform mediump <span class="type">float</span> _Metallic;</span><br><span class="line">uniform <span class="type">float</span> _Glossiness;</span><br><span class="line">uniform mediump <span class="type">float</span> _OcclusionStrength;</span><br><span class="line"><span class="built_in">UNITY_LOCATION</span>(<span class="number">0</span>) uniform mediump sampler2D _MainTex;</span><br><span class="line"><span class="built_in">UNITY_LOCATION</span>(<span class="number">1</span>) uniform mediump sampler2D _OcclusionMap;</span><br><span class="line"><span class="built_in">UNITY_LOCATION</span>(<span class="number">2</span>) uniform mediump samplerCube unity_SpecCube0;</span><br><span class="line"><span class="built_in">UNITY_LOCATION</span>(<span class="number">3</span>) uniform mediump samplerCube unity_SpecCube1;</span><br><span class="line">in highp vec4 vs_TEXCOORD0;</span><br><span class="line">in highp vec4 vs_TEXCOORD1;</span><br><span class="line">in highp vec4 vs_TEXCOORD4;</span><br><span class="line">in highp vec3 vs_TEXCOORD8;</span><br><span class="line"><span class="built_in">layout</span>(location = <span class="number">0</span>) out mediump vec4 SV_Target0;</span><br><span class="line">vec3 u_xlat0;</span><br><span class="line">mediump vec3 u_xlat16_0;</span><br><span class="line">vec3 u_xlat1;</span><br><span class="line"><span class="type">bool</span> u_xlatb1;</span><br><span class="line">mediump vec3 u_xlat16_2;</span><br><span class="line">mediump vec3 u_xlat16_3;</span><br><span class="line">mediump vec4 u_xlat16_4;</span><br><span class="line">vec3 u_xlat5;</span><br><span class="line">mediump vec4 u_xlat16_5;</span><br><span class="line"><span class="type">bool</span> u_xlatb5;</span><br><span class="line">vec3 u_xlat6;</span><br><span class="line">vec3 u_xlat7;</span><br><span class="line">vec3 u_xlat8;</span><br><span class="line">bvec3 u_xlatb8;</span><br><span class="line">mediump vec3 u_xlat16_9;</span><br><span class="line">bvec3 u_xlatb10;</span><br><span class="line">mediump vec3 u_xlat16_11;</span><br><span class="line">mediump vec3 u_xlat16_12;</span><br><span class="line"><span class="type">float</span> u_xlat13;</span><br><span class="line"><span class="type">float</span> u_xlat14;</span><br><span class="line">vec3 u_xlat22;</span><br><span class="line">mediump vec3 u_xlat16_22;</span><br><span class="line">mediump vec3 u_xlat16_24;</span><br><span class="line"><span class="type">float</span> u_xlat26;</span><br><span class="line"><span class="type">float</span> u_xlat27;</span><br><span class="line"><span class="type">float</span> u_xlat39;</span><br><span class="line"><span class="type">float</span> u_xlat40;</span><br><span class="line">mediump <span class="type">float</span> u_xlat16_40;</span><br><span class="line">mediump <span class="type">float</span> u_xlat16_41;</span><br><span class="line">mediump <span class="type">float</span> u_xlat16_42;</span><br><span class="line">mediump <span class="type">float</span> u_xlat16_43;</span><br><span class="line"><span class="type">float</span> u_xlat44;</span><br><span class="line"><span class="type">bool</span> u_xlatb44;</span><br><span class="line">mediump <span class="type">float</span> u_xlat16_48;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> u_xlat16_0.xyz = <span class="built_in">texture</span>(_MainTex, vs_TEXCOORD0.xy).xyz;</span><br><span class="line"> u_xlat1.xyz = u_xlat16_0.xyz * _Color.xyz;</span><br><span class="line"> u_xlat16_2.xyz = _Color.xyz * u_xlat16_0.xyz + <span class="built_in">vec3</span>(<span class="number">-0.0399999991</span>, <span class="number">-0.0399999991</span>, <span class="number">-0.0399999991</span>);</span><br><span class="line"> u_xlat16_2.xyz = <span class="built_in">vec3</span>(<span class="built_in">vec3</span>(_Metallic, _Metallic, _Metallic)) * u_xlat16_2.xyz + <span class="built_in">vec3</span>(<span class="number">0.0399999991</span>, <span class="number">0.0399999991</span>, <span class="number">0.0399999991</span>);</span><br><span class="line"> u_xlat16_41 = (-_Metallic) * <span class="number">0.959999979</span> + <span class="number">0.959999979</span>;</span><br><span class="line"> u_xlat16_3.xyz = u_xlat1.xyz * <span class="built_in">vec3</span>(u_xlat16_41);</span><br><span class="line"> u_xlat0.x = <span class="built_in">dot</span>(vs_TEXCOORD4.xyz, vs_TEXCOORD4.xyz);</span><br><span class="line"> u_xlat0.x = <span class="built_in">inversesqrt</span>(u_xlat0.x);</span><br><span class="line"> u_xlat0.xyz = u_xlat0.xxx * vs_TEXCOORD4.xyz;</span><br><span class="line"> u_xlat39 = <span class="built_in">dot</span>(vs_TEXCOORD1.xyz, vs_TEXCOORD1.xyz);</span><br><span class="line"> u_xlat39 = <span class="built_in">inversesqrt</span>(u_xlat39);</span><br><span class="line"> u_xlat1.xyz = <span class="built_in">vec3</span>(u_xlat39) * vs_TEXCOORD1.xyz;</span><br><span class="line"> u_xlat16_40 = <span class="built_in">texture</span>(_OcclusionMap, vs_TEXCOORD0.xy).y;</span><br><span class="line"> u_xlat16_42 = (-_OcclusionStrength) + <span class="number">1.0</span>;</span><br><span class="line"> u_xlat16_42 = u_xlat16_40 * _OcclusionStrength + u_xlat16_42;</span><br><span class="line"> u_xlat40 = (-_Glossiness) + <span class="number">1.0</span>;</span><br><span class="line"> u_xlat16_4.x = <span class="built_in">dot</span>(u_xlat1.xyz, u_xlat0.xyz);</span><br><span class="line"> u_xlat16_4.x = u_xlat16_4.x + u_xlat16_4.x;</span><br><span class="line"> u_xlat16_4.xyz = u_xlat0.xyz * (-u_xlat16_4.xxx) + u_xlat1.xyz;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> UNITY_ADRENO_ES3</span></span><br><span class="line"> u_xlatb5 = !!(<span class="number">0.0</span><unity_SpecCube0_ProbePosition.w);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"> u_xlatb5 = <span class="number">0.0</span><unity_SpecCube0_ProbePosition.w;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"> <span class="keyword">if</span>(u_xlatb5){</span><br><span class="line"> u_xlat5.x = <span class="built_in">dot</span>(u_xlat16_4.xyz, u_xlat16_4.xyz);</span><br><span class="line"> u_xlat5.x = <span class="built_in">inversesqrt</span>(u_xlat5.x);</span><br><span class="line"> u_xlat5.xyz = u_xlat16_4.xyz * u_xlat5.xxx;</span><br><span class="line"> u_xlat6.xyz = (-vs_TEXCOORD8.xyz) + unity_SpecCube0_BoxMax.xyz;</span><br><span class="line"> u_xlat6.xyz = u_xlat6.xyz / u_xlat5.xyz;</span><br><span class="line"> u_xlat7.xyz = (-vs_TEXCOORD8.xyz) + unity_SpecCube0_BoxMin.xyz;</span><br><span class="line"> u_xlat7.xyz = u_xlat7.xyz / u_xlat5.xyz;</span><br><span class="line"> u_xlatb8.xyz = <span class="built_in">lessThan</span>(<span class="built_in">vec4</span>(<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>), u_xlat5.xyzx).xyz;</span><br><span class="line"> {</span><br><span class="line"> vec3 hlslcc_movcTemp = u_xlat6;</span><br><span class="line"> hlslcc_movcTemp.x = (u_xlatb8.x) ? u_xlat6.x : u_xlat7.x;</span><br><span class="line"> hlslcc_movcTemp.y = (u_xlatb8.y) ? u_xlat6.y : u_xlat7.y;</span><br><span class="line"> hlslcc_movcTemp.z = (u_xlatb8.z) ? u_xlat6.z : u_xlat7.z;</span><br><span class="line"> u_xlat6 = hlslcc_movcTemp;</span><br><span class="line"> }</span><br><span class="line"> u_xlat44 = <span class="built_in">min</span>(u_xlat6.y, u_xlat6.x);</span><br><span class="line"> u_xlat44 = <span class="built_in">min</span>(u_xlat6.z, u_xlat44);</span><br><span class="line"> u_xlat6.xyz = vs_TEXCOORD8.xyz + (-unity_SpecCube0_ProbePosition.xyz);</span><br><span class="line"> u_xlat5.xyz = u_xlat5.xyz * <span class="built_in">vec3</span>(u_xlat44) + u_xlat6.xyz;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> u_xlat5.xyz = u_xlat16_4.xyz;</span><br><span class="line"> }</span><br><span class="line"> u_xlat16_43 = (-u_xlat40) * <span class="number">0.699999988</span> + <span class="number">1.70000005</span>;</span><br><span class="line"> u_xlat16_43 = u_xlat40 * u_xlat16_43;</span><br><span class="line"> u_xlat16_43 = u_xlat16_43 * <span class="number">6.0</span>;</span><br><span class="line"> u_xlat16_5 = <span class="built_in">textureLod</span>(unity_SpecCube0, u_xlat5.xyz, u_xlat16_43);</span><br><span class="line"> u_xlat16_9.x = u_xlat16_5.w + <span class="number">-1.0</span>;</span><br><span class="line"> u_xlat16_9.x = unity_SpecCube0_HDR.w * u_xlat16_9.x + <span class="number">1.0</span>;</span><br><span class="line"> u_xlat16_9.x = <span class="built_in">log2</span>(u_xlat16_9.x);</span><br><span class="line"> u_xlat16_9.x = u_xlat16_9.x * unity_SpecCube0_HDR.y;</span><br><span class="line"> u_xlat16_9.x = <span class="built_in">exp2</span>(u_xlat16_9.x);</span><br><span class="line"> u_xlat16_9.x = u_xlat16_9.x * unity_SpecCube0_HDR.x;</span><br><span class="line"> u_xlat16_22.xyz = u_xlat16_5.xyz * u_xlat16_9.xxx;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> UNITY_ADRENO_ES3</span></span><br><span class="line"> u_xlatb44 = !!(unity_SpecCube0_BoxMin.w<<span class="number">0.999989986</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"> u_xlatb44 = unity_SpecCube0_BoxMin.w<<span class="number">0.999989986</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"> <span class="keyword">if</span>(u_xlatb44){</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> UNITY_ADRENO_ES3</span></span><br><span class="line"> u_xlatb44 = !!(<span class="number">0.0</span><unity_SpecCube1_ProbePosition.w);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"> u_xlatb44 = <span class="number">0.0</span><unity_SpecCube1_ProbePosition.w;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"> <span class="keyword">if</span>(u_xlatb44){</span><br><span class="line"> u_xlat44 = <span class="built_in">dot</span>(u_xlat16_4.xyz, u_xlat16_4.xyz);</span><br><span class="line"> u_xlat44 = <span class="built_in">inversesqrt</span>(u_xlat44);</span><br><span class="line"> u_xlat6.xyz = u_xlat16_4.xyz * <span class="built_in">vec3</span>(u_xlat44);</span><br><span class="line"> u_xlat7.xyz = (-vs_TEXCOORD8.xyz) + unity_SpecCube1_BoxMax.xyz;</span><br><span class="line"> u_xlat7.xyz = u_xlat7.xyz / u_xlat6.xyz;</span><br><span class="line"> u_xlat8.xyz = (-vs_TEXCOORD8.xyz) + unity_SpecCube1_BoxMin.xyz;</span><br><span class="line"> u_xlat8.xyz = u_xlat8.xyz / u_xlat6.xyz;</span><br><span class="line"> u_xlatb10.xyz = <span class="built_in">lessThan</span>(<span class="built_in">vec4</span>(<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>), u_xlat6.xyzx).xyz;</span><br><span class="line"> {</span><br><span class="line"> vec3 hlslcc_movcTemp = u_xlat7;</span><br><span class="line"> hlslcc_movcTemp.x = (u_xlatb10.x) ? u_xlat7.x : u_xlat8.x;</span><br><span class="line"> hlslcc_movcTemp.y = (u_xlatb10.y) ? u_xlat7.y : u_xlat8.y;</span><br><span class="line"> hlslcc_movcTemp.z = (u_xlatb10.z) ? u_xlat7.z : u_xlat8.z;</span><br><span class="line"> u_xlat7 = hlslcc_movcTemp;</span><br><span class="line"> }</span><br><span class="line"> u_xlat44 = <span class="built_in">min</span>(u_xlat7.y, u_xlat7.x);</span><br><span class="line"> u_xlat44 = <span class="built_in">min</span>(u_xlat7.z, u_xlat44);</span><br><span class="line"> u_xlat7.xyz = vs_TEXCOORD8.xyz + (-unity_SpecCube1_ProbePosition.xyz);</span><br><span class="line"> u_xlat6.xyz = u_xlat6.xyz * <span class="built_in">vec3</span>(u_xlat44) + u_xlat7.xyz;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> u_xlat6.xyz = u_xlat16_4.xyz;</span><br><span class="line"> }</span><br><span class="line"> u_xlat16_4 = <span class="built_in">textureLod</span>(unity_SpecCube1, u_xlat6.xyz, u_xlat16_43);</span><br><span class="line"> u_xlat16_11.x = u_xlat16_4.w + <span class="number">-1.0</span>;</span><br><span class="line"> u_xlat16_11.x = unity_SpecCube1_HDR.w * u_xlat16_11.x + <span class="number">1.0</span>;</span><br><span class="line"> u_xlat16_11.x = <span class="built_in">log2</span>(u_xlat16_11.x);</span><br><span class="line"> u_xlat16_11.x = u_xlat16_11.x * unity_SpecCube1_HDR.y;</span><br><span class="line"> u_xlat16_11.x = <span class="built_in">exp2</span>(u_xlat16_11.x);</span><br><span class="line"> u_xlat16_11.x = u_xlat16_11.x * unity_SpecCube1_HDR.x;</span><br><span class="line"> u_xlat16_11.xyz = u_xlat16_4.xyz * u_xlat16_11.xxx;</span><br><span class="line"> u_xlat5.xyz = u_xlat16_9.xxx * u_xlat16_5.xyz + (-u_xlat16_11.xyz);</span><br><span class="line"> u_xlat22.xyz = unity_SpecCube0_BoxMin.www * u_xlat5.xyz + u_xlat16_11.xyz;</span><br><span class="line"> u_xlat16_22.xyz = u_xlat22.xyz;</span><br><span class="line"> }</span><br><span class="line"> u_xlat16_9.xyz = <span class="built_in">vec3</span>(u_xlat16_42) * u_xlat16_22.xyz;</span><br><span class="line"> u_xlat5.xyz = (-vs_TEXCOORD1.xyz) * <span class="built_in">vec3</span>(u_xlat39) + _WorldSpaceLightPos0.xyz;</span><br><span class="line"> u_xlat39 = <span class="built_in">dot</span>(u_xlat5.xyz, u_xlat5.xyz);</span><br><span class="line"> u_xlat39 = <span class="built_in">max</span>(u_xlat39, <span class="number">0.00100000005</span>);</span><br><span class="line"> u_xlat39 = <span class="built_in">inversesqrt</span>(u_xlat39);</span><br><span class="line"> u_xlat5.xyz = <span class="built_in">vec3</span>(u_xlat39) * u_xlat5.xyz;</span><br><span class="line"> u_xlat39 = <span class="built_in">dot</span>(u_xlat0.xyz, (-u_xlat1.xyz));</span><br><span class="line"> u_xlat1.x = <span class="built_in">dot</span>(u_xlat0.xyz, _WorldSpaceLightPos0.xyz);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> UNITY_ADRENO_ES3</span></span><br><span class="line"> u_xlat1.x = <span class="built_in">min</span>(<span class="built_in">max</span>(u_xlat1.x, <span class="number">0.0</span>), <span class="number">1.0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"> u_xlat1.x = <span class="built_in">clamp</span>(u_xlat1.x, <span class="number">0.0</span>, <span class="number">1.0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"> u_xlat0.x = <span class="built_in">dot</span>(u_xlat0.xyz, u_xlat5.xyz);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> UNITY_ADRENO_ES3</span></span><br><span class="line"> u_xlat0.x = <span class="built_in">min</span>(<span class="built_in">max</span>(u_xlat0.x, <span class="number">0.0</span>), <span class="number">1.0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"> u_xlat0.x = <span class="built_in">clamp</span>(u_xlat0.x, <span class="number">0.0</span>, <span class="number">1.0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"> u_xlat13 = <span class="built_in">dot</span>(_WorldSpaceLightPos0.xyz, u_xlat5.xyz);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> UNITY_ADRENO_ES3</span></span><br><span class="line"> u_xlat13 = <span class="built_in">min</span>(<span class="built_in">max</span>(u_xlat13, <span class="number">0.0</span>), <span class="number">1.0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"> u_xlat13 = <span class="built_in">clamp</span>(u_xlat13, <span class="number">0.0</span>, <span class="number">1.0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"> u_xlat16_42 = u_xlat13 + u_xlat13;</span><br><span class="line"> u_xlat16_42 = u_xlat13 * u_xlat16_42;</span><br><span class="line"> u_xlat16_42 = u_xlat16_42 * u_xlat40 + <span class="number">-0.5</span>;</span><br><span class="line"> u_xlat16_48 = (-u_xlat1.x) + <span class="number">1.0</span>;</span><br><span class="line"> u_xlat16_11.x = u_xlat16_48 * u_xlat16_48;</span><br><span class="line"> u_xlat16_11.x = u_xlat16_11.x * u_xlat16_11.x;</span><br><span class="line"> u_xlat16_48 = u_xlat16_48 * u_xlat16_11.x;</span><br><span class="line"> u_xlat16_48 = u_xlat16_42 * u_xlat16_48 + <span class="number">1.0</span>;</span><br><span class="line"> u_xlat16_11.x = -<span class="built_in">abs</span>(u_xlat39) + <span class="number">1.0</span>;</span><br><span class="line"> u_xlat16_24.x = u_xlat16_11.x * u_xlat16_11.x;</span><br><span class="line"> u_xlat16_24.x = u_xlat16_24.x * u_xlat16_24.x;</span><br><span class="line"> u_xlat16_11.x = u_xlat16_11.x * u_xlat16_24.x;</span><br><span class="line"> u_xlat16_42 = u_xlat16_42 * u_xlat16_11.x + <span class="number">1.0</span>;</span><br><span class="line"> u_xlat16_42 = u_xlat16_42 * u_xlat16_48;</span><br><span class="line"> u_xlat26 = u_xlat1.x * u_xlat16_42;</span><br><span class="line"> u_xlat14 = u_xlat40 * u_xlat40;</span><br><span class="line"> u_xlat14 = <span class="built_in">max</span>(u_xlat14, <span class="number">0.00200000009</span>);</span><br><span class="line"> u_xlat27 = (-u_xlat14) + <span class="number">1.0</span>;</span><br><span class="line"> u_xlat40 = <span class="built_in">abs</span>(u_xlat39) * u_xlat27 + u_xlat14;</span><br><span class="line"> u_xlat27 = u_xlat1.x * u_xlat27 + u_xlat14;</span><br><span class="line"> u_xlat39 = <span class="built_in">abs</span>(u_xlat39) * u_xlat27;</span><br><span class="line"> u_xlat39 = u_xlat1.x * u_xlat40 + u_xlat39;</span><br><span class="line"> u_xlat39 = u_xlat39 + <span class="number">9.99999975e-06</span>;</span><br><span class="line"> u_xlat39 = <span class="number">0.5</span> / u_xlat39;</span><br><span class="line"> u_xlat27 = u_xlat14 * u_xlat14;</span><br><span class="line"> u_xlat40 = u_xlat0.x * u_xlat27 + (-u_xlat0.x);</span><br><span class="line"> u_xlat0.x = u_xlat40 * u_xlat0.x + <span class="number">1.0</span>;</span><br><span class="line"> u_xlat27 = u_xlat27 * <span class="number">0.318309873</span>;</span><br><span class="line"> u_xlat0.x = u_xlat0.x * u_xlat0.x + <span class="number">1.00000001e-07</span>;</span><br><span class="line"> u_xlat0.x = u_xlat27 / u_xlat0.x;</span><br><span class="line"> u_xlat0.x = u_xlat0.x * u_xlat39;</span><br><span class="line"> u_xlat0.x = u_xlat1.x * u_xlat0.x;</span><br><span class="line"> u_xlat0.x = u_xlat0.x * <span class="number">3.14159274</span>;</span><br><span class="line"> u_xlat0.x = <span class="built_in">max</span>(u_xlat0.x, <span class="number">0.0</span>);</span><br><span class="line"> u_xlat39 = u_xlat14 * u_xlat14 + <span class="number">1.0</span>;</span><br><span class="line"> u_xlat39 = <span class="built_in">float</span>(<span class="number">1.0</span>) / u_xlat39;</span><br><span class="line"> u_xlat16_42 = <span class="built_in">dot</span>(u_xlat16_2.xyz, u_xlat16_2.xyz);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> UNITY_ADRENO_ES3</span></span><br><span class="line"> u_xlatb1 = !!(u_xlat16_42!=<span class="number">0.0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"> u_xlatb1 = u_xlat16_42!=<span class="number">0.0</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"> u_xlat1.x = u_xlatb1 ? <span class="number">1.0</span> : <span class="built_in">float</span>(<span class="number">0.0</span>);</span><br><span class="line"> u_xlat0.x = u_xlat0.x * u_xlat1.x;</span><br><span class="line"> u_xlat16_41 = (-u_xlat16_41) + <span class="number">1.0</span>;</span><br><span class="line"> u_xlat16_41 = u_xlat16_41 + _Glossiness;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> UNITY_ADRENO_ES3</span></span><br><span class="line"> u_xlat16_41 = <span class="built_in">min</span>(<span class="built_in">max</span>(u_xlat16_41, <span class="number">0.0</span>), <span class="number">1.0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"> u_xlat16_41 = <span class="built_in">clamp</span>(u_xlat16_41, <span class="number">0.0</span>, <span class="number">1.0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"> u_xlat16_24.xyz = <span class="built_in">vec3</span>(u_xlat26) * _LightColor0.xyz;</span><br><span class="line"> u_xlat1.xyz = u_xlat0.xxx * _LightColor0.xyz;</span><br><span class="line"> u_xlat16_42 = (-u_xlat13) + <span class="number">1.0</span>;</span><br><span class="line"> u_xlat16_48 = u_xlat16_42 * u_xlat16_42;</span><br><span class="line"> u_xlat16_48 = u_xlat16_48 * u_xlat16_48;</span><br><span class="line"> u_xlat16_42 = u_xlat16_42 * u_xlat16_48;</span><br><span class="line"> u_xlat16_12.xyz = (-u_xlat16_2.xyz) + <span class="built_in">vec3</span>(<span class="number">1.0</span>, <span class="number">1.0</span>, <span class="number">1.0</span>);</span><br><span class="line"> u_xlat16_12.xyz = u_xlat16_12.xyz * <span class="built_in">vec3</span>(u_xlat16_42) + u_xlat16_2.xyz;</span><br><span class="line"> u_xlat0.xyz = u_xlat1.xyz * u_xlat16_12.xyz;</span><br><span class="line"> u_xlat0.xyz = u_xlat16_3.xyz * u_xlat16_24.xyz + u_xlat0.xyz;</span><br><span class="line"> u_xlat16_3.xyz = u_xlat16_9.xyz * <span class="built_in">vec3</span>(u_xlat39);</span><br><span class="line"> u_xlat16_9.xyz = (-u_xlat16_2.xyz) + <span class="built_in">vec3</span>(u_xlat16_41);</span><br><span class="line"> u_xlat16_2.xyz = u_xlat16_11.xxx * u_xlat16_9.xyz + u_xlat16_2.xyz;</span><br><span class="line"> u_xlat0.xyz = u_xlat16_3.xyz * u_xlat16_2.xyz + u_xlat0.xyz;</span><br><span class="line"> SV_Target0.xyz = u_xlat0.xyz;</span><br><span class="line"> SV_Target0.w = <span class="number">1.0</span>;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">} </span><br></pre></td></tr></table></figure><h1 id="运行编译"><a href="#运行编译" class="headerlink" title="运行编译"></a>运行编译</h1><h2 id="默认编译"><a href="#默认编译" class="headerlink" title="默认编译"></a>默认编译</h2><p>cmd输入malioc 文件名.frag/vert即可看到编译结果</p><p><img src="Blog/source/_posts/RDG101/Untitled2.png"></p><p><em>上图为malioc shader.vert运算结果</em></p><p><img src="Blog/source/_posts/RDG101/Untitled3.png"></p><p><em>上图为malioc shader.frag运算结果</em></p><h2 id="指定型号编译"><a href="#指定型号编译" class="headerlink" title="指定型号编译"></a>指定型号编译</h2><p>可以通过 malioc 文件名 -c 硬件型号来指定Mali GPU编译。(frag 和 vert 均可指定型号进行特定编译)</p><p><img src="Blog/source/_posts/RDG101/Untitled4.png"></p><p><em>上图为malioc shader.frag -c Mali-G78运算结果</em></p><p><img src="Blog/source/_posts/RDG101/Untitled5.png"></p><p><em>上图为malioc shader.frag -c Mali-G72运算结果</em></p><h1 id="参数详解"><a href="#参数详解" class="headerlink" title="参数详解"></a>参数详解</h1><p>从上图可以直观看到shader 的相关属性和模拟硬件的相关信息。</p><p>在中间的列表,是对 Mali 着色器核心中的主要功能单元、算法、加载/存储单元、变化单元和纹理单元进行大致的循环成本细分。一般来说,最长的循环和最短的循环是一个很好的优化方向。我们可以先看以Mali-G72运算编译的详细信息:</p><p><img src="Blog/source/_posts/RDG101/Untitled5.png"></p><p><em>上图为malioc shader.frag -c Mali-G72运算结果</em></p><h2 id="硬件基础信息"><a href="#硬件基础信息" class="headerlink" title="硬件基础信息"></a>硬件基础信息</h2><p>首先可以看到使用Mali -G72 与 Mali -G78 编译后的结果返回结果有些不同。这是由于两个GPU的硬件架构不同所致。</p><p><img src="Blog/source/_posts/RDG101/Untitled6.png"></p><p><em>上图为两个GPU编译后提示的架构细节</em></p><p>具体的细节可以参考下这位dalao所写的mali GPU架构演进,这里只讨论两者在性能优化的区别。</p><p><a href="https://zhuanlan.zhihu.com/p/168712183">ARM Mali GPU架构演进</a></p><p>在Mali Valhall 架构的GPU,所有 Valhall GPU 都实现了两个并行处理引擎。所以前面所看到的在Mali - G72 Bifrost 架构下的Arithmetic 会被拆分为 FMA , CVT , SFU三个数据进行展示。(这三个数据我会在后面Shader信息部分进行解释)</p><p><img src="Untitled7.png"></p><p><em>上图为Valhall shader core (截选自Arm Mali GPU Training Series Ep 3.5 : Mali Offline Compiler)</em></p><h3 id="IDVS-shader-variants"><a href="#IDVS-shader-variants" class="headerlink" title="IDVS shader variants"></a>IDVS shader variants</h3><p><img src="Untitled8.png"></p><p>在Bifrost Valhall 家族的 Mali GPU 使用 index-draven vertex shading(IDVS)。它会在两个binaries编译vertex shaders,position shader,它只计算位置并为每个索引顶点执行,varying shader,它计算剩余的非位置顶点属性输出,并且只对作为剔除后幸存下来的可见图元的一部分的顶点执行。所以可以看到在使用mali compiler 编译处理顶点着色器时,会Position variant 和 Varying variant。</p><p><img src="Untitled9.png"></p><p><em>上图为malioc shader.vert-c Mali-G78的执行结果</em></p><h3 id="Work-registers"><a href="#Work-registers" class="headerlink" title="Work registers"></a>Work registers</h3><p>该shader工作使用的寄存器数量。可用的物理寄存器池在正在执行的着色器线程之间分配。因此,减少工作寄存器的使用可以增加可以同时执行的线程数,有助于保持 GPU工作忙碌。</p><p><img src="Blog/source/_posts/RDG101/Untitled5.png"></p><p><em>上图为malioc shader.frag -c Mali-G72运算结果</em></p><h3 id="Uniform-registers"><a href="#Uniform-registers" class="headerlink" title="Uniform registers"></a>Uniform registers</h3><p>Mali GPU 可以将数据从 API 集统一和统一缓冲区提升到着色器核心寄存器中。然后在每次绘制的基础上加载数据,而不是在每个着色器线程上加载。(比如在shader中Properties声明的常量)</p><h3 id="Stack-spilling"><a href="#Stack-spilling" class="headerlink" title="Stack spilling"></a>Stack spilling</h3><p>对于Valhall 和 Bifrost GPUs,可以看到是否有变量被放置到栈内存中。放置到栈中的内存对于GPU读取是性能消耗较大的。(降低才能提高性能)</p><h3 id="16-bit-arithmetic"><a href="#16-bit-arithmetic" class="headerlink" title="16-bit arithmetic"></a>16-bit arithmetic</h3><p>以 16 位或更低精度执行的算术运算的百分比。数值越高代表shader性能越好。因为使用 mediump 选择的 16 位精度是 32 位精度下的 highp 的两倍。</p><h2 id="Shader信息"><a href="#Shader信息" class="headerlink" title="Shader信息"></a>Shader信息</h2><h3 id="Total-Instruction-Cycles"><a href="#Total-Instruction-Cycles" class="headerlink" title="Total Instruction Cycles"></a><strong>Total Instruction Cycles</strong></h3><p>为程序生成的所有指令的累积执行周期数,与程序控制流无关。</p><h3 id="Shortest-Path-Cycles"><a href="#Shortest-Path-Cycles" class="headerlink" title="Shortest Path Cycles"></a><strong>Shortest Path Cycles</strong></h3><p>通过着色器程序的最短控制流路径的循环数的估计。该行根据设计中存在的功能单元数量对周期成本进行标准化。</p><h3 id="Longest-Path-Cycles"><a href="#Longest-Path-Cycles" class="headerlink" title="Longest Path Cycles"></a><strong>Longest Path Cycles</strong></h3><p>通过着色器程序的最长控制流路径的循环数的估计。该行根据设计中存在的功能单元数量对周期成本进行标准化。并非总是可以根据静态分析确定最长路径,例如,如果统一变量控制循环迭代限制。所以这一行可能表示一个未知的周期计数(“N/A”)。</p><p>报告的统计数据按功能单元细分。在 Shortest Path Cycles 和 Longest Path Cycles 行中的一个或两个行中具有最高周期成本的单位列是一个很好的优化候选者。例如,最高值在 A(算术)列中的着色器是算术边界。通过减少其执行的数学运算的数量或精度来优化着色器。 Bound 列列出了循环计数最高的功能单元,这使您可以快速识别着色器代码中的瓶颈单元。</p><h3 id="A-Arithmetic-operations"><a href="#A-Arithmetic-operations" class="headerlink" title="A = Arithmetic operations"></a>A = Arithmetic operations</h3><p>数学运算操作符。具体代表了shader中sum multiply等操作</p><h3 id="FMA-Fused-multiply-accumulate"><a href="#FMA-Fused-multiply-accumulate" class="headerlink" title="FMA Fused multiply accumulate"></a>FMA Fused multiply accumulate</h3><p>加减乘除运算符</p><h3 id="CVT-Arithmetic-conversion"><a href="#CVT-Arithmetic-conversion" class="headerlink" title="CVT Arithmetic conversion"></a>CVT Arithmetic conversion</h3><p>算术转换操作符</p><h3 id="SFU-Special-functions-unit"><a href="#SFU-Special-functions-unit" class="headerlink" title="SFU Special functions unit"></a>SFU Special functions unit</h3><p>特殊功能单元</p><h3 id="LS-Load-Store-operation"><a href="#LS-Load-Store-operation" class="headerlink" title="LS = Load/Store operation"></a>LS = Load/Store operation</h3><p>读取和存储的操作</p><h3 id="V-Varying-operations"><a href="#V-Varying-operations" class="headerlink" title="V = Varying operations"></a>V = Varying operations</h3><p>在shader中不同单位插值的消耗</p><h3 id="T-Texture-operations"><a href="#T-Texture-operations" class="headerlink" title="T = Texture operations"></a>T = Texture operations</h3><p>采样贴图的消耗</p><h2 id="Shader-properties"><a href="#Shader-properties" class="headerlink" title="Shader properties"></a>Shader properties</h2><p>shader 使用了能影响shader执行表现的相关语言特性信息、</p><h3 id="Has-uniform-computation"><a href="#Has-uniform-computation" class="headerlink" title="Has uniform computation"></a>Has uniform computation</h3><p>这显示是否有任何计算仅依赖于文字常量或统一值,因此为绘制调用或计算分派中的每个线程产生相同的结果。mali 驱动能优化,但还有一定的消耗的。所以建议尽量把这系列的计算移植到在CPU上的application logic 。</p><h3 id="Has-side-effects"><a href="#Has-side-effects" class="headerlink" title="Has side-effects"></a>Has side-effects</h3><p>这显示此着色器是否具有在固定图形管道之外的内存中可见的副作用。可能由内存写入,图片存储,原子的使用。</p><h3 id="Modifies-coverage"><a href="#Modifies-coverage" class="headerlink" title="Modifies coverage"></a>Modifies coverage</h3><p>这显示片段着色器是否具有可以通过着色器执行更改的覆盖掩码,例如,通过使用丢弃语句 </p><p>具有可修改覆盖率的shader必须使用late ZS update,这会降低early ZS test在同一坐标上的later片段的效率</p><h3 id="Uses-late-ZS-test"><a href="#Uses-late-ZS-test" class="headerlink" title="Uses late ZS test"></a>Uses late ZS test</h3><p>这个显示片段shader是否包含了强制late ZS test的逻辑。</p><p>这禁用了early ZS test和hidden surface removal的使用,这可能会导致显著的效率损失</p><h3 id="Uses-late-ZS-update"><a href="#Uses-late-ZS-update" class="headerlink" title="Uses late ZS update"></a>Uses late ZS update</h3><h3 id="Reads-color-buffer"><a href="#Reads-color-buffer" class="headerlink" title="Reads color buffer"></a>Reads color buffer</h3><p>这个显示了fragment shader 是否包含了程序化从color buffer中读取的逻辑 。以这种方式从颜色缓冲区读取的着色器被视为透明的,不能用作hidden surface removal去除遮挡物</p><h1 id="优化建议"><a href="#优化建议" class="headerlink" title="优化建议"></a>优化建议</h1><h2 id="尽量不要使用if、discard"><a href="#尽量不要使用if、discard" class="headerlink" title="尽量不要使用if、discard"></a>尽量不要使用if、discard</h2><p>Discard会使gpu的early-z机制失效,且一般会跟随if语句一起使用,影响gpu的流水线效率。</p><p>一般树木等因为实现方式需要使用discard,如果非预期地使用,则需要关注和优化。</p><h2 id="减低相关运算量"><a href="#减低相关运算量" class="headerlink" title="减低相关运算量"></a>减低相关运算量</h2><p><strong>不要使用反三角函数!不要使用反三角函数!不要使用反三角函数!(重要的事情说三遍!!!)</strong></p><p>使用反三角函数等高复杂度三角函数的计算会增加GPU SFU的开销。</p><p>减低不必要的运算。</p><p>相关shader运算消耗后续我会写相关文章进行性能介绍。(挖个坑……)</p><h2 id="避免类型转换"><a href="#避免类型转换" class="headerlink" title="避免类型转换"></a>避免类型转换</h2><p>Shader中使用浮点和整形混合运算时,GPU需要先把整形转换浮点,比直接使用浮点要额外多出一条指令,这个是比较容易被忽略的细节。</p><h2 id="高精度和浮点数"><a href="#高精度和浮点数" class="headerlink" title="高精度和浮点数"></a>高精度和浮点数</h2><p>高精度计算和浮点数计算比中精度或整形数的计算开销明显要高,要尽可能减少使用。</p><p>比如将highp(32-bit)精度降低为mediump(16-bit)。这使 GPU 能够为每个寄存器存储两倍的变量,降低消耗和寄存器的压力。</p><p>但是在position 和 depth 相关计算方面建议使用highp</p><h2 id="避免触发spilling"><a href="#避免触发spilling" class="headerlink" title="避免触发spilling"></a>避免触发spilling</h2><p>当shader使用寄存器较多,超过了硬件的寄存器数量,会触发spilling机制,使用内存临时代替寄存器,这样数据交换的速度会大幅下降。早期未优化的shader容易出现这种情况。</p><p>遇到此情况时,建议可以减少变量精度,减少变量的有效范围,或者简化shader程序。</p><h2 id="纹理指令数"><a href="#纹理指令数" class="headerlink" title="纹理指令数"></a>纹理指令数</h2><p>一般来说在vert阶段不会去进行纹理采样,高耗时的操作会阻塞后续frag阶段。</p><p>使用纹理的数量最好不要超过4个</p><h2 id="Setpass"><a href="#Setpass" class="headerlink" title="Setpass"></a>Setpass</h2><p>每次setpass call会刷新gpu内部的渲染状态,造成更多的开销,同样材质的物体排序绘制也无法获得收益。</p><ul><li>varring尽量使用vec类型</li><li>uniform应该为常量</li><li>避免常量运算</li><li>避免在fragment中修改深度</li><li>set pass的数量应该为1</li></ul><h1 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h1><p>感谢dalao们的指导,本人第一篇博客,如果觉得对您有帮助的话,请多多支持~</p><h1 id="API-support"><a href="#API-support" class="headerlink" title="API support"></a>API support</h1><p>Mali™ Offline Compiler supports the following API versions:</p><ul><li>OpenGL ES 2.0 and 3.0-3.2</li><li>Vulkan 1.0-1.1</li><li>OpenCL 1.0-1.2 and 2.0</li></ul><p>OpenCL support is only available on Linux and macOS host installations.</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://developer.arm.com/tools-and-software/graphics-and-gaming/arm-mobile-studio/components/mali-offline-compiler">https://developer.arm.com/tools-and-software/graphics-and-gaming/arm-mobile-studio/components/mali-offline-compiler</a></p><p><a href="https://www.youtube.com/watch?v=zEybNlwd7SI&list=PLKjl7IFAwc4QUTejaX2vpIwXstbgf8Ik7&index=15">https://www.youtube.com/watch?v=zEybNlwd7SI&list=PLKjl7IFAwc4QUTejaX2vpIwXstbgf8Ik7&index=15</a></p>]]></content>
<summary type="html">本文主要记录自己使用mali compiler 检测 Unity shader的代码逻辑复杂度及mali compiler的相关指数及shader优化建议.</summary>
<category term="Unity" scheme="http://example.com/categories/Unity/"/>
<category term="性能" scheme="http://example.com/tags/%E6%80%A7%E8%83%BD/"/>
</entry>
<entry>
<title>UnityPBR</title>
<link href="http://example.com/2021/12/20/UnityPBR/"/>
<id>http://example.com/2021/12/20/UnityPBR/</id>
<published>2021-12-19T16:38:20.000Z</published>
<updated>2023-12-28T15:11:14.700Z</updated>
<content type="html"><![CDATA[<p><img src="Blog/source/_posts/UnityPBR/Untitled.png"></p><h1 id="综述"><a href="#综述" class="headerlink" title="综述"></a>综述</h1><ul><li>在移动端PBR的挑战</li><li>我们优化了哪些硬件</li><li>更快的BRDF</li><li>Linear/Gamma</li><li>环境光反射</li></ul><h1 id="PBR-challenges-on-Mobile"><a href="#PBR-challenges-on-Mobile" class="headerlink" title="PBR challenges on Mobile"></a>PBR challenges on Mobile</h1><ul><li>表现</li><li>很多GPU,架构,特点</li><li>线性/gamma的工作流</li><li>缺少高质量贴图压缩格式</li><li>Shader编译不如PC</li><li>Scalar 和 vector 管线</li><li>texCUBElod</li><li>FP32 FP16</li><li>大量shader变体</li></ul><h2 id="优化目标"><a href="#优化目标" class="headerlink" title="优化目标"></a>优化目标</h2><p><img src="Blog/source/_posts/UnityPBR/Untitled1.png"></p><h2 id="表现"><a href="#表现" class="headerlink" title="表现"></a>表现</h2><p><img src="Blog/source/_posts/RDG101/Untitled2.png"></p><h2 id="市场份额"><a href="#市场份额" class="headerlink" title="市场份额"></a>市场份额</h2><p><img src="Blog/source/_posts/RDG101/Untitled3.png"></p><h2 id="优化等级"><a href="#优化等级" class="headerlink" title="优化等级"></a>优化等级</h2><p><img src="Blog/source/_posts/RDG101/Untitled4.png"></p><h2 id="对于PBR重要的GPU特性"><a href="#对于PBR重要的GPU特性" class="headerlink" title="对于PBR重要的GPU特性"></a>对于PBR重要的GPU特性</h2><ul><li>数学逻辑(ALU)和纹理获取(TEX)之间的比例</li><li>Scalar 和 vector 结构</li></ul><p><img src="Blog/source/_posts/RDG101/Untitled5.png"></p><ul><li>FP16<ul><li>PBS 更容易出现失真@低精度</li><li>检查小数(1e-4 OK, 1e-5 not)</li><li>有时由于精度溢出需要额外的clamp</li></ul></li><li>Vector 管线可能需要不同的优化方式</li><li>高端和低端 GPU 的 ALU/TEX 差异很大</li></ul><h2 id="High-end等级的优化"><a href="#High-end等级的优化" class="headerlink" title="High-end等级的优化"></a>High-end等级的优化</h2><p><img src="Blog/source/_posts/RDG101/Untitled6.png"></p><h3 id="为移动端优化BRDF"><a href="#为移动端优化BRDF" class="headerlink" title="为移动端优化BRDF"></a>为移动端优化BRDF</h3><p><img src="Untitled7.png"></p><h3 id="GGX-vs-BlinnPhong"><a href="#GGX-vs-BlinnPhong" class="headerlink" title="GGX vs BlinnPhong"></a>GGX vs BlinnPhong</h3><ul><li>GGX - 更加简单的操作(ADD,MUL)但只用了一个复杂运算器(RCP)</li><li>标准化的Phong - x相同复杂的操作(RCP,EXP,LOG)</li><li>SG相似(RCP,EXP)</li></ul><p>$$GGX = \frac{roughness^4}{\pi((N·H)^2(roughness^4-1)+1)^2}$$</p><p>$$Phong = \frac{1}{\pi·roughness^4}·(N·H)^{(\frac{2}{roughness^4}-2)}$$</p><p><img src="Untitled8.png"></p><h3 id="Simple-vs-Complex-op"><a href="#Simple-vs-Complex-op" class="headerlink" title="Simple vs Complex op"></a>Simple vs Complex op</h3><ul><li>PowerVR G6x00 asm(Phong example)</li><li>可以做很多ops/cycle,但只有 1 个Complex操作</li><li>大部分其他结构的complex 操作 = Latency</li></ul><p><img src="Untitled9.png"></p><h3 id="Geometric-Visbility-term"><a href="#Geometric-Visbility-term" class="headerlink" title="Geometric/Visbility term"></a>Geometric/Visbility term</h3><p><img src="Untitled10.png"></p><h3 id="Fresnel-term"><a href="#Fresnel-term" class="headerlink" title="Fresnel term"></a>Fresnel term</h3><ul><li>C.Schuler提出的拟合:</li></ul><p>$$F = \frac{specColor}{L·H}$$</p><ul><li>适合电解质(反射系数:0.02~0.15)</li><li>导体(又称金属)- 平均值OK(反射系数 0.7~1.0)</li><li>用 + 无穷 替代 1</li></ul><p><img src="Untitled11.png"></p><p><img src="Untitled12.png"></p><p>不使用直接使用Schuler假设 只是 specColor 可以后乘的灵感(更适合Scalar 管线)</p><h3 id="V-F-together"><a href="#V-F-together" class="headerlink" title="V*F together"></a>V*F together</h3><ul><li>修改后的KSK和Schlick Fresnel依赖于L·H</li><li>融合在一起后:</li></ul><p>$$V·F = \frac{(1-L·H)^5}{(L·H)^2(1-roughness^2)+roughness^2}$$</p><p><img src="Untitled13.png"></p><h3 id="近似-V-F"><a href="#近似-V-F" class="headerlink" title="近似 V*F"></a>近似 V*F</h3><ul><li>不是代数简化</li><li>拟合相似曲线</li></ul><p>$$V·F_{approx} = \frac{1}{(L·H)^2·(roughness + 0.5)}·specColor$$</p><p><img src="Untitled14.png"></p><h3 id="近似结果"><a href="#近似结果" class="headerlink" title="近似结果"></a>近似结果</h3><p><img src="Untitled15.png"></p><h3 id="近似-V-F-1"><a href="#近似-V-F-1" class="headerlink" title="近似 V*F"></a>近似 V*F</h3><p>适合电介质,但在金属显得发散</p><p><img src="Untitled16.png"></p><p>可以通过更多的操作来改进,但在实践中无关紧要</p><p><img src="Untitled17.png"></p><h3 id="Comparison-of-Visibility-Terms"><a href="#Comparison-of-Visibility-Terms" class="headerlink" title="Comparison of Visibility Terms"></a>Comparison of Visibility Terms</h3><p><img src="Untitled18.png"></p><h3 id="Final-Specular-BRDF"><a href="#Final-Specular-BRDF" class="headerlink" title="Final Specular BRDF"></a>Final Specular BRDF</h3><p>$$BRDF_{spec} = \frac{roughness^4}{4\pi((N·H)^2(roughness^4-1)+1)^2(L·H)^2(roughness+0.5)} specColor$$</p><ul><li>只有一个除法</li><li>适合Scalar 管线</li></ul><h3 id="环境光BRDF"><a href="#环境光BRDF" class="headerlink" title="环境光BRDF"></a>环境光BRDF</h3><p>只是改装了更简单的功能</p><p>$$BRDF_{env}=(1-max(roughness,N·V))^3 + specColor$$</p><p><img src="Untitled19.png"></p><p><img src="Untitled20.png"></p><p><img src="Untitled21.png"></p><p><img src="Untitled22.png"></p><h2 id="Mid-等级的优化"><a href="#Mid-等级的优化" class="headerlink" title="Mid 等级的优化"></a>Mid 等级的优化</h2><p><img src="Untitled23.png"></p><h3 id="逐顶点光照"><a href="#逐顶点光照" class="headerlink" title="逐顶点光照"></a>逐顶点光照</h3><ul><li>中端硬件:<ul><li>低带宽,GFLOPS are meh</li></ul></li><li>Diffuse and ambient per-vertex</li><li>Specular per-pixel</li><li>Environment reflection vector per-vertex</li><li>高光在切线空间 - 节约矩阵变换</li></ul><p><img src="Untitled24.png"></p><h2 id="Low级别的优化"><a href="#Low级别的优化" class="headerlink" title="Low级别的优化"></a>Low级别的优化</h2><p><img src="Untitled25.png"></p><h3 id="LUT"><a href="#LUT" class="headerlink" title="LUT"></a>LUT</h3><ul><li>低端硬件:<ul><li>Low ALU/TEX 比例</li></ul></li><li>高光强度用LUT<ul><li><N·H,Roughness></li></ul></li><li>记住隐式几何!<ul><li>I = BRDF * N*L</li></ul></li><li>N•H 是余弦 - 高光真的crammed</li></ul><p><img src="Untitled26.png"></p><h3 id="LUT-specular"><a href="#LUT-specular" class="headerlink" title="LUT specular"></a>LUT specular</h3><ul><li>在LUT中存储 1/16强度</li><li>R<em>L 代替在 N</em>H 节约了一组操作</li><li>展开 LUT /w R*L^4 来获得高光的更多空间</li></ul><p><img src="Untitled27.png"></p><h1 id="Linear、Gamma"><a href="#Linear、Gamma" class="headerlink" title="Linear、Gamma"></a>Linear、Gamma</h1><p>线性光照:</p><ul><li>更老的GPU上比较难</li><li>有额外的消耗</li></ul><p>Gamma 和 Linear永远看起来不相同,但我们可以尝试做到</p><ul><li>一致的基础光强度</li><li>一致的高光尺寸</li></ul><h3 id="破解-Gamma-以匹配-Linear"><a href="#破解-Gamma-以匹配-Linear" class="headerlink" title="破解 Gamma 以匹配 Linear"></a>破解 Gamma 以匹配 Linear</h3><p>假设 gamma with 2.0</p><p>仅修复高光强度:</p><ul><li><p>保持线性方程镜面部分的参数(粗糙度)</p></li><li><p>在线性空间中评估镜面反射强度</p></li><li><p>在应用颜色之前将产生的镜面反射强度转换为 sRGB 空间</p><p>= sqrt(specIntensity_Linear) * specColor_sRGB</p></li></ul><p><img src="Untitled28.png"></p><h3 id="Gamma-Hack-的优点"><a href="#Gamma-Hack-的优点" class="headerlink" title="Gamma Hack 的优点"></a>Gamma Hack 的优点</h3><ul><li>无需uncompress colors/textures from sRGB to Linear</li><li>Roughness is Linear already<ul><li>通常存储在alpha 通道</li></ul></li><li>潜在long latency OP(INVSQRT)没有在shader结尾<ul><li>耗时可以被其他操作隐藏</li></ul></li></ul><p><img src="Untitled29.png"></p><p><img src="Untitled30.png"></p><h2 id="Environment-reflections"><a href="#Environment-reflections" class="headerlink" title="Environment reflections"></a>Environment reflections</h2><p>texCUBElod can be really expensive sometimes</p><ul><li>G6xx0 - high-end mobile GPU!</li><li>optional extension on ES2.0</li></ul><p>G6xx0: use dynamic branches to pick 2 closest mips and lerp</p><h3 id="texCUBElod"><a href="#texCUBElod" class="headerlink" title="texCUBElod"></a>texCUBElod</h3><ul><li>Lerp 2个极端的mips<ul><li>很丑但很快</li></ul></li><li>三种lerp方式:<ul><li>hardcoded highest mip#</li><li>middle mip</li><li>2nd order SH</li></ul></li></ul>]]></content>
<summary type="html">本文为Unity于2015年siggraph上发布的unity基于移动端PBR的底层优化</summary>
<category term="Unity" scheme="http://example.com/categories/Unity/"/>
<category term="渲染" scheme="http://example.com/tags/%E6%B8%B2%E6%9F%93/"/>
<category term="Unity" scheme="http://example.com/tags/Unity/"/>
</entry>
<entry>
<title>Effect</title>
<link href="http://example.com/2021/12/18/Effect/"/>
<id>http://example.com/2021/12/18/Effect/</id>
<published>2021-12-17T16:01:09.000Z</published>
<updated>2023-12-28T14:19:03.195Z</updated>
<content type="html"><![CDATA[<p><img src="Blog/source/_posts/Effect/1.png"></p><p><img src="Blog/source/_posts/Effect/2.png"></p><ol><li>概述:我们创造引人注目的视觉效果的理念和目标</li><li>游戏玩法:终点部分,描绘游戏空间及重要程度</li><li>亮度:确定和使用亮度范围</li><li>色彩:色彩关系、色调使用、饱和度水平和调色板距离</li><li>造型:形状语言、轮廓和运动幻象</li><li>节奏:传达游戏性,动画要体现精准位移,减少特效噪声</li><li>联系我们……</li></ol><h1 id="一、概述"><a href="#一、概述" class="headerlink" title="一、概述"></a>一、概述</h1><p><img src="3.jpg"></p><p><strong>我们的理念</strong></p><p>无论是魔法、火焰、烟雾、爆炸还是偶尔的闪光,《英雄联盟》特效部门致力于将所有这些元素变为现实。他们了解游戏设计,并将艺术作品转化为“符文之地”这神奇世界的视觉效果。多年来,已形成了统一的视觉风格,并设定了一组核心目标。</p><p><strong>特效目标</strong></p><ul><li>为游戏提供清晰的视觉效果</li><li>减少视觉混乱</li><li>特效能契合英雄主题</li><li>创造出让玩家惊喜的特效</li></ul><h1 id="二、游戏玩法"><a href="#二、游戏玩法" class="headerlink" title="二、游戏玩法"></a>二、游戏玩法</h1><p><img src="4.jpg"></p><p><strong>通过以下方式定义游戏性:创造聚焦区域</strong></p><p>为获取有说服力、可读性视觉体验,我们为创造的每个体验定义了一系列的<strong>主要和次要元素。</strong>作为我们指路明灯,确保每个特效都能通过<strong>强调特效的焦点来提升游戏清晰度</strong>,同时<strong>减少整体视觉噪音</strong>。</p><p>主要元素规则</p><ul><li>特效交点</li><li>是法术的主要目的</li><li>特效清晰、准确传达了游戏的玩法</li></ul><p>次要元素规则</p><ul><li><p>该元素增强了英雄或法术的主题性</p></li><li><p>通过提升亮度和饱和度来强化主要元素</p></li><li><p>利用多种色调和更宽的饱和度范围来增强特效的整体视觉吸引力</p><blockquote><p>源计划 蕾欧娜的W<br>W 会在周围形成一个爆照的盾牌<br><strong>主要元素</strong>是日蚀法术的<strong>边界</strong>:为躲开爆炸,玩家必须能轻易识别特效半径<br><strong>次要效果</strong>是圆圈内的微弱电流,用于<strong>强化</strong>源计划皮肤主题</p></blockquote></li></ul><p><strong>焦点区域</strong></p><p>关于如何定义主要和次要元素的指南</p><p>主要元素</p><ul><li>高亮度</li><li>亮度或色调上强对比</li><li>清晰的剪影</li><li>高不透明度</li><li>强烈的造型</li><li>剧烈的运动</li></ul><p>次要元素</p><ul><li>低亮度</li><li>小尺寸</li><li>模糊的轮廓</li><li>低不透明度</li><li>简洁的形状</li><li>微妙的运动</li></ul><p><img src="5.jpg"></p><p><strong>准确表达特效区域</strong></p><p>清晰度对竞技体验来说是最重要的,因为它是用来玩和看的。提供准确的游戏表现,使玩家和观众能够清楚地理解和预测游戏。</p><blockquote><p><strong>不准确的描绘</strong><br>提莫蘑菇爆炸半径为600个单位,然而,该蘑菇爆炸的特效只覆盖其半径的一半,并且缺少一个范围指示器</p></blockquote><blockquote><p><strong>准确描述</strong><br>欧米茄小队提莫使用微妙的环来表示AOE,而爆炸是为了准确传递特效范围</p></blockquote><p><img src="6.jpg"></p><p><strong>准确表达特效的命中框</strong></p><p>清晰度对竞技体验来说是最重要的,因为它是用来玩和看的。提供准确的游戏表现,使玩家和观众能够清楚地理解和预测游戏。</p><blockquote><p><strong>不准确的描绘</strong><br>琴女的旧版大招覆盖范围比实际范围更大,当琴女大招没有命中目标,但特效却覆盖到目标时,玩家会困惑</p></blockquote><blockquote><p><strong>准确描述</strong><br>DJ琴女大招清楚显示了矩形的AOE,该特效贴在地面上显示,相机角度不会影响其位置</p></blockquote><p><img src="7.jpg"></p><p><strong>重要程度</strong></p><p>一个特效的视觉呈现应该向玩家和观众传达其重要程度</p><pre><code> eg:if 一个英雄的基本攻击特效看起来与大招重要性相同,就会使玩家或观众感到困惑,同时也让人对大招效果不太满意。 为了确定这些重要程度,我们将特效的特定外观映射为法术的能量峰值,这确保了所有特效的构成与功能相匹配。 在确定一个特效的重要程度时,我们评估3个核心部分:</code></pre><ul><li>可读性:玩家和观众能够立即理解一个特效的目的</li><li>重点突出:特效引导玩家关注重要的法术,同时减少团战时视觉噪音</li><li>重要性的尺寸:每个特效都应与它游戏玩法的重要程度相匹配</li></ul><blockquote><p><strong>拉克丝的普攻和大招</strong><br>因为拉克丝大招的作用比普攻大很多,大招应该永远更显眼。</p></blockquote><p><img src="8.jpg"></p><p><strong>重要性的尺度</strong></p><p>重要性的尺度可以通过以下属性控制:大小、形状、节奏、亮度、饱和度和不透明度。</p><p>每个粒子的重要性应该由特效对玩法的影响程度来决定,如下图所示:</p><blockquote><p><strong>休闲粒子</strong><br>-高亮度<br>-清晰的剪影<br>-隐晦的形状<br>-微妙的运动</p></blockquote><blockquote><p><strong>普攻</strong><br>-小尺寸</p></blockquote><blockquote><p><strong>防御性法术</strong><br>-低饱和度<br>-低不透明度<br>-隐晦的形状<br>-微妙的运动</p></blockquote><blockquote><p><strong>伤害性法术</strong><br>-高饱和度<br>-高不透明度<br>-清晰的剪影</p></blockquote><blockquote><p><strong>改变游戏规则</strong><br>-高饱和度<br>-高不透明度<br>-高亮度<br>-清晰的剪影<br>-剧烈的运动</p></blockquote><blockquote><p><strong>大招</strong><br>-最高的饱和度<br>-最高的不透明度<br>-最高的亮度<br>-大尺寸<br>-有冲击力的动画</p></blockquote><h1 id="三、亮度"><a href="#三、亮度" class="headerlink" title="三、亮度"></a>三、亮度</h1><p><img src="9.jpg"></p><p><strong>如何确定亮度范围</strong></p><p>操纵亮度范围是传达魔法效果的关键。为恰当体现出每个特效的能量水平,所有魔法和能量都有不同程度的亮度范围和不透明度。</p><p> 为确保它们贯穿始终整个游戏,我们在制作特效要坚持一些准则。</p><p>特效亮度范围只能</p><ul><li>更高的亮度范围吸引更多的注意力</li><li>对比可以营造一个清晰的特效范围</li><li>避免使用100%或0%的亮度,因为可能会被场景或UI混淆</li></ul><blockquote><p><strong>各模块的亮度范围</strong><br>-UI亮度范围<br>-角色亮度范围<br>-场景亮度范围<br>-特效亮度范围</p></blockquote><p><img src="10.jpg"></p><p><strong>情景中亮度范围</strong></p><blockquote><p>场景亮度范围<br>特效亮度范围<br>角色高亮范围<br>UI亮度范围</p></blockquote><p><img src="11.jpg"></p><p><strong>使用适当亮度范围增强魔法效果</strong></p><p>eg:奥术大师炸弹人的弹跳炸弹,提高魔法特效中心的亮度,可以让特效看起来更加强大和神奇。</p><blockquote><p><strong>不准确的描述</strong><br>特效没有视觉中心,很难注意到该特效在哪里有最大伤害,因为它与地面相同视觉感</p></blockquote><blockquote><p><strong>准确的描述</strong><br>通过提高炸弹的中心,使特效有清晰的焦点,让玩家在团战中更容易注意到该法术。</p></blockquote><p><img src="12.jpg"></p><p><strong>利用光照增强魔法效果</strong></p><p>光照是体现一个法术神奇特性的关键,通过添加一个简单的光晕来提高特效,可以大大增强魔法效果的表现。</p><blockquote><p><strong>不准确的描绘</strong><br>没有使用光照效果,特效失去很多生命力和神奇的感觉</p></blockquote><blockquote><p><strong>准确的描绘</strong><br>用了辉光和光照,特效就有了更多的生命力,有助于传递特效的运动、方向和持续时间</p></blockquote><p><img src="13.jpg"></p><p><strong>利用亮度范围创建一个焦点区域</strong></p><p>视觉中兴是由对比营造出来的,当我们制作游戏特效时,亮度范围为是我们营造对比最有力的盟友之一。当特效没有体现出我们想要的理想焦点区域时,手动增加深色背景可以帮助改善效果,但该做法需要谨慎使用。if使用过渡,这些特效会在团战时与其他特效形成对比。</p><blockquote><p><strong>准确描绘</strong><br>暗星维鲁斯的Q和苍穹之光维克兹的W是很好的粒子,使用适当的对比来吸引焦点。</p></blockquote><blockquote><p>高对比度 - 高焦点<br>低对比度 - 低焦点<br>高对比度 - 高焦点</p></blockquote><h1 id="四、色彩"><a href="#四、色彩" class="headerlink" title="四、色彩"></a>四、色彩</h1><p><img src="14.jpg"></p><p><strong>色彩对于特效的重要性</strong></p><p>色彩对确定法术的主题起巨大的作用。在本节中,我们将强调适当使用饱和度、色彩关系和《英雄联盟》中几个主题的基础色板。</p><p>特效饱和度范围指南</p><ul><li>更高的饱和度范围吸引更多的注意力</li><li>对比可以营造一个清晰的特效范围</li><li>避免使用100%或0%的饱和度,它可能会被场景或UI混淆</li></ul><blockquote><p>各模块的饱和度范围</p><ul><li>UI饱和度范围</li><li>角色饱和度范围</li><li>场景饱和度范围</li><li>特效饱和度范围</li></ul></blockquote><p><img src="15.jpg"></p><p><strong>英雄和自身特效之间的色彩关系</strong></p><p>英雄的特效比模型有更高更广的亮度范围和饱和度范围</p><blockquote><p><strong>钢铁之翼 凯尔</strong><br>模型色板(外),特效色板(内)</p></blockquote><blockquote><p><strong>屠龙勇士 布隆</strong><br>模型色板(外),特效色板(内)</p></blockquote><p><img src="16.jpg"></p><p><strong>补色和色调在特效中的应用</strong></p><p>最好使用同色系的颜色。当一个特效中有两个互补色时,其中一个颜色必须作为辅助色。</p><p>当两个相反的颜色出现在同一个特效中,这些颜色会争抢成为第一要素(即使亮度级别不同)</p><blockquote><p><strong>补色例子</strong></p></blockquote><blockquote><p><strong>不准确的描述</strong><br>露露的盾牌特效使用高不透明度和高饱和度的互补色,造成严重噪音<br>ps:这些颜色争先成为第一要素,和我们设计的额焦点产生冲突。</p></blockquote><blockquote><p><strong>准确的描述</strong><br>巴德的Q使用低饱和度、低透明度的紫色,和亮黄色平衡。<br>在特效的次要部分使用补色,给特效增加了一种漂亮的丰富性,且没有冲突</p></blockquote><p><img src="17.jpg"></p><p><strong>《英雄联盟》中常见法术的色板</strong></p><p>此处收集了游戏中法术最常用的色板</p><blockquote><p>毒 虚空 治疗</p></blockquote><p><img src="18.jpg"></p><blockquote><p>冰霜 弹药 神秘</p></blockquote><p><img src="19.jpg"></p><blockquote><p>自然 暗影岛 天赐</p></blockquote><p><img src="20.jpg"></p><blockquote><p>海克斯科技 风 水</p></blockquote><p><img src="21.jpg"></p><blockquote><p>队友指示器 敌方指示器</p></blockquote><h1 id="五、造型"><a href="#五、造型" class="headerlink" title="五、造型"></a>五、造型</h1><p><img src="22.jpg"></p><p><strong>用造型语言定义我们的特效风格</strong></p><p>造型是定义特效艺术风格的另一个重要元素,可以帮助减少视觉噪音</p><p>常见的造型包括:简洁的细节、手绘的贴图、软硬混合的形状、清晰的外轮廓和运动物体的贴图</p><p>特效贴图造型指南</p><ul><li>所有的贴图都需要手绘,细节简洁明了</li><li>贴图需要混合柔软和尖锐的形状</li></ul><blockquote><p><strong>准确的描绘</strong><br>手绘贴图配合软硬结合的轮廓线,效果最好</p></blockquote><blockquote><p><strong>不准确的描绘</strong><br>避免使用照片或细节冗余的素材,会造成不必要的噪音</p></blockquote><p><img src="23.jpg"></p><p><strong>创造明确的形状轮廓</strong></p><p>创造轮廓清晰的形状是减少团战中噪音的关键,也能快速传达游戏玩法并表达法术主题</p><blockquote><p><strong>不准确的描述</strong><br>由于特效中有太多细节和对比,所以很难分辨出真正运动的物体在哪,特效形状是什么</p></blockquote><blockquote><p><strong>准确的描述</strong><br>这是个轮廓恰当的最好示范。形状简单,有足够宽的亮度范围来制造焦点</p></blockquote><p><img src="24.jpg"></p><p>用形状层本身制作动画</p><p>在一个特效中加入模糊效果,可以营造运动的错觉,有助于增强特效的方向感,并清楚地传达出游戏玩法。</p><blockquote><p><strong>不准确的描绘</strong><br>如果快速运动的粒子没有运动模糊,会导致视觉噪音和掉帧的错觉</p></blockquote><blockquote><p><strong>准确的描绘</strong><br>这是个很好的例子,例子有指向性的形状和运动模糊,指明了特效运动的方向</p></blockquote><h1 id="六、节奏"><a href="#六、节奏" class="headerlink" title="六、节奏"></a>六、节奏</h1><p><img src="25.jpg"></p><p><strong>特效中节奏的重要性</strong></p><p>节奏对特效很重要,在给特效创建有意思的动画和视觉兴趣点时起到关键作用。</p><p>一个特效随着生命周期的变化方式,为其功能提供了关键的视觉信息。</p><p>特效节奏指引</p><ul><li>所有特效都应有预备和消散</li><li>尾声应被视为次要效果,具有较低亮度、饱和度和不透明度</li><li>能量衰减可以通过改变亮度、色相、饱和度、不透明度或大小来表达</li><li>颜色变调、亮度或不透明度都是能跟特效节奏而变化的元素</li></ul><p><img src="Q1.gif"></p><blockquote><p>塞恩Q的指示器<br>塞恩Q充满眩晕范围所需时间、特效范围都用视觉时钟清晰表达出来</p></blockquote><p><img src="26.jpg"></p><p><strong>用节奏传达游戏玩法</strong></p><p>一个特效的节奏传达了特定的玩法瞬间</p><p><img src="E1.gif"></p><blockquote><p>普朗克 木桶爆炸<br>普朗克的木桶爆炸是一个好例子,展示特效不同阶段</p></blockquote><blockquote><p>预备<br>主爆炸<br>消散</p></blockquote><p><img src="27.jpg"></p><p><strong>为精确的位移提供动画</strong></p><p>元素正确的运动是让特效可信的关键</p><p>准确且有冲击力的节奏清晰传达了游戏玩法,并为玩家提供了竞技性和过瘾的体验。</p><p><img src="A1.gif"></p><blockquote><p><strong>无限烈焰 戴安娜</strong><br>无限烈焰戴安娜的火焰余烬增强了皮肤的主体性,同时创造了有冲击力的瞬间。</p></blockquote><p><img src="W1.gif"></p><blockquote><p>亚索 风墙<br>亚索风墙很好体现了《英雄联盟》中风的运动。</p></blockquote><p><img src="Q2.gif"></p><blockquote><p>律政大亨 蒙多<br>蒙多是一个很好的例子,展示了重力对纸的正确影响。</p></blockquote><p><img src="28.jpg"></p><p><strong>用有活力的节奏提升艺术感</strong></p><p>有活力的节奏创造出具有冲击力的瞬间,创造出更高的满意度和更有趣的特效。</p><p><img src="W2.gif"></p><blockquote><p>准确描述<br>艾克的R用有活力的节奏创造出更具有冲击力和强大的瞬间。</p></blockquote><blockquote><p>不准确的描述<br>投掷特效和爆炸的线性节奏导致了一个无聊的瞬间</p></blockquote><p><img src="29.jpg"></p><p><strong>减少特效在屏幕上停留的时间</strong></p><p>我们有意将一个特效的持续时间降到最低,以减少团战的视觉噪音。</p><blockquote><p><strong>不准确的描绘</strong><br>冰雪女神辛德拉的W极不透明,并且有不必要的长时间逗留<br>让人过于关注这个特效,且很可能掩盖同屏发生的其他特效。</p></blockquote><blockquote><p><strong>准确的描绘</strong><br>仲裁圣女辛德拉的W很快就消失,且即使在它的高光时刻也是有一点透明的,使其他特效可以清晰的展现出来。</p></blockquote>]]></content>
<summary type="html">本文为拳头官方发布的特效制作指南翻译。如有错误,欢迎指正。</summary>
<category term="特效" scheme="http://example.com/categories/%E7%89%B9%E6%95%88/"/>
<category term="特效" scheme="http://example.com/tags/%E7%89%B9%E6%95%88/"/>
</entry>
<entry>
<title>RDG101</title>
<link href="http://example.com/2021/12/16/RDG101/"/>
<id>http://example.com/2021/12/16/RDG101/</id>
<published>2021-12-16T14:56:20.000Z</published>
<updated>2023-12-28T15:11:14.697Z</updated>
<content type="html"><![CDATA[<hr><p>首先上UE 4.26 RenderGraph的流程图</p><p><img src="UE4.26_RenderGraph%E6%B5%81%E7%A8%8B.png"></p><p>以及RDG的相关定义及本ppt相关主题:</p><p><img src="Blog/source/_posts/RDG101/Untitled2.png"></p><p><img src="Blog/source/_posts/RDG101/Untitled3.png"></p><h1 id="Shader-Parameters"><a href="#Shader-Parameters" class="headerlink" title="Shader Parameters"></a>Shader Parameters</h1><p>为了了解RDG结构,有必要了解shader parameter如何在UE引擎中展示</p><p>首先先看下在Shader中input,这些shader input 与一个用户可自定义的C++结构体相关联,并作为一个collection提交。不幸的是,虽然易于创作,但着色器编译器运行时无法验证这种表示</p><p>(下图右侧为理想C++情况)</p><p><img src="Blog/source/_posts/RDG101/Untitled4.png"></p><h2 id="Shader-Parameter-Structs"><a href="#Shader-Parameter-Structs" class="headerlink" title="Shader Parameter Structs"></a><strong>Shader Parameter Structs</strong></h2><p>UE时使用一个宏系统定义shader属性结构,会在编译时自动生成反射。</p><p><img src="Blog/source/_posts/RDG101/Untitled5.png"></p><h2 id="Compile-Time-Reflection-Metadata"><a href="#Compile-Time-Reflection-Metadata" class="headerlink" title="Compile-Time Reflection Metadata"></a><strong>Compile-Time Reflection Metadata</strong></h2><p><img src="Blog/source/_posts/RDG101/Untitled6.png"></p><blockquote><p>在C++中,用户可以遍历访问来自每个成员的信息:<br>Name, Type, Shader Type, Byte offset from start of struct<br>这对于从 RDG / RHI 中的 void* 结构指针中提取资源是必要的。</p></blockquote><p>Engine\Source\Runtime\RenderCore\Private\ShaderParameterMetadata.cpp</p><p>shader 属性宏系统的特点是会自动生成 compile-time 的 reflection metadat。任何用户都可以在runtime时遍历shader 属性结构并获取每个单位的信息。要从公共代码(例如 RHI / RDG)中结构的 void* 指针表示中遍历和提取资源,元数据是必需的。</p><h2 id="0-04-Automatic-Parameter-Alignment自动属性对齐"><a href="#0-04-Automatic-Parameter-Alignment自动属性对齐" class="headerlink" title="0.04 Automatic Parameter Alignment自动属性对齐"></a>0.04 <strong>Automatic Parameter Alignment</strong>自动属性对齐</h2><p><img src="Untitled7.png"></p><p>详细define 请见:Engine\Source\Runtime\RenderCore\Public\ShaderParameterMacros.h</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** Begins & ends a shader parameter structure.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Example:</span></span><br><span class="line"><span class="comment"> *BEGIN_SHADER_PARAMETER_STRUCT(FMyParameterStruct, RENDERER_API)</span></span><br><span class="line"><span class="comment"> *END_SHADER_PARAMETER_STRUCT()</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BEGIN_SHADER_PARAMETER_STRUCT(StructTypeName, PrefixKeywords) \</span></span><br><span class="line"><span class="meta">INTERNAL_SHADER_PARAMETER_STRUCT_BEGIN(StructTypeName, PrefixKeywords, {}, INTERNAL_SHADER_PARAMETER_GET_STRUCT_METADATA(StructTypeName), INTERNAL_SHADER_PARAMETER_STRUCT_CREATE_UNIFORM_BUFFER)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> END_SHADER_PARAMETER_STRUCT()</span></span><br></pre></td></tr></table></figure><p>另一个宏系统的特点就是能自动对齐shader 数据。虚幻引擎使用与平台无关的数据对齐规则来实现着色器的可移植性。</p><p>主要规则是每个成员都与其大小的下一个 2 次幂对齐——但前提是大于四个字节。例如:</p><ul><li>指针是八字节对齐的(即使在 32 位平台上);</li><li>Float、uint32、int32为四字节对齐;</li><li>FVector2D, FIntPoint 为八字节对齐;</li><li>FVector 和 FVector4 是 16 个字节对齐的。</li></ul><p>每个成员的自动对齐将不可避免地创建填充,如上面的评论所示。</p><h3 id="0-05-Sort-Members-to-Minimize-Padding对成员进行排序以最小化填充"><a href="#0-05-Sort-Members-to-Minimize-Padding对成员进行排序以最小化填充" class="headerlink" title="0.05.Sort Members to Minimize Padding对成员进行排序以最小化填充"></a>0.05.<strong>Sort Members to Minimize Padding对成员进行排序以最小化填充</strong></h3><p><img src="Untitled8.png"></p><p>顺便说一句,请考虑组织结构以最小化或消除填充。参数的顺序不会以任何方式影响着色器源,因此这是一个低风险的更改,将导致上传到 GPU 的字节更少。</p><p>在上面的示例中,向上移动“float World”使其位于 FVector2D ViewportSize(三浮点结构)所需的四字节填充区域中。同样,这个三浮点向量将与十六个字节(或四个浮点数)对齐,因此将单浮点成员移动到第四个浮点槽将消除填充。</p><h3 id="0-06-Example-of-Tightly-Packed-Structure"><a href="#0-06-Example-of-Tightly-Packed-Structure" class="headerlink" title="0.06:Example of Tightly Packed Structure"></a>0.06:Example of Tightly Packed Structure</h3><p><img src="Untitled9.png"></p><p>上面截取的代码演示了一个紧密存储的着色器参数结构。每个 FVector {x, y, z} 后跟一个浮点数以完成十六字节的内存槽。</p><h3 id="0-07-No-Need-to-Manually-Pack-Floats无需手动打包浮点数"><a href="#0-07-No-Need-to-Manually-Pack-Floats无需手动打包浮点数" class="headerlink" title="0.07:No Need to Manually Pack Floats无需手动打包浮点数"></a>0.07:No Need to Manually Pack Floats无需手动打包浮点数</h3><p><img src="Untitled10.png"></p><p>shader作者需要手动将松散的浮点数打包成一个更大的 4 宽向量。</p><p>只要遵循填充规则,在使用 SHADER_PARAMETER_STRUCT 系统时这不是必需的。</p><p>通过避免通用参数向量(例如 MyFeatureParams.{x, y, z, w}),保持这些数据松散可以提高可读性。对于着色器源文件也是如此,它可以简单地声明带有友好名称的松散参数。</p><p>一个例外是参数数组。系统不会显式地将参数合并到单个数组中;你仍然必须自己做。</p><h3 id="0-08-First-Shader-Class"><a href="#0-08-First-Shader-Class" class="headerlink" title="0.08.First Shader Class"></a><strong>0.08.First Shader Class</strong></h3><p><img src="Untitled11.png"></p><p>现在我们的着色器有了一个着色器参数结构,我们需要定义着色器本身。在现有的着色器框架中使用新的着色器参数系统是easy的。</p><p>首先,将 SHADER_USE_PARAMETER_STRUCT() 宏添加到shader class。这实现了类的构造函数并配置着色器以使用声明的着色器参数结构。</p><p>光追着色器需要改用 SHADER_USE_ROOT_PARAMETER_STRUCT(),因为 RHI 处理它们的方式存在一些细微差别。此要求是暂时的(4.26),将在着色器系统的未来修订版中删除。</p><p>配置后,着色器将在类中查找 FParameters 成员。这应该分配给您打算使用的着色器参数结构的类型。自然地,每个着色器只允许一个着色器参数结构。</p><p>这两个步骤是您开始使用着色器参数结构系统所需的全部步骤。该类将自动反映和绑定着色器参数。请注意,着色器编译器还将验证针对此结构的绑定。例如,C++ 中的类型现在将生成有用的错误消息,而不是静默失败。</p><h3 id="0-09-Inlining-the-Struct-Definition内联结构定义"><a href="#0-09-Inlining-the-Struct-Definition内联结构定义" class="headerlink" title="0.09:Inlining the Struct Definition内联结构定义"></a><strong>0.09:Inlining the Struct Definition内联结构定义</strong></h3><p><img src="Untitled12.png"></p><p>/Engine/Source/Runtime/Renderer/Private/各类文件夹内</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">FVirtualVoxelGenerateMipCS</span> : <span class="keyword">public</span> FGlobalShader</span><br><span class="line">{</span><br><span class="line"><span class="built_in">DECLARE_GLOBAL_SHADER</span>(FVirtualVoxelGenerateMipCS);</span><br><span class="line"><span class="built_in">SHADER_USE_PARAMETER_STRUCT</span>(FVirtualVoxelGenerateMipCS, FGlobalShader);</span><br><span class="line"></span><br><span class="line"><span class="built_in">BEGIN_SHADER_PARAMETER_STRUCT</span>(FParameters, )</span><br><span class="line"><span class="built_in">SHADER_PARAMETER_STRUCT_INCLUDE</span>(FSceneTextureParameters, SceneTextures)</span><br><span class="line"><span class="built_in">SHADER_PARAMETER</span>(FIntVector, PageCountResolution)</span><br><span class="line"><span class="built_in">SHADER_PARAMETER</span>(uint32, PageResolution)</span><br><span class="line"><span class="built_in">SHADER_PARAMETER</span>(uint32, SourceMip)</span><br><span class="line"><span class="built_in">SHADER_PARAMETER</span>(uint32, TargetMip)</span><br><span class="line"></span><br><span class="line"><span class="built_in">SHADER_PARAMETER_RDG_BUFFER</span>(StructuredBuffer, IndirectDispatchArgs)</span><br><span class="line"><span class="built_in">SHADER_PARAMETER_RDG_TEXTURE_SRV</span>(Texture3D, InDensityTexture)</span><br><span class="line"><span class="built_in">SHADER_PARAMETER_RDG_TEXTURE_UAV</span>(RWTexture3D, OutDensityTexture)</span><br><span class="line"></span><br><span class="line"><span class="built_in">END_SHADER_PARAMETER_STRUCT</span>()</span><br><span class="line">(...)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>与参数结构具有一对一关系的着色器的最佳实践是直接在类上声明参数(作为 FParameters)。这是最简单、最清晰的方法。</p><h3 id="0-10-Assigning-Parameters声明属性"><a href="#0-10-Assigning-Parameters声明属性" class="headerlink" title="0.10:Assigning Parameters声明属性"></a><strong>0.10:Assigning Parameters声明属性</strong></h3><p><img src="Untitled13.png"></p><p>我们的着色器现在配置了一个着色器参数结构。我们现在需要为这些参数赋值并将它们推送到 RHI。首先,FParameters 类型只是一个结构体;您可以实例化并填充它。这使得调试变得微不足道,因为您现在可以在调试器中查看结构体的全部内容。</p><p>新设计的一个关键区别在于着色器参数设置与着色器类分离。这通过将参数设置移动到更高级别的pass代码中来提高清晰度,而不是将其与样板嵌入到着色器类中。这会产生更自然的数据流,更易于阅读和调试。</p><h3 id="0-11:Submitting-Parameters提交属性"><a href="#0-11:Submitting-Parameters提交属性" class="headerlink" title="0.11:Submitting Parameters提交属性"></a>0.11:Submitting Parameters提交属性</h3><p>![](Untitled 14.png)</p><p>/Engine/Source/RenderCore/Public/ShaderParameterStruct.h</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** Set shader's parameters from its parameters struct. */</span></span><br><span class="line"><span class="function"><span class="keyword">template</span><<span class="keyword">typename</span> TRHICmdList, <span class="keyword">typename</span> TShaderClass, <span class="keyword">typename</span> TShaderRHI></span></span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">void</span> <span class="title">SetShaderParameters</span><span class="params">(TRHICmdList& RHICmdList, <span class="type">const</span> TShaderRef<TShaderClass>& Shader, TShaderRHI* ShadeRHI, <span class="type">const</span> <span class="keyword">typename</span> TShaderClass::FParameters& Parameters)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">(...)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>着色器参数系统提供了一个辅助函数 SetShaderParameters,将参数推送到 RHI 命令列表。这提供了在调试器中检查着色器参数值的清晰位置。</p><p>此函数将根据正在使用的特定着色器排列验证结构的内容。如果所需资源为空,则会发出错误。作为旁注,结构的所有成员都默认初始化为 null、零或默认构造函数(取决于类型)。</p><p>这也意味着如果排列不使用资源,则可以安全地忽略它。</p><h3 id="0-12:Defining-a-Uniform-Buffer"><a href="#0-12:Defining-a-Uniform-Buffer" class="headerlink" title="0.12:Defining a Uniform Buffer"></a>0.12:Defining a Uniform Buffer</h3><p><img src="Untitled15.png"></p><p>统一缓冲区使用相同的着色器参数结构模型,但有自己的专用宏来将参数声明为“全局”。</p><h3 id="0-13-Uniform-Buffer-Code-Generation"><a href="#0-13-Uniform-Buffer-Code-Generation" class="headerlink" title="0.13:Uniform Buffer Code Generation"></a><strong>0.13:Uniform Buffer Code Generation</strong></h3><p><img src="Untitled16.png"></p><blockquote><p>Common.ush 已经include生成的shader代码<br>然后就可以作为一个全局结构(存储在unifrom buffer)去访问<br>从长远来看,着色器参数和统一缓冲区之间的 HLSL 不应有语法差异。</p></blockquote><p>全局着色器参数结构具有自动反映在着色器代码中的附加功能(和开销),以及一些 HLSL 语法特殊性的处理。</p><h3 id="0-14-Include-Uniform-Buffers-in-Shader-Parameter-Structs"><a href="#0-14-Include-Uniform-Buffers-in-Shader-Parameter-Structs" class="headerlink" title="0.14 Include Uniform Buffers in Shader Parameter Structs"></a>0.14 Include Uniform Buffers in Shader Parameter Structs</h3><p><img src="Untitled18.png"></p><p>shader属性结构可通过SHADER_PARAMETER_STRUCT_REF()变体依赖于uniform buffers。</p><p>由于此着色器绑定名称由 IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT() 定义,因此引用的统一缓冲区的成员名称仅由 C++ 使用。</p><p>最后,因为只有一个全局定义的绑定名称,所以一次只能绑定一个统一缓冲区的实例。</p><h1 id="Render-Graph-Basics"><a href="#Render-Graph-Basics" class="headerlink" title="Render Graph Basics"></a>Render Graph Basics</h1><h2 id="1-00-The-Render-Graph-Builder"><a href="#1-00-The-Render-Graph-Builder" class="headerlink" title="1.00 The Render Graph Builder"></a><strong>1.00 The Render Graph Builder</strong></h2><p><img src="Untitled19.png"></p><p>render graph API从构建器开始,这个构建器接口通过资源和passes的立即模式声明来促进graph的设置。该graph是通过设置阶段逐步构建。一旦所有setup完成后,graph将执行,它会以依赖项排序的顺序编译并调用所有的pass。除其他外,这种设计可实现 GPU 工作的高效调度和更积极的内存管理。</p><p>API 易于使用。您只需定义一个graph builder 实例,执行设置操作并执行。构建器是单线程的(总是在渲染线程上!)并且在执行之间不保留任何状态。整个图被重建每一帧。</p><p>为了真正实现全帧优化,最终目标是将整个引擎移植到使用相同的图形构建器实例。这将需要一段时间。 “移植”现有代码或编写新渲染代码的首选模式是定义您自己的本地图形构建器实例并手动执行它。随着引擎的各个部分被移植,它们都将合并到同一个构建器实例中。</p><h2 id="1-01-Creating-a-Texture"><a href="#1-01-Creating-a-Texture" class="headerlink" title="1.01 Creating a Texture"></a>1.01 Creating a Texture</h2><p><img src="Untitled20.png"></p><p>创建新Texture,你只需要在graph builder中调用 <em>CreateTexture</em> 。它会在graph builder 实例的生命周期中返回一个可用的RDG texture 句柄。单独的类型是必要的,因为物理 GPU 资源不会立即分配。RDG 指针表示未来资源的句柄,保证对每次使用它的pass都是有效的。</p><p>资源如何与pass相关联的详细信息将在稍后介绍。我们在上面的代码中所做的就是在图形上声明纹理。</p><h2 id="1-02-Creating-a-UAV-for-a-Texture"><a href="#1-02-Creating-a-UAV-for-a-Texture" class="headerlink" title="1.02 Creating a UAV for a Texture"></a><strong>1.02 Creating a UAV for a Texture</strong></h2><p><img src="Untitled21.png"></p><p> 同样,您可以使用构建器来创建资源(纹理或缓冲区)的视图。上面的代码示例在指定的 mip 级别创建了纹理的 UAV。</p><h2 id="1-03-Creating-an-SRV-for-a-Texture"><a href="#1-03-Creating-an-SRV-for-a-Texture" class="headerlink" title="1.03 Creating an SRV for a Texture"></a><strong>1.03 Creating an SRV for a Texture</strong></h2><p><img src="Untitled22.png"></p><p>SRV 也受支持并遵循相同的一般创建模式</p><h2 id="1-04-Pass-Parameters"><a href="#1-04-Pass-Parameters" class="headerlink" title="1.04 Pass Parameters"></a><strong>1.04 Pass Parameters</strong></h2><p><img src="Untitled23.png"></p><p>为了连接RDG资源和pass,我们使用一个修改过的SHADER_PARAMETER_STRUCT 来声明 RDG shader parameters。这个shader 属性的变体接受C++ RDG resource 指针。graph会转换在pass中的struct 并注册 RDG 资源给相关的pass使用。这就是内存管理优势发挥作用的地方,因为graph能够仅根据Pass如何使用资源来推断资源的生命周期。</p><p>请看上面代码,有人可能会问为什么我们使用一个shader parameter struct代表pass parameter而不是其他pass parameter struct。这是一个有意识的设计决定,以简化使用着色器(这是引擎中最常见的pass类型)的 1 对 1 pass的创作。上面的着色器参数结构有两个目的:</p><ol><li>Pass 设置:由于几乎所有的资源都是由shader产生、消耗的。我们可以从shader 的metadata中获取到资源将如何在pass中被使用(Read、Write)。这个信息对于资源转化很重要。</li><li>设置属性值给Command List:pass内的shader不需要‘提取’pass资源;它可以简单地绑定整个结构。 RHI 着色器参数绑定代码会自动取消引用 RDG 资源。</li></ol><p>通过将Pass/资源关联组合到着色器参数结构中,编写单着色器Pass变得微不足道。大量减少样板文件,提高了整个代码库的可维护性。</p><p>总结一下:着色器参数结构提供了使用 RDG 资源的扩展。这个着色器参数结构提供了一个 RDG Pass,它遍历并提取设置Pass资源所需的所有信息。</p><h2 id="1-05-Adding-a-Pass"><a href="#1-05-Adding-a-Pass" class="headerlink" title="1.05 Adding a Pass"></a><strong>1.05 Adding a Pass</strong></h2><p><img src="Untitled24.png"></p><p>为了向图形添加pass,构建器提供了 AddPass 函数。此函数接受在图形执行期间调用的 lambda。 lambda 提供了一个 RHI 命令列表来将工作分派给 RHI。pass接受一个着色器参数结构,并将反映它的所有 <em>RDG</em> 参数。所有其他着色器参数都将被忽略。</p><h2 id="1-06-Profile-Events"><a href="#1-06-Profile-Events" class="headerlink" title="1.06 Profile Events"></a><strong>1.06 Profile Events</strong></h2><p><img src="Untitled25.png"></p><p>每个RDG pass接受一个格式化的名称,该名称暴露给ProfileGPU命令和外部GPU profiler。我们鼓励您填写足够的信息以唯一标识通行证。请注意,出于性能原因,此信息已从发布版本中删除。</p><h2 id="1-07-Pass-Parameter-Setup"><a href="#1-07-Pass-Parameter-Setup" class="headerlink" title="1.07 Pass Parameter Setup"></a><strong>1.07 Pass Parameter Setup</strong></h2><p><img src="Untitled26.png"></p><p>如前所述,graph需要知道pass将访问哪个 RDG 资源,它从pass参数结构中收集。但是,由于graph执行被推迟,因此需要分配此结构以匹配graph的生命周期。graph构建器界面提供了一个 AllocParameters 函数,该函数针对graph的生命周期进行了优化。我们更喜欢为此对象使用 PassParameters 命名约定。</p><p>关于pass参数结构和passes的一些规则:</p><ul><li>pass参数实例一旦通过 AddPass 关联到pass,就被认为是不可变的。pass承担指针的所有权(包括销毁)。只有一次pass,我使用pass参数实例(它们必须是 1 对 1)。要支持多次pass,您必须为每个pass实例化一个唯一的实例。</li><li>分配pass参数实例然后不将其关联到pass是无效的。</li><li>您只能访问pass lambda 中 RDG 类型的底层 RHI 资源,并且仅当 RDG 类型在与pass关联的pass参数实例上声明时。</li></ul><p>如果违反这些条件,RDG 验证层将发出错误。</p><h2 id="1-08-Pass-Execution-Lambda-Function"><a href="#1-08-Pass-Execution-Lambda-Function" class="headerlink" title="1.08 Pass Execution Lambda Function"></a>1.08 Pass Execution Lambda Function</h2><p><img src="Untitled27.png"></p><h2 id="1-09-Render-Target-Bindings-Slots"><a href="#1-09-Render-Target-Bindings-Slots" class="headerlink" title="1.09 Render Target Bindings Slots"></a><strong>1.09 Render Target Bindings Slots</strong></h2><p><img src="Untitled28.png"></p><p>在创作使用光栅管道的pass时,将 RENDER_TARGET_BINDING_SLOTS() 宏添加到pass参数结构中。这将公开渲染目标和深度模板的输入,这些输入将由Pass拾取。着色器会忽略此数据。</p><h2 id="1-10-Binding-a-Color-Render-Target"><a href="#1-10-Binding-a-Color-Render-Target" class="headerlink" title="1.10 Binding a Color Render Target"></a><strong>1.10 Binding a Color Render Target</strong></h2><p><img src="Untitled29.png"></p><p>渲染目标作为绑定数组公开。每个绑定都接受一个 RDG 纹理以及加载/存储操作。</p><h2 id="1-11-Binding-Depth-Stencil-Target"><a href="#1-11-Binding-Depth-Stencil-Target" class="headerlink" title="1.11 Binding Depth Stencil Target"></a><strong>1.11 Binding Depth Stencil Target</strong></h2><p><img src="Untitled30.png"></p><p>同样,深度模板作为单独的绑定公开。它还接受 RDG 纹理以及分别加载/存储深度和模板的动作。此外,您可以指定纹理是读取还是读写。</p><h2 id="1-12-Binding-UAVs-for-Pixel-Shaders"><a href="#1-12-Binding-UAVs-for-Pixel-Shaders" class="headerlink" title="1.12 Binding UAVs for Pixel Shaders"></a><strong>1.12 Binding UAVs for Pixel Shaders</strong></h2><p><img src="Untitled31.png"></p><p>也可以为像素着色器绑定UAV。</p><h2 id="1-13-Registration"><a href="#1-13-Registration" class="headerlink" title="1.13 Registration"></a><strong>1.13 Registration</strong></h2><p><img src="Untitled32.png"></p><blockquote><p>如果需要注册与 RHI 资源不同的资源(例如非常旧的 FRenderTarget),请检查 GRenderTargetPool.CreateUntrackedElement() 以获取 TRefCountPtr<IPooledRenderTarget></p></blockquote><p>渲染 graph 目前使用 IPooledRenderTarget 接口来控制纹理的分配。有时需要将现有资源导入图中(尤其是在 RDG 转换过程中)。构建器公开 RegisterExternalTexture,它返回由现有渲染目标支持的 RDG 纹理实例。</p><h2 id="1-14-Extraction-Queries"><a href="#1-14-Extraction-Queries" class="headerlink" title="1.14 Extraction Queries"></a><strong>1.14 Extraction Queries</strong></h2><p><img src="Untitled33.png"></p><p>池化渲染目标指针也可以从 FRDGTexture 中提取。这允许您跨图形调用保留资源的内容。</p><p>但是,提取会推迟到图执行完毕;这是因为在执行期间可能会根据graph中资源的生命周期分配资源。</p><p>因此,API 公开了 QueueTextureExtraction,它允许您提供一个指针,该指针将在执行graph时填充。</p><h2 id="1-15-Creating-Buffers"><a href="#1-15-Creating-Buffers" class="headerlink" title="1.15 Creating Buffers"></a><strong>1.15 Creating Buffers</strong></h2><p><img src="Untitled34.png"></p><p>API 自然地公开了为它们创建缓冲区和视图的方法。</p><h2 id="1-16-Reading-from-a-Buffer-Using-an-SRV"><a href="#1-16-Reading-from-a-Buffer-Using-an-SRV" class="headerlink" title="1.16 Reading from a Buffer Using an SRV"></a><strong>1.16 Reading from a Buffer Using an SRV</strong></h2><p><img src="Untitled35.png"></p><p>缓冲区只能使用 SHADER_PARAMETER_RDG_BUFFER_SRV() 通过 SRV 从着色器读取。</p><h2 id="1-17-Indirect-Draw-Dispatch-Buffer"><a href="#1-17-Indirect-Draw-Dispatch-Buffer" class="headerlink" title="1.17 Indirect Draw/Dispatch Buffer"></a><strong>1.17 Indirect Draw/Dispatch Buffer</strong></h2><p><img src="Untitled36.png"></p><p>间接绘制/分派缓冲区有点独特,因为它们不被着色器直接使用。相反,在pass参数上将它们声明为 RDG 缓冲区,然后直接在pass中使用 RHI 间接绘制缓冲区。</p><h1 id="Pass-Debugging-and-Methodology"><a href="#Pass-Debugging-and-Methodology" class="headerlink" title="Pass Debugging and Methodology"></a>Pass Debugging and Methodology</h1><h2 id="2-00-VisualizeTexture-Integration"><a href="#2-00-VisualizeTexture-Integration" class="headerlink" title="2.00 VisualizeTexture Integration"></a><strong>2.00 VisualizeTexture Integration</strong></h2><p><img src="Untitled37.png"></p><blockquote><p>List 所有可用texture<br>vis<br>查看名为“DOFGatherForeground”的texture<br>vis DOFGatherForeground</p></blockquote><p>RDG 自动将 FRDGTexture 暴露给可视化纹理工具。任何写操作都会被记录下来。只需在控制台中直接输入您为 CreateTexture() 提供的调试名称,它就会出现。</p><p>如果您有多个Pass修改或重新定义具有相同调试名称的新纹理,则捕获Pass将捕获所有这些Pass,但仅显示最后一个捕获实例。</p><h2 id="2-01-Selecting-a-Resource-Version"><a href="#2-01-Selecting-a-Resource-Version" class="headerlink" title="2.01 Selecting a Resource Version"></a><strong>2.01 Selecting a Resource Version</strong></h2><p><img src="Untitled38.png"></p><blockquote><p>具体看第二次迭代“DOFGatherForeground”<br>vis DOFGatherForeground@1</p></blockquote><p>可以使用 @N 语法选择要可视化的资源版本</p><h2 id="2-02-The-Downside-of-Deferred-Execution"><a href="#2-02-The-Downside-of-Deferred-Execution" class="headerlink" title="2.02 The Downside of Deferred Execution"></a><strong>2.02 The Downside of Deferred Execution</strong></h2><p><img src="Untitled39.png"></p><blockquote><p>在DOF‘s IndirectScatter pass中的断点例子<br>DOF 的 IndirectScatter pass 的 lambda 中的调用堆栈<br>RDG实现的Callstack<br>后处理链中的调用堆栈,而不是 DOF。 :(<br>pass的设置可能包含您的错误的原因,但在执行pass时它早已消失…..</p></blockquote><p>延迟执行的一个主要缺点是它使调试变得困难。如果在pass执行期间出现问题,则问题的根源可能在设置阶段。但是,由于设置堆栈帧早已消失,您将丢失所有中间设置信息。虽然可以在设置代码中设置断点,但问题可能是虚假发生的,或者pass可能是常见的执行路径(例如采样操作)。</p><h2 id="2-03-rdgimmediate"><a href="#2-03-rdgimmediate" class="headerlink" title="2.03 -rdgimmediate"></a><strong>2.03 -rdgimmediate</strong></h2><p><img src="Untitled40.png"></p><blockquote><p>添加后立即执行pass<br>产生与延迟执行相同的功能行为;<br>如果执行中断,则易于检查设置代码;<br>在 AddPass() 之后不能修改 PassParameter 的原因<br>可在运行时使用 r.RDG.ImmediateMode 切换<br>占用大量内存;考虑以较低的分辨率运行:r.Test.SecondaryUpscaleOverride</p></blockquote><p>为了解决这个问题,RDG 能够在立即模式下运行。这将在 AddPass 期间立即执行pass。这种模式使用大量内存,因为在整个设置过程中必须在所有分配上保持引用(我们不知道是否会使用资源!)。但是,调试的好处是巨大的:您可以在执行期间设置断点,并可以清楚地看到出错的调用堆栈。可以在启动时或运行时启用此模式。</p><h2 id="2-04-rdgimmediate-in-action"><a href="#2-04-rdgimmediate-in-action" class="headerlink" title="2.04 -rdgimmediate in action"></a>2.04 -rdgimmediate in action</h2><p><img src="Untitled41.png"></p><p>在这个例子中,我们在景深 IndirectScatter pass中遇到了一个断点。启用立即模式后,我们可以浏览在设置阶段准备的 ConvolutionTextures 数据结构的内容,以调查问题。</p><h2 id="2-05-Early-Validation-of-Shader-Parameters着色器参数的早期验证"><a href="#2-05-Early-Validation-of-Shader-Parameters着色器参数的早期验证" class="headerlink" title="2.05 Early Validation of Shader Parameters着色器参数的早期验证"></a>2.05 Early Validation of Shader Parameters着色器参数的早期验证</h2><p><img src="Untitled42.png"></p><p>一个常见的错误是将所需的着色器参数留空。如前所述,SetShaderParameters 函数将验证是否存在所有必需的着色器参数。但是,最好在设置时发现这些问题。因此,提供 ValidateShaderParameters 以允许您确保在添加到Pass之前所有内容都存在。</p><h2 id="2-06-rdgdebug"><a href="#2-06-rdgdebug" class="headerlink" title="2.06 -rdgdebug"></a>2.06 -rdgdebug</h2><p><img src="Untitled43.png"></p><blockquote><p>额外的验证警告,因为 CPU 成本太高而无法一直检查<br>捕捉诸如(但不限于)之类的东西:<br>pass不需要的资源<br>通过pass产生但不需要的资源<br>可在运行时使用 r.RDG.Debug=1 进行切换。</p></blockquote><p>RDG 提供了丰富的验证层,可以捕获图形设置问题以及性能问题。更昂贵的验证隐藏在 CVar 后面,它可以通过命令行或在运行时启用。验证的常见情况之一是捕获已声明但未实际使用的资源。</p><h2 id="2-07-Dependency-Methodology-White-Listing依赖方法:白名单"><a href="#2-07-Dependency-Methodology-White-Listing依赖方法:白名单" class="headerlink" title="2.07 Dependency Methodology:White-Listing依赖方法:白名单"></a>2.07 Dependency Methodology:White-Listing依赖方法:白名单</h2><p><img src="Untitled44.png"></p><p>AddPass 会访问所有在pass 属性 struct中的所有RDG资源,甚至是那些没有实际被graph execution function使用的属性。这包括分配内存和执行屏障/布局转换等操作,这些操作可能代价高昂。为了消除不必要的成本,重要的是只提供通行证实际使用的资源。由于着色器参数默认初始化为空,因此最简单的解决方案是通过根据使用条件(上例)进行分支分配来将所需资源列入白名单。</p><h2 id="2-08-Dependency-Methodology-Black-Listing依赖方法:黑名单"><a href="#2-08-Dependency-Methodology-Black-Listing依赖方法:黑名单" class="headerlink" title="2.08 Dependency Methodology : Black-Listing依赖方法:黑名单"></a>2.08 Dependency Methodology : Black-Listing依赖方法:黑名单</h2><p><img src="Untitled45.png"></p><p>同样,您也可以使用相同的逻辑来清除满足特定条件时未使用的资源。</p><h2 id="2-09-Automatic-Black-Listing-For-a-Single-Shader-Pass"><a href="#2-09-Automatic-Black-Listing-For-a-Single-Shader-Pass" class="headerlink" title="2.09 Automatic Black-Listing For a Single-Shader Pass"></a>2.09 Automatic Black-Listing For a Single-Shader Pass</h2><p><img src="Untitled46.png"></p><blockquote><p>在修改你的pass属性前自动执行 ValidateShaderParameters()</p></blockquote><p>大多数情况下,pass 只是包装单个计算或像素着色器操作。由于着色器包含许多排列,这会造成潜在的维护噩梦,必须手动调整pass参数以将未使用的资源列入黑名单。</p><p>相反,RDG 提供了一个实用函数,可以为您执行此操作:ClearUnusedGraphResources。该函数采用单个着色器并将着色器未使用的所有资源清零。这将完全从pass中删除这些资源,从而消除了通过图形跟踪它们的成本。</p><p>在清除任何资源之前,实用程序函数会自动为您调用 ValidateShaderParameters。这是通过确保在清除未使用的资源之前存在所有必需的资源来避免混淆。</p><h2 id="2-10-Repetitive-AddPass-Pattern重复的-AddPass-模式"><a href="#2-10-Repetitive-AddPass-Pattern重复的-AddPass-模式" class="headerlink" title="2.10 Repetitive AddPass Pattern重复的 AddPass 模式"></a>2.10 Repetitive AddPass Pattern重复的 AddPass 模式</h2><p><img src="Untitled47.png"></p><h2 id="2-11-Use-Helpers-as-Often-as-Possible-尽可能使用Helpers"><a href="#2-11-Use-Helpers-as-Often-as-Possible-尽可能使用Helpers" class="headerlink" title="2.11 Use Helpers as Often as Possible 尽可能使用Helpers"></a>2.11 Use Helpers as Often as Possible 尽可能使用Helpers</h2><p><img src="Untitled48.png"></p><p>对于常见情况,如计算着色器,实用函数可以删除大量样板文件。更少的样板意味着更少的复制粘贴错误和更容易的维护。</p><h2 id="2-12-GPU-Debugging-UAV-Trick-GPU调试UAV的技巧"><a href="#2-12-GPU-Debugging-UAV-Trick-GPU调试UAV的技巧" class="headerlink" title="2.12 GPU Debugging UAV Trick GPU调试UAV的技巧"></a>2.12 GPU Debugging UAV Trick GPU调试UAV的技巧</h2><p><img src="Untitled49.png"></p><p>顺便说一句,这里有一个巧妙的技巧:有时在调试着色器时,您只需要在纹理中进行良好的老式 printf 调试。这可以在 C++ 中设置一次,并在着色器代码中有条件地启用!</p><p>基本思想是创建一个可选的 RDG 纹理和关联的 UAV,然后将其绑定到您在整个功能开发过程中需要调试的每个Pass。然后,在着色器代码中,您可以有条件地声明纹理资源的存在并写入您想要的任何内容。当资源不活跃时,系统会自动剔除该资源。您可以使用可视化纹理工具查看自定义纹理的内容!</p><h2 id="2-13-Need-More-than-4-Channels-in-a-Texture-在一个纹理中需要-4-个以上的通道?"><a href="#2-13-Need-More-than-4-Channels-in-a-Texture-在一个纹理中需要-4-个以上的通道?" class="headerlink" title="2.13 Need More than 4 Channels in a Texture?在一个纹理中需要 4 个以上的通道?"></a>2.13 Need More than 4 Channels in a Texture?在一个纹理中需要 4 个以上的通道?</h2><p><img src="Untitled50.png"></p><blockquote><p>可能需要编码比像素格式可能提供的更多的信息。 (明显的例子:GBuffer)<br>需要读取和写入更多纹理。<br>需要设置所有这些纹理,就好像它们只是一个一样。<br>想抽象这些细节。</p></blockquote><p>作为另一个方法示例,考虑从多个纹理通道工作的Pass——超过任何单个纹理支持的最多四个通道。最好抽象一些此设置,以便实现将它们视为单个集合。</p><h2 id="2-14-Use-Structs-to-Group-Resources"><a href="#2-14-Use-Structs-to-Group-Resources" class="headerlink" title="2.14 Use Structs to Group Resources"></a>2.14 Use Structs to Group Resources</h2><p><img src="Untitled51.png"></p><p>为了实现这一点,我们可以将这些资源分组到着色器参数结构中并创建方法来初始化它们。这些函数的实现可以抽象出是否包含额外的纹理引用的细节。</p><h2 id="2-15-Resource-Structure-Setup-资源结构设置"><a href="#2-15-Resource-Structure-Setup-资源结构设置" class="headerlink" title="2.15 Resource Structure Setup 资源结构设置"></a>2.15 Resource Structure Setup 资源结构设置</h2><p><img src="Untitled52.png"></p><p>纹理结构的行为类似于本机 RDG 资源。设置代码中的复杂性被抽象为单独的函数,从而产生更加模块化的代码。</p><h2 id="2-16-Flexible-Structure-Nesting-灵活的结构嵌套"><a href="#2-16-Flexible-Structure-Nesting-灵活的结构嵌套" class="headerlink" title="2.16 Flexible Structure Nesting 灵活的结构嵌套"></a>2.16 Flexible Structure Nesting 灵活的结构嵌套</h2><p><img src="Untitled53.png"></p><p>着色器参数结构可以嵌套。在 C++ 中,这只是结构的组合。在着色器代码中,您必须使用结构名称作为结构成员的前缀,并用下划线分隔。在这个例子中,我们可以简单地引用结构,而不是手动列出所有输入纹理。</p><h2 id="2-17-Nesting-Common-Parameters-嵌套通用参数"><a href="#2-17-Nesting-Common-Parameters-嵌套通用参数" class="headerlink" title="2.17 Nesting Common Parameters 嵌套通用参数"></a>2.17 Nesting Common Parameters 嵌套通用参数</h2><p><img src="Untitled54.png"></p><p>没有关于资源结构的任何特定内容。任何着色器参数都可以嵌套。推荐的模式是识别多个Pass之间共享的常见着色器参数。这些参数可以提取到它们自己的结构体中,填充一次,然后再四处复制。使用单一代码路径来设置通用参数可降低维护成本。</p><h2 id="2-18-Include-Shader-Parameter-Struct"><a href="#2-18-Include-Shader-Parameter-Struct" class="headerlink" title="2.18 Include Shader Parameter Struct"></a>2.18 Include Shader Parameter Struct</h2><p><img src="Untitled55.png"></p><p>也可以在 C++ 端嵌套一个子结构,但将所有着色器名称吸收到父作用域中。在上面的示例中,构建了零碎的参数结构,然后将其组合到着色器的参数结构中。为了避免在着色器端创建多余的命名前缀,使用了 SHADER_PARAMETER_STRUCT_INCLUDE 变体。这会导致着色器名称扁平化到全局范围内,删除结构前缀。这在组织 C++ 中的更新频率时非常有用。例如,您可能有一系列引用相同的公共着色器参数的过程。通过包含通用参数,您可以在设置开始时构建它们,然后简单地复制它们。</p><h2 id="2-19-Setup-Nested-Structures-as-You-Please-根据需要设置嵌套结构"><a href="#2-19-Setup-Nested-Structures-as-You-Please-根据需要设置嵌套结构" class="headerlink" title="2.19 Setup Nested Structures as You Please 根据需要设置嵌套结构"></a>2.19 Setup Nested Structures as You Please 根据需要设置嵌套结构</h2><p><img src="Untitled56.png"></p><p>嵌套系统足够灵活,可以支持大多数需求。根据您的功能需求(例如代码重复、冗长等),使用您的判断来组织参数。虚幻引擎提供了实现这一目标所需的工具。</p><h2 id="2-20-Structure-Arrays-结构数组"><a href="#2-20-Structure-Arrays-结构数组" class="headerlink" title="2.20 Structure Arrays 结构数组"></a>2.20 Structure Arrays 结构数组</h2><p><img src="Untitled57.png"></p><p>我们之前介绍了使用 FDOFGatherInputTextures 将资源组织到结构中的便利性。事实证明,景深着色器之一需要为此资源结构的每个纹理生成一个 mip 链。系统支持 C++ 端的着色器参数结构数组,以帮助处理此用例。 C++ 使用直观的 for 循环设置每层的所有 UAV 变得更加方便。着色器代码不是最好的,原因与结构嵌套相同,但至少 C++ 端不受此限制。</p><h2 id="2-21-Event-Scopes"><a href="#2-21-Event-Scopes" class="headerlink" title="2.21 Event Scopes"></a>2.21 Event Scopes</h2><p><img src="Untitled58.png"></p><p>为了在 GPU 调试工具中更好地组织时序,可以在代码中添加事件范围以包含在其中创建的所有pass。<br>当与信息性能相关的信息结合时,事件会更有用。例如,景深可能必须支持 Alpha 通道。这会更改缓冲区布局并增加纹理提取。但是,将这些信息放在每次通过时可能会产生很多噪音。这是可以存储在整个范围内的信息。</p><h2 id="2-22-GPU-Timing-Aggregation"><a href="#2-22-GPU-Timing-Aggregation" class="headerlink" title="2.22 GPU Timing Aggregation"></a>2.22 GPU Timing Aggregation</h2><p><img src="Untitled59.png"></p><p>除了绘制事件之外,UE4 还跟踪存储桶中的 GPU 计时统计信息。这些通过 <code>stat gpu</code> 控制台命令显示。<br>GPU 统计支持仅以范围形式与 RDG 集成。每个Pass的 GPU 计时将在Pass设置时与最内部的范围聚合。</p><h1 id="Screen-Pass-Framework"><a href="#Screen-Pass-Framework" class="headerlink" title="Screen Pass Framework"></a>Screen Pass Framework</h1><h2 id="3-00-What-is-a-Screen-Pass?"><a href="#3-00-What-is-a-Screen-Pass?" class="headerlink" title="3.00 What is a Screen Pass?"></a>3.00 What is a Screen Pass?</h2><p><img src="Untitled60.png"></p><blockquote><p>只是一个读取纹理输入和写入纹理输出的Pass<br>大多数通过引擎;<br>帮助构建问题空间。<br>主要针对像素着色器;<br>该框架的组件也适用于计算。<br>选择了 Screen Pass 命名约定而不是后期处理;<br>更通用<br>SSS 是灯光合成的一部分——并不是真正的后期处理;</p></blockquote><p>首先,什么是screen pass?本质上,它只是一个读取纹理输入和写入纹理输出的Pass。该定义适用于引擎中的大多数pass,并有助于确定问题空间。虽然该框架主要针对像素着色器Pass,但某些组件也适用于compute pass。关于命名的注意事项:我们选择了“screen pass”约定而不是“post process”。我们觉得这更通用。例如,次表面散射在技术上不是后期处理,因为它在照明合成阶段运行。</p><h2 id="3-01-简化Pass创作"><a href="#3-01-简化Pass创作" class="headerlink" title="3.01 简化Pass创作"></a>3.01 简化Pass创作</h2><p><img src="Untitled61.png"></p><blockquote><p>Screen Pass要求;<br>描述输入/输出的着色器参数<br> 纹理范围、视口等。<br>大多数只需要一个像素/计算着色器<br> 隐藏区域网格?全屏三角?<br> 处理任意视口区域<br> 分屏/VR<br> 动态分辨率缩放<br>Screen pass框架为这些问题提供了解决方案</p></blockquote><p>有了前面的定义,让我们考虑一下screen pass的基本要求。</p><p>首先,着色器可能需要有关其纹理的信息;例如,以像素为单位的范围或 UV 坐标中的视口区域。</p><p>接下来,在大多数情况下,screen passes只需要一个像素或计算着色器。像素着色器的一个重要考虑因素是是否使用用于 VR 的 HMD 隐藏区域网格进行渲染,或者只是使用全屏三角形进行渲染。</p><p>最后,我们稍后会更深入地看到,我们需要灵活地渲染到输出纹理的视口或从输入纹理的视口渲染样本。这方面的用例包括分屏或 VR 和动态分辨率缩放。最终,screen pass框架的存在是为了解决这些问题。</p><h2 id="3-02-Texture-Viewports"><a href="#3-02-Texture-Viewports" class="headerlink" title="3.02 Texture Viewports"></a>3.02 Texture Viewports</h2><p><img src="Untitled62.png"></p><blockquote><p>对视口/范围对进行分组的简单类</p><ul><li> 用于派生着色器参数;</li><li> 用于为Screen Pass指定输入/输出视口;</li><li> 用于派生视口之间的 UV 变换。</li></ul></blockquote><p>screen pass 框架明确定义了一个纹理视口。这个数据结构描述了一个在纹理范围内定向的矩形——两者都以像素为单位。我们需要两者才能在 UV 坐标中表达视口并在视口之间映射。正如我们将在后续幻灯片中看到的,框架使用它来导出着色器参数、定义屏幕pass的输入和输出视口,以及在视口之间导出 UV 变换。</p><h2 id="3-03-Understanding-Texture-Viewports"><a href="#3-03-Understanding-Texture-Viewports" class="headerlink" title="3.03 Understanding Texture Viewports"></a>3.03 Understanding Texture Viewports</h2><p><img src="Untitled63.png"></p><blockquote><p>纹理视口区域可以从输入到输出不同。</p><ul><li>例如。解析后处理链以拆分屏幕视口(反之亦然)。</li></ul></blockquote><blockquote><p>想从着色器代码中抽象出来。</p><ul><li>主要是设置细节。着色器不在乎</li></ul></blockquote><p>要了解对纹理视口的需求,请考虑具有单个输入和输出纹理的Pass。一个重要的认识是这两个视口可以不同。分屏就是一个明显的例子。我们可能需要从分屏视口中读取,处理一些中间pass链,然后将结果合成回分屏视口。理想情况下,这些细节尽可能从着色器代码中抽象出来。</p><h2 id="3-04-Multiple-Input-Viewports"><a href="#3-04-Multiple-Input-Viewports" class="headerlink" title="3.04 Multiple Input Viewports"></a>3.04 Multiple Input Viewports</h2><p>![Untitled 64](RDG101/Untitled 64.png)</p><blockquote><p>纹理试图窗口可以区分不同的输入:</p><ul><li>比如:TAA 上采样之后的任何内容也需要深度/速度。<br>我们需要一种简单的方法来在视口之间映射 UV 坐标</li></ul></blockquote><p>在更高级的场景中,纹理视口甚至可能因输入而异。例如,在运动模糊中,深度和速度共享一个视口;然而,场景颜色已经从 TAA 上采样,所以它有第二个视口;最后,速度tile分类纹理有第三个。简而言之,我们需要一种简单的方法来映射这些视口空间之间的 UV 坐标。</p><h2 id="3-05-Texture-Viewport-Parameters"><a href="#3-05-Texture-Viewport-Parameters" class="headerlink" title="3.05 Texture Viewport Parameters"></a>3.05 Texture Viewport Parameters</h2><p><img src="Untitled65.png"></p><blockquote><p>描述着色器的纹理视口</p><ul><li>范围、视口、UV 视口等<br>不与纹理一对一绑定</li><li>可以共享(例如深度/速度);</li><li>根据需要定义尽可能多或少的数量</li></ul></blockquote><p>为了解决着色器参数问题,框架定义了一个从纹理视口派生的新着色器参数结构。它提供纹理的范围和反范围等信息;像素或 UV 坐标中的视口矩形等。根据设计,它不与特定的纹理实例耦合,因为pass的多个输入往往共享相同的纹理视口。相反,您可以根据需要为您的pass定义任意数量。</p><h2 id="3-06-Texture-Viewport-Parameter-Setup"><a href="#3-06-Texture-Viewport-Parameter-Setup" class="headerlink" title="3.06 Texture Viewport Parameter Setup"></a>3.06 Texture Viewport Parameter Setup</h2><p><img src="Untitled66.png"></p><blockquote><p>pass属性结构的成员</p></blockquote><p>要使用纹理视口参数,只需将其添加为我们在前面幻灯片中看到的pass参数结构的成员。您可以直接从纹理视口实例化参数。</p><h2 id="3-07-Defining-in-the-shader"><a href="#3-07-Defining-in-the-shader" class="headerlink" title="3.07 Defining in the shader"></a>3.07 Defining in the shader</h2><p><img src="Untitled67.png"></p><blockquote><p>在HLSL中使用 SCREEN_PASS_TEXTURE_VIEWPORT 宏<br>在 ScreenPass.ush 中定义</p></blockquote><p>在着色器方面,框架提供了一个宏来定义 HLSL 中的纹理视口参数。这消除了添加每个单独成员的需要。它在 ScreenPass.ush 中定义。</p><h2 id="3-08-Draw-Screen-Pass-API"><a href="#3-08-Draw-Screen-Pass-API" class="headerlink" title="3.08 Draw Screen Pass API"></a>3.08 Draw Screen Pass API</h2><p><img src="Untitled68.png"></p><blockquote><p>用于像素着色器的过程</p><ul><li>抽象 HMD 网格与全屏三角形</li><li>抽象的shader设置<br>指定输入/输出纹理视口</li><li>自动 RHI 视口设置</li><li>自动 UV 坐标生成<br>存在其他低级变体:</li><li>比如:手动提交到命令列表</li></ul></blockquote><p>为了更轻松地使用像素着色器,该框架包含实用函数以将像素着色器pass直接添加到渲染图。它抽象了细节,比如是使用 HMD 网格还是全屏三角形。您提供输入和输出纹理视口,实现会自动处理 RHI 视口设置和 UV 坐标生成。如果您需要更多控制,则存在此功能的其他低级变体;例如,如果您需要手动设置您的pass并提交到命令列表。</p><h2 id="3-09-Transform-UV’s-Betweeen-Viewports"><a href="#3-09-Transform-UV’s-Betweeen-Viewports" class="headerlink" title="3.09 Transform UV’s Betweeen Viewports"></a>3.09 Transform UV’s Betweeen Viewports</h2><p><img src="Untitled69.png"></p><blockquote><p>将 UV 从一个视口映射到另一个视口的简单比例/偏置因子;</p><ul><li>Seen it used enough times to warrant making it a first class citizen.</li><li>无需继续派生(或复制粘贴)转换代码。</li><li>可以使用SCREEN_PASS_TEXTURE_VIEWPORT_TRANSFORM宏</li></ul></blockquote><p>最后,该框架提供了一种简单的变换类型来将 UV 坐标从一个视口空间映射到另一个视口空间。要实例化,只需传递您的源视口和目标视口。在着色器代码中,将比例/偏置因子应用于源 UV 坐标。在着色器中,您可以使用 SCREEN_PASS_TEXTURE_VIEWPORT_TRANSFORM 宏来快速定义变换。</p>]]></content>
<summary type="html">Render Graph是近几年的一种组织渲染管线的架构,Unreal 和 Unity 的 SRP都实现了Render Graph。这是一种针对渲染管线的调度任务,使渲染管线的代码更容易扩展和维护,提供可视化的调试工具,收集整帧的渲染任务。本文为UE官方RDG101 ppt翻译版本,如有错误,欢迎指正。</summary>
<category term="UnrealEngine" scheme="http://example.com/categories/UnrealEngine/"/>
<category term="性能" scheme="http://example.com/tags/%E6%80%A7%E8%83%BD/"/>
<category term="渲染" scheme="http://example.com/tags/%E6%B8%B2%E6%9F%93/"/>
</entry>
</feed>