-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.sync-conflict-20230423-102914-2LPQWKV.xml
224 lines (119 loc) · 248 KB
/
atom.sync-conflict-20230423-102914-2LPQWKV.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
<?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>2023-03-12T07:41:38.113Z</updated>
<id>http://example.com/</id>
<author>
<name>NeoZheng</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<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>2022-10-13T15:54:36.795Z</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="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>2022-09-24T16:35:22.634Z</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="Untitled.png"></p><p>UE会在渲染的BasePass阶段将场景相关信息写入存储到GBuffer中。并在后续Lighting阶段将GBuffer中的相关信息进行读取后参与计算光照结果。</p><p><img src="Untitled%201.png"></p><p><em>这里可以看到在直接光的计算中使用的相关Gbuffer的读取。</em></p><p>既然UE在基础的Deferred Rendering中使用到GBuffer,那我们先直接定位到 DeferredShadingRenderer.cpp 查看 Render 函数,这里由于本文篇幅原因不做详细解析,后续有时间笔者再补充下FDeferredShadingSceneRenderer::Render的相关细节流程,这里我直接使用总结的流程图:</p><p><img src="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="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="Untitled%204.png"></p><p><em>此处参考YivanLee大佬的文章</em></p><p>使用GBUFFER_REFACTOR宏来生成的是由C++部分生成 Encode 和 Decode 代码,负责生成代码的文件为 ShaderGenerationUtil.cpp ,可以在文件中查看</p><p><img src="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="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>2022-09-19T14:38:49.227Z</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="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="Untitled%201.png"></p><p>在 <em>HLSLMaterialTranslator.cpp</em> 文件,这里需要找到FHLSLMaterialTranslator::GetMaterialEnvironment 函数并为shadingmodel添加相关defined</p><p><img src="Untitled%202.png"></p><p>在 <em>ShaderMaterial.h</em> 中 FShaderMaterialPropertyDefines 声明映射:</p><p><img src="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="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="Untitled%205.png"></p><p>然后在GetShadingModelColor(uint ShadingModelID) 中修改 ShadingModel ID的颜色</p><p><img src="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>2022-07-07T14:38:24.641Z</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="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="Untitled%201.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="Untitled%202.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="Untitled%203.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文件夹</p><p><img src="Untitled%204.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="Untitled%205.png"></p><p>直接选中脚本进行运行</p><h2 id="直接py调用"><a href="#直接py调用" class="headerlink" title="直接py调用"></a>直接py调用</h2><p><img src="Untitled%206.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>2021-12-23T13:39:39.088Z</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="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="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="Untitled2.png"></p><p><em>上图为malioc shader.vert运算结果</em></p><p><img src="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="Untitled4.png"></p><p><em>上图为malioc shader.frag -c Mali-G78运算结果</em></p><p><img src="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="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="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="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>2021-12-21T16:32:17.322Z</updated>
<content type="html"><![CDATA[<p><img src="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="Untitled1.png"></p><h2 id="表现"><a href="#表现" class="headerlink" title="表现"></a>表现</h2><p><img src="Untitled2.png"></p><h2 id="市场份额"><a href="#市场份额" class="headerlink" title="市场份额"></a>市场份额</h2><p><img src="Untitled3.png"></p><h2 id="优化等级"><a href="#优化等级" class="headerlink" title="优化等级"></a>优化等级</h2><p><img src="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="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="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>2021-12-17T16:20:04.478Z</updated>
<content type="html"><![CDATA[<p><img src="1.png"></p><p><img src="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>2021-12-17T14:11:00.376Z</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="Untitled2.png"></p><p><img src="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="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="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="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>