【Unity3D】顶点和片元着色器

1 前言

​ 上文介绍了渲染管线、固定管线着色器和表面着色器,如下:

​ 固定管线着色器通过命令方式实现光照和贴图等效果,表面着色器通过给 SurfaceOutput 赋值实现光照、贴图和法线贴图等效果,它们都不用关注光照算法是如何实现的,只需要传值就行。

​ 顶点和片元着色器给用户提供了更灵活的用法,但使用也更困难。另外,顶点着色器可以通过控制 MVP 矩阵变换实现对模型位置和姿态的控制。

2 固定颜色

​ 在 Assets 窗口右键,依次选择【Create→Shader→Standard Surface Shader】创建 Shader 脚本,实现固定颜色 Shader 代码如下:

​ VFShader.shader

Shader "MyShader/VFShaderTest" {
    Properties
    {
        // 属性名 ("面板显示名称", 类型) = 默认值
        _MainColor ("显示颜色", Color) = (1, 0, 0, 1)
    }
 
    SubShader
    {
        Pass
        {
            CGPROGRAM // CG语言的开始
            // 编译指令 着色器名称 函数名称
            #pragma vertex vert // 顶点着色器, 每个顶点执行一次
            #pragma fragment frag // 片段着色器, 每个像素执行一次
            // 导入头文件
            #include "UnityCG.cginc"
 
            // 声明属性变量, 必须与外部属性变量名称一致
            fixed4 _MainColor;
 
            // 顶点着色器
            float4 vert(float4 vertex: POSITION) : SV_POSITION
            {
                // 将局部坐标系下坐标转换为裁剪坐标系下坐标
                return UnityObjectToClipPos(vertex); // 等价于: mul(UNITY_MATRIX_MVP, vertex)
            }
 
            // 片元着色器
            fixed4 frag(): COLOR
            {
                return _MainColor;
            }
 
            ENDCG // CG语言的结束
        }
    }
 
    FallBack "Diffuse"
}

​ 创建一个 Material,并将 ShaderTest 绑定到该 Material 上,如下:

img

​ 将该 Material 拖拽到一个 Cube 和 Sphere 游戏对象上。选中绑定的 Material,在 Inspector 窗口调整 Shader 中固定颜色,显示效果如下:

img

3 光照

1)光照原理

​ Phong 光照模型和 Blinn Phong 光照模型是应用比较广泛的光照模型,两者区别在与镜面反射光的计算,Phong 光照模型根据反向量和观察向量计算镜面反射光,Blinn Phong 光照模型根据半向量和法向量计算镜面反射光。

img

​ 光照计算如下:

// 模型自身颜色
fixed4 albedo = tex2D(_MainTex, i.uv) * _ModelColor;
// 环境光
fixed4 ambient = UNITY_LIGHTMODEL_AMBIENT * albedo;
// 漫反射光
fixed4 diffuse = _LightColor0 * albedo * max(0, dot(normal, lightDir)); 
// 镜面反射光(Phong光照模型)
// fixed4 specular = _LightColor0 * _Specular * pow(max(0, dot(reflectDir, viewDir)), _Gloss); 
// 镜面反射光(Blinn Phong光照模型)
fixed4 specular = _LightColor0 * _Specular * pow(max(0, dot(normal, halfDir)), _Gloss); 
// 合成颜色
fixed4 finalColor = fixed4(ambient + diffuse + specular, 1.0);

2)代码实现

​ VFShader.shader

Shader "MyShader/VFShaderTest" {
    Properties {
        _ModelColor ("Model Color", Color) = (1, 1, 1, 1) // 模型颜色
        _Specular ("Specular Color", Color) = (1, 1, 1, 1) // 镜面反射颜色
        _Gloss ("Gloss", Range(8.0, 256)) = 20 // 镜面反射光泽度
    }
 
    SubShader {
        Tags { "RenderType"="Opaque" }
 
        Pass { 
            Tags { "LightMode"="ForwardBase" }
        
            CGPROGRAM
 
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            
            fixed4 _ModelColor; // 模型颜色
            fixed4 _Specular; // 镜面反射颜色
            float _Gloss; // 镜面反射光泽度
            
            struct a2v {
                float4 vertex : POSITION; // 模型空间顶点坐标
                float3 normal : NORMAL; // 模型空间顶点法线向量
            };
            
            struct v2f {
                float4 pos : SV_POSITION; // 裁剪空间顶点坐标
                float3 normal : Normal; // 世界空间顶点法线向量
                float3 worldPos : TEXCOORD0; // 世界空间顶点坐标
            };
 
            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
                o.normal = UnityObjectToWorldNormal(v.normal); // 将模型空间法线向量变换到世界空间
                o.worldPos = mul(unity_ObjectToWorld, v.vertex); // 将模型空间顶点坐标变换到世界空间
                return o;
            }
 
            fixed4 frag(v2f i) : SV_Target {
                fixed3 normal = normalize(i.normal); // 世界空间法线向量
                fixed3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 世界空间灯光向量
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); // 世界空间观察向量
                fixed3 halfDir = normalize(lightDir + viewDir); // 半向量
                fixed3 albedo = _ModelColor; // 模型自身颜色
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT * albedo; // 环境光
                fixed3 diffuse = _LightColor0 * albedo * max(0, dot(normal, lightDir)); // 漫反射光
                fixed3 specular = _LightColor0 * _Specular * pow(max(0, dot(normal, halfDir)), _Gloss); // 镜面反射光(Blinn Phong光照模型)
                return fixed4(ambient + diffuse + specular, 1);
            }
            
            ENDCG
        }
    }
 
    FallBack "Specular"
}

​ 选中绑定的 Material,在 Inspector 窗口调整 Shader 中光照颜色,显示效果如下:

img

4 贴图

​ VFShader.shader

Shader "MyShader/VFShaderTest" {
    Properties
    {
        // 属性名 ("面板显示名称", 类型) = 默认值
        _MainTex ("2阶贴图", 2D) = "white" {}
    }
 
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase"}
            CGPROGRAM // CG语言的开始
            // 编译指令 着色器名称 函数名称
            #pragma vertex vert // 顶点着色器, 每个顶点执行一次
            #pragma fragment frag // 片段着色器, 每个像素执行一次
 
            // 导入头文件
            #include "UnityCG.cginc"
 
            // 声明属性变量, 必须与外部属性变量名称一致
            sampler2D _MainTex;
 
            struct a2v // 顶点着色器输入结构体
            {
                float4 vertex : POSITION; // 模型空间顶点坐标
                half2 texcoord : TEXCOORD0; // 纹理uv坐标
            };
 
            struct v2f // 顶点着色器输出结构体
            {
                float4 pos : SV_POSITION; // 裁剪空间顶点坐标
                half2 uv : TEXCOORD0; // 纹理uv坐标
            };
 
            // 顶点着色器
            v2f vert(a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex); // 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
                o.uv = v.texcoord;
                return o;
            }
 
            // 片元着色器
            fixed4 frag(v2f i) : SV_Target
            {
                return tex2D(_MainTex, i.uv);
            }
 
            ENDCG // CG语言的结束
        }
    }
 
    FallBack "Diffuse"
}

​ 选中绑定的 Material,在 Inspector 窗口选择贴图图片,显示效果如下:

img

​ 声明:本文转自【Unity3D】顶点和片元着色器

posted @ 2023-03-21 09:50  little_fat_sheep  阅读(45)  评论(0编辑  收藏  举报