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

  在Unity中,我们可以使用下面3中形式来编写UnityShader。而不管使用哪种形式,真正意义上的Shader代码都需要包含在ShaderLab语义块中,如下所示:

Shader "MyShader"{
  Properties{
    //所需的各种属性
  }
  SubShader{
    //真正意义上的Shader代码会出现在这里
    //表面着色器(Surface Shader)或者
    //定点/片元着色器(Vertex/Fragment Shader)或者
    // 固定函数着色器 (Fixed Function Shader)
  }
  SubShader{
     //和上一个SubShader类似
  }
}
  1. 表面着色器

表面着色器(Surface Shader)是Unity自己创造的一种着色器代码类型。它需要的代码量很小,Unity在背后做了很多工作,但渲染的代价比较大。它在本质上和下面要讲到的定点/片元着色器是一样的。也就是说,当Unity提供一个表面着色器的时候,它在背后仍旧把它转换成对应的顶点/片元着色器。我们可以理解成,表面着色器是Unity对顶点/片元着色器的更高一层的抽象。它存在的价值在于,Unity为我们处理了很多光照细节,使得我们不需要操心这些“烦人的事情”。

一个非常简单的表面着色器的示例代码如下:

Shader "Custom/Simple Surface Shader"{
    SubShader{
        Tags{ "RenderType" = "Opaque"}
        CGPROGRAM
            #prama surface surf Lambert
            struct Input{
                float4 color : COLOR;
            };
            void surf (Input IN, inout SurfaceOutput o){
                o.Albedo =1;
            }
        ENDCG
    }
    FallBack "Diffuse"
}

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

CGPRORAM和ENDCG之间的代码使用CG/HLSL编写的,也就是说,我们需要把CG/HLSL语言嵌套在ShaderLab语言中。值得注意的是,这里的CG/HLSL是Unity经封装后提供的,他的语法和标准的CG/HLSL语法几乎一样,但还是有细微的不同,例如有些原生的函数和用法Unity并没有提供支持。

  2。最聪明的孩子:定点/片元着色器

    在Unity中我们可以使用CG/HLSL语言来编写 顶点/片元着色器(Verter/Fragment Shader)。它们更加复杂,但灵活性也更高。

  一个非常简单的顶点/片元着色器示例代码如下:

Shader "Custom/Simple VertexFragment Shader"{
    SubShader{
        Pass{
            CGPROGRAM
                #prama vertex vert
                #prama fragment frag
                float4 vert (float4 v : POSITION) : SV_POSITION{
                    return mul (UNITY_MATRIX_MVP ,  v);
                }

                float4 frag ( ) : SV_Target{
                    return fixed4 (1.0 , 0.0 , 0.0 , 1.0);
                }
            ENDCG
        }
        
    }
}

  和表面着色器类似,顶点/片元着色器的代码也需要定义在CGPROGRAM和ENDCG之间,但不同的是,顶点/片元着色器是写在Pass语义块内,而非SubShader内的,原因是,我们需要自已定义每个Pass需要使用的Shader代码。虽然我们可能需要编写更多的代码,但带来的好处是灵活性很高,更重要的是,我们可以控制渲染的实现细节,同样这里的CGPROGRAM和ENDCG之间的代码也是使用CG/HLSL编写的。

3. 被抛弃的角落:固定函数着色器

 上面两种Unity Shader 形式都使用了可编程管线。而对于一些较旧的设备(其CPU仅支持DirectX7.0、OpenGL 1.5 或 OpenGL ES 1.1),例如iPhone3,他们不支持可编程管线着色器,因此,这时候我们就需要使用固定函数着色器(Fixed Function Shader)来完成渲染。这些着色器往往可以完成一些非常简单的效果。

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

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

可以看出,固定函数着色器的代码被定义在Pass语义块中,这些代码相当于Pass中的一些渲染设置,正如我们之前提到的一样。

 对于固定函数着色器来说,我们需要完全使用ShaderLab的语法(即 使用ShaderLab的渲染设置命令) 来编写,而非使用 CG/HLSL。

 由于现在大多数GPU都支持可编程的渲染管线,这种固定管线的编程方式已经逐渐被抛弃。实际上,在Unity5.2中,所有固定函数着色器都会在背后被Unity编译成对应的顶点/片元着色器,因此真正意义上的固定函数着色器已经不存在了。

 

对于选择Shader  的一些建议。

  • 除非你有非常明确的需求必须要使用固定函数着色器,例如需要在非常旧的设备上运行你的游戏(这些设备非常少见),否则请使用可编程管线的着色器,即表明着色器或顶点/片元着色器。
  • 如果你想和各种光源打交道,你可能更喜欢使用表明着色器,但需要小心他在移动平台的性能表现。
  • 如果你需要使用的光照数目非常少,例如只有一个平行光,那么使用顶点/片元着色器是一个更好的选择。
  • 最重要的是如果你有很多自定义的渲染效果,那么请选择顶点/片元着色器。
posted on 2017-07-17 21:38  Mr.糖马儒  阅读(209)  评论(0编辑  收藏  举报