« 一年前,首次献给OpenGL之夜.雷达追踪WIN32下的窗口子类化 »

Shader快速复习:Reflection And Refraction(反射与折射)

 在Shader里面,反射和折射的实现其实很简单。关键是使用者要懂得把它们使用在哪里。一起来复习一下啦。——ZwqXin.com

[Shader快速复习:Per Pixel Lighting(逐像素光照)]
[Shader快速复习:Cube Mapping(立方环境贴图)]

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

首先,反射与折射的物理就不多说了。光线矢量在两种介质之间发生方向的改变,而人眼睛和目标物分别在这两种介质当中,非直线的光线连接两者。然而人总会“自觉”地认为自己的视线总是直线,所以看到目标物在原始位置的上前方,此为折射现象。反射类似。其实这样理解不直观,更“白”点地说,人的视线在图中5线交汇处的点(称O点)停顿了,所看到的只是交界面处的O点的颜色,好比交界面是一块镜子,人所见的只是镜子的像,而这像由两部分组成,一是沿着反射光像来的“投影”,二是镜底沿着折射光线来的“投影”。是的,真真假假在这里已经不重要了,因为这是图形学中反射与折射的“图形学原理”啊。

譬如绘制一个湖面后,我们为了要让湖面真实表现反射与折射,需要真的在湖底把真实世界再绘制一次吗?恩,我在学模板缓冲的时候尝试过(小场景),况且不提效率上的浪费,如果湖面不是场景的“底面”呢?这样就很不好办了吧。折射方面,因为湖底物没有其他参照嘛,肯定要画的。但是有时候你只不过想表达一下湖底的境况——譬如静止的沙床,你其实只需要一张纹理就够了,什么都不用画(如果你觉得可以更随便,干脆忽略折射)……

要做到这样,就要考虑图形学上的折射和反射了。把湖面作为一块大镜子,在上面帖帖花吧~恩,就是帖纹理,多重纹理的混合。

1.在哪里找这种纹理呢?

答案是立方环境帖图[Shader快速复习:Cube Mapping(立方环境贴图)] 。而且不仅是通常的反射向量,折射向量同样可以用来检索cubemap:

  1.  
  2. //在fragment shader里检索纹理
  3. //basecolor 是从湖水的水纹理检索出来的基色
  4. //ReflectColor 和RefractColor 通过混合因子Ratio混合
  5. //结果则是反-折射混合色与基色的混合
  6.  
  7.    vec4 basecolor = tex2D(WaterTex,gl_TexCoord[1].xy);
  8.    vec3 ReflectColor = vec3(textureCube(WaterCube, ReflectVec));
  9.    vec3 RefractColor = vec3(textureCube(WaterCube, RefractVec)); 
  10.  
  11.    vec3 outputCol = mix(RefractColor, ReflectColor, Ratio); 
  12.  
  13.    gl_FragColor = vec4( outputCol * basecolor , 1.0 );

其中,WaterTex是水纹理的纹理句柄号;WaterCube是环境纹理的句柄号(静态场景中最常见的,是直接把天空盒的纹理组成CUBEMAP环境纹理)。

2. 反射向量,折射向量怎么来的啊?

恩,有了上面那张原理图,有了眼睛(相机)坐标,有了湖面中各个顶点的坐标,湖面的法向量,还有两种介质的折射率,当然可以自己动手计算了。但是其实这种小计算,GLSL还是给我们准备好的了:

  1.  
  2. //在vertex  shader里: 
  3.  
  4.    vec4 Vpos = gl_ModelViewMatrix * gl_Vertex;
  5.    vec3 pos = Vpos.xyz / Vpos.w;  //在视图空间下的w矫正的湖面(各顶点)坐标
  6.  
  7.    vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
  8.   
  9.    vec3 InVec =  normalize(pos - eyepos.xyz);  //湖面(各顶点)到眼睛的入射向量
  10.  
  11.    RefractVec = refract(InVec, norm, Eta);//Eta,折射率,0.66
  12.    ReflectVec = reflect(InVec, norm);
  13.  
  14.  
  15.    RefractVec = vec3(matViewInverse * vec4(RefractVec, 1.0) );//
  16.    ReflectVec = vec3(matViewInverse * vec4(ReflectVec, 1.0) );

refract函数和reflect函数,很容易了解。最后是乘以视图转换矩阵的逆矩阵,把向量转回世界空间了。在橙书里提到可利用gl_TextureMatrix纹理矩阵[再弹OpenGL矩阵——到另一个空间去] ,但其实不是说要做什么纹理坐标变换,只是用来保存旋转而已。

最后把它俩作为varying经过插值输出到fragment shader,也不需要重归一化了,因为只用来检索CubeMap(见上篇)。

3.怎样确定反射与折射结果之间的混合因子?

其实就是上面提到的Ratio。有这么一种现象:我们在湖边看湖的时候更多地看到的是反射的结果,在湖的上空望下去则更多看到的是折射的结果——换言之,这个混合因子应该跟我们看湖的角度有关(入射角,上图的Θ)。这叫Fresnel现象,用其发现的关系式表达,则是:......呃,听说挺复杂的,于是Christophe Schlick 为我们计算机图形学编程提供了一条近似的简单式:

Ratio = f + (1 - f) (1 - InVec • norm)fresnelPower

式子的几个参数含义就是上面所说的。另外fresnelPower一般取5就可以了。而 f 则明显得与具体的介质有关,即跟折射率有关:

n1/n2就是空气与水间的折射率了。

 同时对环境帖图拥有反射和折射的模型(renderMonkey内):

折射 反射 SHADER GLSL  www.zwqxin.com
折射 反射 SHADER GLSL  www.zwqxin.com

 
(参考资料:OpenGL Shading Language Secong Edition)

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

发表评论:

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

IE下本页面显示有问题?

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

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

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