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时总是需要很多数据。一般来说50到200个系数才可以足够完美的表现。最麻烦的是,这些系数并不可以通过经验得来,必须要通过采集,所以设计一个通用的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和ωr。Lobe Shape写成如下矩阵式:
我们当然可以直接写成:
一切的“魔术”都在控制CxCyCz3个系数上。通过调整这三个系数,我们就可以实现各项异性Anisotropic材质表面的光照效果。
三、深入剖析
我把Ph.D McAllister叙述的sBRDF的计算方法总结归纳了一下。
ρd的计算比较繁琐。如果你安装了NVIDIA SDK 9.5,其中的HDR演示程序就使用了HDR Diffuse贴图。(PS:NVIDIA 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•ωi与N•ωr的乘积。
有条件的朋友还可以这样:跑到室外选择一个场景,用多个曝光度拍摄环景照片,使用Paul Debevec的HDRShop在每个定角度对环境光的颜色和强度进行编码,创建漫反射查找表十字型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 = -1,Cy=-1,Cz = 1,n = 10,向后散射叶片Cx=1,Cy=1,Cz=1,n=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 P2和3个纹理坐标<s0,t0> <s1,t1> <s2,t2>:
如何使用Tangent呢?对于NVIDIA Geforce6系列后的显卡你可以尝鲜使用Vertex Texture,Ati Radeon X1000系列的卡可以使用R2VB,或者更加直接的使用Vertex Attribute传入Shader。至于Binormal你可以直接在Vertex Shader中使用cross叉乘得到。
GPU GEMS 上他展示的是Cg Shader,这里我把它改成了GLSL的。等我完成了一个GL基础库后放上DEMO。
这是Fragment Shader:
#define NUM_LOBES 2
#define SCL (1.0 / (96.0 / 256.0))
#define BIAS (SCL / 2.0)
#define EXSCL 255.0
uniform float Expos;
uniform sampler2D Diffus;
uniform sampler2D LobeShape[NUM_LOBES];
uniform sampler2D Albedo[NUM_LOBES];
uniform samplerCube EnvDiffuse;
uniform samplerCube EnvSpec;
varying vec2 TexUV;//Texcoords
varying vec3 EyeVec;//Vector to Eye in Local Space
varying vec3 TanVec;//Tangent In World Space
varying vec3 NrmVec;//Normal In World Space
varying vec3 BinVec;//Binormal In World Space
vec3 f3texCUBE_RGBE(samplerCube map,vec3 N)
{
vec4 rgbe = textureCube(map,N);
float f = ldexp(1.0,rgbe.w - 136.0);
return vec3(rgbe*f);
//return vec3(0.0,0.0,0.0);
}
vec3 f3texCUBE_RGBE_Conv(samplerCube map,vec3 normal,float N)
{
vec4 rgbe = textureCubeLod(map,normal,N);
float f = ldexp(1.0,rgbe.w - 136.0);
return vec3(rgbe*f);
}
vec3 ApplyLobe(vec3 toeye,vec4 lobeshape,vec3 lobealbedo,vec3 T,vec3 B,vec3 N,samplerCube tex_s)
{
vec3 Cwr_local = lobeshape.xyz * toeye;
//transform lobe peak in world space
/**//*
TBN Matrix
Tx Ty Tz
Bx By Bz
Nx Ny Nz
TBN InverseMatrix just is
Tx Bx Nx
Ty By Ny
Tz Bz Nz
Cwr_world.x = dot( vec3(T.x,B.x,N.x), Cwr_local);
*/
mat3 TBNMatrixInverse = mat3(T,B,N);
vec3 Cwr_world = Cwr_local*TBNMatrixInverse;
float sharpness = lobeshape.w;
vec3 incrad = f3texCUBE_RGBE_Conv(tex_s,Cwr_world,sharpness);
float lobelen = pow(dot(Cwr_world,Cwr_world),sharpness*0.5);
vec3 irrad = incrad*(Cwr_local.z*lobelen);
vec3 radiance = irrad*lobealbedo;
return radiance;
}
void main()
{
vec4 lobe_shape[NUM_LOBES];
vec4 lobe_albedo[NUM_LOBES];
for(int p=0; p<NUM_LOBES; p++){
lobe_shape[p] = texture2D(LobeShape[p],TexUV.st) + vec4(SCL,SCL,SCL,EXSCL) - vec4(BIAS,BIAS,BIAS,0.0);
lobe_albedo[p] = texture2D(Albedo[p],TexUV.st);
}
vec4 diff_albedo = texture2D(Diffus, TexUV.st);
vec3 exrad = vec3(0.0,0.0,0.0);
for(int l = 0;l<NUM_LOBES; l++){
exrad += ApplyLobe(EyeVec,lobe_shape[l],lobe_albedo[l].xyz,TanVec,BinVec,NrmVec,EnvSpec);
}
vec3 irrad = f3texCUBE_RGBE(EnvDiffuse,NrmVec);
exrad += (diff_albedo.xyz*irrad);
vec4 final_color = vec4(exrad*Expos,diff_albedo.w);
gl_FragColor = final_color;
}
这是Vertex Shader:
attribute vec3 Tangent;
uniform vec3 EyePosInWorld;
uniform mat4 ObjectToWorldMatrix;
uniform mat4 ObjectToWorldInverseTransposeMatrix;//XXX!!!
varying vec2 TexUV;//Texcoords
varying vec3 EyeVec;//Vector to Eye in Local Space
varying vec3 TanVec;//Tangent In World Space
varying vec3 NrmVec;//Normal In World Space
varying vec3 BinVec;//Binormal In World Space
void main()
{
TexUV = gl_MultiTexCoord0.st;
vec3 VertexInWorld = vec3( gl_Vertex*ObjectToWorldMatrix );
EyeVec = normalize( EyePosInWorld - VertexInWorld );
NrmVec = normalize( vec3(vec4(gl_Normal,1.0)*ObjectToWorldInverseTransposeMatrix) );
TanVec = normalize( vec3(vec4(Tangent,1.0)*ObjectToWorldInverseTransposeMatrix) );
BinVec = cross(NrmVec,TanVec);
gl_Position = ftransform();
}
来自sBRDF网站的样本图片:
原文以及相关的资料大家可以在这里找到:
http://sbrdf.cs.unc.edu/index.html
我编译好的命令行工具以及一个SVB样本:
http://webdisk.cech.com.cn/download/file_share_3252301.html
转载于:https://www.cnblogs.com/Jedimaster/archive/2007/04/30/733581.html
sBRDF空间双向反射分布函数完全解析相关推荐
- PBR:双向反射分布函数(BRDF)介绍与Cook-Torrance模型的实现
PBR:双向反射分布函数(BRDF)介绍与Cook-Torrance模型的实现 BRDF简介 再介绍BRDF之前我们要引入渲染方程这个东西: 其中L表示辐射率,其公式为: 它表示了一个拥有辐射强度Φ的 ...
- 图形学理论知识 BRDF 双向反射分布函数
图形学理论知识 BRDF 双向反射分布函数 Bidirectional Reflectance Distribution Function BRDF理论 BRDF表示的是双向反射分布函数(Bidire ...
- 计算机图形学基础:双向反射分布函数 BRDF
文章目录 光照.照明(Illumination) 预备知识 球面坐标(Spherical Coordinate) 立体角(Solid Angle) 投影面积(Foreshortened Area) 光 ...
- 图形学笔记(十三)光线追踪3——双向反射分布函数BRDF(反射方程、递归方程)、辐射度量学基础radiometry、立体角、Radiant Energy、Flux、Irrdiance、Radiance
图形学笔记(十二)光线追踪2--使用AABB包围盒加速光线追踪.空间划分(八叉树.KD树.BSP树).物体划分(BVH加速结构).光线与物体求交 图形学笔记(十四)光线追踪4--蒙特卡洛(Monte ...
- Cell Genomics封面|北大吴华君组利用空间多组学技术解析肿瘤内空间异质性(附招聘)...
Cell Genomics封面|吴华君课题组利用空间多组学技术解析肿瘤内空间异质性 肿瘤内异质性(intra-tumor heterogeneity,ITH)是癌症复发转移的重要驱动因素之一.随着单细 ...
- 2022年江西省中职组“网络空间安全”赛项模块A解析
2022年山西省中职组"网络空间安全"赛项模块A解析 A模块基础设施设置/安全加固(200分) A-1:登录安全加固 A-2:Web安全加固(Web) A-3:流量完整性保护与事件 ...
- 2022年江西省中职组“网络空间安全”赛项模块B解析
2022年江西省中职组"网络空间安全"赛项模块B解析 模块B 网络安全事件响应.数字取证调查和应用安全(400分) B-1:系统漏洞利用与提权 B-2:Linux操作系统渗透测试 ...
- 2016-12-29 DNS简介上 域名空间、域、迭代解析、递归解析、DNS服务器
2016-12-29 DNS简介上 域名空间.域.迭代解析.递归解析.DNS服务器 Domain name system 域名系统(Domain Name System,DNS)是把名字映射到 ...
- 深入探索Java反射机制:解析原理与应用
深入探索Java反射机制:解析原理与应用
最新文章
- C语言鸽巢排序pigeonhole sort算法(附完整源码)
- postfix 部署ssl后还是25_宝塔面板的邮局管理器Postfix无法启动解决办法
- 链队列的基本运算java_链式队列基本操作的实现问题
- LeetCode 219. 存在重复元素 II(哈希)
- c语言最长公共子序列_序列比对(二十四)——最长公共子序列
- 5.7和5.6的mysql_mysql5.6与5.7版本的区别
- IBM Machine Learning学习笔记(二)——Supervised Learning: Regression
- 《Adobe Illustrator CC经典教程》—第0课0.2节使用Adobe Creative Cloud进行同步设置
- 联通积分兑换的Q币怎么兑换到QQ上
- cmd长ping记录日志和时间_Ping记录时间的方法
- java代码混淆工具
- 项目工时估算PERT法
- word里怎么在右上角添加标注[1]
- 计算机专业人才培养评价意见,谈高职计算机专业人才培养综合评价.pdf
- 自我激励的100种方法
- Caffe中的损失函数
- 怎么删掉计算机云u盘,win10系统删除360云u盘图标的方法
- 颜色值透明度的百分数对应十六进制表
- C/C++编程:回车符和换行符
- java版溺尸掉三叉戟吗_溺尸 - Minecraft Wiki,最详细的官方我的世界百科