-
Notifications
You must be signed in to change notification settings - Fork 28
/
Copy pathChapter 10 Blending.html
358 lines (252 loc) · 36 KB
/
Chapter 10 Blending.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
<head>
<link rel="stylesheet" href="github-markdown.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.11.0/styles/default.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.8.3/katex.min.css">
<script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML'></script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
inlineMath: [ ['$','$'], ['\\(','\\)'] ]
}
});
</script>
</head>
<body class="markdown-body"></body>
<style>
.markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 980px;
margin: 0 auto;
padding: 45px;
}
@media (max-width: 767px) {
.markdown-body {
padding: 15px;
}
}
</style>
<h1><element id = "10"> Chapter 10 Blending </element></h1>
<p>观察下方图片<a href="#Image10.1">10.1</a>。
我们在这一帧中最开始渲染的是地形和木箱,因此地形和木箱的像素就被储存在后台缓冲中。
然后我们使用混合技术来绘制一个水面到后台缓冲,因此水的像素和地形以及木箱的像素在后台缓冲中得到了混合,看起来就是木箱和地形穿过了水面。
在本章中,我们尝试混合技术,这个技术能够就是混合(组合)现在正在光栅化的像素(我们称之为源)和之前已经完成光栅化并存储在后台缓冲中的像素(我们称之为目标)。
这个技术能够让我们去渲染透明的物体,例如水和玻璃。</p>
<p><img src="Images/10.1.png" id = "Image10.1"> </img></p>
<p><strong>目标</strong></p>
<ul>
<li>理解如何使用混合,并且能够在<code>Direct3D</code>中使用它。</li>
<li>学习<code>Direct3D</code>支持的不同的混合模式。</li>
<li>发现如何通过使用Alpha去控制图元的透明度。</li>
<li>学习如何使用HLSL中的Clip函数阻止一个像素绘制到后台缓冲中。</li>
</ul>
<h2><element id = "10.1"> 10.1 THE BLENDING EQUATION </element></h2>
<p>我们设<strong>C<sub>src</strong></sub>表示从我们的像素着色器(<strong>PixelShader</strong>)输出的第<strong>i</strong>,<strong>j</strong>个像素,也就是我们正在光栅化的像素,然后我们设<strong>C<sub>dst</strong></sub>表示现在在后台缓冲中第<strong>i</strong>,<strong>j</strong>个像素。
如果不使用混合技术的话,像素<strong>C<sub>src</strong></sub>将会替代<strong>C<sub>dst</strong></sub>(前提是他能够通过深度和模板测试)。
但是在使用混合的情况下,<strong>C<sub>src</strong></sub>和<strong>C<sub>dst</strong></sub>将会混合成一个新的颜色<strong>C</strong>,并且<strong>C</strong>将会替代<strong>C<sub>dst</strong></sub>作为后台缓冲里面的像素(你可以认为新的颜色<strong>C</strong>将会被写入到后台缓冲中去)。
<code>Direct3D</code>使用下面的这个方程来混合源和目标像素的颜色:</p>
<p><strong><center>C = C<sub>src</sub> × F<sub>src</sub> ^ C<sub>dst</sub> × F<sub>dst</sub></center></strong></p>
<p>F将会在下面介绍,主要是通过F来让我们有更多的方式去实现更多的效果。
<code>×</code> 符合表示的是向量的点积,<code>^</code>(他那个符合我打不出,我们暂且叫做混合操作)符号是一种由我们自主定义的运算,具体下面会介绍。</p>
<p>上面的方程是用于处理颜色分量中的<strong>RGB</strong>的,对于<strong>Alpha</strong>值我们单独使用下面的方程处理,这个方程和上面的是极为相似的:</p>
<p><strong><center>A = A<sub>src</sub>F<sub>src</sub> ^ A<sub>dst</sub>F<sub>dst</sub></center></strong></p>
<p>这个方程基本上是一样的,但是这样分开处理的话就可以让<code>^</code>运算可以不同了。我们将RGB和Alpha分开处理的动机就是让我们能够单独处理RGB和Alpha值,而两者不互相影响。</p>
<h2><element id = "10.2"> 10.2 BLEND OPERATIONTS </element></h2>
<ul>
<li><code>D3D12_BLEND_OP_ADD</code>: <strong>C = C<sub>src</sub> × F<sub>src</sub> + C<sub>dst</sub> × F<sub>dst</sub></strong></li>
<li><code>D3D12_BLEND_OP_SUBTRACT</code>: <strong>C = C<sub>dst</sub> × F<sub>dst</sub> - C<sub>src</sub> × F<sub>src</sub></strong></li>
<li><code>D3D12_BLEND_OP_REV_SUBTRACT</code>: <strong>C = C<sub>src</sub> × F<sub>src</sub> - C<sub>dst</sub> × F<sub>dst</sub></strong> </li>
<li><code>D3D12_BLEND_OP_MIN</code>: <strong>C = min(C<sub>src</sub>, C<sub>dst</sub>)</strong></li>
<li><code>D3D12_BLEND_OP_MAX</code>: <strong>C = max(C<sub>src</sub>, C<sub>dst</sub>)</strong></li>
</ul>
<p>Alpha的混合操作也是一样的。
当然你可以使用不同的操作来分别处理RGB和Alpha的混合。
举个例子来说,你可以在RGB的混合中使用<code>+</code>,然后在Alpha的混合中使用<code>-</code>。</p>
<p><strong><center>C = C<sub>src</sub> × F<sub>src</sub> + C<sub>dst</sub> × F<sub>dst</sub></center></strong> </p>
<p><strong><center>A = A<sub>dst</sub>F<sub>dst</sub> - A<sub>src</sub>F<sub>src</sub></center></strong></p>
<p>最近在<code>Direct3D</code>中加入了一个新的特征(<code>D3D12_LOGIC_OP</code>),我们可以使用逻辑运算符来代替上面的混合操作。
具体的我就没必要放进去了,毕竟很简单理解。</p>
<p>但是你需要注意的是,如果你使用逻辑运算符代替混合操作,你需要注意的是逻辑运算符和传统运算符是不能同时使用的,你必须在这两种里面选择一种使用。
并且你使用逻辑运算符的话,你需要确保你的<code>Render Target</code>的格式支持(支持的格式应当是<code>UINT</code>的变种)。</p>
<h2><element id = "10.3"> 10.3 BLEND FACTORS </element></h2>
<p>通过改变<code>Factors(因素)</code>,我们可以设置更多的不同的混合组合,从而来实现更多的不同的效果。
我们将会在下面解释一些混合组合,但是你还是要去体验一下他们的效果,从而能够有一个概念。
下面将会介绍一些基础的<code>Factors</code>。你可以去看<strong>SDK</strong>文档里面的<code>D3D12_BLEND</code>枚举了解到更多高级的<code>Factors</code>。
我们设<strong>C<sub>src</sub> = (r<sub>src</sub>, g<sub>src</sub>, b<sub>src</sub>)</strong>,<strong>A<sub>src</sub> = a<sub>src</sub></strong>(这个RGBA值是由像素着色器输出的),
<strong>C<sub>dst</sub> = (r<sub>dst</sub>, g<sub>dst</sub>, b<sub>dst</sub>)</strong>,<strong>A<sub>dst</sub> = a<sub>dst</sub></strong>(这个RGBA值是存储在后台缓冲中的)。</p>
<ul>
<li><code>D3D12_BLEND_ZERO</code>: <strong>F = (0, 0, 0, 0)</strong></li>
<li><code>D3D12_BLEND_ONE</code>: <strong>F = (1, 1, 1, 1)</strong></li>
<li><code>D3D12_BLEND_SRC_COLOR</code>: <strong>F = (r<sub>src</sub>, g<sub>src</sub>, b<sub>src</sub>)</strong></li>
<li><code>D3D12_BLEND_INV_SRC_COLOR</code>: <strong>F<sub>src</sub> = (1 - r<sub>src</sub>, 1 - g<sub>src</sub>, 1 - b<sub>src</sub>)</strong></li>
<li><code>D3D12_BLEND_SRC_ALPHA</code>: <strong>F = (a<sub>src</sub>, a<sub>src</sub>, a<sub>src</sub>, a<sub>src</sub>)</strong></li>
<li><code>D3D12_BLEND_INV_SRC_ALPHA</code>: <strong>F = (1 - a<sub>src</sub>, 1 - a<sub>src</sub>, 1 - a<sub>src</sub>, 1 - a<sub>src</sub>)</strong></li>
<li><code>D3D12_BLEND_DEST_ALPHA</code>: <strong>F = (a<sub>dst</sub>, a<sub>dst</sub>, a<sub>dst</sub>, a<sub>dst</sub>)</strong></li>
<li><code>D3D12_BLEND_INV_DEST_ALPHA</code>: <strong>F = (1 - a<sub>dst</sub>,1 - a<sub>dst</sub>, 1 - a<sub>dst</sub>, 1 - a<sub>dst</sub>)</strong></li>
<li><code>D3D12_BLEND_DEST_COLOR</code>: <strong>F = (a<sub>dst</sub>, a<sub>dst</sub>, a<sub>dst</sub>)</strong></li>
<li><code>D3D12_BLEND_INV_DEST_COLOR</code>: <strong>F = (1 - a<sub>dst</sub>, 1 - a<sub>dst</sub>, 1 - a<sub>dst</sub>)</strong></li>
<li><code>D3D12_BLEND_SRC_ALPHA_SAT</code>: <strong>F = (a'<sub>src</sub>, a'<sub>src</sub>, a'<sub>src</sub>, a'<sub>src</sub>), a'<sub>src</sub> = clamp(a<sub>src</sub>, 0, 1)</strong></li>
<li><code>D3D12_BLEND_BLEND_FACTOR</code>: <strong>F = (r, g, b, a)</strong></li>
</ul>
<p>最后一个枚举类型中的参数 <strong>(r ,g ,b ,a)</strong> 通过下面这个函数设置。</p>
<p><code>ID3D12GraphicsCommandList::OMSetBlendFactor</code></p>
<p>参数是一个<code>Float[4]</code>,表示4个分量的值,如果设置为<code>nullptr</code>那么就默认全是1。</p>
<h2><element id = "10.4"> 10.4 BLEND STATE </element></h2>
<p>我们已经讨论过了混合操作符和混合因素,但是我们如何在<code>Direct3D</code>里面设置这些参数呢?
和其他的<code>Direct3D</code>的状态一样,混合状态也是<code>PSO(渲染管道)</code>的一个部分。
之前我们使用的都是默认的混合状态(禁用状态)。</p>
<p>我们如果要使用非默认的混合状态,我们必须填充<code>D3D12_BLEND_DESC</code>结构。</p>
<pre><code> struct D3D12_BLEND_DESC{
bool AlphaToCoverageEnable; // false
bool IndependentBlendEnable; // false
D3D11_RENDER_TARGET_BLEND_DESC RenderTarget[8];
};
</code></pre>
<ul>
<li><p><code>AlphaToCoverageEnable</code>:设置成<code>true</code>开启<code>alpha-to-coverage</code>技术,这个技术是多重采样技术在渲染某些纹理 <strong>(这里的翻译无法保证正确性,因此就使用某些纹理代替)</strong> 的时候使用的。设置成<code>false</code>来关闭这个技术。<code>alpha-to-coverage</code>技术需要多重采样开启才能使用(换言之就是必须在创建后台缓冲和深度缓冲的时候开启多重采样)。</p></li>
<li><p><code>IndependentBlendEnable</code>:<code>Direct3D</code>最多支持同时渲染8个<code>Render Target</code>。如果这个属性设置成<code>true</code>,那么就可以在渲染不同的<code>Render Target</code>的时候使用不同的混合参数(例如混合因素,混合操作符,混合是否开启等)。如果设置成<code>false</code>,那么所有的<code>Render Target</code>就会使用同样的混合方法(具体来说就是<code>D3D12_BLEND_DESC::RenderTarget</code>中的第一个元素作为所有的<code>Render Target</code>使用的混合方法)。对于现在来说,我们一次只使用一个<code>Render Target</code>。</p></li>
<li><p><code>RenderTarget</code>:第i个元素描述第i个<code>Render Target</code>使用的混合方法,如果<code>IndependentBlendEnable</code>设置成<code>false</code>,那么所有的<code>Render Target</code>就全部使用<code>RenderTarget[0]</code>这个混合方法去进行混合。</p></li>
</ul>
<p><code>D3D12_RENDER_TARGET_BLEND_DESC</code>结构如下所示:</p>
<pre><code> struct D3D12_RENDER_TARGET_BLEND_DESC{
bool BlendEnable; // false
bool LogicOpEnable; // false
D3D12_BLEND SrcBlend; // D3D12_BLEND_ONE
D3D12_BLEND DestBlend; // D3D12_BLEND_ZERO
D3D12_BLEND_OP BlendOp; // D3D12_BLEND_OP_ADD
D3D12_BLEND SrcBlendAlpha; // D3D12_BLEND_ONE
D3D12_BLEND DestBlendAlpha; // D3D12_BLEND_ZERO
D3D12_BLEND_OP BlendOpAlpha // D3D12_BLEND_OP_ADD
D3D12_LOGIC_OP LogicOp; // D3D12_LOGIC_OP_NOOP
UINT8 RenderTargetWriteMask; // D3D12_COLOR_WRITE_ENABLE_ALL
};
</code></pre>
<ul>
<li><code>BlendEnable</code>: 设置成<code>true</code>就开启混合否则就是关闭,注意的是<code>LogicOpEnable</code>和<code>BlendEnable</code>不能同时开启,你必须使用其中的一个来进行混合。</li>
<li><code>LogicOpEnable</code>: 设置成<code>true</code>就开启使用逻辑运算符的混合,然后和<code>BlendEnable</code>不能同时使用。</li>
<li><code>SrcBlend</code>: <strong>RGB</strong>混合中的<strong>F<sub>src</sub></strong>。</li>
<li><code>DestBlend</code>: <strong>RGB</strong>混合中的<strong>F<sub>dst</sub></strong>。</li>
<li><code>BlendOp</code>: <strong>RGB</strong>混合中使用的操作符。</li>
<li><code>SrcBlendAlpha</code>: <strong>Alpha</strong>混合中的<strong>F<sub>src</sub></strong>。</li>
<li><code>DestBlendAlpha</code>: <strong>Alpha</strong>混合中的<strong>F<sub>dst</sub></strong>。</li>
<li><code>BlendOpAlpha</code>: <strong>Alpha</strong>混合中使用的操作符。</li>
<li><code>LogicOp</code>: 混合中使用的逻辑运算符。</li>
<li><code>RenderTargetWriteMask</code>: 用于控制混合结束后哪些颜色(<strong>R,G,B,A</strong>)可以写入到后台缓冲中去。举个例子来说就是如果我们想禁止将<strong>RGB</strong>写入到后台缓冲中去的话,我们就可以设置成<code>D3D12_COLOR_WRITE_ENABLE_ALPHA</code>。如果混合是关闭的,那么就没有任何限制输出到后台缓冲中去。</li>
</ul>
<p><strong>Notice:</strong> 由于混合需要处理每一个像素,所以他的开销很大。你最好只在需要的时候才打开它。</p>
<h2><element id = "10.5"> 10.5 EXAMPLES </element></h2>
<p>在这个部分,我们看一些混合操作的特效例子。当然我们只看关于<strong>RGB</strong>混合的例子。</p>
<h3><element id = "10.5.1"> 10.5.1 No Color Write </element></h3>
<p>如果我们只是想单纯的留下目标像素,即源像素不会和目标像素进行混合以及覆盖,那么就可以使用这个方法。
举个例子来说就是,将目标像素输出到<code>Depth/Stencil Buffer</code>中去。方程在下面:</p>
<p><strong><center>C = C<sub>src</sub> × (0, 0, 0) + C<sub>dst</sub> × (1, 1, 1)</center></strong></p>
<p><strong><center>C = C<sub>dst</sub></center></strong></p>
<p>另外一个方法就是将<code>RenderTargetWriteMask</code>设置成0。
这样的话就禁止了所有的颜色输出到<code>Back Buffer</code>中。</p>
<h3><element id = "10.5.2"> 10.5.2 Adding/Subtracting </element></h3>
<p><img src = "Images/10.2.png" id = "Image10.2"></img></p>
<p>如果我们想将源像素和目标像素加起来(参见<a href="#Image10.2">10.2</a>)。方程如下:</p>
<p><strong><center>C = C<sub>src</sub> × (1, 1, 1) + C<sub>dst</sub> × (1, 1, 1)</center></strong></p>
<p><strong><center>C = C<sub>src</sub> + C<sub>dst</sub></center></strong></p>
<p><img src = "Images/10.3.png" id = "Image10.3"></img></p>
<p>我们当然也可以相减啊(参见<a href="#Image10.3">10.3</a>)。方程如下:</p>
<p><strong><center>C = C<sub>src</sub> × (1, 1, 1) - C<sub>dst</sub> × (1, 1, 1)</center></strong></p>
<p><strong><center>C = C<sub>src</sub> - C<sub>dst</sub></center></strong></p>
<h3><element id = "10.5.3"> 10.5.3 Multiplying </element></h3>
<p><img src = "Images/10.4.png" id = "Image10.4"></img></p>
<p>如果我们想将源像素和目标像素相乘(参见<a href="#Image10.4">10.4</a>)。方程如下:</p>
<p><strong><center>C = C<sub>src</sub> × (0, 0, 0) x C<sub>dst</sub> × C<sub>src</sub></center></strong></p>
<p><strong><center>C = C<sub>src</sub> x C<sub>dst</sub></center></strong></p>
<h3><element id = "10.5.4"> 10.5.4 Transparency </element></h3>
<p>现在我们假设<strong>Alpha</strong>分量用来控制源像素的不透明度(0就是<strong>0%</strong>,0.4就是<strong>40%</strong>)。
我们设不透明度为<strong>A</strong>,透明度为<strong>T</strong>。那么透明度和不透明度的关系就是<strong>T = 1 - A</strong>。
比如不透明度是0.4,那么透明度就是0.6。现在我们想将源像素和目标像素在保留源像素的不透明度的情况下,将目标像素透明。方程如下:</p>
<p><strong><center>C = C<sub>src</sub> × (a<sub>src</sub>, a<sub>src</sub>, a<sub>src</sub>) + C<sub>dst</sub> × (1 - a<sub>src</sub>, 1 - a<sub>src</sub>, 1 - a<sub>src</sub>)</center></strong></p>
<p><strong><center>C = a<sub>src</sub> × C<sub>src</sub> + (1 - a<sub>src</sub>) × C<sub>dst</sub></center></strong></p>
<p>举个例子就是,我们假设<strong>a<sub>src</sub> = 0.25</strong>,就是不透明度为<strong>25%</strong>。
当源像素和目标像素混合的时候,我们希望保留 <strong>25%</strong> 的源像素,<strong>75%</strong> 的目标像素(这里目标像素在源像素的前面,其实就是说决定目标像素的物体在决定源像素的物体前面)。方程如下:</p>
<p><strong><center>C = C<sub>src</sub> × (a<sub>src</sub>, a<sub>src</sub>, a<sub>src</sub>) + C<sub>dst</sub> × (1 - a<sub>src</sub>, 1 - a<sub>src</sub>, 1 - a<sub>src</sub>)</center></strong></p>
<p><strong><center>C = 0.25 × C<sub>src</sub> + 0.75 × C<sub>dst</sub></center></strong></p>
<p>通过使用混合的方法,我们就可以绘制如<a href="#Image10.1">10.1</a>的物体了。这里你就需要在混合的时候注意一些东西,否则你就会绘制的时候出现问题。
我们必须遵守如下规则:</p>
<p><strong>首先绘制那些不需要混合的物体。
然后将需要混合的物体按照他们和摄像机的距离排序。
然后按照从后往前的顺序绘制物体。</strong></p>
<p>从后往前绘制物体的原因我不晓得如何用中文描述了,这个我觉得很显然啊。所以下面是我的<strong>BB</strong>:</p>
<p>对于一个物体来说,我们要将他进行混合的话,我们保留的是后面的像素,因此我们需要先绘制保留的像素,然后在绘制其他的像素。</p>
<p>对于<a href="#10.5.1"><strong>10.5.1</strong></a>,绘制的顺序已经没有意义了,反正不会输出。对于<a href="#10.5.2"><strong>10.5.2</strong></a>和<a href="#10.5.3"><strong>10.5.3</strong></a>我们仍然先绘制不需要混合的物体,然后绘制需要混合的物体。
这是因为我们需要在开始混合之前将所有的不进行混合的像素确定下来。在这里我们并不需要排序需要混合的物体。因为这个运算是满足交换律的。我们设源像素为<strong>B</strong>。</p>
<p><strong><center>B' = B + C<sub>0</sub> + ... + C<sub>n-1</sub></center></strong></p>
<p><strong><center>B' = B - C<sub>0</sub> + ... - C<sub>n-1</sub></center></strong></p>
<p><strong><center>B' = B ^ C<sub>0</sub> + ... ^ C<sub>n-1</sub></center></strong></p>
<h3><element id = "10.5.5"> 10.5.5 Blending and the Depth Buffer </element></h3>
<p>当我们进行前面说的几种混合(不包括第一种)的时候,在深度测试的时候可能会有一点问题。
这里我们以加法混合作为例子,其他的混合是差不多的思路。</p>
<p>原文说了那么多其实就是想告诉你如果你开启了深度测试的话,你不按照从后往前的顺序绘制的话,那么就可能有像素因为深度测试而丢弃,从而没有累加到混合的方程中去,导致最后的颜色有点小问题。
因此我就不直接翻译原文了(原文里面有一部分单词语法没理解,我英语是个渣,但是意思是这个意思没错)。</p>
<h2><element id = "10.6"> 10.6 ALPHA CHANNELS </element></h2>
<p>从<a href="#10.5.4"><strong>10.5.4</strong></a>中我们使用<strong>Alpha</strong>分量在<strong>RGB</strong>混合中去控制物体的不透明度。
用于混合的源像素的颜色来自像素着色器。
回顾上一个章节,我们返回<code>Diffuse</code>材质的<strong>Alpha</strong>值作为像素着色器输出的<strong>Alpha</strong>值。
因此<code>Diffuse Map</code>的<strong>Alpha</strong>通道的值就是用于透明度的。</p>
<p>你可以使用图片编辑软件(例如PS)给任何图片加入<strong>Alpha</strong>通道,然后将图片存储为支持<strong>Alpha</strong>通道的图片格式,例如<strong>DDS</strong>。</p>
<h2><element id = "10.7"> 10.7 CLIPPING PIXELS </element></h2>
<p>有时候我们想从正在处理的源像素中完全剔除一部分像素。
我们可以通过时候<code>HLSL</code>内置的函数<code>Clip(x)</code>(<strong>这个函数只能在像素着色器中使用</strong>)。
这个函数在<strong>x < 0</strong>的时候将会丢弃现在正在处理的像素,即这个像素不会被绘制到后台缓冲中去,也不会进行之后的一系列处理。
这个方法通常用于绘制电线或者篱笆贴图。参见<a href="#Image10.6">10.6</a>。我们可以通过这样的方法绘制的时候一些地方不透明一些地方透明。</p>
<p><img src = "Images/10.6.png" id = "Image10.6"></img></p>
<pre><code>float4 PS(VertexOut pIn) : SV_TARGET
{
clip(pIn.color.a - x); //如果当前像素的Alpha小于x,那么就剔除他。
return pIn.color; //返回颜色。
}
</code></pre>
<p>注意的是<code>clip</code>开销也是有的,所以建议只在需要使用的时候使用。</p>
<p>我们可以使用混合做到同样的效果,但是这种方法相比较来说更为有效。
对于一个被剔除的像素来说,之后的关于他的操作会被跳过(例如混合,深度测试什么的)。</p>
<p>图片<a href="#Image10.7">10.7</a>显示了一个混合例子。
他使用透明混合来绘制半透明的水,使用剔除测试(<code>Clip Test</code>)来绘制栅栏盒。
值得注意的是,由于我们可以透过盒子看到盒子的背面,所以我们要关闭背面剔除。</p>
<p><img src = "Images/10.7.png" id = "Image10.7"></img></p>
<h2><element id = "10.8"> 10.8 FOG</element></h2>
<p>为了在游戏中模仿准确的天气环境,我们需要能够实现一个雾的特效。
参见图片<a href="#Image10.8">10.8</a>。可以显然看出雾的效果。
雾可以遮掩原处的物体,并且阻止<code>Popping</code>。
<code>Popping</code>就是当一个物体原本在可视范围(<code>Far Plane</code>)外的时候,然后因为摄像机的移动导致这个物体移动到了可视范围(<code>Frustum</code>)内,因此这个物体就可以被看见。
这样物体突然出现,会有一种比较奇怪的感觉。
但是如果使用雾层的话,那么远处的物体即使出现也会因为雾的效果模糊,从而不会显得那么突然。
注意即使你的场景发生在晴天,你也最好在远处保持一层雾层,因为即使在晴天,远处的物体的出现和消失也是一个和距离有关的函数。我们可以使用雾来模仿这一个大气环境现象。</p>
<p><img src = "Images/10.8.png" id = "Image10.8"></img></p>
<p>我们实现雾的方法:我们需要确定雾的颜色,雾的起始位置距离摄像机多远,雾的范围(这个范围从雾的起始点开始到完全遮掩物体)。
在三角形上面的一个点的颜色是一个权重的平均值:</p>
<p><img src = "Images/10.9.png" id = "Image10.9"></img></p>
<p><strong><center>foggedColor = litColor + s(fogColor - litColor)</center></strong></p>
<p><strong><center>foggedColor = (1 - s) × litColor + s × fogColor</center></strong></p>
<p>参数<strong>s</strong>的范围在 <strong>[0,1]</strong> 之间他是一个关于摄像机到物体某一个面上面的一个点的距离的函数。
随着距离的逐渐增大,这个点会逐渐被雾遮住。<strong>s</strong>的定义:</p>
<p><strong><center>s = saturate((dist(p,E) - fogStart) / fogRange)</center></strong></p>
<p><strong>dist(p,E)</strong> 表示的是摄像机到点的距离。<strong>saturate</strong> 将参数保持在 <strong>[0,1]</strong> 范围内(<strong>大于1就是1,小于0就是0,否则就是原本的值</strong>)。(看图都能看明白...)</p>
<p>图片<a href="#Image10.10">10.10</a>就绘制了这个函数<strong>s</strong>。我们可以看到当<strong>dist(p, E) <= fogStart</strong>的时候,<strong>s = 0</strong>,并且最终的颜色就等于原本的颜色了(<strong>foggedColor = litColor</strong>)。</p>
<p><img src = "Images/10.10.png" id = "Image10.10"></img></p>
<p>换句话来说,雾并没有修改距离摄像机小于<strong>fogStart</strong>的顶点的颜色。
雾只会对那些距离摄像机大于<strong>fogStart</strong>的物体起作用。</p>
<p>我们设<strong>fogEnd = fogStart + fogRange</strong>。当<strong>dist(p, E) >= fogEnd,s = 1</strong>的时候最终的颜色就是雾的颜色了(<strong>foggedColor = fogColor</strong>)。</p>
<p>换句话来说,雾完全遮住了距离摄像机大于等于<strong>FogEnd</strong>的点的颜色,所以你只能看见雾的颜色。</p>
<p>当<strong>FogStart < dist(p, E) < fogEnd</strong>的时候,我们可以看到<strong>s</strong>随着 <strong>dist(p, E)</strong> 的增长而线性增长。
这意味着,当距离越来越远的时候,雾的颜色在最后的颜色中占的比重越来越大,原本的颜色占的比重越来越小了。
当然,随着距离越来越远,雾造成的模糊越来越剧烈。</p>
<p>...省略下面的代码部分。</p>
<p>有些场景并不想使用雾,我们可以定义一个宏定义来在编译着色器的时候决定是否开启雾效。
这样的话,如果我们不想使用雾效的话,就不会产生额外的运算开销了。具体参见<code>D3D_SHADER_MACRO</code>。</p>
<h2><element id = "10.9"> 10.9 SUMMARY</element></h2>
<ul>
<li>混合是一项能够让我们将正在处理的像素(<strong>Source Pixel</strong>)和已经处理好的像素(<strong>Destination Pixel</strong>)进行混合的技术。</li>
<li>混合是有一个混合方程的,注意的是<strong>RGB</strong>和<strong>Alpha</strong>混合是独立的。</li>
<li><strong>F<sub>src</sub>, F<sub>dst</sub>...</strong> 称作混合因素(<code>Blend Factors</code>),他提供了方法让我们自己定义混合方程。对于<strong>Alpha</strong>混合是不能使用带有 <strong>_COLOR</strong> 关键字的参数。</li>
<li>源<strong>Alpha</strong>值来自于漫反射材质。在我们的框架中,漫反射材质定义为一张纹理,并且纹理的<strong>Alpha</strong>通道存储了<strong>Alpha</strong>信息。</li>
<li>源像素可以通过使用<code>HLSL</code>函数<code>Clip(x)</code>来将其丢弃,从而不对其进行进一步处理。这个函数只能在像素着色器里使用,如果<strong>x < 0</strong>的话,那么现在处理的像素就会被丢弃。</li>
<li>使用雾来模拟各种各样的天气和大气远景。在我们的线性雾模型中,我们定义一个雾的颜色,一个雾的起始点(距离摄像机的距离),一个雾的范围。一个面上的点的颜色将使用方程来计算他的颜色。</li>
</ul>
<p>终于写完了。虽然感觉和原版差距好大。但是我觉得应该能够看懂吧?<strong>2333</strong>。
感觉原版的书及其啰嗦啊,虽然很厉害就是了。</p>