您的位置:首页 > 其它

Alpha混合物体的深度排序[译]

2009-09-21 11:48 253 查看
原文地址:http://blog.csdn.net/xoyojank/archive/2009/02/21/3918091.aspx


Alpha混合物体的深度排序

document.body.oncopy = function() {
if (window.clipboardData) {
setTimeout(function() {
var text = clipboardData.getData("text");
if (text && text.length>300) {
text = text + "/r/n/n本文来自CSDN博客,转载请标明出处:" + location.href;
clipboardData.setData("text", text);
}
}, 100);
}
}

function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}

先说个题外话
,
本来我想解答一下最近
Creators Club
论坛上经常出现的一个问题
,
意外的是在网上竟然找不到什么全面的答案
..

这是个有着复杂答案的简单问题
:



为什么我的透明物体的绘制顺序不对

,

或者有些不见了

?”

当绘制一个
3D
场景时
,
对图形进行深度排序是非常重要的
,
这样离镜头近才画在远处物体的前面
.
我们不会希望看到远处的山把近在眼前的建筑给挡住了
!

如今有三种深度排序方法得到了广泛的应用
:

深度缓冲

(
也叫做
z-buffering)

油画家算法

背面剔除

不幸的是
,
每种都有其局限性
.
为了达到好的结果
,
大多数游戏是把三种方法结合起来使用的
.

深度缓冲


深度缓冲简单而有效
,
结果也很完美
.
但是对于透明的物体它就无能为力了
!

这是因为深度缓冲只记录了当前已经绘制的最近像素
.
对于不透明的物体
,
这已经能够满足我们的需要了
.
看一下这个绘制两个三角形的例子
, A

B:



如果我们先画
B
再画
A,
深度缓冲会看到新的像素
(A

)
比之前的
(B

)
要近
,
那么它就画在了前面
.
如果我们用相反的顺序画
(

A

B),
深度缓冲会看到
B
的像素比之前
A
已经画的要远
,
所以就把它们给丢弃掉了
.
无论哪种情况我们都会得到正确的结果
: A
在前面
, B
隐藏在后面
.

但是当这些几何图形是透明的
,

B
透过
A
是部分可见的时会怎样呢
?
如果我们先画
B
再画
A
的话是没有问题的
,
但反过来就不行了
.
在这种情况下
,
深度缓冲会从
B
取一个像素
,
同时注意到已经绘制了一个更近的像素
(A

),
然后它就没辙了
!
唯一的选择是绘制
B(
这会得到一个错误的结果
, B
会画在
A
前面
,

A

alpha
混合却没有起作用
),
或者完全抛弃
B.
不爽
!

结论
:
深度缓冲对于不透明的物体是很完美的
,
但对于透明的物体却不实用
.

油画家算法


深度缓冲没法应付以错误的顺序来绘制透明物体的情况
,
这很好解决
,
对吧
?
保证它们按正确的顺序绘制就可以了
!
如果对场景中的所有物体进行排序
,
那我们就可以先画远处的
,
再画近处的
,
这样就可以确保前面例子中的
B
可以在
A
之前绘制
.

不幸的是
,
这说起来容易做起来难
.
对物体进行排序在很多情况下并不适用
,

A

B
相交的情况该怎么办
?



如果
A
是个玻璃杯而
B
是它里面的一个玻璃球时就是这样
.
现在我们就没法对它们进行排序了
,
因为
A
的一部分比
B

,
而另一部分又比
B

.

甚至我们不需要两个不同的物体来复现这个问题
.
组成玻璃杯的那些三角形会怎样
?
要让它们显示正确
,
需要在前面的绘制之前先绘制后面的
.
所以
,
只对物体进行排序是不够的
:
我们要对每一个三角形进行排序
.

问题是
,
对每个三角形进行排序的代价太大
!
就算我们能够承受
,
这也不是在所有的场合下都能得到正确的结果的
.
比如说两个透明的三角形相交时会怎样呢
?



没有方法对这样的三角形进行排序
,
因为我们需要把
B
的上半部分画在
A
的前面
, A
的下半部分画在
B
的前面
.
唯一的解决方案就是把三角形从相交处分割开来
,
但是这样的消耗是不可承受的
.

结论
:
油画家算法需要你在选择排序的粒度好好权衡一下
.
如果你仅仅对一些大的的物体进行排序
,
速度很快但不是很精确
;
如果你对一些小物体进行排序
(
包括三角形个体的极限情况
),
速度会慢一些
,
但更加精确
.

背面剔除


一般不把背面剔除当成是一种排序技术
,
但它确实是一种重要的方法
.
它的局限性就是只适用于凸面体
.

考虑一下一个简单的凸面体
,
如一个球体或立方体
.
无论你从哪个角度看
,
每个屏幕上的像素都会被覆盖两遍
:
一次是物体的前面
,
一次是后面
.
如果你用背面剔除丢弃了背面的三角形
,
那就只剩前面了
.
哈哈
,
如果每个屏幕上的像素只进行一次判断
,
那你就自动得到了一个完美的混合结果
,
没有必要排序任何东西
.

当然
,
大多数的游戏不会只画球体或立方体
J

所以只是背面剔除的话不是一个妥善的解决方案
.

结论
:
背面剔除对于凸面体是完美的
,
但是对于其它的就无能为力了
.

我该怎样让我的游戏看起来更好一些

?


最常用的方法
:

1.

设置
DepthBufferEnable

DepthBufferWriteEnable

true

2.

绘制所有的不透明物体

3.

保持
DepthBufferEnable

true,
但是设置
DepthBufferWriteEnable

false

4.


alpha
混合的物体按照与摄像机的距离进行排序
,
然后从后到前画出来

这依赖于三种排序技术的结合
:

不透明的物体按深度缓冲排序

透明物体和不透明物体仍然会被深度缓冲处理
(
所以你永远不会通过一个不透明物体看到一个透明的
)

油画家算法对透明的物体排序
(
两个透明物体相交时仍然会有排序错误
)

依赖背面剔除来对单个透明物体上的三角形排序
(
如果物体不是凸面体也会产生错误
)

结果并不是非常完美
,
但是非常高效
,
易于实现
,
对于大多数游戏来说也够用了
.

当然还可以采取一些措施来改进排序的精确度
:

避免

alpha

混合

!
你的不透明物体越多
,
排序就越容易
,
也越精确
.
仔细思考一下
,
真得每个地方都需要
alpha
混合吗
?
如果关卡设计师要在玻璃窗上再加一层
,
那你应该考虑把设计改成更易于实现的方案
.
如果你正使用
alpha
混合来绘制树木之类的图形
,
那考虑用
alpha
测试来代替它
,
只分完全透明和完全不透明这两种情况
,
这样不透明的地方仍然可以通过深度缓冲来排序
.

放松

,

不用担心

.
可能排序错误并不是很严重呢
?
你可以试着调整一下你的图形
(

alpha
通道更加柔和
,
更加透明一些
)
来让这个错误看起来没有那么显眼
.
这个方法用在了我们的

Particle 3D sample


,
它并不会对单独一个烟雾中的粒子进行排序
,
而是选择了一个合适的粒子纹理让它看起来是好的
.
如果你把烟雾的纹理换成更加不透明的
,
那排序错误可能就比较容易觉察了
.

如果你有透明物体不是凸面体
,
或许你可以尝试让它们更加







一些

?
就算它们不是完全地凸面体
,
那它们越


”,
排序错误就越少
.
还有就是考虑把复杂的模型分成多块
,
这样它们就可以分开进行排序
.
一个人体看起来一点也不像凸面体
,
但你把它分成头
,
胳膊
,
驱干等几部分后
,
每一块都接近凸面体了
.

如果你有部分区域透明的纹理
(
如树叶
),
并且图案边缘包含了一些半透明的像素用于反走样
,
那你可以使用

pass

渲染技术

:

Pass 1:
绘制不透明部分
: alpha
混合关闭
, alpha
测试只接受
100%
不透明的区域
,
深度缓冲开启

Pass 2:
绘制边缘
: alpha
混合开启
, alpha
测试设置只接受
alpha<1

,
深度缓冲开启
,
深度写入关闭

以每个物体渲染两次的代价
,
为纹理中间完全不透明的部分提供了
100%
正确的深度缓冲排序
,
和相对精确的半透明边缘排序
.
这个方法为纹理裁剪的边缘进行了一些反走样
,
同时也保证了不用对每一棵树或者草叶进行额外的排序
.
在我们的

Billboard sample

中使用了这个技巧
:
请阅读一下
Billboard.fx
中的
pass
和注释
.

使用
z prepass

.
当你需要淡出一个原来不透明的物体又不想透过它看到的是它自己的另一部分时
,
这是一个好方法
.
例如从右边看一个人类的身体
.
如果它是玻璃做的
,
你应该会希望透过右手臂看到躯干和左手臂
.
但如果它是实心的
(
不透明
)
你会希望透过右手臂看到后面的背景
,
而不应该是躯干和左手臂
.
要达到这个目标需要这样做
:

设置
ColorWriteChannels=None,
开启深度缓冲

绘制物体到深度缓冲
(
不影响颜色缓冲
)

设置
ColorWriteChannels=All, DepthBufferFunction=Equal,
开启
alpha
混合

再次绘制这个物体
,
这样就只有最近的这一面与颜色缓冲进行混合了

Published Wednesday, February 18, 2009 1:47 PM by

ShawnHargreaves

(xoyojank:

这篇文章有助于理解
N3


Batch

机制
,

呵呵
)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: