[Unity3D]Shader学习笔记之ShaderLab基础

原创 2016年08月30日 20:08:11

简介

  Unity Shader为控制渲染过程提供了一层抽象。如果没有它,开发者需要和很多文件设置打交道,才能让画面呈现出想要的效果;而在Unity Shader的帮助下,开发者只需要使用ShaderLab来编写Unity Shader文件就可以完成所有工作。
  在Unity中,所有的Unity Shader都是使用ShaderLab来编写的。ShaderLab是Unity提供的编写Unity Shader的一种说明性语言
  一个Unity Shader的基础结构如下:

Shader "Shader Name"
{
    Properties
    {
        //属性
    }

    SubShader
    {
        //显卡A使用的子着色器
    }

    SubShader
    {
        //显卡B使用的子着色器
    }

    FallBack "VertexLit"
}

   Unity在背后会根据使用的平台来把这些结构编译成真正的代码和Shader文件,而开发者只需要和Unity Shader打交道即可。

结构

Shader命名

Shader "Custom/MyShader" {}

  如上定义了一个名叫“MyShader”且在“Custom”路径下的UnityShader。路径名用“/”分隔。

材质面板属性定义

Properties
{
    Name("显示名字", 属性类型) = 默认值
    //....
}

  属性定义模块是包含在Shader命名模块下的。

支持的属性类型

属性类型 默认值定义语法 例子
Int number _Int(“Int”, Int) = 2
Float number _Float(“Float”, Float) = 1.5
Range(min,max) number _Range(“Range”,Range(0.0, 5.0)) = 3.0
Color (number,number,number,number) _Color(“Color”, Color) = (1,1,1,1)
Vector (number,number,number,number) _Vector(“Vector”, Vector) = (2,3,6,1)
2D “defaulttexture”{} _2D(“2D”, 2D) = “”{}
Cube “defaulttexture”{} _Cube(“Cube”, Cube) = “white”{}
3D “defaulttexture”{} _3D(“3D”, 3D) = “black”{}

  范例:

Shader "Custom/Shader"
{
    Properties
    {
        _Int("Int", Int) = 0
        _Float("Float", Float) = 0.0
        _Range("Range", Range(0,1)) = 0.0
        _Color("Color", Color) = (1,1,1,1)
        _Vector("Vector", Vector) = (0,0,0,0)
        _2D("2D", 2D) = ""{}
        _Cube("Cube", Cube) = "white"{}
        _3D("3D", 3D) = "black"{}
    }

    Fallback "Diffuse"
}

  属性面板显示如下:
  
这里写图片描述

   Unity允许重载默认的材质编辑面板,以提供更多自定义数据类型。
   即使不在Properties语义块中声明这些属性,也可以在Cg代码片中定义变量。

SubShader

SubShader
{
    //可选
    [Tags]

    //可选
    [RenderSetup]

    Pass
    {
    }

    //其他Pass
}

  每一个Unity Shader文件可以包含多个SubShader语义块,但最少要有一个。当Unity需要加载这个Unity Shader时,Unity会扫描所有的SubShader语义块,然后选择第一个能够在目标平台上运行的SubShader。如果不支持的话,Unity就会使用Fallback语义指定的Unity Shader。
  SubShader中定义了一系列Pass以及可选的状态([RenderSetup])和标签([Tags])设置。每个Pass定义了一个完整的渲染流程,但如果Pass的数目过多,往往会造成渲染性能的下降。所以应尽量使用最小数目的Pass

状态设置

  常见的渲染状态设置选项

状态名称 设置指令 解释
Cull Cull Back/Font/Off 设置剔除模式:剔除背面/正面/关闭剔除
ZTest ZTest Less Greater/LEqual/GEqual/Equal/NotEqual/Always 设置深度测试时使用的函数
ZWrite ZWrite On/Off 开启/关闭深度写入
Blend Blend SrcFactor DstFactor 开启并设置混合模式

  当在SubShader块中设置了上述渲染状态时,将会应用到所有的Pass。如果不想这样,可以单独在Pass块中设置。  

SubShader的标签

Tags{"TagName1" = "Value1" "TagName2" = "Value2"}

  SubShader的标签([Tags])是一个键值对(Key/Value Pair),它的键和值都是字符串类型。
  这些键值对是SubShader和渲染引擎之间的沟通桥梁。用来告诉Unity的渲染引擎希望怎样以及如何渲染这个对象。

  SubShader的标签块支持的标签类型如下:

标签类型 说明 例子
Queue 控制渲染顺序,指定该物体属于哪一个渲染队列,通过这种方式可以保证所有的透明物体可以在所有不透明物体后面被渲染,也可以自定义渲染队列来控制物体的渲染顺序 Tags{“Queue” = “Transparent”}
RenderType 对着色器进行分类,例如这是一个不透明的着色器,或是一个透明的着色器等,这可以被用于着色器替换(Shader replacament)功能 Tages{“RenderType” = “Opaque”}
DisableBatching 一些SubShader在使用Unity的批处理功能时会出现问题,例如使用了模型空间下的坐标进行顶点动画。这时可以通过该标签来直接指明是否对该SubShader使用批处理 Tags{“DisableBatching” = “True”}
ForceNoShadowCasting 控制使用该SubShader的物体是否会投射阴影 Tags{“ForceNoShadowCasting” = “True”}
IgnoreProjector 控制使用该SubShader的物体是否受Projector的影响 Tags{“IgnoreProjector” = “True”}
CanUseSpriteAtlas 当该SubShader是用于精灵(Sprite)时,将该标签设置为“False” Tags{“CanUseSpriteAtlas” = “False”}
PreviewType 指明材质面板将如何预览该材质。默认情况下,材质将显示为一个球形,可以设置为“Plane”或“SkyBox”来改变预览类型 Tags{“PreviewType” = “Plane”}

  上述标签仅可以在SubShader中声明,而不可以在Pass块中声明。Pass块虽然也可以定义标签,但这些标签是不同于SubShader的标签类型。

Pass语义块

Pass
{
    [Name]
    [Tags]
    [RenderSetup]
    //其他代码
}

  在Pass中定义该Pass的名称,如下:

Name "MyPassName"

  通过这个名称,可以是使用ShaderLab的UsePass命令来直接使用其他Unity Shader中的Pass。例如:

UsePass "MyShader/MYPASSNAME"

  这样可以提高代码的复用性。需要注意的是,Unity内部会把所有Pass的名称转换成大写字母表示,因此,在使用UsePass命令时必须使用大写形式的名字。
  Pass也可以设置渲染状态。SubShader的状态设置同样适用与Pass。
  Pass也可以设置标签,但它的标签不同于SubShader的标签,这些标签也是告诉渲染引擎应该怎样来渲染物体。
  Pass中使用的标签类型如下:

标签类型 说明 例子
LightMode 定义该Pass在Unity的渲染流水线中的角色 Tags{“LightMode” = “ForwardBase”}
RequireOptions 用于定义当满足某些条件时才能渲染该Pass,它的值是一个由空格分隔的字符串。目前,Unity支持的选项有SoftVegetation。 Tags{“RequireOptions” = “SoftVegetation”}

  除了上面普通的Pass定义外,Unity Shader还支持一些特殊的Pass,以便进行代码复用或实现更复杂的效果。

  • UsePass:该命令用来复用其他Unity Shader中的Pass
  • GrabPass:该Pass负责抓取屏幕并将结果存储在一张纹理中,以用于后续的Pass处理。

Fallback

Fallback "ShaderName"
//或
Fallback Off

  紧跟在各个SubShader语义块后面,用于告诉Unity,当上面所有的SubShader在这块显卡上都不能运行时,就用Fallback指定的这个Shader。
  Fallback会影响到阴影的投射,因此正确设置是非常重要的

Unity Shader的形式

  必要的结构如下:

Shader "MyShader"
{
    Properties
    {
        //所需的各种属性
    }

    SubShader
    {
        //真正意义上的Shader代码会出现在这里
        //表面着色器(Surface Shader)或者
        //顶点/片元着色器(Vertex/Fragment Shader)或者
        //固定函数着色器(Fixed Function Shader)
    }

    SubShader
    {
        //和上一个SubShader类似
    }
}

表面着色器

  表面着色器是Unity自己创造的一种着色器代码类型。它需要的代码少,Unity在背后做了很多工作,但渲染的代价比较大。
  在背后Unity任然把它转换成对应的顶点/片元着色器。可以理解成,表面着色器是Unity对顶点/片元着色器的更高一层的抽象。它存在的价值在于,Unity为我们处理了很多光照细节,使我们不需要在操行这些。
  简单范例:

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

  表面着色器被定义在SubShader语义块(而非Pass语义块)中CGPROGRAM和ENDCG之间。
  表面着色器不需要关心使用多少个Pass、每个Pass如何渲染等问题,Unity会在背后处理好。
  CGPROGRAM和ENDCG之间的代码使用Cg/HLSL编写的,语法几乎和标准的一样,但有些原生的函数并没有提供支持。

顶点/片元着色器

  在Unity中可以使用Cg/HLSL语言来编写顶点/片元着色器,它们更加复杂,但更加灵活。
  简单范例如下:

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

  顶点/片元着色器是写在Pass语义内的,而非SubShader内

固定函数着色器

  上面两种都使用的可编程管线,但对于一些老旧设备,它们并不支持可编程管线着色器,因此就要使用到固定函数着色器,但往往只可以完成一些非常简单的效果。
  简单范例如下:

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

  对于它来说,需要完全使用ShaderLab的语法来编写,而非使用Cg/HLSL。

其他

Unity Shader不是真正的Shader

  在Unity Shader中,可以做的事情远多于一个传统意义上的Shader。

传统Shader Unity Shader
仅可编写特定类型的Shader,例如:顶点着色器、片元着色器等 可以在同一个文件中同时包含顶点着色器和片元着色器
无法设置一些渲染设置,例如:是否开启混合、深度测试等。这些是开发者在另外的代码中设置的 通过一行特定的指令就可以完成这些设置
需要编写冗长的代码来设置着色器的输入和输出,要小心的处理这些输入和输出的位置对应关系等 只需要在特定语句块中声明一些属性,就可以依靠改变材质来方便的改变这些属性。而且对于模型自带的数据(如顶点位置、纹理坐标、法线等),Unity Shader也提供了直接访问的方法,不需要开发者自行编码来传给着色器
高度封装,可编写的Shader类型和语法被限制
对于一些类型的Shader,如曲面细分着色器、几何着色器等的支持相对较差
一些高级的Shader语法不支持

Unity Shader和Cg/HLSL之间的关系

  通常,Cg的代码片段是位于Pass语义内部的,如下:

Pass
{
    //Pass的标签和状态设置
    CGPROGRAM
    //编译指令,例如
    #pragma vertex vert
    #pragma fragment frag
    //Cg代码
    ENDCG
    //其他一些设置
}

  从本质上讲Unity中只存在顶点/片元着色器,表面着色器和固定函数着色器会在背后转化为顶点/片元着色器。

这里写图片描述

版权声明:本文为博主原创文章,转载请注明 http://blog.csdn.net/u012741077

相关文章推荐

【Unity3D】ShaderLab学习笔记

【Unity3D】ShaderLab学习笔记 1、什么是Shader: Shader(着色器)实际上就是一小段程序,它负责将输入的Mesh(多边形网格,一般使用三角形)以指定的方式和输入的贴图或者...

Unity3D基础篇----Shader学习笔记(3)

这一篇,我们来系统的介绍一下关于Unity的光照模型。

Unity3D基础篇----Shader学习笔记(2)

这一篇我们会学习中更为立体的Shader,即通过光照计算物体表面的漫反色,通过兰伯特光照原理实现逐顶点以及逐像素漫反色。...

Unity3D基础篇----Shader学习笔记(4)

这一篇,我们来继续学习Shader中纹理的添加以及实现纹理中凹凸的映射。

unity3d学习笔记(十一)-NGUI结合Shader制作小map

在做这个demo的过程中,制作小地图着实刁难了我一把,百度了很多文章,花了好长的时间,需要的知识点实在太多了,尤其是shader语言,好在最后成功把它啃下来了,先声明一下,本篇文章将会是这个系列中最难...

[Unity3D]Shader学习笔记之图像编程接口与着色语言

图像编程接口  如果开发者直接访问CPU、寄存器、显存等硬件设备将会是一件非常麻烦的事情,为了方便使用在这些硬件的基础上实现了一层抽象,这就是图像编程接口。   OpenGL和DirectX就是这些...

[Unity3D]Shader学习笔记之渲染流水线

流水线的概念 渲染流水线 应用阶段 Application Stage 几何阶段 Geometry Stage 光栅化阶段 Rasterizer Stage CPU和GPU之间的通信 GPU流水线 几...

Unity3d shader 学习笔记

这里记录一些shader入门的一些基础公式。 光照模型: 漫反射光照模型核心公式: lambert=max(0,N⃗ ⋅L⃗ )lambert= max(0,\vec N \cdot \vec L...
  • jimhy
  • jimhy
  • 2017年05月20日 21:29
  • 217

unity3d shader 学习笔记

原文地址:http://blog.csdn.net/canglang_123/article/details/44941829 在unity中我们经常会使用shader,但是从来没有深究过,最近在...

unity3d学习笔记(十一)--NGUI结合Shader制作小地图

在做这个demo的过程中,制作小地图着实刁难了我一把,花了好几天的时间,需要的知识点实在太多了,尤其是shader语言,好在最后成功把它啃下来了,接下来我把这个过程分享给大家,下面上一张截图: 看...
  • lzhq1982
  • lzhq1982
  • 2013年10月17日 19:56
  • 15011
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:[Unity3D]Shader学习笔记之ShaderLab基础
举报原因:
原因补充:

(最多只允许输入30个字)