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·nl为光线方向的相反方向,n为法线,l·nl·nl·nl·n4 = l·(nnnn4) / 4,而mipmap则是事先计算(nnnn4) / 4,所以对于漫反射表面,对法线贴图使用传统方式的mipmap基本没问题。为什么是基本没问题而不是完全没问题呢?因为这里存在一个近似,若l·< 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 (法线贴图)原理解析——高级篇的更多相关文章

  1. 3D游戏常用技巧Normal Mapping (法线贴图)原理解析——基础篇

    http://www.cnblogs.com/wangchengfeng/p/3470310.html

  2. (转)Unity3D 游戏贴图(法线贴图,漫反射贴图,高光贴图)

    原帖网址http://www.u3dpro.com/read.php?tid=207  感谢jdk900网友的辛苦编写 我们都知道,一个三维场景的画面的好坏,百分之四十取决于模型,百分之六十取决于贴图 ...

  3. 【转载】法线贴图Nomal mapping 原理

    法线贴图多用在CG动画的渲染以及游戏画面的制作上,将具有高细节的模型通过映射烘焙出法线贴图,贴在低端模型的法线贴图通道上,使之拥有法线贴图的渲染效果,却可以大大降低渲染时需要的面数和计算内容,从而达到 ...

  4. shader复杂与深入:Normal Map(法线贴图)1

    转自:http://www.zwqxin.com/archives/shaderglsl/review-normal-map-bump-map.htmlNormal Map法线贴图,想必每个学习计算机 ...

  5. 【Unity Shader】六、使用法线贴图(Normal Map)的Shader

    学习资料: http://www.sikiedu.com/course/37/task/456/show# http://www.sikiedu.com/course/37/task/458/show ...

  6. Esfog_UnityShader教程_NormalMap法线贴图

    咳咳,好久没有更新了,一来是这段时间很忙很忙,再来就是自己有些懒了,这个要不得啊,赶紧补上.在前面我们已经介绍过了漫反射和镜面反射,这两个是基本的光照类型,仅仅依靠它们就想制作出精美的效果是远远不够的 ...

  7. 【Unity Shaders】学习笔记——SurfaceShader(七)法线贴图

    [Unity Shaders]学习笔记——SurfaceShader(七)法线贴图 转载请注明出处:http://www.cnblogs.com/-867259206/p/5627565.html 写 ...

  8. 一个有趣的模拟光照的shader(类似法线贴图)

    最近使用unity,碰到到一个很有趣的例子.场景无光线,却模拟出了光照,效果挺好.其思路与法线贴图原理异曲同工. 原作者提供的效果印象深刻. 模型除了使用原来的diffuse贴图外,还用到了一张模拟记 ...

  9. 翻译:非常详细易懂的法线贴图(Normal Mapping)

    翻译:非常详细易懂的法线贴图(Normal Mapping) 本文翻译自: Shaders » Lesson 6: Normal Mapping 作者: Matt DesLauriers 译者: Fr ...

随机推荐

  1. Windows 8.1 归档 —— Step 3 软件的选择与安装

    这里我将列举出一些 Windows 8.1 下实用的软件 --运行库-- 用精简版系统或者绿色版软件时,使用软件经常遇见的情况是“由于应用程序配置不正确,应用程序未能启动”,提示缺少dll文件等.大部 ...

  2. 截图技巧——鼠标按着不懂,同时点击截图快捷键(QQ为ctrl+alt+a)即可避免按下快捷键鼠标按下等操作消失截不到的尴尬。

    截图技巧--鼠标按着不懂,同时点击截图快捷键(QQ为ctrl+alt+a)即可避免按下快捷键鼠标按下等操作消失截不到的尴尬.

  3. Ehcache(2.9.x) - API Developer Guide, Class Loading

    About Class Loading Class loading, within the plethora of environments that Ehcache can be running, ...

  4. HDOJ2020绝对值排序

    绝对值排序 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submi ...

  5. 百练 2973 Skew数 解题报告

    思路: 计算出每一个skew数的不同位数表示的权值,然后用该位与权值相乘.用int数组来装权值,用char数组来装skew数. 代码: #include<stdio.h> #include ...

  6. Android--仿QQ侧滑菜单

    首先还是先看一下效果图吧,这个示例在网上能找到很多,我主要在此是做一个小结和一些代码的分析,当是自己在学习过程中的一个积累,因为网上同样的文章很多,所以也无法探究最初的来源是哪里了. 我们可以从效果图 ...

  7. Android对象类系列化public class User implements Parcelable

    package com.gaojinhua.android.activitymsg; import android.os.Parcel; import android.os.Parcelable; / ...

  8. 转 DevExpress-ASPxPageControl 动态添加 TabPage 内容

    话不多说想看代码,在细说. 前台,很简单.主要看后台 </dx:ASPxPageControl> 后台,注意注释说明. private void GetUserAttendCellsNew ...

  9. Swift类与结构体

    类和结构体有很多共性: 定义属性存储数据 定义方法执行功能处理 定义下标,通过下标访问他们的值 初始化他们的状态 通过扩展(Extension)扩展其功能 遵守协议(Protocol),协议提供一种特 ...

  10. WF4.0 基础篇 (十八) Flowchar

    本节主要介绍WF4 中 Flowchart的使用 本文例子下载: http://files.cnblogs.com/foundation/FlowcharSample.rar 本文例子说明 Flowc ...