UnityShader笔记 Cubemap

本文详细介绍了如何在Unity3D中使用Shader实现金属表面的反射效果,通过CubeMap纹理和反射向量进行环境映射。接着讲解了折射效果的实现,通过refract函数模拟光线折射。最后,讨论了Fresnel效应,即视角角度变化对反射和折射比例的影响,使用Schlick菲涅尔近似公式进行近似计算。Shader代码展示了具体的实现细节。
摘要由CSDN通过智能技术生成

反射效果

 

使用CubeMap可以实现金属表面的反射效果

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/ReflectionMat"
{
    Properties{
        //TintColor:色调
        _Color("TintColor", Color) = (1.0, 1.0, 1.0, 1.0)  
        _CubeMap("CubeMap", Cube) = "_Skybox"{}
        _Specular("Specular", Color) = (1.0, 1.0, 1.0, 1.0)
        _Gloss("Gloss", Range(8.0, 256)) = 20
        _ReflectScale("ReflectScale", float) = 1
    }
    SubShader{
        Pass{
            Tags{
                "LightMode" = "ForwardBase"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
            #include "UnityCG.cginc"

            half4 _Color;
            samplerCUBE _CubeMap;
            half4 _Specular;
            half _Gloss;
            float _ReflectScale;

            struct a2v{
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f{
                float4 vertex : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float2 uv : TEXCOORD3;
            };

            v2f vert(a2v i){
                v2f o;
                UNITY_INITIALIZE_OUTPUT(v2f, o);
                o.vertex = UnityObjectToClipPos(i.vertex);

                o.worldNormal = normalize(UnityObjectToWorldNormal(i.normal));
                o.worldPos = mul(unity_ObjectToWorld, i.vertex);
                //o.uv = i.texcoord;

                return o;
            }

            half4 frag(v2f i) : SV_TARGET{
                half3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                half3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));

                half3 worldReflectDir = reflect(-viewDir, i.worldNormal);

                half3 reflectDir = normalize(reflect(-lightDir, i.worldNormal));

                half3 albedo = texCUBE(_CubeMap, worldReflectDir).rgb * _Color;

                half3 diffuse = _LightColor0.rgb * albedo * (0.5 * saturate(dot(i.worldNormal, lightDir)) + 0.5);

                half3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * _Color;

                //half3 specular = _LightColor0.rgb * _Color * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

                return half4(diffuse, 1.0);
            }
            ENDCG
        }
    }

    FallBack "Specular"
}

         原理:利用CubeMap映射模型表面的纹理,对CubeMap纹理进行采样需要一个三维向量(不需要标准化),采样以CubeMap中心店为原点,以该向量为方向的射线与CubeMap相交的坐标的纹理。环境经过模型反射到摄像机的过程不好分析,但由于光路可逆,我们可以用视角方向和模型各顶点的法线方向求得环境到模型光线的方向向量,利用该向量在对应的CubeMap进行采样。这里的CubeMap用ScriptableWizard接口利用脚本自动生成。

        反射的结果和纹理的清晰度有关,如上图就是用FaceSize为64的CubeMap绘制的纹理,而下图为用FaceSize为128重新采样的结果。

 折射效果

Shader "Custom/RefractionMat"
{
    Properties{
        //TintColor:色调
        _Color("TintColor", Color) = (1.0, 1.0, 1.0, 1.0)  
        _CubeMap("CubeMap", Cube) = "_Skybox"{}
        _Specular("Specular", Color) = (1.0, 1.0, 1.0, 1.0)
        _Gloss("Gloss", Range(8.0, 256)) = 20
        _ReflectScale("RefractScale", Range(0, 1.0)) = 1
        _AmbientScale("AmbientScale", Range(0, 1.0)) = 0.5
    }
    SubShader{
        Pass{
            Tags{
                "LightMode" = "ForwardBase"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
            #include "UnityCG.cginc"

            half4 _Color;
            samplerCUBE _CubeMap;
            half4 _Specular;
            half _Gloss;
            float _ReflectScale;
            half _AmbientScale;

            struct a2v{
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f{
                float4 vertex : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float2 uv : TEXCOORD3;
            };

            v2f vert(a2v i){
                v2f o;
                UNITY_INITIALIZE_OUTPUT(v2f, o);
                o.vertex = UnityObjectToClipPos(i.vertex);

                o.worldNormal = normalize(UnityObjectToWorldNormal(i.normal));
                o.worldPos = mul(unity_ObjectToWorld, i.vertex);
                //o.uv = i.texcoord;

                return o;
            }

            half4 frag(v2f i) : SV_TARGET{
                half3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                half3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));

                half3 worldRefractDir = refract(-viewDir, i.worldNormal, _ReflectScale);

                half3 reflectDir = normalize(reflect(-lightDir, i.worldNormal));

                half3 albedo = texCUBE(_CubeMap, worldRefractDir).rgb * _Color;

                half3 diffuse = _LightColor0.rgb * albedo * (0.5 * saturate(dot(i.worldNormal, lightDir)) + 0.5);

                half3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * _Color;

                //half3 specular = _LightColor0.rgb * _Color * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

                return half4(diffuse + ambient * _AmbientScale, 1.0);
            }
            ENDCG
        }
    }

    FallBack "Specular"
}

        只需要将模型映射纹理的方式修改成折射即可,计算折射使用refract函数。 如果折射率为1时,则呈现透明效果。

 Fresnel反射

Blinn-Phong经常并不能很好的表现模型的真实材质,如真实世界的水面,在近距离下看到的是清澈见底的水面,但远距离的水面却是波光粼粼的。换个说法就是视角方向与水面法线夹角小的时候,水面折射光线更多;反之,视角方向与水面法线夹角大的时候,主要是反射光线。

Shader "Custom/FresnelMat"
{
    Properties{
        //TintColor:色调
        _Color("TintColor", Color) = (1.0, 1.0, 1.0, 1.0)  
        _CubeMap("CubeMap", Cube) = "_Skybox"{}
        _Specular("Specular", Color) = (1.0, 1.0, 1.0, 1.0)
        _Gloss("Gloss", Range(8.0, 256)) = 20
        _FresnelScale("FresnelScale", Range(0, 1.0)) = 1
        _AmbientScale("AmbientScale", Range(0, 1.0)) = 0.5
    }
    SubShader{
        Pass{
            Tags{
                "LightMode" = "ForwardBase"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
            #include "UnityCG.cginc"

            half4 _Color;
            samplerCUBE _CubeMap;
            half4 _Specular;
            half _Gloss;
            float _FresnelScale;
            half _AmbientScale;

            struct a2v{
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f{
                float4 vertex : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float2 uv : TEXCOORD3;
            };

            v2f vert(a2v i){
                v2f o;
                UNITY_INITIALIZE_OUTPUT(v2f, o);
                o.vertex = UnityObjectToClipPos(i.vertex);

                o.worldNormal = normalize(UnityObjectToWorldNormal(i.normal));
                o.worldPos = mul(unity_ObjectToWorld, i.vertex);
                //o.uv = i.texcoord;

                return o;
            }

            half4 frag(v2f i) : SV_TARGET{
                half3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                half3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));

                half3 worldReflectDir = reflect(-viewDir, i.worldNormal);

                half3 reflectDir = normalize(reflect(-lightDir, i.worldNormal));

                half3 albedo = texCUBE(_CubeMap, worldReflectDir).rgb * _Color;

                half fresnel = _FresnelScale + (1 - _FresnelScale) * pow((1 - dot(viewDir, i.worldNormal)), 5);

                half3 diffuse = _LightColor0.rgb * _Color * (0.5 * saturate(dot(i.worldNormal, lightDir)) + 0.5);

                half3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * _Color;

                //half3 specular = _LightColor0.rgb * _Color * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

                return half4(lerp(diffuse, albedo, saturate(fresnel)) + ambient * _AmbientScale, 1.0);
            }
            ENDCG
        }
    }

    FallBack "Specular"
}

 这里使用的近似等式为Schlick菲涅尔近似等式。

代码中使用的lerp函数作用是根据第三个参数的权值,来取得前两个参数之间的某个值。

游戏开发技术杂谈2:理解插值函数lerp - 知乎 (zhihu.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值