Shader 知识点 第二篇 3种着色器

着色器类型

固定管线着色器(已经淘汰)
顶点片段着色器
表面着色器(最终编译为顶点片段着色器,可以理解为unity封装过的顶点片段着色器)


顶点片段着色器

顶点片段着色器 ——
(1)缺点是不能直接和光照交互
(2)用 CG或HLSL编写,嵌入在 Pass 块中
(3)CG代码必须编写在 CGPROGRAM 和 ENDCG 之间

编译指令

编译指令 说明
pragma vertex name 将名称为name的函数编译为顶点着色器(通常命名 vert)
pragma fragment name 将名称为name的函数编译为片段着色器(通常命名 frag)
pragma fragmentoption optime 添加“optime”到已经编译的opengl片元程序
pragma target name 指定着色器目标(常见值 3.0)

顶点数据结构体

CPU 阶段 ————> GPU阶段,顶点数据必须以一个结构体的形式提交给CG/HLSL顶点程序。

常用的顶点结构定义在 UnityCG.cginc 文件中,通常够用了,也可以自定义结构体。

常用顶点结构体 说明(这个是CPU阶段传过来的顶点数据,作为顶点着色器的输入)
appdata_base 顶点位置、法线、一个纹理坐标 (vertex、normal、texcoord)
appdata_tan 顶点位置、切线、法线、一个纹理坐标 (vertex、tangent、normal、texcoord)
appdata_full 顶点位置、切线、法线、两个纹理坐标、颜色 (vertex、tangent、normal、texcoord、texcoord1、color)

常见变换矩阵 UNITY_MATRIX_MVP


表面着色器

优点是可以处理光照,最终编译为顶点片段着色器,由unity处理光照、阴影、不同的渲染路径

CGPROGRAM—ENDCG 代码放在 SubShader 中,而不是 Pass 块中。将会被编译为多个 Pass

需要懂的 ——
(1)表面着色器编译指令、可选参数
(2)表面函数的输入输出结构体
(3)自定义光照模型
(4)顶点变换函数
(5)最终颜色修改函数

SubShader
{
    Tags{"RenderType"="Opaque"}   //标签
    LOD 200                       //渲染设置(这里没有Pass,直接写在外边)

    CGPROGRAM                    //注意这里的包含的东西从哪里开始
    #pragma surface surf Standard fullforwardshadows
    #pragma target 3.0

    sampler2D _MainTex;
    half _Glossiness;
    half _Metallic;
    fixed4 _Color;

    struct Input{ float2 uv_MainTex; };   //输入结构体一般要自己定义,必须命名 Input,
                                          //另外不需要指定语义它就能直接被解析,也是挺神奇的
                                          //见过的一般是用上贴图变量名,前面加 uv

    void surf(Input IN, inout SurfaceOutputStandard o)
    {
        fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
        o.Albedo =  c.rgb;
        o.Alpha = c.a;
        o.Metallic = _Metallic;
        o.Smoothness = _Glossiness;
    }
    ENDCG

}

(1)表面着色器编译指令、可选参数

#pragma surface   [optionalparams]

//表面函数通常为 void Surf(Input IN, inout SurfaceOutputStandard o){}

//内置光照模型 Lambert、BlinnPhong (自定义光照模型在下边第3点)

//可选参数,很多,详细看吴亚峰296页
部分可选参数 说明
alpha Alpha混合模式,用于半透明材质
alphatest : VariableName Alpha测试模式,用于透明镂空着色器,镂空值为浮点型变量
vertex : VertexFunction 自定义名为 VertexFunction 的顶点函数
finalColor : ColorFunction 自定义名为 ColorFunction 的最终颜色修改函数
decal : add 附加印花着色器
decal : blend 附加半透明印花着色器

(2)表面函数的输入输出结构体 (Input 、SurfaceOutput)

Input —— 用于为表面着色器函数输入所需的纹理坐标和其他数据
SurfaceOutput —— 在表面着色器中写入相应的值,用于输出数据

Input 一般是自定义的,并且必须命名为 Input ,貌似没有内置的 Input 结构体
SurfaceOutput 一般是使用内置的,如果自定义,那么必须包含内置的那些参数

与顶点片段着色器中定义结构体不同,表面着色器定义结构体不需要指定语义。。。。

Input结构体 说明
float2 uv_纹理名称 第一个纹理坐标
float2 uv2_纹理名称 第二个纹理坐标
float3 viewDir 视线方向()
float4 color 每个顶点颜色的插值
float4 screenPos 屏幕空间的位置。为了获取反射效果,需要包含屏幕坐标
float3 worldPos 世界坐标空间位置
float3 worldRefl 世界空间中的反射向量,但必须表面着色器不写入 o.Normal 参数
float3 worldNormal 世界空间中的法线向量,但必须表面着色器不写入 o.Normal 参数
float3 worldRefl; INTERNAL_DATA 世界坐标反射向量,但必须表面着色器 写入 o.Normal
(要基于逐像素法线贴图获取反射向量,请使用 WorldReflectionVector( IN , o.Normal))
float3 worldNormal; INTERNAL_DATA 世界坐标法线向量,但必须表面着色器 写入 o.Normal
(要基于逐像素法线贴图获取法线向量,请使用 WorldNormalVector( IN , o.Normal ))

表面着色器的输出结构体是内置定义好的,用户只需要在表面函数中,为需要的变量赋值就可以了。

struct SurfaceOutput
{
    half3 Albedo;     //漫反射颜色值  r g b
    half3 Normal;     //法线坐标
    half3 Emission;   //自发光颜色
    half Specular;    //镜面反射系数
    half Gloss;       //光泽系数
    half Alpha;       //透明度
};

(3)自定义光照模型

编写表面着色器的过程,
就是描述一个表面的属性(颜色、法线、反射系数、光泽度、透明度等),然后由光照模型完成光照交互的计算。

系统内置 Lambert、BlinnPhong 光照模型。

自定义光照模型,以 Lighting 开头的函数实现

//正向渲染路径、非视线方向相关
half4 Lighting(SurfaceOutput s, half3 lightDir, half atten){ }

//正向渲染路径、视线方向相关
half4 Lighting(SurfaceOutput s, half3 lightDir, half3 viewDir, hlaf atten){ }

//延迟光照路径
half4 Lighting_PrePass(SurfaceOutput s, half4 light){ }
//例如

float4 LightingPhong(SurfaceOutput s, float3 lightDir, half3 viewDir, half atten)
{
    float4 c;
    //一系列计算
    c.rgb = s.Albedo *...;   //用到表面属性(先执行了表面函数,再将表面属性传入光照模型函数)
    c.a = s.Alpha;
    return c;       //返回最终光照颜色
}

(4)顶点变换函数

顶点变换函数:修改顶点着色器中的输入顶点数据、为表面着色器函数传递顶点数据。

可用于程序性动画、沿法线挤压等

在表面着色器的编译指令中 指定 vertex : Name

#pragma surface surf Lambert vertex:vert

顶点函数可以有多种形式

void Name(inout appdata_full v){}  //用于只修改顶点着色器中的输入顶点数据

half4 Name(inout appdata_full v , out Input o){}  //用于修改顶点着色器中的输入数据,
                                                  //并且为表面着色器函数传递数据

// appdata_full 是顶点着色器的输入结构体
// Input 是表面着色器的输入结构体

例如

void vert(inout appdata_full v)
{
    v.vertex.xyz += v.normal * _Amount;  //通过法线挤压实现充气效果
}

(5)最终颜色修改函数

最终颜色修改函数,用于修改表面着色器的最终颜色。可用于绘制物体表面的最终颜色。

(应该是先执行了表面函数,然后才执行最终颜色修改函数,至于光照模型函数的顺序,则不清楚。。。)


表面着色器的编译指令

#pragma surface surf Lambert finalcolor:myColorFunction

最终颜色修改函数的声明如下

void Name(Input IN, SurfaceOutput o, inout fixed4 color){}

// Input结构体,用于顶点变换函数为最终颜色修改函数传递数据(涉及顶点变换函数。。。)
// SurfaceOutput,用于为最终颜色修改函数传递数据(传递物体表面的参数数据)
// inout 类型的 color ,为最终颜色修改函数输出最终颜色

例如

fixe4 _ColorTint;

void myFinalColor(Input IN,SurfaceOutput o,inout fixed4 color)
{
    color *= _ColorTint;  //通过调色数值修改最终颜色
}

你可能感兴趣的:(Shader,入门到放弃)