入门图形学:透明原理

     因为我们后面要大量使用透明效果(或者说透明效果是实现我们需要的着色特效的基础部分),所以这里就来非常通俗详细说一下透明的原理。

     先来从最简单易懂的二维层面来讲解。

     我猜大家使用最常见的透明效果就是在ui中使用.png(带有透明通道)的sprite吧,我们在ps中通过背景透明的模板文件制作保存的.png图片就是这个了,.png的图片在rgb三通道分量之外还附带一个alpha通道分量,这个通道分量专门控制“透明度”,这里为了方便理解我专门用ps做一张.png图片,如下:

     

     左边是ps中制作的256*256分辨率背景透明的图片呈现效果,通常为了表达这个背景是透明的,ps都是使用灰白棋盘格表达背景色。右边则是这张.png图片提交到博文中表现的效果,因为博文背景色为白色,所以.png灰白棋盘格透明部分就变为白色了,这也很符合预期。

     原因就是256*256的.png图片的透明棋盘格部分显示成白色的背景色了,这其中就涉及到了颜色混合。我们先看一看同样的.psd文件保存成.png和.jpeg的数据属性,如下:

     

     左边为.png右边为.jpg,一眼就看出来每个像素的差别,.png比.jpg的位深度(位深度)多了8个bit,这里并不代表.png就比.jpg的色彩表现力更强,这8个bit就是代表的alpha分量,这个alpha就是“透明度”的计算机数学表示了,我们使用最简单的背景前景混合计算方式就可以对一个像素进行颜色混合操作了,如下:

     pixel color = (alpha/2^8) * foreground pixel color + (1 - alpha/2^8) * background pixel color

     这种计算方法,当我们的pixel处于透明灰白棋盘区间(alpha = 0)时,则渲染的颜色值就变成了纯的background color,当然了,alpha既然占有8个bit,我们也可以设置成alpha = 2^6(64),则表现得颜色为“四分之一”前景色和“四分之三”背景色的混合体,如下:

     

     最左边为ps中显示图,中间为上传到博客的混合图,最右边为ps中的参数图,很明显就能看出来了,背景透明25%的blue color填充,经过background white color和foreground blue color混合后就成了上面那样子。

     这种计算方式也是符合物理上的实际情况的,我们的物理硬件显示器关机后就是一块黑色面板(这里不讨论其他黑科技显示器),同时我们的液晶面板像素点,也只是RGB三原色组成,意味着物理上就不存在alpha这个概念,而显示器的最底层的背景色则为黑色。而图形学中的“透明”也就意味着一层一层的颜色混合,当我们在原始黑色背景色的基础上混合一张.png图片,则得到alpha控制的混合权重计算后的颜色,接下来就是一层一层混合处理了,这也就是纯二维的“透明”渲染最详细的讲解了。

      接下来讲解三维中“透明”渲染的原理,首先和二维ps中的图层概念一样,我们也需要将场景中的三维物体进行一个前后排序,从渲染流程上来讲,模型网格顶点从建模空间变换到世界空间变换到视口空间变换到裁剪空间最后到标准设备空间,其中就在视口空间进行顶点的“前后”排序,距离视口点越近就代表越靠前,接着经过裁剪变换和透视除法以及视图变换,就生成了一个名为Depth(Z)的数值,这个Depth在三维空间中就代表了顶点到视口的距离,在标准设备空间中则代表了片段的前后关系,Depth越小代表距离相机越近,也代表“片段图层”中更上面。Depth就储存在名为深度缓冲区的显存空间中。

      既然存在Depth这个值,那么图形渲染中渲染一个新片段A的过程中,则需要将新片段A与已渲染的片段B进行Depth比较测试,如果A的Depth比B的Depth大,则表面距离“眼睛”更远,则可以丢弃掉,如果A的Depth比B的Depth小,则片段A渲染覆盖片段B,同时更新新的Depth值,这两个过程一个(比较Depth的过程)叫深度测试(ZTest),一个(更新Depth值的过程)叫深度写入(ZWrite),都是可以通过CG api访问控制的。

      以上是渲染过程中处理渲染前后关系的常用方法,假如我们的新片段A是透明(半透明)的呢?或者说我们设置片段A的alpha<1.0。那么渲染过程中,就需要将片段A与已渲染的片段B进行Depth比较(ZTest),然后确定是否渲染,同时使用和二维透明混合一样的计算方法,进行最终颜色的混合计算,得到一个新像素颜色值。当然这里我们并不更新Depth(ZWrite Off),因为前者片段A属于透明(半透明),依赖片段B进行颜色混合,如果我们更新了Depth(ZWrite On),那么假如片段A所在的三维空间中的物体存在背面,则理想情况是其背面应该要被渲染出来的,假设背面经过一系列空间变换后变为片段C,那么片段C的Depth就要比片段A的Depth大(距离“眼睛”更远),则被抛弃掉了。如果我们不更新Depth(ZWrite Off),则片段C保留渲染,同时进行颜色混合。

      但是关闭写入Depth(ZWrite Off)会导致一系列错误的渲染情况,比如我们渲染一个透明(半透明)片段A,再渲染一个不透明片段B。三维空间中的情况是,片段B所在的顶点在片段A所在的顶点“后面”(距离“眼睛”更远),因为渲染片段A并没有更新Depth,结果导致片段B的Depth可能比深度缓冲中Depth要小(距离“眼睛”更近),则不透明片段B将半透明片段A给覆盖了。

      所以说渲染中想要处理透明渲染,则需要提前处理不透明渲染。

      CG中实现透明效果:

      第一种方法就是,判断一个片段的alpha是否满足一定条件(也就是大于某个阀值),一般就是片段A.alpha>float(0.0f-1.0f),如果alpha大于阀值,则按照不透明片段流程覆盖渲染(ZTest,ZWrite),如果alpha小于这个阀值,则直接丢弃片段,这种方式消耗低性能高,但是只能处理透明和不透明两种情况,学名叫AlphaTest。

      第二种方法就是颜色混合,通过alpha这个系数进行颜色混合计算,和二维的透明颜色混合一样,假如我们的颜色缓冲区已存在片段B的颜色值,现在需要渲染不透明片段A,则新的颜色值为:

      newColor = A.color * A.alpha + B.color * (1-A.alpha)

      随后将newColor更新到颜色缓冲区,当然颜色混合的计算公式不仅仅是上面一种而已。

      这个过程的学名叫AlphaBlend。

      以上就是二维和三维中的透明原理,具体的代码编写在以后的着色器中,我们实际操作和讲解,因为原理理解以后,随便看一个Shader代码就能明白如何制作透明效果了。

      so,我们接下来继续。

  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值