822思享实验室

Shader基础笔记(1)-基本结构

2018-03-03  本文已影响7人  邱宏健

新建Shader的分类

  1. Standard Surface Shader - 产生一个包含标准光照模型的表面着色器模板。
  2. Unlit Shader(推荐) - 产生一个不包含光照(但包含雾效)的基本顶点/片元着色器。
  3. Image Effect Shader - 为实现各种屏幕处理后效果提供的基本模板。
  4. Compute Shader - 特殊Shader,利用GPU的并行特性计算一些计算。

Shader 的基本结构

Shader "MyShader/Temp01"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        Pass
        {    
        CGPROGRAM
        
        ENDCG
        }
    }
}

Shader 基本结构说明

1. 给Shader起个名字

Shader "MyShader/Temp01"
这一行代码设置了 Shader 在材质中出现的位置及名称:

2. 属性

Properties
{
    _MainTex ("Texture", 2D) = "white" {}
}

声明这些属性是为了在材质面板中能够方便地调整各种材质属性。在 Unity 中,这些属性的名字通常由一个下划线开始。括号中的第一个参数,是属性显示在材质面板上的名字。第二个参数,是数据类型。

下面的代码给出了一个展示所有属性类型的例子:

Shader "MyShader/Temp01"
{
   Properties
   {
       // Unmbers and Sliders
      _Int("Int", Int) = 2
      _Float("Float", Float) = 1.5
      _Range("Range", Range(0.0, 5.0)) = 3.0
      
      // Colors and Vectors
      _Color("Color", Color) = (1,1,1,1) 
      _Vector("Vector", Vector) = (2,3,6,1)
      
      // Textures
      _2D("2D", 2D) = ""{}
      _Cube("Cube", Cube) = "white"{}
      _3D("3D", 3D) = "black"{}
   }
   
   FallBack "Diffuse"
}

TIPS:编译器对 Shader 的支持普遍不好,其中比较好用的还是 JetBrans 公司的 Rider。

3. SubShader

每一个 Unity Shader 文件可以包含多个 SubShader 语义块,但至少一个。当 Unity 需要加载这个 Unity Shader 时, Unity 会扫描所有的SubShader 语义块,然后选择第一个能够在目标平台上运行的 SubShader。如果都不支持,Unity 会使用 Fallback 语义指定的 Unity Shader。

每个Pass定义了一次完整的渲染流程,但如果Pass的数目过多,往往会造成渲染性能的下降。因此,应当尽量使用最小数目的Pass。

SubShader 语义块中包含的定义通常如下:
···
Subshader{
// 标签,可选的
[Tags]

// 状态,可选的
[RenderSetup]

Pass{
}
// Other Passes

}
···
SubShader 内的具体内容放到以后再写。

最简单的 Unity Shader

尽管 Unity Shader 可以做的事情非常多(例如设置渲染状态等),但其最重要的任务还是指定各种着色器所需的代码。这些着色器代码可以写在 SubShader 语义块中(表面着色器的做法),也可以写在 Pass 语义块中(顶点/片元着色器和固定函数着色器的做法)。

在 Unity 中,我们可以使用2种最常用的形式来编写 Unity Shader。

1. 表面着色器(Surface Shader)

表面着色器是 Unity 自己创造的一种着色器代码类型,它需要的代码量很少,Unity 在背后做了很多工作,但渲染的代价比较大。其本质与接下来的顶点/片元着色器一样,我们可以理解成 Unity 对顶点/片元着色器的更高一层的抽象。它存在的价值在于,Unity为我们处理了很多光照细节,使得我们不需要再操心这些“烦人的事情”。

一个非常简单的例子:

Shader "MyShader/Temp01"
{  
   Subshader{
       Tags { "RenderType" = "Opaque" }
       CGPROGRAM
       #pragma surface surf Lambert
       struct Input{
           float4 clolr : COLOR;
       };
       void surf (Input IN, inout SurfaceOutput o){
           o.Albedo = 1;
       }
       ENDCG
   }
   
   FallBack "Diffuse"
}

可见,表面着色器被定义在 SubShader 语义块(而非Pass语义块)中的 CGPROGRAM 和 ENDCG 之间。原因是:表面着色器不需要开发者关心使用多少个 Pass、每个 Pass 如何渲染等问题,Unity 会在背后为我们做好这些事情。我们要做的只是告诉它:“嘿,使用这些纹理去填颜色,使用这个法线纹理去填充法线,使用Lambert光照模型,其他的不要来烦我!”

2. 顶点/片元着色器(Vertex/Fragment Shader)

顶点/片元着色器更加复杂,但也更加灵活。

一个非常简单的例子:

Shader "MyShader/Temp01"
{  
   Subshader{     
       Pass{
           CGPROGRAM
           #pragma vertex vertex
           #pragma fragment fra
           
           float4 vert(float4 v : POSITION) : SV_POSITION {
               return UnityObjectToClipPos (v);
           }
           
           fixed4 frag() : SV_Target {
               return fixed4(1.0, 0.0, 0.0, 1.0);
           }
           ENDCG
       }
   }
}

我们将主要使用顶点/片元着色器。

扩展阅读

Unity官网的 Unity Shader 文档:官方文档

上一篇下一篇

猜你喜欢

热点阅读