2024-02-01 Unity Shader 开发入门4 —— ShaderLab 语法

news2024/11/16 0:16:39

文章目录

  • 1 材质和 Shader
    • 1.1 Unity Shader 和 Shader 的区别
    • 1.2 Unity 中的材质和 Shader
    • 1.3 创建材质
    • 1.4 创建 Shader
  • 2 ShaderLab 的基本结构
    • 2.1 什么是 ShaderLab
    • 2.2 ShaderLab 的基本结构
  • 3 Shader 名称
  • 4 Shader 属性
    • 4.1 Shader 属性的作用
    • 4.2 Shader 属性的基本语法
    • 4.3 数值类型
    • 4.4 颜色和向量类型
    • 4.5 纹理贴图类型
  • 5 子着色器
    • 5.1 SubShader 基本构成
    • 5.2 渲染标签 Tags
      • 5.2.1 渲染队列 Queue
      • 5.2.2 渲染类型 RenderType
      • 5.2.3 禁用批处理
      • 5.2.4 禁止阴影投影
      • 5.2.5 忽略投影机
      • 5.2.6 其他标签
      • 5.2.7 注意事项
    • 5.3 渲染状态 States
      • 5.3.1 语法结构
      • 5.3.2 剔除方式
      • 5.3.3 深度缓冲
      • 5.3.4 深度测试
      • 5.3.5 混合方式
      • 5.3.6 其他渲染状态
      • 5.3.7 注意事项
    • 5.4 渲染通道 Pass
      • 5.4.1 语法结构
      • 5.4.2 Pass 名称
      • 5.4.3 Pass 中的渲染标签
      • 5.4.4 Pass 中的渲染状态
      • 5.4.5 其他着色器代码
      • 5.4.6 GrabPass 命令
  • 6 备用 Shader
    • 6.1 作用
    • 6.2 语法
  • 7 Shader 编写形式
    • 7.1 表面着色器
    • 7.2 顶点/片元着色器
    • 7.3 固定函数着色器

1 材质和 Shader

1.1 Unity Shader 和 Shader 的区别

​ Shader 是更通用的概念,用于描述图形渲染程序中的着色器程序;而 Unity Shader 是特指在 Unity 中使用的着色器,可以认为 Unity Shader 是对 Shader 的一种封装。

​ Unity Shader 对底层图形渲染技术进行封装,并提供名为 ShaderLab 的语言,使得编写和管理着色器更加轻松。之后提到 Shader 时,主要指的就是 Unity Shader。

1.2 Unity 中的材质和 Shader

​ 如果想要在 Unity 中体现 Shader 的渲染效果,必须配合使用材质(Material)和 Shader(Unity Shader)才能达到目标。一般的使用流程是:

  1. 创建一个材质。
  2. 创建一个 Unity Sahder,把该 Shader 赋给上一步中创建的材质。
  3. 将材质赋予给想要渲染的对象。
  4. 在材质面板中调整 Unity Shader 的相关属性,以达到最终效果。

​ 也就是说,Unity 中的 Shader 必须配合材质才能正常使用。

1.3 创建材质

​ 在 Project 窗口中右键创建材质:Create -> Material。

  1. 创建好材质并选中后,在 Inspector 窗口的 Shader 选项中选择对应的着色器进行关联使用。
  2. Inspector 窗口下方的内容为选中的 Shader 的可编辑变量,它们会直接影响渲染结果。
  3. 关联好 Shader 后,材质需要赋值给 GameObject 对象上依附的 Mesh Renderer 等相关渲染器组件上进行使用。
image-20240201012043380

​ Unity Shader 中提供了对应的可编辑属性,能够直接在材质中进行编辑,而不需要去修改 Shader 代码来达到不同效果。

1.4 创建 Shader

​ 我们可以在 Project 窗口中右键创建 Shader:Create -> Shader。

image-20240201012424153
  1. Standard Surface Shader(标准曲面着色器)

    包含标准光照模型的表面着色器模板。

  2. Unlit Shader

    不包含光照的基本顶点/片元着色器。

  3. Image Effect Shader

    用于实现屏幕后处理效果的基本模板。

  4. Compute Shader

    利用 GPU 并行计算一些和常规渲染流水线无关的内容。

  5. Ray Tracing Shader

    用于实现光线追踪效果的着色器。

​ 之后的学习重点主要是顶点/片元着色器,因此重点学习 Unlit Shader 着色器的编写。

2 ShaderLab 的基本结构

2.1 什么是 ShaderLab

​ ShaderLab 是 Unity 自定义的语法规则,是专门用于在 Untiy 中编写和管理着色器的语言。
​ 它提供一种结构化的方式来描述 Unity 着色器的各个部分,从而更轻松地创建和管理着色器。

​ Unity 通过 ShaderLab 语言对 Shader 进行包装和组织。

2.2 ShaderLab 的基本结构

​ ShaderLab 主要由 4 个部分组成:

  1. Shader 名称
  2. Shader 属性
  3. 1~n 个子着色器
  4. 备用的 Shader
// 第一部分
Shader "着色器名字" 
{ 
      // 第二部分
      Properties
      {
          // 材质面板上可以看到的属性
      }

      // 第三部分
      SubShader
      {
          // 顶点-片段着色器 / 表面着色器 / 固定函数着色器
      }
      SubShader
      {
          // 更加精简的版本
          // 目的是适配旧设备
      }
      ..... 可以有 n 个 SubShader 代码块

      // 第四部分
      Fallback "备用的Shader"
}

3 Shader 名称

  1. 直接修改 Shader 文件中 Shader 后的名字即可。
  2. Shader 的名字决定在材质面板的选择路径。
image-20240201020001746 image-20240201020054124
  • 注意:
    1. 不要使用中文命名 Shader。
    2. Shader 的文件名和在文件中的命名建议保持一致。

4 Shader 属性

4.1 Shader 属性的作用

​ 为了增加 Shader 的可调节性,某些变量不会直接在 Shader 程序中写死,而是作为开放的属性显示在材质面板上,供使用时调节。这些开放的属性通过属性来定义。
​ Shader 的属性具有两个特点:

  1. 可以在材质面板被编辑。
  2. 可以在后续当作输入变量提供给所有子着色器使用。

4.2 Shader 属性的基本语法

  1. 属性声明在 Shader 文件中

    Shader 属性存在于 Shader 语句块中的 Properties 属性语句块,需要在 Properties 语句块中按照语法规则声明属性。

  2. Unity Shader 的属性主要分成三大类

    • 数值
    • 颜色和向量
    • 纹理贴图
  3. 属性的基本语法

    _Name("Display Name", type) = defaultValue[{options}]

    • Name:属性名字,规则是在前面加一个下划线。
    • Display Name:材质面板上显示的名字。
    • type:属性的类型。
    • defaultValue:将 Shader 指定给材质的时候初始化的默认值。

4.3 数值类型

​ 数值类型有三种:

  1. 整形

    _Name("Display Name", Int) = number

  2. 浮点型
    _Name("Display Name", Float) = number

  3. 范围浮点型
    _Name("Display Name", Range(min,max)) = number

  • 注意:

    Unity Shader 中的数值类型属性基本都是浮点型(Float)数据。虽然提供了整数(Int),但是编译时最终都会转换为浮点型,因此更多使用的还是 Float 类型。

4.4 颜色和向量类型

​ 颜色和向量类型属性之所以归纳在一起,是因为都可以由一个四个数组成的类型表示。

  • 颜色是由 R、G、B、A 四个分量代表
  • 向量是由 X、Y、Z、W 四个分量代表
  1. 颜色

    _Name("Display Name", Color) = (number1, number2, number3, number4)

    注意:颜色值中的 R、G、B、A 的取值范围是 0 ~ 1 (映射 0 ~ 255)。

  2. 向量

    _Name("Display Name", Vector) = (number1, number2, number3, number4)

    注意:向量值中的 X、Y、Z、W 的取值范围没有限制。

4.5 纹理贴图类型

​ 纹理贴图类型有四种

  1. 2D 纹理

    最常用的纹理,漫反射贴图、法线贴图都属于 2D 纹理。
    _Name("Display Name", 2D) = "defaulttexture"{}

  2. 2D Array 纹理

    纹理数组,允许在纹理中存储多层图像数据,每层看做一个 2D 图像。一般使用脚本创建,较少使用,了解即可。
    _Name("Display Name", 2DArray) = "defaulttexture"{}

  3. Cube map texture 纹理

    立方体纹理,由前后左右上下 6 张有联系的 2D 贴图拼成的立方体,比如天空盒和反射探针。
    _Name("Display Name", Cube) = "defaulttexture"{}

  4. 3D 纹理

    一般使用脚本创建,极少使用,了解即可。
    _Name("Display Name", 3D) = "defaulttexture"{}

​ 注意:

  1. 关于 defaulttexture 默认值取值

    • 不写:默认贴图为空
    • white:默认白色贴图(RGBA:1, 1, 1, 1)。
    • black:默认黑色贴图(RGBA:0, 0, 0, 1)。
    • gray:默认灰色贴图(RGBA:0.5, 0.5, 0.5, 1)。
    • bump:默认凸贴图(RGBA:0.5, 0.5, 1, 1),一般用于法线贴图默认贴图。
    • red:默认红色贴图(RGBA:1, 0, 0, 1)。
  2. 关于默认值后面的 {} 为固定写法。老版本中括号内可以控制固定函数纹理坐标的生成,但是新版本中没有该功能了。

5 子着色器

5.1 SubShader 基本构成

​ 每个 Shader 中都会包含至少一个 SubShader,当 Unity 显示物体时,会在 Shader 文件中去检测 SubShader 语句块,选择第一个能够在当前显卡运行的 SubShader 执行。
​ 因此在 Shader 当中实现高级效果时,为了避免在在某些设备上无法执行,可能会存在多个 SubShader 语句块,用于适配这些低端设备。

​ SubShader 当中包含最终的渲染相关代码,决定了最终的渲染效果。

​ SubShader 语句块中主要由 3 部分构成:

  1. 渲染标签

    通过标签来确定什么时候以及如何对物体进行渲染。

  2. 渲染状态

    通过状态来确定渲染时的剔除方式、深度测试方式、混合方式等等内容。

  3. 渲染通道

    具体实现着色器代码的地方(每个 SubShader 语句块中至少有一个渲染通道,可以有多个)。

// 第三部分
SubShader
{
    // 1.渲染标签 Tags
    Tags{ "标签名1" = "标签值1" "标签名2" = "标签值2" .....}

    // 2.渲染状态 States
    .....

    // 3.渲染通道 Pass
    Pass
    {
      // 第一个渲染通道
    }
    Pass
    {
      // 第二个渲染通道
    }
    .............
}

​ 注意:

  • 在 SubShader 中每定义一个渲染通道 Pass,就会让物体执行一次渲染;n 个 Pass,就会有 n 次渲染。
  • 在实现一些复杂渲染效果时需要使用多个 Pass 进行组合实现,但是需要尽量减少数量,因为更多的 Pass 会增加性能消耗。

5.2 渲染标签 Tags

​ 渲染标签通过键值对的形式进行声明,并且没有数量限制,可以使用任意多个标签。

5.2.1 渲染队列 Queue

​ 主要作用:确定物体的渲染顺序。

Tags{ "Queue" = "标签值" }

​ 常用 Unity 预先定义好的渲染队列标签值:

  1. Background (队列号:1000)

    最早被渲染的物体的队列,一般用来渲染天空盒或者背景。

    Tags{ "Queue" = "Background" }

  2. Geometry (队列号:2000)

不透明的几何体通常使用该队列,当没有声明渲染队列时,Unity 会默认使用这个队列。

Tags{ "Queue" = "Geometry" }

  1. AlphaTest (队列号:2450)

    有透明通道的,需要进行 Alpha 测试的几何体会使用该队列。

    当所有 Geometry 队列实体绘制完后再绘制 AlphaTest 队列,效率更高。
    Tags{ "Queue" = "AlphaTest" }

  2. Transparent (队列号:3000)

    该队列中几何体按照由远到近的顺序进行绘制,半透明物体的渲染队列,所有进行透明混合的几何体都应该使用该队列。比如:玻璃材质,粒子特效等。

    Tags{ "Queue" = "Transparent" }

  3. Overlay (队列号:4000)

    用是放在最后渲染的队列,于叠加渲染的效果,比如镜头光晕等。

    Tags{ "Queue" = "Overlay" }

  4. 自定义队列

    基于 Unity 预先定义好的这些渲染队列标签来进行加减运算(加减号两旁不允许空格)来定义自己的渲染队列。比如:

    Tags{ "Queue" = "Geometry+1" },代表的队列号就是 2001。

    Tags{ "Queue" = "Transparent-1" },代表的队列号就是 2999。

    自定义队列在一些特殊情况下,特别有用。比如,水的渲染想要在不透明物体之后,半透明物体之前进行渲染,就可以自定义。

​ 注意:

​ 自定义队列只能基于预先定义好的各类型进行计算,不能在 Shader 中直接赋值数字。如果实在想要直接赋值数字,可以在材质面板中进行设置。

5.2.2 渲染类型 RenderType

​ 主要作用:对着色器进行分类,之后可以用于着色器替换功能。
​ 摄像机上有对应的 API,可以指定这个渲染类型来替换成别的着色器。

Tags{ "RenderType" = "标签值" }

​ 常用 Unity 预先定义好的渲染类型标签值:

渲染类型用处说明
Opaque (不透明的)用于普通 Shader,比如:不透明、自发光、反射等
Transparent (透明的)用于半透明 Shader,比如:透明、粒子
TransparentCutout (透明切割)用于透明测试 Shader,比如:植物叶子
Background (背景)用于天空盒 Shader
Overlay (覆盖)用于 GUI 纹理、Halo(光环)、Flare(光晕)
TreeOpaque用于地形系统中的树干不常用
TreeTransparentCutout用于地形系统中的树叶不常用
TreeBillboard用于地形系统中的 Billboarded 树不常用
Grass用于地形系统中的草不常用
GrassBillboard用于地形系统中的 Billboarded 草不常用

5.2.3 禁用批处理

​ 主要作用:当使用批处理时,模型会被变换到世界空间中,模型空间会被丢弃,这可能会导致某些使用模型空间顶点数据的 Shader 最终无法实现想要的结果。可以通过开启禁用批处理来解决该问题(了解即可)。

  • 总是禁用批处理

    Tags{ "DisableBatching" = "True" }

  • 不禁用批处理(默认值)

    Tags{ "DisableBatching" = "False" }

  • LOD 效果激活时才会禁用批处理,主要用于地形系统上的树

    Tags{ "DisableBatching" = "LODFading" }

5.2.4 禁止阴影投影

​ 主要作用:控制该 SubShader 的物体是否会投射阴影。

  • 不投射阴影

    Tags{ "ForceNoShadowCasting" = "True" }

  • 投射阴影(默认值)

    Tags{ "ForceNoShadowCasting" = "False" }

5.2.5 忽略投影机

​ 主要作用:物体是否受到 Projector(投影机)的投射。
​ Projector 是 Unity 中的一个功能。

  • 忽略 Projector(一般半透明 Shader 需要开启该标签)

    Tags{ "IgnoreProjector" = "True" }

  • 不忽略 Projector(默认值)

    Tags{ "IgnoreProjector" = "False" }

5.2.6 其他标签

  1. 是否用于精灵

    想要将该 SubShader 用于 Sprite 时,将该标签设置为 False。

    Tags{ "CanUseSpriteAtlas" = "False" }

  2. 预览类型

    材质在预览窗口默认为球形,如果想要改变为平面或天空盒,只需要改变预览标签即可。

    • 平面

      Tags{ "PreviewType" = "Panel" }

    • 天空盒

      Tags{ "PreviewType" = "SkyBox" }

5.2.7 注意事项

​ 以上这些标签只能在 SubShader 语句块中声明,Pass 渲染通道语句块中也可以声明渲染标签。

​ 但是这节内容的标签都不能在 Pass 中声明,Pass 中有专门的标签类型。

5.3 渲染状态 States

5.3.1 语法结构

​ 渲染状态通过 渲染状态关键词 + 空格 + 状态类型 决定。如果存在多个渲染状态,可以通过空行隔开。

5.3.2 剔除方式

​ 主要作用:设置多边形的剔除方式,有背面剔除、正面剔除、不剔除。
​ 剔除,即不渲染,背面剔除即背面不渲染,正面剔除即正面不渲染,不剔除即都渲染。

  • Cull Back:背面剔除。

  • Cull Front:正面剔除。

  • Cull Of:不剔除。

    不设置的话,默认为背面剔除。一般情况下,需要两面渲染时,会设置为不剔除。

5.3.3 深度缓冲

​ 主要作用:是否写入深度缓冲。
​ 深度缓冲(Depth Buffer):深度缓冲是与屏幕像素对应的缓冲区,用于存储每个像素的深度值(距离相机的距离)。在渲染场景前,深度缓冲被初始化为最大深度值,表示所有像素都在相机之外。最后留在深度缓冲中的信息会被渲染。

  • ZWrite On:写入深度缓冲。
  • ZWrite Off :不写入深度缓冲。

​ 不设置的话,默认为写入。一般情况下,在做透明等特殊效果时,会设置为不写入。

5.3.4 深度测试

​ 主要作用:设置深度测试的对比方式。

​ 深度测试的主要目的是确保在渲染时,像素按照正确的深度(距离相机的距离)顺序进行绘制,从而创建正确的遮挡关系和透视效果。在渲染场景之前,深度缓冲被初始化为最大深度值,表示所有像素都在相机之外。在渲染过程中,对于每个像素,深度测试会将当前像素的深度值与深度缓冲中对应位置的值进行比较。

​ 一般情况下 :

  1. 如果当前像素的深度值小于深度缓冲中的值,说明当前像素在其他物体之前,则被绘制,并更新深度缓冲。
  2. 如果当前像素的深度值大于等于深度缓冲中的值,说明当前像素在其他物体之后,则被丢弃,不会被绘制,并保持深度缓冲不变。
  • ZTest Less:小于当前深度缓冲中的值,就通过测试,写入到深度缓冲中。
  • ZTest Greater:大于当前深度缓冲中的值,就通过测试,写入到深度缓冲中。
  • ZTest LEqual:小于等于当前深度缓冲中的值,就通过测试,写入到深度缓冲中。
  • ZTest GEqual:大于等于当前深度缓冲中的值,就通过测试,写入到深度缓冲中。
  • ZTest Equal:等于当前深度缓冲中的值,就通过测试,写入到深度缓冲中。
  • ZTest NotEqual:不等于当前深度缓冲中的值,就通过测试,写入到深度缓冲中。
  • ZTest Always:始终通过深度测试写入深度缓冲中。

​ 不设置的话,默认为 LEqual 小于等于。一般情况下,只有在实现一些特殊效果时才会区修改深度测试方式,比如透明物体渲染会修改为 Less,描边效果会修改为 Greater 等。

5.3.5 混合方式

​ 主要作用:设置渲染图像的混合方式(多种颜色叠加混合,比如透明、半透明效果和遮挡的物体进行颜色混合)。

  • Blend One One:线性减淡
  • Blend SrcAlpha OneMinusSrcAlpha:正常透明混合。
  • Blend OneMinusDstColor One:滤色。
  • Blend DstColor Zero:正片叠底。
  • Blend DstColor SrcColor:X 光片效果。
  • Blend One OneMinusSrcAlpha:透明度混合。
  • 等等

​ 不设置的话,默认不会进行混合。一般情况下,需要多种颜色叠加渲染时,需要设置混合方式,具体情况具体处理。

5.3.6 其他渲染状态

  1. LOD:控制 LOD 级别,在不同距离下使用不同的渲染方式处理。
  2. ColorMask:设置颜色通道的写入蒙版,默认蒙版为 RGBA。
  3. 等等

​ 目前主要掌握剔除方式、深度缓冲、深度测试、混合方式即可。

5.3.7 注意事项

​ 以上这些状态不仅可以在 SubShader 语句块中声明,Pass 渲染通道语句块中也可以声明这些渲染状态。

​ 如果在 SubShader 语句块中使用,会影响之后的所有渲染通道 Pass。如果在 Pass 语句块中使用只会影响当前 Pass 渲染通道,不会影响其他的 Pass。

5.4 渲染通道 Pass

5.4.1 语法结构

Pass{
    1. Name 名称
    2. 渲染标签
    3. 渲染状态
    4. 其他着色器代码
}

5.4.2 Pass 名称

​ 主要作用:对 Pass 命名的主要目的是利用 UsePass 命令在其他 Shader 中复用该 Pass。只需要在其他 Shader 当中使用
UsePass "Shader路径/Pass名"
​ 注意:

  • Unity 内部会把 Pass 名称转换为大写字母。因此在使用 UsePass 命令时必须使用大写形式的名字。
Pass{
	Name MyPass
}

​ 在其他 Shader 中复用该 Pass 代码时,使用命令:UsePass "TeachShader/Lesson4/MYPASS"

5.4.3 Pass 中的渲染标签

​ Pass 中的渲染标签语法和 SubShader 中相同
Tags{ "标签名1" = "标签值1" "标签名2" = "标签值2" "标签名2" = "标签值2" .......}
​ 但是 SubShader 语句块中的渲染标签不能够在 Pass 中使用,Pass 当中有自己专门的渲染标签。

  1. Tags{ "LightMode" = "标签值" }

    主要作用:指定了该 Pass 应该在哪个阶段执行。

    可以将着色器代码分配给适当的渲染阶段,以实现所需的效果。

    标签值说明
    Always始终渲染;不应用光照
    ForwardBase在前向渲染中使用;应用环境光、主方向光、顶点 / SH 光源和光照贴图
    ForwardAdd在前向渲染中使用;应用附加的每像素光源(每个光源有一个通道)
    Deferred在延迟渲染中使用;渲染 G 缓冲区
    ShadowCaster将对象深度渲染到阴影贴图或深度纹理中
    MotionVectors用于计算每对象运动矢量
    PrepassBase在旧版延迟光照中使用;渲染法线和镜面反射指数
    PrepassFinal在旧版延迟光照中使用;通过组合纹理、光照和反光来渲染最终颜色
    Vertex当对象不进行光照贴图时在旧版顶点光照渲染中使用;应用所有顶点光源
    VertexLMRGBM对象不进行光照贴图时在旧版顶点光照渲染中使用;在光照贴图为 RGBM 编码的平台上(PC 和游戏主机)
    VertexLM当对象不进行光照贴图时在旧版顶点光照渲染中使用;在光照贴图为双 LDR 编码的平台上(移动平台)

    关于向前渲染、延迟渲染、旧版光照等概念了解:https://docs.unity.cn/cn/2019.4/Manual/RenderingPaths.html。

  2. Tags{ "RequireOptions" = "标签值" }

    主要作用:用于指定当满足某些条件时才渲染该 Pass。

    目前 Unity 仅支持:

    Tags{ "RequireOptions" = "SoftVegetation" }

    仅当 Quality 窗口中开启了 SoftVegetation 时才渲染此通道。

  3. Tags{ "PassFlags" = "标签值" }

    主要作用:一个渲染通道 Pass 可指示一些标志来更改渲染管线向 Pass 传递数据的方式。

    目前 Unity 仅支持:

    Tags{ "PassFlags" = "OnlyDirectional" }

    在 ForwardBase 向前渲染的通道类型中使用时,此标志的作用是仅允许主方向光和环境光/光照探针数据传递到着色器。这意味着非重要光源的数据将不会传递到顶点光源或球谐函数着色器变量。

5.4.4 Pass 中的渲染状态

​ 5.3 节中 SubShader 语句块的渲染状态同样适用于 Pass,比如:

  • 剔除方式:决定模型正面背面是否能够被渲染。
  • 深度缓冲 / 深度测试:决定景深关系的确定以及透明效果的正确表达等。
  • 混合方式:决定透明半透明颜色的正确表现,以及一些特殊颜色效果的表现。

​ 这些渲染状态都可以在单个 Pass 中进行设置,需要注意:

  1. 如果在 SubShader 语句块中使用会影响之后的所有渲染通道 Pass。
  2. 如果在 Pass 语句块中使用只会影响当前 Pass 渲染通道,不会影响其他的 Pass。
  3. 不仅如此,Pass 中还可以使用固定管线着色器的命令。

5.4.5 其他着色器代码

​ 其他代码部分是实现着色器的核心代码,会用到 CG 或 HLSL 等着色器语言来进行逻辑书写。

5.4.6 GrabPass 命令

​ 可以利用 GrabPass 命令把即将绘制对象时的屏幕内容抓取到纹理中,在后续通道中可使用此纹理,从而执行基于图像的高级效果。

举例:
将绘制该对象之前的屏幕抓取到 _BackgroundTexture 中:

GrabPass
{
    "_BackgroundTexture"
}

​ 注意:
​ 该命令一般写在某个 Pass 前,在之后的 Pass 代码中可以利用 _BackgroundTexture 变量进行处理。

6 备用 Shader

6.1 作用

​ 复习:ShaderLab 当中允许有多个 SubShader 子着色器,当执行渲染时,从上到下使用第一个能够正常执行的 SubShader 子着色器来渲染对象。
​ 备用 Shader 主要作用是当 Shader 文件中的所有 SubShader 子着色器都无法正常运行时,让物体能够使用一个最低级的 Shader 渲染出来(效果略差,但至少能够显示)。

6.2 语法

Fallback "Shader名"
​ 或者
Fallback Off

​ 在 Fallback 关键词后面空格并通过一个字符串来告诉 Unity “最低级的 Unity Shader” 在哪,也可以直接关闭 Fallback 功能,但这意味着“放弃治疗”。

7 Shader 编写形式

​ Shader 最主要的作用是指定各种着色器所需的代码。这些着色器代码既可以放在 SubShader 子着色器语句块中,也可以放在其中的 Pass 渲染通道语句块中,不同的 Shader 形式放置着色器代码的位置也有所不同。

​ 我们一般会使用以下 3 种形式来编写 Unity Shader:

  1. 表面着色器(可控性较低)。
  2. 顶点/片元着色器(重点学习)。
  3. 固定函数着色器(基本已弃用,了解即可)。

7.1 表面着色器

​ 表面着色器(Surface Shader)是 Unity 创造的一种着色器代码类型,本质是对顶点/片元着色器的一层封装。它需要的代码量很少,很多工作由 Unity 帮助完成。其缺点是渲染的消耗较大,可控性较低;优点在于,帮助我们处理很多光照细节,可以直接使用而无需自己计算实现光照细节。

​ 创建 Shader 时,选择创建 Standard Surface Shader。通过观察 Shader 文件的内部结构,发现着色器相关代码被放在 SubShader 语句块中(并非 Pass)的 CGPROGRAM 和 ENDCG 之间。

image-20240201030757968 image-20240201030912289

​ 表面着色器的特点:

  1. 直接在 SubShader 语句块中书写着色器逻辑。
  2. 不需关心也不需使用多个 Pass,Unity 会在内部帮助处理。
  3. 可以使用 CG 或 HLSL 两种 Shader 语言去编写 Shader 逻辑。
  4. 代码量较少,可控性较低,性能消耗较高。
  5. 适用于处理需要和各种光源打交道的着色器(主机、PC 平台时更适用,移动平台需要考虑性能消耗)。

7.2 顶点/片元着色器

​ 在创建 Shader 时,选择创建 Unlit Shader 来快速创建顶点/片元着色器模板。
​ 通过观察,发现顶点/片元着色器的着色器代码编写在 Pass 语句块中,需要自己定义每个 Pass 需要使用的 Shader 代码。

image-20240201031131057 image-20240201031205411

​ 虽然比起表面着色器来说我们需要编写的代码较多,但是好处是灵活性更高,可控性更强,可以控制更多的渲染细节,决定对性能影响的高低。

​ 顶点/片元着色器的特点:

  1. 需要在 Pass 渲染通道中编写着色器逻辑。
  2. 可以使用 CG 或 HLSL 两种 Shader 语言去编写 Shader 逻辑。
  3. 代码量较多,灵活性较强,性能消耗更可控,可以实现更多渲染细节。
  4. 适用于光照处理较少,自定义渲染效果较多时(移动平台首选)。

7.3 固定函数着色器

​ 表面着色器、顶点/片元着色器这两种 Unity Shader 形式都使用了可编程管线。
​ 而对于一些老设备(DX7.0、OpenGL1.5 或 OpenGL ES 1.1),它们不支持可编程管线着色器,这时就需要使用固定函数着色器来进行渲染,这些着色器只能实现一些非常简单的效果。

​ 固定函数着色器的特点:

  1. 需要在 Pass 渲染通道中编写着色器逻辑
  2. 需要使用 ShaderLab 语法中的渲染设置命令来编写,而非 CG 和 HLSL 着色器语言。

​ 但是由于这些旧设备目前市面上几乎已经不存在,所以固定函数着色器几乎不会再使用,了解即可。
​ 即使现在在 Unity 中使用固定函数着色器来编写 Shader,在内部也会被编译为顶点/片元着色器,因此真正意义的固定函数着色器已经不存在了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1423943.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

飞桨paddlespeech语音唤醒推理C INT8 定点实现

前面的文章(飞桨paddlespeech语音唤醒推理C定点实现)讲了INT16的定点实现。因为目前商用的语音唤醒方案推理几乎都是INT8的定点实现,于是我又做了INT8的定点实现。 实现前做了一番调研。量化主要包括权重值量化和激活值量化。权重值由于较小且…

操作方法:将 PDF 转换为 Word 文档的 7 种方法

只要您有合适的工具,将 PDF 另存为 Word 文档就是小菜一碟。为了简化 PDF 转 Word 的转换,从而提高工作效率,这篇文章将围绕 Windows、Mac 和在线上的几个免费 PDF 转 Word 转换器,轻松帮助您学习如何将 PDF转换为 Word。 Windows…

AIGC,ChatGPT4 实际需求效办公自动化函数应用

用实际需求来给大家演示一下ChatGPT如何助力办应用。 首先我们来提取年份值 我们将公式复制到表格即可。 接下来进行向下填充。 就得到了所有年份, 接下来我们完成第二个需求,按年份统计销售额。 Prompt:有一个表格C列是年份,D列…

java+springboot电影订票选座及评论网站影评系统ssm+vue

广大观影消费者需要知道自己的空闲时间,在自己可以接受的地理距离范围内,是否有感兴趣的影片可供观看,也需要清楚哪家影院在销售自己需要的电影票;同时手握电影排期及上映信息的电影院的运营者也急需根据消费者的观影需求实时调整…

FFMPEG 之 DXVA2 硬解

一:FFMPEG 支持的硬解方式有很多: DXVA2、D3D11VA、CUDA、QSV、OPENCL、DRM、VAAPI、VDPAU、VIDEOTOOLBOX、MEDIACODEC。 有的支持 Windows 平台,有的支持 linux 平台,有的支持 apple ios 平台,…

Java 面向对象进阶 03 static 注意事项、重新认识main方法(黑马)

案例代码如下: 非静态的方法虚拟机会自带一个关键字,如下: 但是不能手动出来,它的类型就是当前类; 通过输出this,得到当前s1,s2 的地址; 所以在输出时会有隐藏的this,所以this所指向…

LaTeX 文本对齐:ragged2e 宏包

✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。 🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心&…

268. Missing Number(丢失的数字)

题目描述 给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。 问题分析 因为这些数是在[0, n] 范围内的数,我们要是一一标记其是否出现很麻烦,但是我们考虑到如果用所有数的和,减去…

【网络安全|漏洞挖掘】ChatGPT 再曝安全漏洞,被指泄露私密对话

风靡全球的聊天机器人 ChatGPT 近日再次陷入安全风波,被曝泄露用户同机器人的私密对话,其中包含用户名、密码等敏感信息。 ArsTechnica 网站援引其读者提供的截图报道称,ChatGPT 泄露了多段非用户本人的对话内容,包含了大量敏感信…

Web前端入门 - HTML JavaScript Vue

ps:刚开始学习web前端开发,有什么不正确、不标准的内容,欢迎大家指出~ Web简介 90年代初期,Web1.0,静态页面,不和服务器交互,网页三剑客指Dreamweaver、Fireworks、Flash2000年代中期&#xf…

HiveSQL题——数据炸裂和数据合并

目录 一、数据炸裂 0 问题描述 1 数据准备 2 数据分析 3 小结 二、数据合并 0 问题描述 1 数据准备 2 数据分析 3 小结 一、数据炸裂 0 问题描述 如何将字符串1-5,16,11-13,9" 扩展成 "1,2,3,4,5,16,11,12,13,9" 且顺序不变。 1 数据准备 with da…

算法练习-逆波兰表达式求值(思路+流程图+代码)

难度参考 难度:中等 分类:栈与队列 难度与分类由我所参与的培训课程提供,但需要注意的是,难度与分类仅供参考。且所在课程未提供测试平台,故实现代码主要为自行测试的那种,以下内容均为个人笔记&#xff0c…

能耗在线监测系统在节能管理中的应用

上海安科瑞电气股份有限公司 胡冠楠 咨询家:“Acrelhgn”,了解更多产品资讯 摘要:开展能耗在线监测系统建设,对加强政府部门和企业节能管理中的应用前景,分析系统在能源消费预测分析、能效对标、节能监察、能源精细化…

【Oracle云】OCI DevOps Services 构建自动化流水线 (1) - 基础架构流程 OCI 代码仓库使用

OCI DevOps Services 是 Oracle Cloud Infrastructure (OCI) 提供的一项独立的 CI/CD 服务,旨在支持用户构建自动化的流水线,实现更高效、可靠的软件交付。在本系列的第一篇文章中,我们将深入探讨 OCI DevOps Services 的基础架构流程&#x…

探索设计模式的魅力:精准解读桥接模式-用桥接模式构建可扩展的软件系统

设计模式专栏:http://t.csdnimg.cn/nolNS 目录 一、了解桥接模式:探索抽象和实现的分离 1.1 开-闭原则 1.2 组合/聚合复用原则 1.3 定义 1.4 用意 1.5 基本思想 1.6 组成部分 1.7 桥梁模式的示意性系统的结构图 二、桥接模式的优势&#xff1a…

RK356X RKAndroid12 TF卡配置 自动挂载

RK356X RKAndroid12 TF卡配置 自动挂载 RK3568 有三个SDMMC接口&#xff0c;分别为SDMMC0 SDMMC1 SDMMC2 DTS 配置 1. max-frequency <150000000>; 此配置设置 SD 卡的运行频率&#xff0c;虽然设置为 150M &#xff0c;但是还要根据 SD 卡的不同模式进行调整。这…

C语言系列-浮点数在内存中的存储

&#x1f308;个人主页: 会编程的果子君 ​&#x1f4ab;个人格言:“成为自己未来的主人~” 目录 浮点数在内存中的存储 浮点数的存储 浮点数存的过程 浮点数取的过程 题目解析 浮点数在内存中的存储 常见的浮点数&#xff1a;3.14159.1E10等&#xff0c;浮点数家族包括&…

使用websocket后端接入文心一言

最近再写项目练手&#xff0c;想着最近大模型那么火&#xff0c;也想接入项目来玩一玩&#xff0c;于是去了解了一下相关的api和通信协议&#xff0c;最后选择了文心一言进行集成&#xff0c;国内的相对稳定。ERNIE-Bot-turbo - 千帆大模型平台 | 百度智能云文档 (baidu.com) …

elementUI实现selecttree自定义下拉框树形组件支持多选和搜索

elementUI实现selecttree自定义下拉框树形组件支持多选和搜索 效果图定义子组件父组件应用 效果图 定义子组件 主要结合el-select和el-tree两个组件改造的。 <template><div class"selectTree"><el-select filterable :filter-method"filterMe…

AI学习(4): PyTorch实战-手写数字识别

1.介绍 在之前的文章中介绍了PyTorch的环境安装&#xff0c;和张量(tensor)的基本使用&#xff0c;为防止陷入枯燥的理论学习中&#xff0c;在这篇文章&#xff0c;我们将进行项目实战学习&#xff0c;项目主要内容: 基于MNIST数据集&#xff0c;实现一个手写数字识别的神经网…