UnityShader4:UnityShader的形式

前置:UnityShader2:Shader与材质

一、Standard Surface Shader 代码解析

在前置中已经了解了什么是表面着色器,下面这是 Standard Surface Shader,也是创建 Shader 时的默认代码之一

Shader "Custom/NewSurfaceShader"
{
    Properties
    {
        _Color("Color", Color) = (1,1,1,1)
        _MainTex("Albedo (RGB)", 2D) = "white" {}
        _Glossiness("Smoothness", Range(0,1)) = 0.5
        _Metallic("Metallic", Range(0,1)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows
        #pragma target 3.0

        sampler2D _MainTex;
        struct Input
        {
            float2 uv_MainTex;
        };
        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        UNITY_INSTANCING_BUFFER_START(Props)
        UNITY_INSTANCING_BUFFER_END(Props)

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

直接一行一行注释(对于紫色的内容,可以暂时不用去详细了解):

  • Tags { "RenderType" = "Opaque" }:这个在 UnityShader3:ShaderLab 这一章中有过讲解,声明为不透明着色器
  • LOD 200:设置当前着色器的 LOD(Shader Level of Detail) 值,只有其小于 Shader.globalMaximumLOD 全局最大细节级别时这个 SubShader 才会被执行,注意这个和模型 LOD 不是一个东西
  • CGPROGRAM  ……  CGEND:在这两句声明之间的代码是着色器语言的主要部分,并且是用 CG(C for Graphics) 编写的,CG 和 GLSL 一样,是一种着色器语言
  • #pargma 关键词 函数名 光照模型 [其它选项]:编译指令声明,代码中的含义为:表明当前为表面着色器、函数名为 surf、使用 Standard 基于物理系统的光照模式、有一个完整的向前的阴影
  • #pragma target 3.0:设置着色器模型等级,越高支持的功能越多,可以参考文档
  • sampler2D:对应二维纹理,在 Properties 中为 2D 属性
  • half:描述的是一个16位浮点数,同理 float 为32位浮点数
  • fixed4:对应4维颜色属性,同理 half4 就是4个 float 属性组成的4维向量
  • struct Input { float2 uv_MainTex; }:输入结构体用于模述 UV 的坐标,里面的 float2 变量必须以 uv_开头,后面的 _MainTex 自动对应 Properties 中的 _MainTex,理论上这个结构体里还可以有其它成员,不过目前只需要关心纹理坐标
  • UNITY_INSTANCING_BUFFER_START:用于批量渲染,减少 DrawCall 次数,超纲了,可以先去了解下 OpenGL 的实例化操作:《OpenGL基础42:实例化》

void surf (Input IN, inout SurfaceOutputStandard o) {}:第一个参数 Input IN 就是前面定义的 Input 结构体,第二个参数用了 inout 声明,这表示这个参数既是输入的也是输出的,这个 surf 函数虽然没有返回值,但是它的第二个参数是有输出功能

从上述程序中可以看出:表面着色器被定义在 SubShader 语义块中的 CGPROGRAM 和 ENDCG 之间,并且没有 Pass 语义块,这是因为表面着色器不需要开发者关心使用了多少个 Pass、每个 Pass 如何渲染等问题,Unity 会在背后为我们做好这些事情,我们要做的只是告诉它使用哪些纹理去填充颜色,如何填充法线,使用哪种光照模型等

上面的代码对应的预览效果(无纹理):

二、固定函数着色器

固定渲染管线已经过时了,没有必要去学习/关心,但也可以在这一章顺便提一下

一个非常简单的固定函数着色器例子如下:

Shader "Tutorial/Basic" {
    Properties { 
        _Color("Main Color", Color) = (1,0.5,0.5,1)
    }
    SubShader {
        Pass { 
            Material {
                Diffuse [_Color] 
            } 
            Lighting On
        }
    }
}

关于固定函数着色器可以了解的是:

  1. 固定函数着色器需要完全按照 ShaderLab 的语法,而非使用 CG/HLSL
  2. 在 Unity 5.2 以后的版本中,固定函数着色器也会被 Unity 编译成对应的顶点/片元着色器,因此真正意义上的固定函数着色器已经不存在了
  3. 部分旧设备只支持固定管线,这样就只能用固定函数着色器了,当然一般抛弃这些平台是最好的选择

三、扩展

UnityShader形式有三种,前面主要提到了表面着色器和固定函数着色器,然而事实上,最常用的还是顶点/片元着色器,可以自己实现一些五花八门的渲染效果,只不过关于顶点片元着色器,需要了解的东西不少,这篇就暂时不讲了

传统的 Shader 和 UnityShader 的区别有:

  • UnityShader 可以在一个文件里同时包含需要的顶点着色器和片元着色器代码,而传统着色器需要一一对应
  • UnityShader 处理输入和输出非常的简单/方便,只需要在特定语句块中声明一些属性就可以依靠材质来方便地改变它们,而对于模型自带的数据:如顶点位置、纹理坐标、法线等, UnityShader 也提供了直接访问的方法
  • UnityShader 只需要通过一行特定的指令即可以完成一些渲染设置,例如是否开启混合、深度测试等,而对于传统 Shader 开发者需要自行在外环境设置
  • UnityShader 对曲面细分着色器(Tessellation Shader)、几何着色器(Geometry Shader)不能够很好的支持

关于 CG/HLSL 和 GLSL:

对于上面的表面着色器,是通过在 ShaderLab 内部嵌套 CG/HLSL 语言来编写这些着色器代码的,由于 CG 和 DX9 风格的 HLSL 从写法上来说几乎是同一种语言,因此在 Unity 里 CG 和 HLSL 等价

当然也可以用 GLSL 来写,不过这样对于PC、Xbox 360这些的仅支持DirectX的平台来说,就不能正常的运行了

本文[系列]参考于:UnityShader入门摘要

其它参考资料:https://docs.unity3d.com/Manual/SL-Reference.html

https://docs.unity3d.com/Manual/ShaderTut1.html

猜你喜欢

转载自blog.csdn.net/Jaihk662/article/details/109848113