30.折射结合漫反射及阴影

  1. 30.高级纹理-立方体纹理-折射-折射结合漫反射及阴影
    1. 30.1 知识点
      1. 实现带漫反射和阴影的折射效果
        1. 主要步骤
        2. 新建一个Shader,复制Lesson29_RefractionBase的代码,取名叫Lesson30_RefractionDiffuseShadow
        3. 将折射率A和折射率B 合并为一个 折射率比值变量,以后在外部算好了传入,避免内部计算浪费性能。同时声明漫反射和阴影的属性。处理新增属性的映射。同时添加渲染路径、编译指令、内置文件
        4. 因为要在片元着色器中处理光和阴影,v2f结构体需要加入世界空间法线\世界空间顶点位置和阴影宏
        5. 顶点函数把之前临时变量改成赋值到v2f的变量中,同时添加接受阴影处理宏
        6. 片元函数得到光的方向并进行兰伯特漫反射颜色计。保留立方体纹理采样(利用texCUBE函数),使用UNITY_LIGHT_ATTENUATION进行衰减计算。最终利用lerp函数计算最终的颜色,颜色在漫反射颜色和折射颜色之间
        7. 添加FallBack,保证当当前 Shader 不支持某些特性或图形硬件无法完全支持时,可以使用备用 的反射Shader
        8. 创建材质赋值,可以看到有漫反射的效果,和上节课的基础实现有明显区别,同时可以投射和接受阴影,调整折射率和折射程度可以看到效果
    2. 30.2 知识点代码
      1. Lesson30_高级纹理_立方体纹理_折射_折射结合漫反射及阴影.cs
      2. Lesson30_RefractionDiffuseShadow.shader

30.高级纹理-立方体纹理-折射-折射结合漫反射及阴影


30.1 知识点

实现带漫反射和阴影的折射效果

主要步骤

  1. 新建 Shader
    创建一个新的 Shader 文件,并复制上一节 Lesson29_RefractionBase 的代码作为基础。

  2. 优化折射率计算
    将折射率 A 和折射率 B 合并为一个折射率比值变量,在外部计算后直接传入 Shader,减少内部计算开销,提高性能。

  3. 结合漫反射与阴影的折射效果
    在实现折射效果的基础上,加入漫反射和阴影的支持。该部分的逻辑与之前的反射实现非常相似,因此可以直接复用部分代码,避免重复书写。

    • 属性定义
      添加与漫反射颜色和折射颜色相关的属性代码。
    • 渲染路径与编译指令
      复制渲染路径、编译指令以及必要的内置文件,调整属性映射。
    • 结构体扩展
      增加支持光照和阴影计算所需的结构体字段。
    • 顶点着色器修改
      调整顶点着色器代码,使其支持新增功能,同时处理阴影计算。
    • 片元着色器优化
      在片元着色器中结合漫反射、折射效果以及阴影计算,完善最终渲染效果。

新建一个Shader,复制Lesson29_RefractionBase的代码,取名叫Lesson30_RefractionDiffuseShadow

Shader "Unlit/Lesson30_RefractionDiffuseShadow"
{
    Properties
    {
        //立方体纹理贴图
        _Cube("Cubemap", Cube) = ""{}
        //介质A折射率
        _RefractiveIndexA("RefractiveIndexA", Range(1,2)) = 1
        //介质B折射率
        _RefractiveIndexB("RefractiveIndexB", Range(1,2)) = 1.3
        //折射程度
        _RefracAmount("RefracAmount", Range(0,1)) = 1
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque" // 设置渲染类型为不透明(Opaque)
            "Queue"="Geometry" // 设置渲染队列为Geometry,通常用于普通几何物体
        }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            samplerCUBE _Cube;
            fixed _RefractiveIndexA;
            fixed _RefractiveIndexB;
            fixed _RefracAmount;

            struct v2f
            {
                //裁剪空间下顶点坐标
                float4 pos:SV_POSITION;
                //折射向量
                float3 worldRefr:TEXCOORD0;
            };

            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;
                
                //顶点坐标转换
                v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
                
                //法线转世界
                fixed3 worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
                
                //顶点转世界
                fixed3 worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
                
                //视角方向获取 摄像机 - 顶点位置 计算反射向量时要取反
                fixed3 worldViewDir = UnityWorldSpaceViewDir(worldPos);
                
                //计算折射向量
                //第三个参数一定是 介质A/介质B的结果 我们可以声明一个变量在外部算好传进来 这里我们用两个变量只是为了讲解知识
                v2f.worldRefr = refract(-normalize(worldViewDir), normalize(worldNormal),
                                      _RefractiveIndexA / _RefractiveIndexB);

                return v2f;
            }

            fixed4 frag(v2f v2f):SV_TARGET
            {
                //立方体纹理采样
                fixed4 cubemapColor = texCUBE(_Cube, v2f.worldRefr);
                //结合折射程度进行计算返回
                return cubemapColor * _RefracAmount;
            }
            ENDCG
        }
    }
}

将折射率A和折射率B 合并为一个 折射率比值变量,以后在外部算好了传入,避免内部计算浪费性能。同时声明漫反射和阴影的属性。处理新增属性的映射。同时添加渲染路径、编译指令、内置文件

Shader "Unlit/Lesson30_RefractionDiffuseShadow"
{
    Properties
    {
        //漫反射颜色
        _Color("Color", Color) = (1,1,1,1)
        //折射颜色
        _RefractColor("RefractColor", Color) = (1,1,1,1)
        //折射率比值 介质A折射率/介质B折射率 一般空气比其他的小 所以空气作为A时一般小于1
        _RefractRatio("RefractRatio", Range(0.1, 1)) = 0.5
        //立方体纹理贴图
        _Cube("Cubemap", Cube) = ""{}
        //折射程度
        _RefracAmount("RefracAmount", Range(0,1)) = 1
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque" // 设置渲染类型为不透明(Opaque)
            "Queue"="Geometry" // 设置渲染队列为Geometry,通常用于普通几何物体
        }
        
        Pass
        {
            Tags
            {
                "LightMode"="ForwardBase"
            }
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //Shader变体
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            //灯光内置文件
            #include "AutoLight.cginc"

            fixed4 _Color;
            fixed4 _RefractColor;
            samplerCUBE _Cube;
            fixed _RefractRatio;
            fixed _RefracAmount;

            ENDCG
        }
    }
}

因为要在片元着色器中处理光和阴影,v2f结构体需要加入世界空间法线\世界空间顶点位置和阴影宏

struct v2f
{
    //裁剪空间下顶点坐标
    float4 pos:SV_POSITION;
    //世界空间下法线
    fixed3 worldNormal:NORMAL;
    //世界空间下的顶点位置
    float3 worldPos:TEXCOORD0;
    //折射向量
    float3 worldRefr:TEXCOORD1;
    //阴影相关
    SHADOW_COORDS(2)
};

顶点函数把之前临时变量改成赋值到v2f的变量中,同时添加接受阴影处理宏

v2f vert(appdata_base appdata_base)
{
    v2f v2f;
    //顶点坐标转换
    v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
    //法线转世界
    v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
    //顶点转世界
    v2f.worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
    //视角方向获取 摄像机 - 顶点位置
    fixed3 worldViewDir = UnityWorldSpaceViewDir(v2f.worldPos);
    //计算折射向量
    //第三个参数一定是 介质A/介质B的结果 我们可以声明一个变量在外部算好传进来 这里我们用两个变量只是为了讲解知识
    v2f.worldRefr = refract(-normalize(worldViewDir), normalize(v2f.worldNormal), _RefractRatio);

    //阴影相关处理
    TRANSFER_SHADOW(v2f);

    return v2f;
}

片元函数得到光的方向并进行兰伯特漫反射颜色计。保留立方体纹理采样(利用texCUBE函数),使用UNITY_LIGHT_ATTENUATION进行衰减计算。最终利用lerp函数计算最终的颜色,颜色在漫反射颜色和折射颜色之间

fixed4 frag(v2f v2f):SV_TARGET
{
    //漫反射光照相关计算
    //得到光的方向
    fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(v2f.worldPos));
    //漫反射颜色
    fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(normalize(v2f.worldNormal), worldLightDir));

    //立方体纹理采样
    fixed3 cubemapColor = texCUBE(_Cube, v2f.worldRefr).rgb * _RefractColor.rgb;

    //得到光照衰减以及阴影相关的衰减值
    UNITY_LIGHT_ATTENUATION(atten, v2f, v2f.worldPos);

    //我们利用lerp 在漫反射颜色和反射颜色之间 进行插值 0和1就是极限状态 0 没有折射效果 1只有折射效果 0~1之间就是两者叠加
    fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + lerp(diffuse, cubemapColor, _RefracAmount) * atten;

    //结合折射程度进行计算返回
    return fixed4(color, 1.0);
}

添加FallBack,保证当当前 Shader 不支持某些特性或图形硬件无法完全支持时,可以使用备用 的反射Shader

//内置的阴影投射的Pass
FallBack "Reflective/VertexLit"

创建材质赋值,可以看到有漫反射的效果,和上节课的基础实现有明显区别,同时可以投射和接受阴影,调整折射率和折射程度可以看到效果




30.2 知识点代码

Lesson30_高级纹理_立方体纹理_折射_折射结合漫反射及阴影.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson30_高级纹理_立方体纹理_折射_折射结合漫反射及阴影 : MonoBehaviour
{
    void Start()
    {
        #region 知识点 实现带漫反射和阴影的 折射效果

        //1.新建一个Shader,复制Lesson29_RefractionBase的代码

        //2.将折射率A和折射率B 合并为一个 折射率比值变量
        //  以后在外部算好了传入,避免内部计算浪费性能

        //3.折射效果结合漫反射和阴影的做法
        //  和之前反射几乎一模一样
        //  我们可以直接复制之前的代码
        //  避免书写重复内容

        //  3-1:属性相关代码复制
        //      漫反射颜色和折射颜色
        //  3-2:渲染路径、编译指令、内置文件复制
        //      属性映射复制修改
        //  3-3:结构体相关内容复制
        //  3-4:顶点着色器相关内容修改
        //  3-5:片元着色器相关内容复制修改

        #endregion
    }
}

Lesson30_RefractionDiffuseShadow.shader

Shader "Unlit/Lesson30_RefractionDiffuseShadow"
{
    Properties
    {
        //漫反射颜色
        _Color("Color", Color) = (1,1,1,1)
        //折射颜色
        _RefractColor("RefractColor", Color) = (1,1,1,1)
        //折射率比值 介质A折射率/介质B折射率 一般空气比其他的小 所以空气作为A时一般小于1
        _RefractRatio("RefractRatio", Range(0.1, 1)) = 0.5
        //立方体纹理贴图
        _Cube("Cubemap", Cube) = ""{}
        //折射程度
        _RefracAmount("RefracAmount", Range(0,1)) = 1
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque" // 设置渲染类型为不透明(Opaque)
            "Queue"="Geometry" // 设置渲染队列为Geometry,通常用于普通几何物体
        }
        
        Pass
        {
            Tags
            {
                "LightMode"="ForwardBase"
            }
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //Shader变体
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            //灯光内置文件
            #include "AutoLight.cginc"

            fixed4 _Color;
            fixed4 _RefractColor;
            samplerCUBE _Cube;
            fixed _RefractRatio;
            fixed _RefracAmount;

            struct v2f
            {
                //裁剪空间下顶点坐标
                float4 pos:SV_POSITION;
                //世界空间下法线
                fixed3 worldNormal:NORMAL;
                //世界空间下的顶点位置
                float3 worldPos:TEXCOORD0;
                //折射向量
                float3 worldRefr:TEXCOORD1;
                //阴影相关
                SHADOW_COORDS(2)
            };

            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;
                //顶点坐标转换
                v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
                //法线转世界
                v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
                //顶点转世界
                v2f.worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
                //视角方向获取 摄像机 - 顶点位置
                fixed3 worldViewDir = UnityWorldSpaceViewDir(v2f.worldPos);
                //计算折射向量
                //第三个参数一定是 介质A/介质B的结果 我们可以声明一个变量在外部算好传进来 这里我们用两个变量只是为了讲解知识
                v2f.worldRefr = refract(-normalize(worldViewDir), normalize(v2f.worldNormal), _RefractRatio);

                //阴影相关处理
                TRANSFER_SHADOW(v2f);

                return v2f;
            }

            fixed4 frag(v2f v2f):SV_TARGET
            {
                //漫反射光照相关计算
                //得到光的方向
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(v2f.worldPos));
                //漫反射颜色
                fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(normalize(v2f.worldNormal), worldLightDir));

                //立方体纹理采样
                fixed3 cubemapColor = texCUBE(_Cube, v2f.worldRefr).rgb * _RefractColor.rgb;

                //得到光照衰减以及阴影相关的衰减值
                UNITY_LIGHT_ATTENUATION(atten, v2f, v2f.worldPos);

                //我们利用lerp 在漫反射颜色和反射颜色之间 进行插值 0和1就是极限状态 0 没有折射效果 1只有折射效果 0~1之间就是两者叠加
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + lerp(diffuse, cubemapColor, _RefracAmount) * atten;

                //结合折射程度进行计算返回
                return fixed4(color, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Reflective/VertexLit"
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏