http://blog.sina.com.cn/s/blog_89d90b7c0102vfqz.html
球面贴图一般用于环境反射,如下图(左边为球面贴图,右边为正常贴图),一个镜面水晶球在这只猫的前方,而这只猫也正看着这个水晶球,效果是这只大头猫的头变的更大了。
原理:
1.将顶点转换到摄像机空间
2.根据该顶点的摄像机【入射向量 I 】和【法线向量N】计算【反射向量R】
3.将【反射向量R】与【摄像机向量(0,0,1)】相加,计算出【过渡向量m】
4.求【过渡向量m】单位向量,再将值映射为uv的值域上,求得纹理坐标
如图:
基础知识:
求反射向量 http://www.cnblogs.com/graphics/archive/2013/02/21/2920627.html
代码和详细分析如下:
}
<span style="background-color: rgb(255, 255, 255);">Shader "Custom/球面贴图" { Properties { _Reflectivity ("Reflectivity", Range (0,1)) = 0.5 _MainTex("Base", 2D) = "white" _Environment ("Environment", 2D) = "white" } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex; sampler2D _Environment; float4 _MainTex_ST; float _Reflectivity; struct v2f { float4 pos : SV_POSITION; // 输出的坐标信息 float2 uv : TEXCOORD0; // 输出的主材质纹理信息 float2 uv2:TEXCOORD1; // 输出的球面纹理信息 } ; // 求反射向量。这个底层有封装,拆开就是这样的 float3 reflect(float3 I,float3 N) { return I - 2.0*N *dot(N,I); } float2 GetUV(float3 r) { // 开根号对过渡向量m求模(反射向量r+摄像机向量(0,0,1)为过渡向量m) float m = sqrt(r.x * r.x + r.y * r.y + (r.z + 1.0) * (r.z + 1.0)); // 求过渡向量m的单位向量 float3 n = float3(r.x / m, r.y / m, r.z / m); // 顶点值域为[-1,1],转为UV的值域[0,1] // 在边缘处UV信息也是更靠近图片里面,才形成镜面水晶球的效果吧 return float2(0.5 * n.x + 0.5,0.5 * n.y + 0.5); } v2f vert (appdata_base v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); // 将顶点信息转换到摄像机坐标 float3 posEyeSpace = mul(UNITY_MATRIX_MV,v.vertex).xyz; // 获取摄像机的入射向量 float3 I = posEyeSpace - float3(0,0,0); // 将顶点的法线向量转换到摄像机坐标 float3 N = mul((float3x3)UNITY_MATRIX_MV,v.normal); // 求法线单位向量 N = normalize(N); // 根据入射向量和法线向量求反射向量 float3 R = reflect(I,N); // 根据反射向量获取最终的UV信息 o.uv2 = GetUV(R); return o; } float4 frag (v2f i) : COLOR { float4 reflectiveColor = tex2D(_Environment,i.uv2); float4 decalColor = tex2D(_MainTex,i.uv); float4 outp = lerp(decalColor,reflectiveColor,_Reflectivity); return outp; } ENDCG } }</span>