当前位置: 首页 > cg教程 > 建筑表现教程

解决三维出图黑白边缘溢出问题:STRAIGHT与PREMULT ALPHA剖析

  • 2015-12-15 22:30:11
  • 作者/来源:戴老师/安之语博客
  • 己被围观
  • 被点评
像素抗锯齿的基本概念:  首先是黑边白边产生的原因:AA抗锯齿,要明确我们渲染出来的图都是位图,在位图中只有完全水平或者垂直的直线才有可能是所有像素都是100%不透明的,任何斜

像素抗锯齿的基本概念:

  首先是黑边白边产生的原因:AA抗锯齿,要明确我们渲染出来的图都是位图,在位图中只有完全水平或者垂直的直线才有可能是所有像素都是100%不透明的,任何斜线都必然带有很多透明像素才能让视觉效果看着像斜线。这就是抗锯齿。
1  从小图看的话,就是黑色的直线。
2  从大图看的话,实则斜线的边缘很多黑色都是半透明的。
  因为每个像素都是一个方格,而且并不是无限小,你想象一根有粗细的绳子放在一片小格子上,那么有的格子会被绳子完全沾满,有的可能占了一半,有的可能占了三分之一。那么占满的格子就显示纯黑色,占了一半的格子就显示50%黑,占了三分之一的格子就显示33%黑。从近处看是很多像素方格,但是放远了看,就很像一根斜线了。这是位图对图形的一种近似描述方式。而轮廓线边缘这种像素呈半透明现象要做抗锯齿(Anti-Aliasing)。叫这个名字的原因是,如果边缘轮廓像素都是纯黑色的话,图像看起来就会有明显的锯齿感!
3  当我们在三维软件里渲染某一个物体或者角色,并且这个物体或者角色没有占满整个画面的时候。我们就会面临这个轮廓边缘的问题。同样是由于抗锯齿,渲染的物体的边缘像素都是半透明的。4  

Alpha的两种类型,直通和预乘:

  带Alpha的图片有两种计算方法,一种叫做直通Alpha(Straight Alpha),一种叫做预乘Alpha(Premultiplied Alpha)。这两种类型在AE软件里是市场可以看到的,当你导入一张带通道的图片,比如说TGA,AE就会问你,这张图的Alpha是怎么来的,是直通类型还是预乘类型。
  这两种类型的唯一区别在于,直通Alpha图片保留最原本的RGB数值;而预乘Alpha,是原本的RGB信息乘以Alpha的数值以后得到的结果(预乘意思就是预先乘以alpha)。
5  我在三维软件中渲染了一个透明的球。
6  它的Alpha通道明显是这样的。
  但其实这张图片原本并不是你看到的那样,它其实应该是下图所示,你看到的球里面多出来的颜色,是场景本身的背景。过程是这样的:本来完好的一张图,前景和背景都有,你分开渲染的时候,前景部分使用alpha通道抠除,就会得到透明效果,而没有被alpha通道抠除之前的效果呢,就是下图这样的。这两种状态呢,其实就是对应的我们Alpha通道的两种计算方式:直通和预乘。
7  这张图其实就是一张直通类型的图片,它其实就是我们之前透明的预乘图片,除以Alpha值得到的结果,这一个除法的步骤,相当于预乘的原图乘以Alpha的逆运算,抵消掉了,就可以得到原始的可以看到背景的图片了;图中黑色区域没有原先的背景,是由于我们这张直通的图片,是通过预乘的图片除以Alpha通道的值得来的,因为纯透的地方Alpha值为0,任何数除以0都得到无限大,所以这些地方原本的信息已经丢失了,只好显示黑色。
  所以你会发现,直通和预乘,就是两个逆运算的关系。你原图不用Alpha去抠,就是直通的效果,乘以Alpha以后就是预乘的效果,预乘的效果除以Alpha又得到直通的效果。这就是直通和预乘的关系。
  那么定义这两种Alpha的计算方式又有啥意义呢?且慢慢道来。

直通和预乘的计算方式:

  如果我们要把它合成到一个另外的背景上面去,我们把这层本身叫做A,下面的背景层叫做B。
  那么在我们最最熟悉的ps里的透明度算法就是这样的:result(结果色)=A*alphaA+B*(1-alphaA)。这个公式就像是在算计两个图层的贡献值一样,当alpha为白的时候,则结果色全部都来自图层A,背景被掏空;当alpha为黑的时候,则结果色全部来自图层B,前景完全消失,只看到背景;当alpha为0.5的时候,则前景和背景各贡献一半,则看起来就是半透明的。
  当你有照片A和B,你把A放在B的上面,把A图层的透明度往下调的时候,就相当于启用上面的公式了,透明度为100%,完全显示A照片,透明度为50%,各显示一半,透明度为0%,则完全显示B照片。因为这个操作实在是太熟悉了,所以很多人会忽略在调节透明度的过程中,A图层的透明度在下降,同时B图层的透明度也在上升。在A图层的透明度为100%的时候,B图层等于透明度是0%的,所以你才在最终结果里只看到A图层。
  然而,三维软件里面渲染出来的图片,对于alpha的理解是使用的预乘类型。是另一种算法:result=A+B*(1-Alpha)
  这个本质上还是跟上面说的透明度变化公式是一致的。虽然这里的A没有乘以A图层本身的Alpha,但是你还记得,预乘类型的图片,已经相当于在原始图片上乘以一个Alpha了。所以最终result = A原始(或者直通)*Alpha +B*(1—Alpha)。 
  这么说来,你会感觉,预乘就是为什么会有预乘这种奇怪的算法?因为图片先乘了以后,再合成的时候就少算一个步骤,速度会快。这应该是当时开发的时候的思路。

PS中渲染输出黑白边解决方法:

  说回图片黑边白边的问题。当你把渲染好的图片导入ps的时候,比如说你用的TGA,那么,你会有一个背景为黑色或者白色的实心图片,顺带一个alpha通道,你要把这个图合到背景上去,你就会用alpha把图抠了,再放到背景上去。这时候图片就会有黑边,除非你背景是黑色看不出来。
8  原因是这样的,你导入ps的图片本身是这样的。
9  这是三维软件里直接渲染出来的图,是一种预乘类型的图。也就是说,这图已经被Alpha乘过了,你就可以理解成已经被Alpha扣过了。但是PS这个软件吧,它理解不了这个事情,它以为世界上所有的图片都是直通类型的,就是没有过被Alpha扣过的图。也就是说,它以为这图本身的背景就是黑色的。而其实这个图是有一个黄色背景的。
  然后,你在PS里面做的事情是用Alpha通道去扣这张图,而这张图原本其实是一张预乘的图。或者说,这张图已经被Alpha扣过一次了,那么你再去扣一次,会让很多地方看起来比从前更黑,于是黑边就这么产生了。(在图像边缘,有抗锯齿的地方,抗锯齿就是透明,我就不放大看了。)
而在ps里面想要解决的方案是,把一张直通的图导进去。
10  再用Alpha去抠,才能得到正确的结果。
11  MentalRay里渲染得到直通图的方法是勾除Premultiply,Vray里无法实现。

PS中消除黑白边的另外一个方法:

  这一段内容呢,在提供方法的同时,更能够加深你对alpha通道的理解。讲得非常棒。
  方法是把背景层给抠一个洞,是用的是前景层Alpha的反向。前景层是用Add(Linear Dodge线性增加)模式叠加在背景层上,也是可以得到完美的效果的。
12  原理是这样的,前景层我么用的是一张三维软件里面渲染出来,默认预乘了的图。就相当于是A*alphaA,背景层原本是B,我们要得到正确的结果就要往公式上靠:
result=A*alphaA+B*(1-alphaA)

  在PS里面,正常叠加模式的公式其实是result=A*alphaA+B*(1-alphaA)

  我们一般的做法,把前景层用通道抠一次,直接用normal叠在背景上,得到的结果其实是:
  result=A*alphaA*alphaA+B*(1-alphaA)
  因为前景层本身就是A*alphaA;背景层是B。

  Add叠加模式的公式是:
  result=A+B
  如果直接把前景层用add模式叠在背景上,得到的结果是:
  result=A*alphaA+B
  视觉结果是过亮的。

  为了得到正确的结果,我们需要手动把背景层反抠一个洞,模拟ps在做透明度合成时背后发生的事情,才能得到正确的结果。
  当使用A图层的alpha的反向去抠B图层,则B图层就变成了B*(1-alphaA)
  最后的结果就变成:
  result=A*alphaA+B*(1-alphaA)
  这就是上图我在ps里做的事情,结果是非常正确的,只不过前景层很不好移动位置。因为要同时移动前景层和背景层的反向Alpha(在我上面的图中就是背景层的遮罩),我还真不知道怎么能很方便地一起移动他们。  

在AE及Nuke中解决Alpha的问题:

  因为ps它本身不是一个针对做三维合成用户的后期软件,所以在面对Alpha问题的时候,能力很弱,解决起来很麻烦。但是在真正的能做合成的后期软件中,解决起来是很方便的。
  使用AE导入TGA文件,选择预乘类型,背景色为你渲染的时候背景的颜色。问题就完美解决了。(注意,在分层渲染,分离前景的时候,要保证背景是纯色,尽量是纯黑,要是你垫在一个五颜六色的背景上渲染,之后又用Alpha去抠,会抠不干净)
  Nuke导进去就是好的,默认预乘。


*CGahz.COM 收集整理,转载请注明来自CG爱好者网(www.cgahz.com)

分享到:

更多精彩内容

已有13条评论 发表评论