29.折射基础实现

29.高级纹理-立方体纹理-折射-折射基础实现


29.1 知识点

折射向量计算函数

在 CG 中,内置函数 refract 用于计算折射向量:

  • 函数原型refract(入射方向单位向量, 顶点法线单位向量, 入射介质折射率/射入介质折射率)
  • 计算原理:利用斯涅耳定律计算折射向量。

通过调用此函数,可以直接获取目标介质中的折射向量,无需额外编写逻辑计算。

折射的基础实现

主要步骤

  1. 关键属性
    定义以下四个关键属性:

    • 介质 A 的折射率。
    • 介质 B 的折射率。
    • 立方体纹理贴图。
    • 折射程度。
  2. 关键流程

    • 编译指令、内置文件、属性映射、结构体相关
    • 顶点着色器
      • 顶点坐标转裁剪坐标。
      • 顶点法线转世界坐标。
      • 顶点坐标转世界坐标。
      • 计算视角方向(世界空间)。
      • 利用折射函数计算折射向量。
    • 片元着色器
      • 使用 texCUBE 函数进行立方体纹理采样。
      • 结合折射程度返回最终颜色。

创建一个新的 Shader,命名为 Lesson29_RefractionBase

Shader "Unlit/Lesson29_RefractionBase"
{
}

声明属性,包括介质A折射率、介质B折射率、立方体纹理贴图和折射程度,已经对应映射属性。同时包括渲染标签编译指令以及内置文件等

Shader "Unlit/Lesson29_RefractionBase"
{
    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;

            
            ENDCG
        }
    }
}

声明v2f结构体,包括裁剪空间下顶点坐标和折射向量

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;
}

创建材质,使用小工具生成正方体纹理进行测试,可以看到折射效果


29.2 知识点代码

Lesson29_高级纹理_立方体纹理_折射_折射基础实现.cs

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

public class Lesson29_高级纹理_立方体纹理_折射_折射基础实现 : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 折射向量计算函数

        //折射向量计算函数
        //  CG中提供了内置函数 refract 用于进行折射向量的计算
        //  refract(入射方向单位向量, 顶点法线单位向量, 入射介质折射率/射入介质折射率)
        //  便可以得到在目标介质中的折射向量

        //  该函数内部就是利用了斯涅耳定律进行的计算
        //  我们无需再自己写逻辑计算了

        #endregion

        #region 知识点二  

        //1.属性声明
        //  我们将声明4个关键属性
        //  1-1:介质A折射率
        //  1-2:介质B折射率
        //  1-3:立方体纹理贴图
        //  1-4:折射程度

        //2.编译指令、内置文件、属性映射、结构体相关

        //3.顶点着色器
        //  关键步骤:
        //  3-1:顶点坐标转裁剪坐标
        //  3-2:顶点法线转世界坐标
        //  3-3:顶点坐标转世界坐标
        //  3-4:世界空间下 视角方向计算
        //  3-5:利用折射函数计算折射向量

        //4.片元着色器
        //  关键步骤:
        //  3-1:立方体纹理采样(利用texCUBE函数)
        //  3-2:结合折射程度返回最终颜色

        #endregion
    }
}

Lesson29_RefractionBase.shader

Shader "Unlit/Lesson29_RefractionBase"
{
    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
        }
    }
}


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

×

喜欢就点赞,疼爱就打赏