unityshader学习笔记4

顶点/片元着色器的基本结构:

Shader "Custom/SimpleShader"
{
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            float4 vert(float4 v : POSITION) : SV_POSITION {
                return mul(UNITY_MATRIX_MVP, v);
            }
            fixed4 frag() : SV_Target {
                return fixed4(1.0, 1.0, 1.0, 1.0);
            }
            ENDCG
        }
    }
}

Properties属性不是必需的,可以选择不声明任何材质属性

编译指令: (name就是指定的函数名)

#pragma vertex name ---------告诉Untiy哪个函数包含了顶点着色器的代码

#pragma fragment name ---------告诉Unity哪个函数包含了片元着色器的代码

 

float4 vert(float4 v : POSITION) :SV_POSITION {

  return mul(UNITY_MATTIX_MVP,  v);

}

1.顶点着色器代码,它是逐顶点执行的

2.vert函数的输入v包含了这个顶点的位置,这是通过POSITION语义指定的

3.它的返回值是一个float4类型的变量,它是该顶点在裁剪空间中的位置

4.POSITIONSV_POSITION都是CG/HLSL中的语义,它们是不可忽略的,这些语义告诉系统用户需要哪些输入值,以及用户的输入是什么.:

    POSITION告诉Unity,把模型的顶点坐标填充到输入参数v

    SV_POSITION告诉Unity,顶点着色器的输出是裁剪空间中的顶点坐标 [DirectX10以后有了新的语义类型,即系统数值语义,以SV开头(system-value).]

如果没有这些语义来限定输入和输出参数的话,渲染器就不知道用户的输入输出是什么.

语义实际上是一个赋给Shader输入和输出的字符串,这个字符串表达了这个参数的含义,可以让Shader知道从哪里读取数据,并把数据输出到哪里,Unity并没有支持所有语义.

  *应用阶段传递模型数据给顶点着色器(顶点着色器输入)Unity支持的常用语义:

    POSITION-----------模型空间中的顶点位置,通常float4

    NORMAL------------顶点法线,通常float3

    TANGENT------------顶点切线,通常float4

    TEXCOORDn,TEXCOORD0,TEXCOORD1--------该顶点的纹理坐标,通常float2float4

    COLOR---------------顶点颜色,通常fixed4float4

  *顶点着色器传递数据给片元着色器(顶点着色器输出)Unity支持的常用语义:

    SV_POSITION-------裁剪空间中的顶点坐标,必须包含一个

    COLOR0--------------通常用于输出第一组顶点颜色,非必需

    COLOR0--------------通常用于输出第二组顶点颜色,非必需

    TEXCOORD0~TEXCOORD7-------通常用于输出纹理坐标,非必需

    通常把自定义数据从顶点着色器传递给片元着色器,一般选用TEXCOORD0

  *片元着色器输出时Unity支持的常用语义:

    SV_Target-----------输出值将会存储到渲染目标中

  *填充到这些语义中的数据哪里来的:

    在Unity中,它们是由使用该材质的Mesh Render组件提供的.

    每帧调用Draw Call的时候,Mesh Render组件会把它负责渲染的模型数据发送给UnityShader

 

fixed4 frag() : SV_Target {

  return fixed4(1.0, 1.0, 1.0, 1.0);

}

frag函数的输出是一个fixed4类型的变量,并使用SV_Target语义进行限定,告诉渲染器,把用户的输出颜色存储到一个渲染目标中.

 

 

上述在顶点着色器中使用了POSITION语义得到了模型的顶点位置,现在我们想要得到更多模型数据:

Shader "Custom/SimpleShader"
{
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //使用一个结构体来定义顶点着色器的输入             struct a2v { //a2v:a表示应用(application),v表示顶点着色器(vertex shader),a2v的意思是把数据从应用阶段传递到顶点着色器中                 //POSITION语义告诉Unity,用模型空间的顶点坐标填充vertex变量                 float4 vertex : POSITION;                 //NORMAL语义告诉Unity,用模型空间的法线方向填充normal变量                 float3 normal : NORMAL;           //TEXCOORD0语义告诉Unity,用模型的第一套纹理坐标填充texcoord变量                 float4 texcoord : TEXCOORD0;             };             float4 vert(a2v v) : SV_POSITION {                 //使用v.vertex来访问模型空间的顶点坐标                 return mul(UNITY_MATRIX_MVP, v.vertex);             }             fixed4 frag() : SV_Target {                 return fixed4(1.0, 1.0, 1.0, 1.0);             }             ENDCG         }     } }

  

顶点着色器与片元着色器之间通信:

Shader "Custom/SimpleShader" {
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 texcoord : TEXCOORD0;
            };
            //使用一个结构体来定义顶点着色器的输出
            struct v2f {
             //SV_POSITION语义告诉Unity,pos里面包含了顶点在裁剪空间中的位置信息
                float4 pos : SV_POSITION;
                //COLOR0语义可以用于存储颜色信息
                fixed3 color : COLOR0;
            };
            v2f vert(a2v v) {
                //声明输出结构
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                //v.normal包含了顶点的法线方向,其分量范围在[-1.0, 1.0]
                //下面代码把分量范围映射到了[0.0, 1.0]
                //存储到0.color中传递给片元着色器
                o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
                return o;
            }
            fixed4 frag(v2f i) : SV_Target {
                //将插值后的i.color显示到屏幕上
                return fixed4(i.color, 1.0);
            }
            ENDCG
        }
    }
}

我们声明了一个新的结构体v2f,用于在顶点着色器和片元着色器之间传递信息

v2f中也需要定义每个变量的语义,:

  SV_POSITION 必须包含,否则渲染器将无法得到裁剪空间中的顶点坐标,也就无法把顶点映射到屏幕上

  COLOR0 中的数据可以自行定义,一般存储颜色,类似的有COLOR1

需要注意的是  顶点着色器是逐顶点调用的,片元着色器是逐片元调用的.

片元着色器中的输入实际上是把顶点着色器的输出进行插值后得到的结果

 

如何使用属性:

Shader "Custom/SimpleShader"
{
    Properties {
        //声明一个Color类型的属性
        _Color ("Color Tint", Color) = (1.0, 1.0, 1.0, 1.0)
    }
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            //在CG代码中,我们需要定义一个与属性名称和类型都匹配的变量
            fixed4 _Color;

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

            struct v2f {
                float4 pos : SV_POSITION;
                fixed3 color : COLOR0;
            };

            v2f vert(a2v v) {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target { 
                fixed3 c = i.color;
                //使用Color属性来控制输出颜色
                c *= _Color.rgb;
                return fixed4(c, 1.0);
            }
            ENDCG
        }
    }
}

 

上面代码中,我们首先添加了Properties语义块,并在其中声明了一个属性_Color,它的类型是Color,初始值是(1.0,1.0,1.0,1.0)对应白色.

为了在CG代码中可以访问它,还需在CG代码片段中提前定义一个新的变量,这个变量的名称和类型必须与Properties语义块中的属性定义相匹配

 

           ShaderLab属性类型和CG变量类型的匹配关系:

           CG/HLSL中的3种数值类型的精度

 

 

内置的包含文件:

Unity为我们提供了一些非常有用的变量和帮助函数,可以使用#include指令将它们包含进来:

  CGPROGRAM

  #include UnityCG.cginc

  ...

  ENDCG

posted @ 2022-10-08 10:53  亲爱的贤锅锅  阅读(44)  评论(0编辑  收藏  举报