Unity Shader 学习笔记(15) 立方体纹理、反射、折射、菲涅尔反射

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/l773575310/article/details/78583264

Unity Shader 学习笔记(15) 立方体纹理、反射、折射、菲涅尔反射

参考书籍:《Unity Shader 入门精要》


立方体纹理(Cubemap)

是环境映射(Environment Mapping)的一种实现方法。用于模拟物体周围的环境,使用了环境映射的物体可以让物体像镜子一样反射周围环境。

采样方法:用一个三维纹理坐标表示方向,原点是立方体中心。交点就是采样的结果。

天空盒子(Skybox)

用于模拟室内背景。

创建环境映射的立方体纹理方法有三种:
- 由特殊布局的纹理创建。提供如立方体展开图的交叉布局、全景布局等。
- 创建一个Cubemap,把6张纹理拖拽到面板中。
- 由脚本生成,可以从任意位置观察场景的图像存储到6张图像中。调用Camera.RenderToCubemap(cubemap)生成,创建cubemap要标记Readable才能修改。


反射(Reflection)

用环境映射纹理代替了高光反射。

Properties {
    _Color ("Color Tint", Color) = (1, 1, 1, 1)
    _ReflectColor ("Reflection Color", Color) = (1, 1, 1, 1)    // 反射颜色
    _ReflectAmount ("Reflect Amount", Range(0, 1)) = 1          // 反射程度
    _Cubemap ("Reflection Cubemap", Cube) = "_Skybox" {}        // 环境纹理
}
struct v2f {
    ...
    fixed3 worldViewDir : TEXCOORD2;
    fixed3 worldRefl : TEXCOORD3;
    SHADOW_COORDS(4)
};

v2f vert(a2v v) {
    v2f o;
    ...

    o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);    //计算视角方向
    o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);  // 计算反射方向

    TRANSFER_SHADOW(o);
    return o;
}

fixed4 frag(v2f i) : SV_Target {
    ...

    // 对立方体采样,CG的texCUBE函数。
    fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb * _ReflectColor.rgb;

    UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

    // 对漫反射和立方体做插值,作为漫反射和高光反射的颜色。
    fixed3 color = ambient + lerp(diffuse, reflection, _ReflectAmount) * atten;

    return fixed4(color, 1.0);
}


折射(Refraction)

斯涅耳定律(Snell’s Law),n为折射率:
- n1sinθ1 = n2sinθ2

在实际中,需要计算两次折射,第一次是光线进入内部,第二次是内部射出。但要在实时渲染时模拟第二次折射比较复杂,所以通常只模拟第一次折射。

Properties {
    _Color ("Color Tint", Color) = (1, 1, 1, 1)
    _RefractColor ("Refraction Color", Color) = (1, 1, 1, 1)        // 折射颜色
    _RefractAmount ("Refraction Amount", Range(0, 1)) = 1           // 折射程度
    _RefractRatio ("Refraction Ratio", Range(0.1, 1)) = 0.5         // 折射比例
    _Cubemap ("Refraction Cubemap", Cube) = "_Skybox" {}            // 模拟折射环境纹理
}
v2f vert(a2v v) {
    v2f o;
    ...

    o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
    // 计算折射方向,第一个参数为入射方向,第二个参数是表面法线,第三个参数是入射折射率和出射比值。
    o.worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefractRatio);

    TRANSFER_SHADOW(o);
    return o;
}

fixed4 frag(v2f i) : SV_Target {
    ...
    // 对立方体采样
    fixed3 refraction = texCUBE(_Cubemap, i.worldRefr).rgb * _RefractColor.rgb;

    UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

    // 混合漫反射颜色和折射颜色
    fixed3 color = ambient + lerp(diffuse, refraction, _RefractAmount) * atten;
    return fixed4(color, 1.0);
}

菲涅尔反射(Fresnel reflection)

即光线照射物体时,一部分发生反射,另一部分进入物体发生折射或散射。

近处湖面可以直接看到湖底(折射),远处湖面就只能看到镜面反射了。

Schilick菲涅尔近似等式(F0为反射系数,v为视角方向,n为表面法线):

Empricial菲涅尔近似等式:

使用Schilick菲涅尔近似等式实现:

Properties {
    _Color ("Color Tint", Color) = (1, 1, 1, 1)
    _FresnelScale ("Fresnel Scale", Range(0, 1)) = 0.5  // 对应F0
    _Cubemap ("Reflection Cubemap", Cube) = "_Skybox" {}
}
v2f vert(a2v v) {
    v2f o;
    ...
    // 同反射计算
    o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
    o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);

    TRANSFER_SHADOW(o);
    return o;
}

fixed4 frag(v2f i) : SV_Target {
    ...

    fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb;

    // Schlick菲涅尔近似等式
    fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir, worldNormal), 5);

    fixed3 color = ambient + lerp(diffuse, reflection, saturate(fresnel)) * atten;

    return fixed4(color, 1.0);
}

普通反射和菲涅尔反射系数都为 0 对比:

普通反射和菲涅尔反射系数都为 0.2 对比:

普通反射和菲涅尔反射系数都为 1 对比(通过公式可以看出两者值是一样的):

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页