本章理论非常简单,示例程序中的四个实际上真真正正跟本章主题能挂上钩的也就是反射跟折射这两个演示程序了,最后的一个程序是折射跟反射的叠加,没啥技术含量,只要反射跟折射都搞明白了,最后这个演示简直就是浮云。
背景绘制就省略了,前几章也已经练习过好多次了。
我们看看反射跟折射。
基本原理:
1. 反射——将ViewVector向量看作是反射光线,根据ViewVector,及反射点表面发现来计算入射光线,根据入射光线对CubeMap采样,作为反射点的纹理颜色。
2. 折射——将ViewVector向量看作是折射光线,根据ViewVector,及反射点表面发现来计算入射光线,根据入射光线对CubeMap采样,作为折射点的纹理颜色。
反射跟折射在VS2.0,PS2.0下均有内建函数,因此可以直接使用。但是本书在出版的时候,只有reflect函数已经可以使用了,但是refract函数支持的并不是很好。
reflect函数自己实现也十分简单,我们不做讨论。
单单看一下refract函数是如何用HLSL代码实现的:
1 VS_OUTPUT vs_main( VS_INPUT Input ) 2 { 3 VS_OUTPUT Output = (VS_OUTPUT)0; 4 5 //定点变换及纹理坐标传递 6 Output.Position = mul(float4(Input.Position,1),matViewProjection); 7 Output.Tex = Input.Tex; 8 9 //计算折射角余弦值,根据余弦值计算其正弦值 10 float3 viewVec = normalize(vViewPosition.xyz - Input.Position); 11 float cosIV = dot(viewVec, Input.Normal); 12 float sinIV = sqrt( 1 - cosIV * cosIV ); 13 14 //有了折射角的正弦值、折射率,根据斯捏耳定律计算出入射角的正弦值及余弦值。 15 float sinRv = saturate( 1.14 * sinIV ); 16 float cosRv = sqrt( 1 - sinRv * sinRv ); 17 18 //x为反向顶点法线,y为入射光线在折射平面的介质分界线上的投影,非常Trick的一个算法 19 float3 x = - Input.Normal; 20 float3 y = normalize( cross( cross( viewVec , Input.Normal ), Input.Normal)); 21 22 //根据入射光线的两个方向上的分量计算入射光线(折射部分)。 23 Output.Refract = x * cosRv + y * sinRv; 24 25 //计算入射光线(反射部分) 26 Output.Reflect = -reflect(vViewPosition - Input.Position,Input.Normal); 27 28 //考虑到折射临界角. 29 Output.Factors.x = sinRv; 30 Output.Factors.y = 1 - sinRv; 31 32 return( Output ); 33 }
关键是要理解18,19,23行代码计算折射部分入射光的原理。
应该注意的问题:
1. Vertex Stream Mapping在设置的时候,一定要和Vertex Shader中的布局一致,法线尤为重点,如果法线后边的语义声明定义为了TEXCOORD,会出现非常奇怪的透明情况,而非折射。
2. 计算折射光线的入射光线最后几步,19,20行中的入射光线分量一定不要定义错了,它们是float3,切忌定义为float,因为它们是向量而非scale。定义为scale会出现一坨黄颜色的茶壶,没有任何折射情形。
最中运行结果如下:
//The end