3D游戏常用技巧Normal Mapping (法线贴图)原理解析——高级篇
1、概述
上一篇博客,3D游戏常用技巧Normal Mapping (法线贴图)原理解析——基础篇,讲了法线贴图的基本概念和使用方法。而法线贴图和一般的纹理贴图一样,都需要进行压缩,也需要生成mipmap。但是由于法线贴图存储的是法线信息,压缩和生成mipmap的方法自然会有所变化。
现在已经许多用于法线贴图压缩和生成mipmap的工具,大部分商业游戏引擎也集成了相关方法,只需要点几下鼠标就可以完成。本文仅针对法线贴图的纹理压缩和mipmap的方法进行原理性的说明,至于在具体的工具中如何操作,可以参看相关工具的说明文档。
法线贴图压缩的中文资料还是比较多的,也不太复杂;但是生成mipmap的方法中文资料不多,《Real-Time Rendering 3rd》讲解的比较详细,本文的这部分内容主要来源于这本书,如果想详细了解的,可以看原书,网上有电子版。
2、法线贴图的压缩
传统的jpg等压缩方式解压时间太长,且压缩比不固定,所以在实时渲染中一般采用DXTC及其改进方法,简单来说,就是把4*4的像素当做一个Block,对其进行简化表示,详情参见百度百科http://baike.baidu.com/view/736449.htm。由于法线贴图存储的数据并不是RGB信息而是法线方向,所以需要在一般纹理压缩的方法的基础上进行一定的改变。
压缩的第一步很简单,由于归一化的法线长度为1,且在切线空间下,法线的z分量不可能为负数,所以只需要存储x和y值即可。本文的压缩方法在这一步压缩的基础上,利用现有的纹理压缩方法,进行进一步压缩。
在支持DirectX10的显卡上,可以使用BC5格式进行压缩。BC5的压缩方法内存情况如图1所示,该格式有两个颜色通道(R和G),每个通道使用两个1Byte的值来表示,每个像素使用3Bit在这两个颜色值之间进行插值。将法线贴图中每个法线的x和y值利用BC5格式进行压缩,如图2所示。对每个Block(16个像素)存储x的最大、小值和y的最大、小值,然后每个像素利用3Bit进行插值,相当于在图2右图所示的8*8区域内取样(为了简化表示,图2只画了4*4点)。

图1 BC5压缩方法

图2 法线贴图压缩示意图,右图框内应该是8*8个点,为了画图方便简单表示为4*4
对于不支持DirectX10的显卡,可以使用DXT5格式进行压缩(DXT5为DirectX9.0的纹理压缩格式,如果连DirectX9.0都不支持,建议直接送博物馆),将法线的x和y值存储到纹理的alpha和Green通道即可。之所以是存储到这两个通道,而非其他通道,是因为每个DXT5中每个Block选择的两个参考像素alpha通道有8Bit,RGB通道分别为5、6、5Bit,所以使用alpha和Green通道可以获得较高精度。
3、法线贴图的mipmap
使用一般纹理mipmap方法生成的法线贴图对于漫反射表面基本没问题,但是在镜面表面会导致严重的视觉问题。对于漫反射表面来说,光照的计算公式为l·n,l为光线方向的相反方向,n为法线,l·n1 + l·n2 + l·n3 + l·n4 = l·(n1 + n2 + n3 + n4) / 4,而mipmap则是事先计算(n1 + n2 + n3 + n4) / 4,所以对于漫反射表面,对法线贴图使用传统方式的mipmap基本没问题。为什么是基本没问题而不是完全没问题呢?因为这里存在一个近似,若l·n < 0,则光照值为0(光照不能为负),若将这个因素考虑进去,漫反射表面也会有问题,不过在实际当中这种情况表现不明显,所以可以认为基本没问题。
对于镜面表面来说,当视线偏离反射光线方向的时候,光照强度会急剧下降,反映在公式中是因为其含有cosm(h·n)项(具体公式可以Google),而漫反射光照是线性变化,所以对于镜面表面,不能使用传统方法生成法线贴图的mipmap。法线贴图对于镜面反射的mipmap如图3所示,第一幅图中有4个像素,每个像素有法线和镜面反射波瓣(红色的是法线,周围一圈是镜面反射波瓣,镜面反射波瓣用于表示不同方向的反射强度)。图2中间部分,表示正确的mipmap情况,分别从4个像素合并为2个像素,从两个像素合并为1个像素。而现有的方法中,没有方法可以做到这样的mipmap,所以只能用其他方法进行近似。

图3 法线贴图的mipmap示意图
图2的底部左图,是使用一般纹理的mipmap方法对法线进行平均,可以看到这种方法产生出的镜面反射波瓣和正确的镜面反射波瓣差距很大,其根本原因是使用线性方法对非线性的参数进行计算。图2底部图右图,每次在平均法线的同时,改变表面的光泽度(即改变镜面光公式中的m),虽然最终结果与正确的mipmap有一些差距,但是比一般纹理的mipmap的方法要好很多。
所以,对法线贴图的mipmap方法之一,就是在使用一般纹理的mipmap方法对法线进行平均的同时,每张mipmap都必须附带一张光泽贴图(gloss map),记录每个像素点的光泽度(即m),m的计算原则就是让最后的镜面反射波瓣与正确的镜面反射波瓣最接近。当然,还有其他很多方法能得到不错的结果,具体可以参看《Real-Time Rendering 3rd》,或去搜索相关论文。
参考资料
[1]Akenine-Möller T, Haines E, Hoffman N. Real-time rendering 3 [M].
3D游戏常用技巧Normal Mapping (法线贴图)原理解析——高级篇的更多相关文章
- 3D游戏常用技巧Normal Mapping (法线贴图)原理解析——基础篇
http://www.cnblogs.com/wangchengfeng/p/3470310.html
- (转)Unity3D 游戏贴图(法线贴图,漫反射贴图,高光贴图)
原帖网址http://www.u3dpro.com/read.php?tid=207 感谢jdk900网友的辛苦编写 我们都知道,一个三维场景的画面的好坏,百分之四十取决于模型,百分之六十取决于贴图 ...
- 【转载】法线贴图Nomal mapping 原理
法线贴图多用在CG动画的渲染以及游戏画面的制作上,将具有高细节的模型通过映射烘焙出法线贴图,贴在低端模型的法线贴图通道上,使之拥有法线贴图的渲染效果,却可以大大降低渲染时需要的面数和计算内容,从而达到 ...
- shader复杂与深入:Normal Map(法线贴图)1
转自:http://www.zwqxin.com/archives/shaderglsl/review-normal-map-bump-map.htmlNormal Map法线贴图,想必每个学习计算机 ...
- 【Unity Shader】六、使用法线贴图(Normal Map)的Shader
学习资料: http://www.sikiedu.com/course/37/task/456/show# http://www.sikiedu.com/course/37/task/458/show ...
- Esfog_UnityShader教程_NormalMap法线贴图
咳咳,好久没有更新了,一来是这段时间很忙很忙,再来就是自己有些懒了,这个要不得啊,赶紧补上.在前面我们已经介绍过了漫反射和镜面反射,这两个是基本的光照类型,仅仅依靠它们就想制作出精美的效果是远远不够的 ...
- 【Unity Shaders】学习笔记——SurfaceShader(七)法线贴图
[Unity Shaders]学习笔记——SurfaceShader(七)法线贴图 转载请注明出处:http://www.cnblogs.com/-867259206/p/5627565.html 写 ...
- 一个有趣的模拟光照的shader(类似法线贴图)
最近使用unity,碰到到一个很有趣的例子.场景无光线,却模拟出了光照,效果挺好.其思路与法线贴图原理异曲同工. 原作者提供的效果印象深刻. 模型除了使用原来的diffuse贴图外,还用到了一张模拟记 ...
- 翻译:非常详细易懂的法线贴图(Normal Mapping)
翻译:非常详细易懂的法线贴图(Normal Mapping) 本文翻译自: Shaders » Lesson 6: Normal Mapping 作者: Matt DesLauriers 译者: Fr ...
随机推荐
- AutoHotKey 脚本集中营(一)
前言:实战是最好的学习.本集中营共分 x+mc2 期(x 为已发行的期数,m 为我的空闲时间,c 为我的更新动力),旨在通过对脚本代码的阅读而提高 AHK 语言的熟练程度.脚本的难度与期数成正比,有兴 ...
- html5 js os build
1.NodeJS6.6 install https://nodejs.org/en/ c:\nodejs>npm install -g grunt-cliC:\Users\police\Ap ...
- GD库使用小结---2
接着上一篇.GD库可以折腾很多用法出来,当然得跟画图相关,除了前面的验证码.水印外,还可以进行图片的缩放,裁剪.旋转等操作,这在很多应用中可以见到. 1. 加水印 前面已经知道,我们可以使用image ...
- Ehcache(2.9.x) - API Developer Guide, Cache Eviction Algorithms
About Cache Eviction Algorithms A cache eviction algorithm is a way of deciding which element to evi ...
- Linux 命令 - ps: 显示当前进程的快照
命令格式 ps [options] 实例 a) 查看所有的进程. huey@huey-K42JE:~$ ps aux | head USER PID %CPU %MEM VSZ RSS TTY STA ...
- Redis - 排序
SORT 命令格式 SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALP ...
- ios开发:OC对象的内存分析
最近要开始准备找实习单位了,做做笔试题,看看各位大神的面试经历,发现自己要学习的东西真的还有很多,虽然也做过几个的项目,但是真正拿过笔试题一看,才发现自己对基础这方面的东西,确实有点忽视了,所以最近开 ...
- IOS 如何选择delegate、notification、KVO?(转)
前面分别讲了delegate.notification和KVO的实现原理,以及实际使用步骤,我们心中不禁有个疑问,他们的功能比较类似,那么在实际的编程中,如何选择这些方式呢? 在网上看到一个博客上详细 ...
- robolectric环境的搭建
最近在学习测试驱动开发(Test-Driven Development),测试驱动开始是极限编程的一种方式,提倡在真正编写代码之前先根据需求编写测试代码(当然这个测试代码是不可能通过的),然后根据测试 ...
- 30分钟搭建一个小型网站框架(python django)
最近因为要做一个小型的网站,需求很简单有点像公司内部的管理网站,和室友一起倒腾,发现了一些坑.我自己之前没有接触过python 但是发现真的非常好上手. 我们没人会前端,所以最怕修改网页,一开始选择了 ...