凹凸贴图(BumpMap)实现原理以及与法线贴图
(NormalMap)的区别
凹凸贴图(Bump Map)实现原理
以及与法线贴图(Normal Map)的区别
1 前⾔
翻译这篇教程的⽬的是为了帮助那些对图形渲染技术有兴趣却⼜苦于不到免费中⽂学习资料的⼈。在我的⾝边没有任何⼀位从事计算机专业的前辈,从刚学会 WINDOWS的基本操作到现在,我的计算机技术完全都是⼀步步⾃学过来的,算算学编程的历史也近5年时间了。我往往要花⼀半以上的学习时间⽤来查学习资 料(记得我学GIF图像压缩解压算法时,⽤了近2个星期时间才编出了显⽰GIF图⽚的程序,主要原因就是资料不够,只看⼀两篇短篇幅的教程来写程序,其中 多数时间花在调试代码和猜格式上)。所以我对那些不是从事计算机专业,⾝边⼜没有计算机⾼⼿的学习者深有同感——查⼀堆堆资料是⾃学中最痛苦的过程。这⼏ 天我也正在学BUMP MAPPING算法,在看完⼀篇简单的E⽂教程后,我觉得⾃⼰也确实应该为中国的教程事业做出⼀点我微薄的贡献。我并不是个3D⾼⼿,也只是个初学 OpenGL的新⼿,所以只能先做翻译了。我也希望我的翻译⼩样能够抛砖引⽟,看到中国越来越多的⾼⼿能够真正写点⾃⼰的东西,拿出来与⼤家分享。由于本 ⼈⽔平有限,翻译难免有错误和不妥之处,请多加指正。
为了尊重原作者,同时也是为了读者能够对照原⽂,在此贴出原⽂地址:
2 具体实现
2.1 BMEM技术(凹凸映射Bump Mapping)
BMEM技术通过⼀张叫做⾼度图(Height map)的灰度图来储存每⼀点的⾼度信息然后直接由API处理。
凹凸映射和纹理映射⾮常相似。然⽽,纹理映射是把颜⾊加到多边形上,⽽凹凸映射是把粗糙信息加到多边形上。这在多边形的视觉上会产⽣很吸引⼈的效果。我们 只需要添加⼀点信息到本来需要使⽤⼤量多边形的物体上。需要注意的是这个物体是平的,但是它看起来却是粗糙不平的。让我们来看看上边的那个⽴⽅体。如果你 很近地观察它时,你会发现它上⾯的很多细节。它看起来好像是由成千上万个多边形构成的,其实它只是由6个矩形构成。你或许会问:“这和纹理映射有什么不 同?”它们的不同之处在于——凹凸映射是⼀种负责光⽅向的纹理映射。
(1)凹凸映射背后的原理
让我们来看看⼀个粗糙的表⾯。
从远处看,你判断这个物体是粗糙的的唯⼀证据是在它表⾯上下的亮度有改变。你的⼤脑能够获得这些亮暗不⼀的图案信息,然后判断出它们是表⾯中有凹凸的部 位。上边的⼀幅图就说明了这⼀点。你可以发现它是⼀个浮雕式的表⾯。⼀些矩型和字母被印⼊表⾯,但是它们摸上去就像是⼀个隐藏的监控器的玻璃。如果这个图 像是在适当的位置上,那么它除了改变亮度,不需要再做任何其他的⼯作。
那么你也许会问:我是怎么知道哪些点要亮,哪些点要暗呢?这不难。绝⼤多数⼈⽣活在这样⼀种环境下——这个环境的⼤多数光源来⾃上⽅(译者注:⽐如⽩天主 要的光来⾃太阳,夜晚主要的光来⾃天花板上的⽇光灯)。所以向上倾的地⽅就会更亮,⽽向下倾的地⽅就会更暗。所以这种现象使你的眼睛看到⼀个物体上亮暗区 域时,可以判断出它的凹凸情况。相对亮的块被判断是⾯向上的,相对暗的块被判断是⾯向下的。所以我只需要给物体上的线条简单得上⾊。
如果你想要更多的证据,这⾥还有⼀幅⼏乎相同的图,不同于前的是它旋转了180度。所以它是前⼀幅图倒转的图像。那些先前看起来是凹进去的区域,现在看起 来是凸出来的了。
这时候你的⼤脑并没有被完全欺骗,你脑中存留的视觉印象使你仍有能⼒判断出这是前⼀幅图,只是它的光源变了,是从下往上照的,你的⼤脑可能强迫性地判断出 它是第⼀幅图。事实上,你只要始终盯着它,并且努⼒地想像着光是从右下⽅向照射的,你就会理解它是凹的(译者注:因为⽇常⽣活的习惯,你会很容易把这些图 形判断成凸出的图形,但是因为有了上⼀幅对照图的印象,你可能才会特别注意到这些图块其实还是凹⼊的,只是判断⽅法不符合我们⽇常⽣活习惯,因为这时⼤多 数光不是从上⽅照射,⽽是从下往上照射)。
(2)什么是凹凸图(Bump Map)
凹凸图和纹理图很相似。但是不同的是,凹凸图包含的不是颜⾊信息,⽽是凹凸信息。最通常的⽅法是通过存储⾼度值实现。我们要⽤到⼀个灰⾊的纹理图,灰⾊的 亮度体现出每个点分别凸出多少(见上图)。这就是⼀个⾮常⽅便的保存凹凸图的⽅法,⽽且这种图很容易制作。这副图具体⼜是怎样被渲染器使⽤的呢?你接着往 下看就会明⽩了。
当然,你并不⼀定要把⾃⼰局限于这些简单的图形,你可以扩展,⽤它来做⽊材,做⽯头,做脱了漆的墙⾯,做任何你想做的物体。 (3)凹凸贴图是怎么⼯作的
凹凸映射是补⾊渲染技术(Phong Shading Technique)的⼀项扩展,只是在补⾊渲染⾥,多边形表⾯上的法线将被改变,这个向量⽤来计算该点的亮度。当你加⼊了凹凸映射,法线向量会略微地改 变,怎么改变则基于凹凸图。改变法线向量就会改变多边形的点的颜⾊值。就这么简单。
现在,有⼏种⽅法来达到这个⽬的(译者注:这个⽬的指改变法线向量)。我并没有实际编写补⾊渲染和凹凸映射的程序,但是我在这⾥将介绍⼀种我喜欢的⽅法来 实现!
现在我们需要将凹凸图中的⾼度信息转换成补⾊渲染⽤到的法线的调节信息。这个做起来不难,但是解释起来⽐较费劲。
好的,我们现在将凹凸位图的信息转换成⼀些⼩向量——⼀个向量对应于⼀个点。请看下边⼀副放⼤的凹凸图。相对亮的点⽐相对暗的点更为凸出。看清楚了吗?
现在计算每个点的向量,这些向量表征了每个点的倾斜情况,请看下图的描绘,图中红⾊⼩圆点表⽰向量是向下的。
有很多计算向量的⽅法,不同的⽅法精确度不同,但是选择什么⽅法要取决于你所要求的精确度是个什么层次。最通常的⽅法是分别计算每个点上X和Y的倾斜度:
x_gradient = pixel(x-1, y) - pixel(x+1, y)
.
y_gradient = pixel(x, y-1) - pixel(x, y+1)
在得出了这两个倾斜度后,你就可以计算多边形点的法线了。
这⾥有⼀个多边形,图上绘出了它的⼀条法线向量——n。除此,还有两条向量,它们将⽤来调节该点法线向量。这两条向量必须与当前被渲染的多边形的凹凸图对 齐,换句话说,它们要与凹凸图使⽤同⼀种坐标轴。下边的图分别是凹凸图和多边形,两副图都显⽰了U、V两条向量(译者注:也就是平⾯2D坐标的两条轴):
现在你可以看到被调节后的新法线向量了。这个调节公式很简单:
New_Normal = Normal + (U * x_gradient) + (V * y_gradient)
有了新法线向量后,你就可以通过补⾊渲染技术计算出多边形每个点的亮度了。
2.2 法线贴图法(Normal Map)
但事实上游戏编程员却通常并不喜欢使⽤BMEM技术,因为他执⾏速度慢,因此他们通常使⽤DP3技术:直接把⾼度图(Height map)转换成⼀张法线图(Normal Map),其图的RGB分别是原⾼度图该点的法线指向:Nx、Ny、Nz,这张图可由Direct3D的专门函数帮助我们计算。最后在渲染的时候直接将该 ⾼度图的每个像素与光源的向量点乘,即可得到表⽰每⼀点的明暗系数的图:根据⾼度图,越突出的地⽅,法线与光源夹⾓越⼩,该点的数值越⼤。接着将这张图乘 到渲染线中即可,这样就使模型在背光的凹处有阴影⽽在⾯向光源处更亮的效果,这样的3D模型看起来就像真的凹凸不平⼀样!这些都可以直接在渲染流⽔线中由 机器完成。
具体可以使⽤以下简单的语句来实现:
1. //将光源位置转 换成ARGB
2. DWORD Vector2ARGB(D3DXVECTOR3 *v,float height)
3. {
4. DWORD r=(DWORD)(127.0f*v->x+128.0f);
5. DWORD g=(DWORD)(127.0f*v->y+128.0f);
6. DWORD b=(DWORD)(12
7.0f*v->z+12
8.0f);
7. DWORD a=(DWORD)(255.0f*height);
8. return((a<<24L)+(r<<16L)+(g<<8L)+b);
9. }
10. //⽣成法线图
11. D3DXComputeNormalMap(pNormalMap,pHeightMap,NULL,0,D3D_CHANNEL_RED,1.0f);//pHeightMap 为原⾼度图的指
针,pNormalMap为⼀张空纹理,⽤于存放法线图
12. //在渲染程序段中可以这样写:
13.
14.
15. //light是单位化的光源向量
16. DWORD F=Vector2ARGB(&light,0.0f);
17.
18. //pD是D3D的设备指针,这句将光源法线参数输⼊
19. pD->SetRenderState(D3DRS_TEXTUREFACTOR,F);
20.
modulate21. //设置原纹理,如上⾯的球,如有需要可以贴上纹理样式
22. pD->SetTexture(1,TEXTURE);
23.
24. //使⽤上⾯⽣成好的法线图
25. pD->SetTexture(0,normalmap);
26.
27. //设置“来源1”为法线图
28. pD->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);
29.
30. //将“来源1”(法线图)与“来源2”(光源法线)进⾏点乘
31. pD->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_DOTPRODUCT3);
32.
33. //设置“来源2”为光线的光源法线参数
34. pD->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_TFACTOR);
35.
36. //这步和下⾯⼏步将图⽚的原纹理加上
37. pD->SetTextureStageState(1,D3DTSS_COLORARG1,D3DTA_TEXTURE);
38. pD->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_MODULATE);
39. pD->SetTextureStageState(1,D3DTSS_COLORARG2,D3DTA_CURRENT);
复制代码
2.3 假凹凸贴图
三维计算机图形程序员有时使⽤计算量较⼩的假 凹凸贴图模拟 凹凸贴图效果。其中⼀个⽅法是使⽤纹素索引变化取代曲⾯法线变化,这种⽅法经常⽤于⼆维 凹凸贴图。在 GeForce 2 类型的图形加速硬件中就使⽤了这项技术。(或者说利⽤⼩尺⼨的灰度图来为⼀张更⼤尺⼨的表⾯起伏不⼤的平⾯提供⾼度图的索引)
全屏的⼆维假 凹凸贴 图,可以很容易地⽤简单快速的渲染循环实现,在⼆⼗世纪九⼗年代的⽰范影像是⼀个⾮常普通的 视觉效果。
3 与位移映射之间的差别
位移映射与 凹凸贴图之 间 区别在例 图中已经很明显地显现出来了:在 凹凸贴图中,只有法线进⾏了扰动,⽽⼏何体本⾝没有扰动,这样的结果就是⼈为改变只出现在物体的轮廓上,⽽球体本⾝仍然是原来的圆 形。即 凹凸贴图只 是视觉上的改变,就像⼀个画得很透视的图⽚;⽽位移映射却真的将3D物体变得“凹凸不平”。
from :
Bump Map存储的是⾼度差,⽽⾮Normal Map直接存储法线值。这⼀点值得让⼈思考。即,Bump存储⼀张8bit灰度图,⽽Normal存储⼀张24bit RGB彩⾊图!
记得第⼀次见到这个技术好像还是98年前后的时候,好像是MGA系列的显卡⽤这个东西作为⾃⼰卖点(那会鄙⼈还上中学,年代久远实在记不清了)。⾄于为什 么不直接存储法线,我觉得是为了数据存储的最⼩化考虑。因为⾼度差可以很容易的⽤定点表⽰,并⽤定点纪录当前⾼度图所在的刻度范围,理论上这种⽅式表⽰的 ⾼度图不会有太⼤的精度问题。反向,法线则不⾏。⽽且⾼度差图像可以很容易的做到压缩,对简单的压缩就是⼀个⾼度值,后⾯跟着幅图像在当前⾼度的掩码图。
因为当时DRAM还是不便宜的,所以⾼压缩⽐例的纹理存储是⾸要被满⾜的⽬标。当然,我也是按照常理推测,也没有经历过那个年代。如果有⽹友知道更⾼深的 原因,还望指教 :〉
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论