sBRDF空间双向反射分布函数完全解析

SBRDF空间双向反射分布函数解析

 

声明:本文大部分内容基于Ph.D David K. McAllister的博士论文《A GENERALIZED SURFACE APPEARANCE REPRESENTATION FOR COMPUTER GRAPHICS》以及《GPU Gems1》里他的文章。如果有兴趣推荐大家研究博士论文原文,其中关于用相机对材质进行采样的一段非常有趣。我重构他的代码生成了一个简单的命令行工具可以从他的SVB格式中抽取出基本的纹理。他的代码用VC8编译有不少问题,我修复了大部分,主要是在库的链接以及C++的语法上,完全修复用VC6编译一次看看。马上又要考试了,再不看书要挂科了。

 

一、悉数光照模型

       Phong光照模型是基于经验的局部光照模型,拥有漫反射Diffuse以及镜面反射Specular的效果,但是却省略了一切物理光学特性。可是目前的图形API和硬件都是按照Phong光照模型设计的。

       Torrance et al.提出的Microfacet-Based Equation微平面公式模拟了大部分材质表面的特性。具体形式如下:


D
代表任意一种分布函数,包括Gaussian分布函数或者是带指数项Cos函数。

G是几何衰减系数,可以由表面被遮挡的程度或者是阴影来表示。(PS:Ambient Occlusion?

F是菲Fresnel衰减系数。

MBE已经被Debevec et al.修正后用于人类皮肤模拟。

Ward 1992提出的Ward反射模型使用了椭圆型高斯锐化函数Elliptical Gaussian Sharpness Function在物体表面模拟了一个十字形的各项异性高光Anisotropic Highlights,可惜依旧不是基于物理特性的。(PS:有兴趣的朋友可以打开3dsmax等工具的材质编辑器试验一下)

 二、Lafortune BRDF表达式的出现

       BRDF使用若干个基本函数表现物体表面的光学特性。这些函数主要包括,spherical harmonics球面调和函数,spherical wavelets球面波,Zernike多项式。不过这些函数都不是专门为BRDF设计的。所以在使用BRDF时总是需要很多数据。一般来说50200个系数才可以足够完美的表现。最麻烦的是,这些系数并不可以通过经验得来,必须要通过采集,所以设计一个通用的BRDF接口还很难做到。

       下面是McAllister制作的采集装配:(PS:我想应该可以找个ARM单片机以及几个步进电机完成自动采集


      
幸运的是,Lafortune, Foo et al. 1997引入了一个简单而十分有效的BRDF表达式,它的最大特点是用几个Lobe叶片就模拟了BRDF,而且最最重要的是,一切数值都可以简单得用纹理表示,所以现在我们可以简单的使用GPU进行实时渲染。入射向量以及反射向量都被转化到Local Coordinate局部坐标系中计算,避免了只有使用很多系数才能获得满意效果的窘境。还是让我们看一下Lafortune表达式:


       其中ρd是漫反射项,右边的求和式代表各种高光叶片Specular Lobe凸起形状的和,也就是表现了我们注视模型上那一点的时候所看到的高光形状。每一个叶片Lobej都有一个反照率ρs,j以及表达叶片形状Lobe Shape函数sj,还有最重要的入射出射向量ωiωrLobe Shape写成如下矩阵式:

我们当然可以直接写成:

       一切的“魔术”都在控制CxCyCz3个系数上。通过调整这三个系数,我们就可以实现各项异性Anisotropic材质表面的光照效果。

 三、深入剖析

       我把Ph.D McAllister叙述的sBRDF的计算方法总结归纳了一下。

        ρd的计算比较繁琐。如果你安装了NVIDIA SDK 9.5,其中的HDR演示程序就使用了HDR Diffuse贴图。(PSNVIDIA Geforce FX5900的演示程序Dawn里仙女皮肤表面的Diffuse项是通过计算法线与入射光夹角的余弦加权平均值计算得到的)。Lafortun与许多其他Image-Based基于图像的算法Pulli, Cohen et al. 1997; Rushmeier, Bernardini et al. 1998把这一项当作bi-directional reflectance samples双向反射样本的最小值:

fr,k是当前像素处第k反射样本。

可是这仅仅是理论上的成立。真实情况下,用仪器测得的数据是会有错误的,因为我们无法保证样本总是完美的。Marschner (Marschner 1998)提出的方法就是上述被NVIDIA所使用的方法,其中的Weight权等于NωiN•ωr的乘积。

有条件的朋友还可以这样:跑到室外选择一个场景,用多个曝光度拍摄环景照片,使用Paul DebevecHDRShop每个定角度对环境光的颜色和强度进行编码,创建漫反射查找表十字型Cube Map立方体贴图。

样图如下

       
    然后让我们来处理求和式。当我们计算得到了ρd后,系统将每个Specular Lobe高光叶片形成的高光进行削减,下面的公式表现了这个操作。其中ωi,kωr,k依旧是入射和出射向量:

为了求得所有色彩通道都可以使用的数值,接下来,使用下式计算每一个luminance-weighted average of each fs,k经过亮度权调整过的fs,k的平均值:(PS:这是SONY Trinitron的数据,更加一般的系数是ITU HDTV标准 0.2125, 0.7154, 0.0721以及用于CRT显示器非线性色彩的0.299, 0.587, 0.114

       由于Lafortune表达式是一个非线性函数的叠加,所有的系数必须使用非线性方法进行重估。McAllister使用了Levenberg-Marquardt (L-M) method (Presse, Flannery et al. 1988),简称LM方法。(PS:具体L-M算法代码大家可以不必惊慌,我们可以用Tech-X Corporation的免费TxMath库。其中包含了L-M算法的实现,McAllister也使用了这个库。

        使用L-M需要一些已知的叶片系数。比如表现一个向前散射的叶片可以让Cx = -1Cy=-1Cz = 1n = 10,向后散射叶片Cx=1Cy=1Cz=1n=5。我们也可以使用随机的数值。

       L-M还需要fs,k公式的Jacobian Matrix雅各比矩阵。计算方法如下:使用一个很小的Epsilon调整参数多次计算模型上的每一个点。由于计算时间过长,这部操作还无法变成图形软件中的插件使用与实时计算,只有预先离线计算好。McAllister指出这个优化方法非常适合一个或者两个叶片,更多的就不适合了。如果只有一个或者两个叶片预测的准确度高达90%以上。

       如果我们不使用L-M方法,McAllister还展示了一个只针对于一个叶片的搜索方法。将fs,k写成这样,使用Brent Line Search搜索算法搜索:

       返回使得误差数值最小的那一组Cx Cy Cz n

       啊哈,最后我们终于得到了镜面项:

 

四、渲染

       首先我们需要准备一些材料,包括模型表面的N法向量,T切向量。(PS:曾经看过许多关于切线T的计算,无一例外的都是说可以简单的使用纹理st的方向代替,这对于部分UV贴图的简单模型可能管用,但是对于复杂的有的物体来说肯定是不正确的)。T切向量的计算方式如下,假设一个三角形由3个顶点坐标P0 P1 P23个纹理坐标<s0,t0> <s1,t1> <s2,t2>


      
如何使用Tangent呢?对于NVIDIA Geforce6系列后的显卡你可以尝鲜使用Vertex TextureAti Radeon X1000系列的卡可以使用R2VB,或者更加直接的使用Vertex Attribute传入Shader。至于Binormal你可以直接在Vertex Shader中使用cross叉乘得到

       GPU GEMS 上他展示的是Cg Shader,这里我把它改成了GLSL的。等我完成了一个GL基础库后放上DEMO

       这是Fragment Shader:

  这是Vertex Shader:

  来自sBRDF网站的样本图片:


原文以及相关的资料大家可以在这里找到:

http://sbrdf.cs.unc.edu/index.html

  我编译好的命令行工具以及一个
SVB样本:

        http://webdisk.cech.com.cn/download/file_share_3252301.html

posted on 2007-04-30 19:02  Bo Schwarzstein  阅读(5367)  评论(3编辑  收藏  举报