« shader复习与深入:Normal Map(法线贴图)Ⅱshader复习与深入:Diffraction(衍射) »

shader复习与深入:Anisotropic Lighting(各向异性光照)

 Anisotropic Lighting(各向异性光照)是一种模拟有大量细小齐整沟槽(grooves)的表面的光照情况的图形学技术。在这种表面,光产生的效果是普通的图形学光照模型所无法模拟的。——ZwqXin.com

普通的图形学光照模型:[Shader快速复习:Per Pixel Lighting(逐像素光照)]

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/shaderglsl/review-anisotropic-lighting.html

大概是一年前了,虚拟物理实验室项目工作中,为了模拟透镜表面那种粘性的高光效果,尝试用GLSL shader实现Anisotropic Lighting,参考资料是这篇文章(Anisotropic Lighting )以及NVIDIA SDK的一个HLSL DEMO

其实最实在的样例还是CD背面(为什么?google!)。在现实生活中,镜面高光的产生自然跟接触面“是个镜面”有关,事实上,把任何一个“光-物体”接触处“微观”到一定程度,都可以是一个平面,接触点只要恰好在这个平面上就好了,就有镜面光了(即使那是多么微不足道)。关键在于,对于有微小沟槽的表面,一旦光“陷入”了沟槽,就会被这微小沟槽的“无数法线”搞晕:该沿那条法线(作为中线)反射出去呢(能不能反射得出去还是另一个问题)?光它自己又有粒子性又有波动性,故增加了它必须“考虑”的烦恼。其实我们外人来想就觉得这样很自然:光被叭喇叭喇地拆碎,然后沿着不同的法线四散出去。(至于另一问题:反弹来反弹去始终是要出去的,除非那接触物很爱吸光。)这样的结果是一束光进、好多束光出,形成扩散的高光(然后根据能量首恒可知,扩散的高光不会有一进一出时那么亮,但照亮物体的范围变大了)。当然,出来光的还有diffuse成分嘛。

当然了,以上只是我当时看完那篇文章后对模型的YY,实际发生了什么最好请示学光电的人。但大概情况就是这样,而该文章给出了更强的YY(其实是抽象假设喇):从微小沟槽内的法线形成的法线平面上,选择一个方向作为the most significant Normal(最具代表性/最重要的法线?),然后按镜面反射公式反射出来的光线方向R作为the most significant light reflection 。首先怎么选那个法线?文章说是光源向量在法线平面的投影喔。这样麻烦了点,于是文章又直接抛出以下公式让我们计算出NdotL和VdotR,还记得吗,它们就是Phong模型里头diffuse和specular成分的原材料(VdotR == NdotH)[Shader快速复习:Per Pixel Lighting(逐像素光照)] 。具体怎么根据上面the most significant light reflection的理论推导出来的,有兴趣的同学找[Banks94]和[Stalling97]来看吧(我是不知道怎么找了~顺带一提这是某AMD文章Per-Pixel Strand Based Anisotropic Lighting里提及的)。

上式中只需要计算LdotT和VdotT,L、V是光源向量和视向量,T则麻烦,它是沟槽的方向向量。有人把这两个公式“变成”纹理检索过程:LdotT和VdotT分别作为UV检索的S和T坐标,出来的是NdotL和VdotR。(这方法好,很好很强大 - -)注意我们只要一张RGB或RGBA格式的图就够了,毕竟我们只需要两个通道。对第一图只需要一个T方向检索量(LdotT)就够了,第二图则两个一起上,而且S、T可互换(式子[==纹理]很对称嘛)。


(from Per-Pixel Strand Based Anisotropic Lighting

在说一次:T很麻烦,它是沟槽的方向向量。对CD那种圆切线还好说,很多物件的切线很难得到的啊(这也是NormalMap的难处[shader复习与深入:Normal Map(法线贴图)Ⅰ] )。前面提到NVidia有个SDK sample,弄的也是Anisotropic Lighting,不过就很简洁:不要用T了,用LdotN和HdotN来检索纹理吧。它明显没有阐述这里头的数学原理,也就给了一张新的Texture:


(from NV SDK Anisotropic Lighting

注意这与上图是颇有区别的,但我不好推测这是代表个啥公式,但它就能方便地用LdotN(T)和HdotN(S)来检索。其中N是顶点法向量(不是那个the most significant哦),H是半向量。然后我也不知道它出来的是不是就是NdotL和VdotR(这个N和R都是the most significant),文章讲得很模糊。但sample的效果很有Anisotropic Lighting的feel。我上面提及去年做的那个透镜表面粘光效果就是直接用这个弄成的。今天重新修整了这个shader:

  1. //www.ZwqXin.com  Anisotropic lighting
  2.  
  3. //vertex shader
  4. uniform vec4 lightpos;
  5. uniform vec4 eyepos;
  6. varying vec2 texCoord0;
  7.  
  8. void main(void)
  9. {
  10.    vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
  11.  
  12.    vec4 pos = gl_ModelViewMatrix * gl_Vertex;
  13.      vec3 vlightpos = (gl_ModelViewMatrix * lightpos).xyz;   
  14.    vec3 veyepos = (gl_ModelViewMatrix * eyepos).xyz;
  15.    
  16.     vec3 vlightdir = normalize(vlightpos - pos.xyz);
  17.     vec3 veyedir = normalize(veyepos - pos.xyz);
  18.     
  19.     //h = normalize(l + e)
  20.     vec3 halfVec = normalize(veyedir + vlightdir);       
  21.  
  22.     texCoord0.x = 2.0 * dot(halfVec, norm) - 1.0;//每个顶点的值随视线变化而变化    
  23.     texCoord0.y = 2.0 * dot(vlightdir, norm) - 1.0;//每个顶点拥有固定值   
  24.   
  25.    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
  26. }
  27.  
  28. //fragment shader:
  29. uniform sampler2D tLookup;
  30. uniform float intensity;
  31. varying vec2 texCoord0;
  32.  
  33. void main(void)
  34. {
  35.      vec4 col =  intensity * texture2D(tLookup, texCoord0.xy);
  36.  
  37.    //gl_FragColor =  col; //1
  38.    gl_FragColor =  vec4(col.rgb * (col.a), 1.0); //2
  39. }

fragment shader中,1是不应用镜面光分量的结果,2是应用镜面光分量(在alpha通道)的结果(见下面两图)。其实用vec3(col.r)或vec3(col.g)代替col.rgb也可,后者的r分量比gb小点所以结果呈现青色。其实这些无什么所谓,要弄颜色的话取某单通道再乘个颜色量就好。另外建议纹理的S_WRAP选择GL_MIRRORED_REPEAT,看SPEC就知道其作用了,保险点好。

Anisotropic lighting http:www.zwqxin.com
Anisotropic lighting http:www.zwqxin.com

最后说一下另一个Anisotropic lighting的shader实现。在《Gems 1》里看到的,在《GLSL Shading Language》里Diffraction一节里也用到了。它没有用特制纹理,而是直接拿tangent切线计算,而且算法貌似是完全不一样的。其中还有TdotH,真不知道是怎么搞的,据介绍是WARD92里提出的,在Jos Stam的Diffraction开山论文《Diffraction Shaders》里也提到,貌似跟BRDF算法有关,这个……这个就不要轻易理解了。

  1. //Anisotropic Lighting 2  www.ZwqXin.com
  2.  
  3. //vertex shader
  4. uniform vec4 baseColor;
  5. uniform float controlR;
  6.  
  7. uniform vec4 lightpos;
  8. uniform vec4 eyepos;
  9.  
  10. attribute vec3 rm_tangent;
  11.  
  12. void main(void)
  13. {
  14.    vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
  15.    vec3 tang = normalize(gl_NormalMatrix * rm_tangent); 
  16.    
  17.    vec4 pos = gl_ModelViewMatrix * gl_Vertex;
  18.    vec3 vlightpos = (gl_ModelViewMatrix * lightpos).xyz;  
  19.    vec3 veyepos = (gl_ModelViewMatrix * eyepos).xyz;
  20.    
  21.     vec3 vlightdir = normalize(vlightpos - pos.xyz);
  22.     vec3 veyedir = normalize(veyepos - pos.xyz);
  23.     
  24.     //h = normalize(l + e)
  25.     vec3 halfVec = normalize(veyedir + vlightdir);
  26.     
  27. //0.3是光的波长,干脆就只用后面的调节系数controlR 调节好了
  28.     float u = dot(tang, halfVec)*0.3; 
  29.     float w = dot(norm, halfVec);
  30.     
  31.     float e = controlR * u / w;
  32.     float c = exp(-e * e);
  33.     
  34.     vec4 aniColor = baseColor * c;
  35.       
  36.    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
  37.    gl_FrontColor = aniColor;
  38. }
  39.  
  40. //fragment shader
  41. void main(void)
  42. {
  43.    gl_FragColor =  gl_Color;
  44. }

以上两种shader都是在顶点级处理的,搬到fragment shader处理也就那么简单,边界柔和点而已。给出两种shader的结果(但我并没有作比较的意思,以下两图光照条件和角度都8一样):

Anisotropic lighting http:www.zwqxin.com
(用前一种方法)
Anisotropic lighting http:www.zwqxin.com
(用后一种方法)

最后再佩服一下造出前一种方法中那张纹理的人。这也就是所谓的预处理的一种吧,把函数关系式用纹理表达;这也是GPGPU中纹理概念[Vertex Texture Fetch 顶点纹理拾取] 的又一延伸吧,还是有种神奇的感觉……

该纹理下载:Aniso2.rar

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/shaderglsl/review-anisotropic-lighting.html

  • quote 1.beyondborders
  • 你好!我最近刚开始看glsl,你的文章让我学到很多东西。
    我想问问你在Anisotropic lighting的实现代码中:
    vec3 vlightpos = (gl_ModelViewMatrix * lightpos).xyz;
    vec3 veyepos = (gl_ModelViewMatrix * eyepos).xyz;
    这两句中lightpos,eyepos应该都是在world space里面指定,而不是object space中指定的吧?那么直接用gl_ModelViewMatrix左乘应该不会被转到eye space空间的吧?
    zwqxin 于 2011-3-4 23:00:23 回复
    是的,这里欠缺仔细的考虑了。THX指出。
  • 2011-3-4 22:24:38 回复该留言

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

IE下本页面显示有问题?

→点击地址栏右侧【兼容视图】←

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

Copyright 2008-2013 ZwqXin. All Rights Reserved. Theme edited from ipati.