• 【Unity3D】顶点和片段着色器


    1 前言

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

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

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

    2 固定颜色

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

            VFShader.shader

    1. Shader "MyShader/VFShaderTest" {
    2. Properties
    3. {
    4. // 属性名 ("面板显示名称", 类型) = 默认值
    5. _MainColor ("显示颜色", Color) = (1, 0, 0, 1)
    6. }
    7. SubShader
    8. {
    9. Pass
    10. {
    11. CGPROGRAM // CG语言的开始
    12. // 编译指令 着色器名称 函数名称
    13. #pragma vertex vert // 顶点着色器, 每个顶点执行一次
    14. #pragma fragment frag // 片段着色器, 每个像素执行一次
    15. // 导入头文件
    16. #include "UnityCG.cginc"
    17. // 声明属性变量, 必须与外部属性变量名称一致
    18. fixed4 _MainColor;
    19. // 顶点着色器
    20. float4 vert(float4 vertex: POSITION) : SV_POSITION
    21. {
    22. // 将局部坐标系下坐标转换为裁剪坐标系下坐标
    23. return UnityObjectToClipPos(vertex); // 等价于: mul(UNITY_MATRIX_MVP, vertex)
    24. }
    25. // 片元着色器
    26. fixed4 frag(): COLOR
    27. {
    28. return _MainColor;
    29. }
    30. ENDCG // CG语言的结束
    31. }
    32. }
    33. FallBack "Diffuse"
    34. }

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

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

    3 光照

            1)光照原理

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

            光照计算如下: 

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

            2)代码实现

            VFShader.shader

    1. Shader "MyShader/VFShaderTest" {
    2. Properties {
    3. _ModelColor ("Model Color", Color) = (1, 1, 1, 1) // 模型颜色
    4. _Specular ("Specular Color", Color) = (1, 1, 1, 1) // 镜面反射颜色
    5. _Gloss ("Gloss", Range(8.0, 256)) = 20 // 镜面反射光泽度
    6. }
    7. SubShader {
    8. Tags { "RenderType"="Opaque" }
    9. Pass {
    10. Tags { "LightMode"="ForwardBase" }
    11. CGPROGRAM
    12. #pragma vertex vert
    13. #pragma fragment frag
    14. #include "UnityCG.cginc"
    15. #include "Lighting.cginc"
    16. fixed4 _ModelColor; // 模型颜色
    17. fixed4 _Specular; // 镜面反射颜色
    18. float _Gloss; // 镜面反射光泽度
    19. struct a2v {
    20. float4 vertex : POSITION; // 模型空间顶点坐标
    21. float3 normal : NORMAL; // 模型空间顶点法线向量
    22. };
    23. struct v2f {
    24. float4 pos : SV_POSITION; // 裁剪空间顶点坐标
    25. float3 normal : Normal; // 世界空间顶点法线向量
    26. float3 worldPos : TEXCOORD0; // 世界空间顶点坐标
    27. };
    28. v2f vert(a2v v) {
    29. v2f o;
    30. o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
    31. o.normal = UnityObjectToWorldNormal(v.normal); // 将模型空间法线向量变换到世界空间
    32. o.worldPos = mul(unity_ObjectToWorld, v.vertex); // 将模型空间顶点坐标变换到世界空间
    33. return o;
    34. }
    35. fixed4 frag(v2f i) : SV_Target {
    36. fixed3 normal = normalize(i.normal); // 世界空间法线向量
    37. fixed3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 世界空间灯光向量
    38. fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); // 世界空间观察向量
    39. fixed3 halfDir = normalize(lightDir + viewDir); // 半向量
    40. fixed3 albedo = _ModelColor; // 模型自身颜色
    41. fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT * albedo; // 环境光
    42. fixed3 diffuse = _LightColor0 * albedo * max(0, dot(normal, lightDir)); // 漫反射光
    43. fixed3 specular = _LightColor0 * _Specular * pow(max(0, dot(normal, halfDir)), _Gloss); // 镜面反射光(Blinn Phong光照模型)
    44. return fixed4(ambient + diffuse + specular, 1);
    45. }
    46. ENDCG
    47. }
    48. }
    49. FallBack "Specular"
    50. }

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

    4 贴图

            VFShader.shader

    1. Shader "MyShader/VFShaderTest" {
    2. Properties
    3. {
    4. // 属性名 ("面板显示名称", 类型) = 默认值
    5. _MainTex ("2阶贴图", 2D) = "white" {}
    6. }
    7. SubShader
    8. {
    9. Pass
    10. {
    11. Tags {"LightMode"="ForwardBase"}
    12. CGPROGRAM // CG语言的开始
    13. // 编译指令 着色器名称 函数名称
    14. #pragma vertex vert // 顶点着色器, 每个顶点执行一次
    15. #pragma fragment frag // 片段着色器, 每个像素执行一次
    16. // 导入头文件
    17. #include "UnityCG.cginc"
    18. // 声明属性变量, 必须与外部属性变量名称一致
    19. sampler2D _MainTex;
    20. struct a2v // 顶点着色器输入结构体
    21. {
    22. float4 vertex : POSITION; // 模型空间顶点坐标
    23. half2 texcoord : TEXCOORD0; // 纹理uv坐标
    24. };
    25. struct v2f // 顶点着色器输出结构体
    26. {
    27. float4 pos : SV_POSITION; // 裁剪空间顶点坐标
    28. half2 uv : TEXCOORD0; // 纹理uv坐标
    29. };
    30. // 顶点着色器
    31. v2f vert(a2v v)
    32. {
    33. v2f o;
    34. o.pos = UnityObjectToClipPos(v.vertex); // 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
    35. o.uv = v.texcoord;
    36. return o;
    37. }
    38. // 片元着色器
    39. fixed4 frag(v2f i) : SV_Target
    40. {
    41. return tex2D(_MainTex, i.uv);
    42. }
    43. ENDCG // CG语言的结束
    44. }
    45. }
    46. FallBack "Diffuse"
    47. }

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

  • 相关阅读:
    Vue.js核心技术解析与uni-app跨平台实战开发学习笔记 第12章 Vue3.X新特性解析 12.7 watch监听的使用
    零零信安-D&D数据泄露报警日报【第42期】
    微服务-sentinel详解
    开源在医疗健康领域的应用
    Go语言中ipv4与Uint32转换
    IIFE立即执行函数表达式使用
    Node端异常捕获
    HTTPS的加密流程
    C++ 图解二叉树非递归后序 + 实战力扣题
    第二十章 多线程
  • 原文地址:https://blog.csdn.net/m0_37602827/article/details/126942376