阿卡内酱
阿卡内酱
发布于 2026-04-14 / 3 阅读
0
0

03.Unity Shader基础

3.1 概述

3.1.1 材质与Unity Shader

Unity Shader需要与材质配合使用。具体流程为:(1)创建一个材质;(2)创建一个Shader,并把该Shader赋予材质;(3)将材质赋予一个物体;(4)在材质页面修改数据以达到预期效果。

3.1.3 Unity中的Shader

Unity提供了四种基本的Shader——Standard Surface Shader,Unlit Shader ,Image Effect Shader 以及Compute Shader。其中,Standard Surface Shader 会产生一个包含了标准光照模型的表面着色器模板,Unlit Shader 则会产生一个不包含光照(但包含雾效)的基本的顶点/片元着色器,Image Effect Shader 则为我们实现各种屏幕后处理效果提供了一个基本模板。最后,Compute Shader 会产生一种特殊的Shader文件,这类Shader旨在利用GPU的并行性来进行一些与常规渲染流水线无关的计算。
常用Unlit Shader来生成一个基本的顶点/片元着色器模板。

3.2 ShaderLab

ShaderLab是Unity提供的Shader语言,用户只需要和ShaderLab打交道即可

Shader "ShaderName" {
    Properties {
        // 属性
    }
    SubShader {
        // 显卡A使用的子着色器
    }
    SubShader {
        // 显卡B使用的子着色器
    }    
    Fallback "VertexLit"
}

Properties语块内包含着色所需要的属性,会出现在材质面板上

3.3 Unity Shader的结构

3.3.1 Shader命名

在Shader开头需要为Shader命名,可以用/来表示在材质的Shader选择界面的层级"Folder/Name"

Shader "Custom/MyShader" {
}

3.3.2 Properties

Properties {
    Name ("display name", PropertyType) = DefaultValue
    Name ("display name", PropertyType) = DefaultValue
    // 更多属性
}

Name:该属性的名称,通常由下划线开头
display name:展现在材质面板的名称
PropertyType:该属性的数据类型
DefaultValue:默认值,不同属性类型有不同的默认值创建方式
表  Properties语义块支持的属性类型

属 性 类 型

默认值的定义语法

例 子

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" {}

对于2D、Cube、3D这样的纹理类型,默认值为一个字符串跟花括号,字符串要么为空,要么为内置的纹理,花括号的用处原本是用于指定一些纹理属性的。

Shader "Custom/ShaderLabProperties" {
    Properties {
        // Numbers 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"
}

可以通过重载材质默认面板来显示自定义类型,编写相应的C#代码并在对应Shader末尾使用CustomEditor "CustomShaderGUI"来自定义面板

Shader "MyShader" {
	Properties {}
	SubShader {}
	CustomEditor "CustomShaderGUI"
}

3.3.3 SubShader

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

SubShader {
    // 可选的
    [Tags]

    // 可选的
    [RenderSetup]

    Pass {
    }
    // Other Passes
}
  • 标签(Tags)
    标签为一个字符串键值对,这些键值对是SubShader和渲染引擎之间的沟通桥梁。它们用来告诉Unity的渲染引擎:我希望怎样以及何时渲染这个对象。
    Tags { "TagName1" = "Value1" "TagName2" = "Value2" }

表 SubShader的标签类型

标 签 类 型

说 明

例 子

Queue

控制渲染顺序,指定该物体属于哪一个渲染队列,通过这种方式可以保证所有的透明物体可以在所有不透明物体后面被渲染(详见第8章),我们也可以自定义使用的渲染队列来控制物体的渲染顺序

Tags { "Queue" = "Transparent" }

RenderType

对着色器进行分类,例如这是一个不透明的着色器,或是一个透明的着色器等。这可以被用于着色器替换(Shader Replacement)功能

Tags { "RenderType" = "Opaque" }

DisableBatching

一些SubShader 在使用Unity的批处理功能时会出现问题,例如使用了模型空间下的坐标进行顶点动画(详见11.3节)。这时可以通过该标签来直接指明是否对该SubShader 使用批处理

Tags { "DisableBatching" = "True" }

ForceNoShadowCasting

控制使用该SubShader 的物体是否会投射阴影(详见8.4节)

Tags { "ForceNoShadowCasting" = "True" }

IgnoreProjector

如果该标签值为“True”,那么使用该SubShader 的物体将不会受Projector的影响。通常用于半透明物体

Tags { "IgnoreProjector" = "True" }

CanUseSpriteAtlas

当该SubShader 是用于精灵(sprites)时,将该标签设为“False”

Tags { "CanUseSpriteAtlas" = "False" }

PreviewType

指明材质面板将如何预览该材质。默认情况下,材质将显示为一个球形,我们可以通过把该标签的值设为“Plane”“SkyBox”来改变预览类型

Tags { "PreviewType" = "Plane" }

  • 状态设置(RenderSetup)
    ShaderLab提供了一系列渲染状态的设置指令,这些指令可以设置显卡的各种状态,例如是否开启混合/深度测试等。
    表 常见的渲染状态设置选项

状 态 名 称

设 置 指 令

解 释

Cull

Cull Back | Front| Off

设置剔除模式:剔除背面/正面/关闭剔除

ZTest

ZTest Less Greater| LEqual| GEqual| Equal| NotEqual| Always

设置深度测试时使用的函数

ZWrite

ZWrite On|Off

开启/关闭深度写入

Blend

Blend SrcFactor DstFactor

开启并设置混合模式

SubShader内的状态设置会应用到该SubShader内的所有Pass里。如果不想这样就在每个Pass内分别设置状态

  • Pass语义块
    每个Pass定义了一次完整的渲染流程,但如果Pass的数目过多,往往会造成渲染性能的下降。因此,我们应尽量使用最小数目的Pass。Pass内也可以声明状态与标签。

Pass { 
    [Name]
    [Tags] 
    [RenderSetup] 
    // Other code
}

在Pass内可以定义改Pass块的名称Name "MyPassName",且可以在其他Shader内直接通过名称使用该Pass,例如Use "MyShader/MYPASSNAME(ShaderName/PassName),这样可以提高代码复用性,但注意,Unity内部会自动把Pass的名称转为全大写的格式,所以使用时请用大写。

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

  • UsePass :如我们之前提到的一样,可以使用该命令来复用其他Unity Shader中的Pass ;

  • GrabPass :该Pass 负责抓取屏幕并将结果存储在一张纹理中,以用于后续的Pass 处理

3.3.4 Fallback

紧跟在各个SubShader语义块后面的,可以是一个Fallback指令。它用于告诉Unity,“如果上面所有的SubShader在这块显卡上都不能运行,那么就使用这个最低级的Shader吧!”。

Fallback "name"
// 或者
Fallback Off

3.4 Unity Shader的形式

在SubShader层级编写的代码控制的是表面着色器,在Pass内编写的代码控制的是片元/表面着色器。

3.4.1 表面着色器

Unity自创的着色器代码类型,渲染代价大,本质为片元/顶点着色器的更上一层抽象。需要处理更多光照细节时使用

3.4.2 顶点/片元着色器

更加复杂但灵活性高

3.4.3 固定函数着色器

为一些老旧设备使用的着色器

3.4.4 Shader选择

  • 如果你想和各种光源打交道,你可能更喜欢使用表面着色器,但需要小心它在移动平台的性能表现。

  • 如果你需要使用的光照数目非常少,例如只有一个平行光,那么使用顶点/片元着色器是一个更好的选择。

  • 最重要的是,如果你有很多自定义的渲染效果,那么请选择顶点/片元着色器。

其他

Unity官网上关于Unity Shader方面的文档正在不断补充中,由于Unity封装了很多功能和细节,因此,如果读者在使用Unity Shader的过程中遇到了问题可以去到官方文档(http://docs.unity3d.com/Manual/SL-Reference.html )中查看。除此之外,Unity也提供了一些简单的着色器编写教程(http://docs.unity3d.com/Manual/ShaderTut1.html ,http://docs.unity3d.com/Manual/ShaderTut2.html )。由于在Unity Shader中,绝大多数可编程管线的着色器代码是使用Cg语言编写的,读者可以在NVIDIA提供的Cg文档(http://http.developer.nvidia.com/Cg/ )中找到更多的内容。NVIDIA同样提供了一个系列教程(http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter01.html )来帮助初学者掌握Cg的基本语法。


评论