shader实例(二十二)TexGen-球面贴图SphereMap

http://blog.sina.com.cn/s/blog_89d90b7c0102vfqz.html

球面贴图一般用于环境反射,如下图(左边为球面贴图,右边为正常贴图),一个镜面水晶球在这只猫的前方,而这只猫也正看着这个水晶球,效果是这只大头猫的头变的更大了。shader实例(二十二)TexGen-球面贴图SphereMap


原理:

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>

原文地址:https://www.cnblogs.com/nafio/p/9137319.html