本教程涵盖了折射映射以及立方体映射的实现。
这是章节“反射平面”的变体,应该先阅读一下。
折射映射
水晶球就是弯曲、透明表面的例子。
在章节“反射表面”中,我们反射了观察射线以及在反射方向执行了立方体纹理中的纹理查找。这里,我们在弯曲、透明的表面折射观察射线,然后在折射方向上进行查找。这个效果将会忽略二次折射当射线从透明物体内部离开时;但是,大多数人很难注意到这个区别因为这种折射并不是我们日常生活的一部分。
我们使用射线函数来代替反射函数;于是,片元着色器应该是这样的:
float4 frag(vertexOutput input) : COLOR
{
float refractiveIndex = 1.5;
float3 refractedDir = refract(normalize(input.viewDir),
normalize(input.normalDir), 1.0 / refractiveIndex);
return texCUBE(_Cube, refractedDir);
}
注意refract
有第三个变量,它是外部介质的折射率(比如空气为1.0)除以物体的折射率(比如几种玻璃为1.5)。同样也要注意第一个变量必须是归一化的,它对于reflect
并不是必要的。
完整的着色器代码
改写完片元着色器后,完整的着色器代码应该是这样的:
Shader "Cg shader with refraction mapping" {
Properties {
_Cube("Reflection Map", Cube) = "" {}
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// User-specified uniforms
uniform samplerCUBE _Cube;
struct vertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float3 normalDir : TEXCOORD0;
float3 viewDir : TEXCOORD1;
};
vertexOutput vert(vertexInput input)
{
vertexOutput output;
float4x4 modelMatrix = _Object2World;
float4x4 modelMatrixInverse = _World2Object;
output.viewDir = mul(modelMatrix, input.vertex).xyz
- _WorldSpaceCameraPos;
output.normalDir = normalize(
mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
return output;
}
float4 frag(vertexOutput input) : COLOR
{
float refractiveIndex = 1.5;
float3 refractedDir = refract(normalize(input.viewDir),
normalize(input.normalDir), 1.0 / refractiveIndex);
return texCUBE(_Cube, refractedDir);
}
ENDCG
}
}
}
总结
恭喜!这里是另一个教程的结尾。我们学到了:
- 如何用
refract
指令把反射映射改写为折射映射。
扩展阅读
如果你想了解得更多:
- 关于反射映射和立方体贴图,你应该阅读章节“反射表面”。
- 关于
refract
指令,你可以在“Nivida的Cg教程附录E”中查找。