深度排序与alpha混合
原文:
https://blogs.msdn.microsoft.com/shawnhar/2009/02/18/depth-sorting-alpha-blended-objects/
翻译:李现民
最后修改:2012-07-03
“为什么我的透明物体的绘制顺序是错误的,或者为什么它们的一部分不见了?”
当绘制一个3D场景的时候,将图形按深度排序非常重要,只有这样靠近摄像机的物体才能被绘制在(离摄像机)更远的物体的上面。我们不会希望远方的山脉被绘制在近在眼前的建筑物的上面!
当前得到广泛应用的深度排序技术有三种:
不幸的是,每种技术都有它的局限性。为了获得好的绘制结果,大多数游戏需要依赖于以上三种技术的组合。
1 深度缓冲
深度缓冲是一种简单而有效的办法,并且当你只绘制不透明物体时,其绘制结果非常完美,但该方法无法处理透明的物体!
这是因为深度缓冲算法仅仅跟踪记录了到目前为止所绘制的最近像素点,对不透明物体而言这足够了。举例说来,如果我们需要绘制两个三角形,A和B:
如果我们按照先B后A的顺序绘制,则深度缓冲会发现来自于A的新像素点比之前绘制的来自于B的像素点要近,因此直接覆盖绘制就可以了。如果我们按照相反的 顺序绘制(先A后B),则深度缓冲会发现来自于B的像素点比已经绘制的来自于于A的像素点要远,因此将会直接丢弃它们。无论哪种情况下我们都会得到正确的 结果:A在上面,而B在后面被隐藏。
但如果物体(几何体)是透明的怎么办?也就是物体B部分可以见时(透过物体A的半透明三角面片)。如果我们按照先B后A的顺序绘制,仍然会得到正确的结 果,但反之就会出错了。在第二种情况下,深度缓冲会首先从B得到一个像素点,然后发现已经绘制了某个来自于A的更近的像素点,但却不知道如何处理这种情 况。它仅有两种选择是:绘制B上的像素点(结果将是错误的,因为这会将更远处的B混合到更近的A之上,但alpha blending的顺序是不可交换的)或者直接将B整个丢弃。这很不好!
结论:深度缓冲对不透明物体是完美的,但对透明物体却没什么用。
2 画家算法
既然深度缓冲算法无法以错误的顺序正确绘制透明物体,那么一定存在一个简单的修正办法,对吧?只要我们总是保证以正确的顺序绘制就可以了!我们首先将场景 中的所有物体排序,这样我们就可以先绘制远处的物体,然后在其上绘制更近一些的物体,这样就可以保证前面示例中的B物体总是在A物体之前绘制。
不幸的是,这说起来容易做起来难。在很多情况下将对象排序是不够的。例如,A和B相互交叉的情况该怎么办?
这种情况很可能发生:比如说A是一个玻璃杯而B是一个放在里面的玻璃珠。现在根本无法以正确的方式对它们进行排序,因为A的一部分比B更近,但另一部分却更远。
我们甚至不需要使用两个单独的物体重现这个问题。组成玻璃杯的那些三角面片怎么处理呢?为了使结果看起来是正确的,我们需要在绘制玻璃杯的正面之前先绘制其背面。因此仅仅将物体进行排序还不够:我们真正需要的是排序每一个三角面片。
困难在于,将每个三角面片都排序的代价是极其昂贵的!而且即使我们可以承受这种代价,这也不足以保证在所有情况下都可以得到正确的绘制结果。比如两个透明的三角形相互交叉的情况如何处理?
没有办法对这些三角形排序,因为我们需要将B的上半部分绘制在A的前面,同时将其下半部分绘制在A的后面。唯一的解决办法是在检测这种情况发生时将这些三角形在它们相交的地方拆分,但这种做法的代价过于高昂了。
结论:画家算法需要你对排序的粒度作出权衡。如果仅仅对少量大型物体进行排序,则算法会非常快但精确度不高;反之,如果对大量小型物体进行排序(极限情况是对三角面片排序),则算法会很慢但会更加精确。
3 背面剔除
人们通常不认为背面剔除是一种排序技术,但事实上它是的确是一种重要的(排序)方法。它的局限性在于仅仅适用于凸面体。
考虑一个简单的凸面体,比如一个球体或一个立方体。无论你从哪个角度观察它,每一个屏幕像素都会被精确的覆盖两次:一次被物体的前面覆盖,另一次是被它的 背面覆盖。如果使用背面剔除丢弃物体背面的三角面片,那么就只剩下前面的了。哈哈!如果每一个屏幕像素只被覆盖一次,那你自动就会获得完美的alpha blending结果,而不需要任何排序。
当然,大多数游戏不会仅仅绘制球体或立方体:),所以背面剔除本身并不是一个完整的解决方案。
结论:背面剔除对凸面体是完美的,但对于其它的就无能为力了。
4 我该如何让游戏看起来更好一些?
最常用的方法是:
设置 DepthBufferEnable 与 DepthBufferWriteEnable 为 true
绘制所有的不透明物体(几何体)
保持 DepthBufferEnable=true,但修改 DepthBufferWriteEnable=false
将物体按它与摄像机之间的距离进行排序,然后以从后向前的顺序绘制
这种方法依赖于前述三种排序技术的组合:
不透明物体使用深度缓冲排序
透明物体与不透明物体仍然会被深度缓冲处理(所以你永远不会透过一个不透明物体看到一个透明物体)
画家算法按物体的相对关系对透明物体排序(如果两个透明物体相交的话会引起排序错误)
依赖背面剔除对单个透明物体上的所有三角面片进行排序(如果透明物体不是凸面体则会引起排序错误)
结果并非完美,但却非常有效并易于实现,而且对大多数游戏而言已经足够好了。
有很多方法可以用于改进排序的精确度:
避免alpha blending!你 的不透明物体越多,排序就越容易,也越精确。你真的需要在每个地方都使用alpha blending嘛?如果你的关卡设计需要在玻璃窗上再加一层,那么是否可以考虑修改设计以便实现起来更加容易呢?如果你正在使用alpha blending实现诸如树木之类的裁剪(cut-out)图形,是否可以考虑使用alpha test替代?就是简单地考虑接受/拒绝两种情况,这样被接受的像素点由于是不透明的,因而仍然可以使用深度缓冲排序。
放松,不要紧张。也许排序错误实际上并不那么糟糕呢?也许你可以试着调整显卡(使alpha通道更加柔和,更加半透明化一些)使排序错误看起来并不那么明显。在我们的3D粒子采样中就使用了这种方法,我们并没有尝试对单个烟雾中的粒子排序,而是挑选了一个粒子纹理使它看起来是OK的。如果你将烟雾纹理换成更加不透明的,那么排序错误就会变得比较明显了。
如果你的alpha混合模型不是凸面体,也许你可以试着将它们改的更加“凸”一些呢?即使不是完美的凸面体,只要它们越接近凸面体,排序错误就会越少。考虑将复杂的模型拆分成可独立排序的多个部件。比如一个人体模型无论如何都不是凸面体,但如果你把它拆分成躯干、头、手臂等,那么每一部分都可以近似认为是凸面体了。
如果你的纹理遮罩(texture masks)基本上是用于开/关裁剪(cut-outs)的,只是边缘部分有一些透明的像素用于反走样,你可以使用双pass绘制技术:
Pass 1:绘制不透明部分:关闭alpha blending,并且alpha test只接受100%不透明的区域,深度缓冲开启(补充:深度写入开启)
Pass 2:绘制边缘部分:开启alpha blending,并且alpha test只接受alpha < 1的像素,深度缓冲开启,深度写入关闭
以将物体绘制两次为代价,这种方法为纹理中间不透明部分提供了100%正确的深度缓冲排序,以及相对精确的半透明边缘部分排序。这是一种很好的方法,既对纹理裁剪的边缘部分做了一些反走样,同时也利用了深度缓冲的优点避免了对单个树木或草叶进行额外的排序。我们在广告牌采样中使用了这种技术:请参考Billboard.fx中的注释与effect passes部分。
使用z prepass。当你需要淡出一个正常状态下不透明的物体而又不想透过它自己的近端部分看到它的远端部分时,这是一种非常好的技术。假如从右边观察一个人 体。如果它是玻璃做的,那么你会期望透过它的右手臂看到躯干和左手臂。但是如果在整个淡出过程中它是一个实体人的话(不透明,也许是幽灵,或者正在传送, 又或者被杀死后正在重生),你会期望只看到透明的右手臂部分,以及它后面的背景,而不会同时看到躯干与左手臂。要达到这种效果需要:
设置 ColorWriteChannels=None,并启用深度缓冲
绘制物体到深度缓冲(但不影响颜色缓冲)
设置 ColorWriteChannels=All, DepthBufferFunction=Equal,并启用alpha blending
重绘物体,这时只有物体的最近端才会被混合到颜色缓冲中
深度排序与alpha混合的更多相关文章
- 深度排序与alpha混合 【转】
翻译:李现民 最后修改:2012-07-03 原文:Depth sorting alpha blended objects 先说个题外话,本来我想回答在 Creators Club论坛上的一个常见 ...
- 【转载】Alpha混合物体的深度排序
原文:Alpha混合物体的深度排序 先说个题外话, 本来我想解答一下最近Creators Club论坛上经常出现的一个问题, 意外的是在网上竟然找不到什么全面的答案.. 这是个有着复杂答案的简单问题: ...
- Unity3D ShaderLab 修改渲染队列进行深度排序
Unity3D ShaderLab 修改渲染队列进行深度排序 为了更深刻的理解透明度,我们还需要学习一下深度排序,简单来说就是物体被渲染的先后顺序. Unity允许我们通过代码来控制某个特定物体渲染到 ...
- D3D中深度测试和Alpha混合的关系
我在学习D3D的深度测试和Alpha混合的时候,有一些遗憾.书上提供的例子里说一定要先渲染不透明物体,再渲染透明物体,对渲染状态的设置也有特殊要求.我看的很晕.自己查图形学的书,上网找资料,结果还是糊 ...
- 【Unity Shaders】Transparency —— 使用渲染队列进行深度排序
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 【转载】D3D深度测试和Alpha混合
原文:D3D深度测试和Alpha混合 1. 深度测试 a) 深度缓冲区:屏幕上每个像素点的深度信息的一块内存缓冲区.D3D通过比较当前绘制的像素点的深度和对应深度缓冲区的点 ...
- 《逐梦旅程 WINDOWS游戏编程之从零开始》笔记8——载入三维模型&Alpha混合技术&深度测试与Z缓存
第17章 三维游戏模型的载入 主要是如何从3ds max中导出.X文件,以及如何从X文件加载三维模型到DirextX游戏程序里.因为复杂的3D物体,要用代码去实现,那太反人类了,所以我们需要一些建模软 ...
- 【STM32H7教程】第56章 STM32H7的DMA2D应用之刷色块,位图和Alpha混合
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第56章 STM32H7的DMA2D应用之刷色块, ...
- DirectDraw打造极速图形引擎(Alpha混合)
显然DirectDraw是Windows下写2D图形程序的最好选择,虽然Direct3D也可以写,但是没DirectDraw简单方便,特别对于初学者,一来就接触那么多函数和参数总不是件愉快的事,所以我 ...
随机推荐
- js五星好评2
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Dapper Extensions中修改Dialect
如果是MySql数据库,则修改为:DapperExtensions.DapperExtensions.SqlDialect = new MySqlDialect(); DapperExtensions ...
- How To View the HTML Source in Google Chrome
Whether you are new to the web industry or a seasoned veteran, viewing the HTML source of different ...
- 网络流-最大流 Dinic模板
#include <bits/stdc++.h> using namespace std; #define MP make_pair #define PB push_back #defin ...
- 谷歌插件--Advanced REST client
早上在测试调用服务去获取数据的时候,因为自己的单元测试不是很熟悉,问了同事,同事给我介绍了一个插件Advanced REST client,这个可以在谷歌的“扩展与应用”中找打,使用 安装之后会提示要 ...
- shell 命令总结
删除0字节文件 find . -type f -size 0 -exec rm -rf {} \; find . type f -size 0 -delete 查看进程,按内存从大到小排列 ps -e ...
- 开启你的kali linux DIY之旅
更新源 首先 是kali2016.2更新源的问题,网上找了好久,都不是很满意.后来把kali 2016.2安装到实体机中才发现,里面自带了更新源.下面我把这个官方自带更新源分享出来(速度蛮快的) de ...
- Unity3D关于VR的Demo(一)
https://blog.csdn.net/qq_15807167/article/details/52048998?locationNum=8&fps=1 阅读数:9716 最近有点忙,只有 ...
- bzoj1634 / P2878 [USACO07JAN]保护花朵Protecting the Flowers
P2878 [USACO07JAN]保护花朵Protecting the Flowers 难得的信息课......来一题水题吧. 经典贪心题 我们发现,交换两头奶牛的解决顺序,对其他奶牛所产生的贡献并 ...
- P4289 [HAOI2008]移动玩具(bfs)
P4289 [HAOI2008]移动玩具 双向bfs+状态压缩+记忆化搜索 双向bfs用于对bfs的优化,每次找到可扩展节点少的一边进行一次bfs,找到的第一个互相接触的点即为最短路径 矩阵范围仅4* ...