Unity Shader CG属性变量的使用

1、CG中声明属性变量

Shader通过Properties代码块声明开放出来的属性,如果想要在Shader程序中访问这些属性,则需要在CG代码块中再次进行声明。
示例:

Shader "Custom/CG Properties"
{
    Properties
    {
        _MyFloat ("Float Property", Float) = 1 // 浮点类型
        _MyRange ("Range Property", Range(0, 1)) = 0.1 // 范围类型
        _MyColor ("Color Property", Color) = (1, 1, 1, 1) // 颜色类型
        _MyVector ("Vector Property", Vector) = (0, 1, 0, 0) //向量类型
        _MyTex ("Texture Property", 2D) = "white" {} // 2D贴图类型
        _MyCube ("Cube Property", Cube) = "" {} // 立方体贴图类型
        _My3D ("3D Property", 3D) = "" {} // 3D贴图类型
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // 在CG中声明属性变量
            float _MyFloat; // 浮点类型
            float _MyRange; // 范围类型
            fixed4 _MyColor; // 颜色类型
            float4 _MyVector; //向量类型
            sampler2D _MyTex; // 2D贴图类型
            samplerCUBE _MyCube; // 立方体贴图类型
            sampler3D _My3D; // 3D贴图类型

            void vert ()
            {
            }
            void frag ()
            {
            }
            ENDCG
        }
    }
}

在这里插入图片描述

2、在Shader中使用颜色

使用CG声明属性变量后,实现修改颜色功能。
示例如下:

Shader "Custom/CG Properties"
{
    Properties
    {
        _MainColor ("Main Color", Color) = (1, 1, 1, 1) // 开放颜色属性
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // 在CG中声明属性变量
            fixed4 _MainColor; // 颜色类型

            void vert (in float4 vertex:POSITION,out float4 position:SV_POSITION)
            {
            	//将顶点从模型空间变换到齐次裁切空间
            	position=UnityObjectToClipPos(vertex);
            }
            void frag (out fixed4 color:SV_TARGET)
            {
            	//调用颜色变量
            	color=_MainColor;
            }
            ENDCG
        }
    }
}

3、在Shader中使用贴图

纹理贴图在Properties代码块中被定义之后,需要在CG代码块中再次声明。
问题贴图采样使用的函数为:

tex2D(Texture,texcoord);
  • Texture:表示纹理贴图;
  • texcoord : 表示对贴图纹理进行采样;
    另外,CG还需要额外声明一个变量用于存储贴图的其他信息。比如经常用到的平铺(Tiling)和偏移(Offset)属性。
    在这里插入图片描述
    在CG中声明一个纹理变量的Tiling和Offset的语法如下:
float4 {TextureName}_ST;
  • TextureName:纹理属性的名称;
  • ST:Scale和Transform的首字母,表示UV的缩放和平移。

在CG所声明的变量为float4类型,其中 x x x y y y分量分别为Tiling的 X X X值和 Y Y Y值, z z z w w w分量分别为Offset的 X X X值和 Y Y Y值。
纹理的计算公式为
t e x c o o r d = u v ⋅ { T e x t u r e N a m e } . x y + { T e x t u r e N a m e } . z w texcoord=uv·\{TextureName\}.xy+\{TextureName\}.zw texcoord=uv{ TextureName}.xy+{ TextureName}.zw

需要特别注意的是,在计算纹理坐标的时候,一定要先乘以平铺值再加偏移值。
需要配合调用tex2D()函数,使用texcoord对贴图纹理进行采样.

一个完整示例:

Shader "Custom/Texture Property"
{
    Properties
    {
        _MainColor ("MainColor", Color) = (1, 1, 1, 1)
        _MainTex ("MainTex", 2D) = "white" {}   // 开放纹理属性
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            fixed4 _MainColor;
            sampler2D _MainTex;  // 声明纹理属性变量以及ST变量
            float4 _MainTex_ST;

            void vert (in float4 vertex : POSITION,in float2 uv : TEXCOORD0, out float4 position : SV_POSITION,
                    out float2 texcoord : TEXCOORD0)
            {
                position = UnityObjectToClipPos(vertex);
                // 使用公式计算纹理坐标
                texcoord = uv * _MainTex_ST.xy + _MainTex_ST.zw;
            }

            void frag (in float4 position : SV_POSITION,in float2 texcoord : TEXCOORD0,out fixed4 color : SV_TARGET)
            {
            	//调用tex2D()函数,使用texcoord对纹理_MainTex进行采样
                color = tex2D(_MainTex, texcoord) * _MainColor;
            }
            ENDCG
        }
    }
}

通常情况下,纹理资源都需要按照这种流程进行使用,除非能够确定某个纹理资源永远不会用到Tiling和Offset,则可以省略该资源ST变量的声明,同时不再计算其纹理坐标。

      void vert (in float4 vertex : POSITION,in float2 uv : TEXCOORD0, out float4 position : SV_POSITION,
                    out float2 texcoord : TEXCOORD0)
            {
                position = UnityObjectToClipPos(vertex);
                // 使用公式计算纹理坐标
                texcoord = uv;
            }

4、在Shader中使用立方体贴图

立方体贴图是由前、后、左、右、上、下、六个方向组成的立方体盒子,也可以在Unity中使用全景图转换得到,通常用于环境反射。
立方体贴图采样使用的函数为:

texCUBE(Cube,r);
  • Cube:表示立方体贴图;
  • r : 表示视线方向在物体表面上的反射方向;

Cube可以直接在CG中声明这个属性变量,然后直接获取,而 r r r的获取比较复杂:
物体表面的反射向量示例图:
在这里插入图片描述

假设从摄像机指向顶点的方向为视线向量的 v v v,从物体表面反射出去的方向为反射向量 r r r,物体表面的法线向量为 n n n,且这些向量已经标准化过。

反射向量 r r r的计算公式为
r = 2 { v + [ ( − v ) ⋅ n ] n } − v = 2 [ ( − v ) ⋅ n ] n + v r=2\{v+[(-v)·n]n\}-v=2[(-v)·n]n+v r=2{ v+[(v)n]n}v=2[(v)n]n+v

  • v v v: 摄像机指向顶点的方向为视线向量;
  • n n n:物体表面的法线向量;

一个完整立方体贴图示例:

Shader "Unlit/Cubemap Property"
{
    Properties
    {
        _MainTex ("Main Tex", 2D) = "white" {}
        _MainColor("Main Color",Color)=(1,1,1,1)
        //添加Cubemap属性和反射强度
        _Cubemap("Cubemap",Cube)=""{}
        _Reflection("Reeflection",Range(0,1))=0
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
           
           sampler2D _MainTex;
           float4 _MainTex_ST;
           fixed4 _MainColor;

           //声明Cubemap和反射属性变量
           samplerCUBE _Cubemap;
           fixed _Reflection;


            void vert (in float4 vertex:POSITION,
            in float3 normal:NORMAL,
            in float4 uv : TEXCOORD0,
            out float4 position : SV_POSITION,
            out float4 worldPos : TEXCOORD0,
            out float3 worldNormal : TEXCOORD1,
            out float2 texcoord : TEXCOORD2)
            {
               position=UnityObjectToClipPos(vertex);
               //将顶点坐标变换到世界空间
               worldPos=mul(unity_ObjectToWorld,vertex);
               //将法线向量变换到世界空间
               worldNormal=mul(normal,(float3x3)unity_WorldToObject);
               worldNormal=normalize(worldNormal);

               texcoord=uv*_MainTex_ST.xy+_MainTex_ST.zw;
            }

            void frag (in float4 position:SV_POSITION,
            in float4 worldPos:TEXCOORD0,
            in float3 worldNormal:TEXCOORD1,
            in float2 texcoord:TEXCOORD2,
            out fixed4 color:SV_TARGET) 
            {
               fixed4 main=tex2D(_MainTex,texcoord)*_MainColor;
               //计算世界空间中从摄像机指向顶点的方向向量
               float3 viewDir=worldPos.xyz-_WorldSpaceCameraPos;
               viewDir=normalize(viewDir);
               //套用公式计算反射向量
               float3 refDir=2*dot(-viewDir,worldNormal)*worldNormal+viewDir;
               refDir=normalize(refDir);
               //对Cubemap采样
               fixed4 reflection=texCUBE(_Cubemap,refDir);
               //使用_Reflection对颜色和反射进行线性插值计算
               color=lerp(main,reflection,_Reflection);
            }
            ENDCG
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_40120946/article/details/122116535
今日推荐