Unity系列文章目录

文章目录

  • Unity系列文章目录
  • 前言
  • 一、Unity Shader 概述
  • 二、使用步骤
    • 1.3.1.2 Unity 中的材质
    • 2.Unity 中的Shader
    • 3.Unity Shader 的基础:ShaderLab
    • 4.Unity Shader 的结构
  • 参考

前言

通过前面的学习内容我们已经知道,Shader 并不是什么神秘的东西,它们其实就是渲染流水
线中的某些特定阶段,如顶点着色器阶段、片元着色器阶段等。
在没有Unity 这类编辑器的情况下,如果我们想要对某个模型设置渲染状态,可能需要类似
下面的代码:

// 初始化渲染设置
void Initialization() {// 从硬盘上加载顶点着色器的代码
string vertexShaderCode = LoadShaderFromFile(VertexShader.shader);
// 从硬盘上加载片元着色器的代码
string fragmentShaderCode = LoadShaderFromFile(FragmentShader.shader);
// 把顶点着色器加载到GPU 中
LoadVertexShaderFromString(vertexShaderCode);
// 把片元着色器加载到GPU 中
LoadFragmentShaderFromString(fragmentShaderCode);
// 设置名为"vertexPosition"的属性的输入,即模型顶点坐标
SetVertexShaderProperty("vertexPosition", vertices);
// 设置名为"MainTex"的属性的输入,someTexture 是某张已加载的纹理
SetVertexShaderProperty("MainTex", someTexture);
// 设置名为"MVP"的属性的输入,MVP 是之前由开发者计算好的变换矩阵
SetVertexShaderProperty("MVP", MVP);
// 关闭混合
Disable(Blend);
// 设置深度测试
Enable(ZTest);
SetZTestFunction(LessOrEqual);
// 其他设置
…
}
// 每一帧进行渲染
void OnRendering() {// 调用渲染命令
DrawCall();
// 当涉及多种渲染设置时,我们可能还需要在这里改变各种渲染设置
...
}

VertexShader.shader:

// 输入:顶点位置、纹理、MVP 变换矩阵
in float3 vertexPosition;
in sampler2D MainTex;
in Matrix4x4 MVP;
// 输出:顶点经过MVP 变换后的位置
out float4 position;
void main() {// 使用MVP 对模型顶点坐标进行变换
position = MVP * vertexPosition;
}

FragmentShader.shader:

// 输入:VertexShader 输出的position、经过光栅化程序插值后的该片元对应的position
in float4 position;
// 输出:该片元的颜色值
out float4 fragColor;
void main() {// 将片元颜色设为白色
fragColor = float4(1.0, 1.0, 1.0, 1.0);
}

上述伪代码仅仅是简化后的版本,当渲染的模型数目、需要调整的着色器属性不断增多时,
上述过程将变得更加复杂和冗长。而且,当涉及透明物体等多物体的渲染时,如果没有编辑器的
帮助,我们要非常小心如渲染顺序等问题。
Unity 的出现改善了上面的状况。它提供了一个地方能够让开发者更加轻松地管理着色器代
码以及渲染设置(如开启/关闭混合、深度测试、设置渲染顺序等),而不需要像上面的伪代码一
样,管理多个文件和函数等。Unity 提供的这个“方便的地方”,就是Unity Shader。

一、Unity Shader 概述

那么如何充分利用Unity Shader 来为我们的游戏增光添彩呢?
3.1.1 一对好兄弟:材质和Unity Shader
总体来说,在Unity 中我们需要配合使用材质(Material)和Unity Shader 才能达到需要的效
果。一个最常见的流程是:
(1)创建一个材质;
(2)创建一个Unity Shader,并把它赋给上一步中创建的材质;
(3)把材质赋给要渲染的对象;
(4)在材质面板中调整Unity Shader 的属性,以得到满意的效果。
图3.1 显示了Unity Shader 和材质是如何一起工作来控制物体的渲染的。

可以发现,Unity Shader 定义了渲染所需的各种代码(如顶点着色器和片元着色器)、属性(如
使用哪些纹理等)和指令(渲染和标签设置等),而材质则允许我们调节这些属性,并将其最终赋
给相应的模型。

二、使用步骤

1.3.1.2 Unity 中的材质

Unity 中的材质需要结合一个GameObject 的Mesh 或者Particle Systems 组件来工作。它决定
了我们的游戏对象看起来是什么样子的(这当然也需要Unity Shader 的配合)。
为了创建一个新的材质,我们可以在Unity 的菜单栏中选择Assets -> Create -> Material 来创
建,也可以直接在Project 视图中右击 -> Create -> Material 来创建。当创建了一个材质后,就可
以把它赋给一个对象。这可以通过把材质直接拖曳到Scene 视图中的对象上来实现,或者在该对
象的Mesh Renderer 组件中直接赋值,如图3.2 所示。
在Unity 5.x 版本中,默认情况下,一个新建的材质将使用Unity 内置的Standard Shader,这
是一种基于物理渲染的着色器,我们将在第18 章中讲到。
对于美术人员来说,材质是他们十分熟悉的一种事物。Unity 的材质和许多建模软件(如Cinema
4D、Maya 等)中提供的材质功能类似,它们都提供了一个面板来调整材质的各个参数。这种可视化
的方法使得开发者不再需要自行在代码中设置和改变渲染所需的各种参数,如图3.3 所示。

2.Unity 中的Shader

为了和前面通用的Shader 语义进行区分,我们把Unity 中的Shader 文件统称为Unity Shader。
这是因为,Unity Shader 和我们之前提及的渲染管线的Shader 有很大不同,我们会在3.6.2 节中进
行更加详细的解释。
为了创建一个新的Unity Shader,我们可以在Unity 的菜单栏中选择Assets -> Create -> Shader 来
创建,也可以直接在Project 视图中右击 -> Create -> Shader 来创建。在Unity 5.2 及以上版本中,Unity
一共提供了4 种Unity Shader 模板供我们选择—Standard Surface Shader,Unlit Shader,Image Effect
Shader 以及Compute Shader。其中,Standard Surface Shader 会产生一个包含了标准光照模型(使用了Unity 5 中新添加的基于物理的渲染方法,详见第18 章)的表面着色器模板,Unlit Shader 则会产生一
个不包含光照(但包含雾效)的基本的顶点/片元着色器,Image Effect Shader 则为我们实现各种屏幕
后处理效果(详见第12 章)提供了一个基本模板。最后,Compute Shader 会产生一种特殊的Shader
文件,这类Shader 旨在利用GPU 的并行性来进行一些与常规渲染流水线无关的计算,而这不在本书
的讨论范围内,读者可以在Unity 手册的Compute Shader 一文(http://docs.unity3d.com/Manual/
ComputeShaders.html)中找到更多的介绍。总体来说,Standard Surface Shader 为我们提供了典型的表
面着色器的实现方法,但本书的重点在于如何在Unity 中编写顶点/片元着色器,因此在后续的学习中,
我们通常会使用Unlit Shader 来生成一个基本的顶点/片元着色器模板。
一个单独的Unity Shader 是无法发挥任何作用的,它必须和材质结合起来,才能发生神奇的
“化学反应”!为此,我们可以在材质面板最上方的下拉菜单中选择需要使用的Unity Shader。当
选择完毕后,材质面板中就会出现该Unity Shader 可用的各种属性。这些属性可以是颜色、纹理、
浮点数、滑动条(限制了范围的浮点数)、向量等。当我们把材质赋给场景中的一个对象时,就可
以看到调整属性所发生的视觉变化。
Unity Shader 本质上就是一个文本文件。和Unity 中的很多外部文件类似,Unity Shader 也有
导入设置(Import Settings)面板,在Project 视图中选中某个Unity Shader 即可看到。在Unity 5.2
版本中,Unity Shader 的导入设置面板如图3.4 所示。
在该面板上,我们可以在Default Maps 中指定该Unity Shader 使用的默认纹理。当任何材质
第一次使用该Unity Shader 时,这些纹理就会自动被赋予到相应的属性上。在下方的面板中,Unity
会显示出和该Unity Shader 相关的信息,例如它是否是一个表面着色器(Surface Shader)、是否是
一个固定函数着色器(Fixed Function Shader)等,还有一些信息是和我们在Unity Shader 中的标
签设置(详见3.3.3 节)有关,例如是否会投射阴影、使用的渲染队列、LOD 值等。
对于表面着色器(详见3.4.1 节)来说,我们可以通过单击Show generated code 按钮来打开
一个新的文件,在该文件里将显示Unity 在背后为该表面着色器生成的顶点/片元着色器。这可以
方便我们对这些生成的代码进行修改(需要复制到一个新的Unity Shader 中才可保存)和研究。
同样地,如果该Unity Shader 是一个固定函数着色器,在Fixed function 的后面也会出现一个Show
generated code 按钮,来让我们查看该固定函数着色器生成的顶点/片元着色器。Compile and show
code 下拉列表可以让开发者检查该Unity Shader 针对不同图像编程接口(例如OpenGL、D3D9、
D3D11 等)最终编译成的Shader 代码,如图3.5 所示。直接单击该按钮可以查看生成的底层的汇
编指令。我们可以利用这些代码来分析和优化着色器。

除此之外,Unity Shader 的导入面板还可以方便地查看其使用的渲染队列(Render queue)、
是否关闭批处理(Disable batching)、属性列表(Properties)等信息。

3.Unity Shader 的基础:ShaderLab

“计算机科学中的任何问题都可以通过增加一层抽象来解决。”— 大卫·惠勒

学习和编写着色器的过程一直是一个学习曲线很陡峭的过程。通常情况下,为了自定义渲染
效果往往需要和很多文件和设置打交道,这些过程很容易消磨掉初学者的耐心。而且,一些细节
问题也往往需要开发者花费较多的时间去解决。
Unity 为了解决上述问题,为我们提供了一层抽象—Unity Shader。而我们和这层抽象打交
道的途径就是使用Unity 提供的一种专门为Unity Shader 服务的语言—ShaderLab。
什么是ShaderLab?
“ShaderLab is a friend you can afford.”—尼古拉斯·弗朗西斯(Nicholas Francis),Unity 前首
席运营官(COO)和联合创始人之一。
Unity Shader 是Unity 为开发者提供的高层级的渲染抽象层。图3.6 显示了这样的抽象。Unity
希望通过这种方式来让开发者更加轻松地控制渲染。

在Unity 中,所有的Unity Shader 都是使用ShaderLab 来编写的。ShaderLab 是Unity 提供的
编写Unity Shader 的一种说明性语言。它使用了一些嵌套在花括号内部的语义(syntax)来描述
一个Unity Shader 文件的结构。这些结构包含了许多渲染所需的数据,例如Properties 语句块中定
义了着色器所需的各种属性,这些属性将会出现在材质面板中。从设计上来说,ShaderLab 类似
于CgFX 和Direct3D Effects(.FX)语言,它们都定义了要显示一个材质所需的所有东西,而不仅
仅是着色器代码。
一个Unity Shader 的基础结构如下所示:

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

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

4.Unity Shader 的结构

在上一节的伪代码中我们见到了一些ShaderLab 的语义,如Properties、SubShader、Fallback
等。这些语义定义了Unity Shader 的结构,从而帮助Unity 分析该Unity Shader 文件,以便进行正
确的编译。在下面,我们会解释这些基础的语义含义和用法。
3.3.1 给我们的Shader 起个名字
每个Unity Shader 文件的第一行都需要通过Shader
语义来指定该Unity Shader 的名字。这个名字由一个字
符串来定义,例如“MyShader”。当为材质选择使用的
Unity Shader 时,这些名称就会出现在材质面板的下拉列
表里。通过在字符串中添加斜杠(“/”),可以控制Unity
Shader 在材质面板中出现的位置。例如:
Shader “Custom/MyShader” { }
那么这个Unity Shader 在材质面板中的位置就是:
Shader -> Custom -> MyShader,如图3.7 所示。
3.3.2 材质和Unity Shader 的桥梁:Properties
Properties 语义块中包含了一系列属性(property),
这些属性将会出现在材质面板中。
Properties 语义块的定义通常如下:

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

开发者们声明这些属性是为了在材质面板中能够方便地调整各种材质属性。如果我们需要在
Shader 中访问它们,就需要使用每个属性的名字(Name)。在Unity 中,这些属性的名字通常由
一个下划线开始。显示的名称(display name)则是出现在材质面板上的名字。我们需要为每个
属性指定它的类型(PropertyType),常见的属性类型如表3.1 所示。除此之外,我们还需要为每
个属性指定一个默认值,在我们第一次把该Unity Shader 赋给某个材质时,材质面板上显示的就
是这些默认值。

参考

Unity Shader入门精要
作者:冯乐乐

Unity Shader入门精要第3 章 Unity Shader 基础相关推荐

  1. Unity Shader入门精要--第4 章 学习Shader 所需的数学基础

    Unity系列文章目录 文章目录 Unity系列文章目录 前言 4.1 背景:农场游戏 4.2 笛卡儿坐标系 4.2.2 三维笛卡儿坐标系 4.2.3 左手坐标系和右手坐标系 4.2.4 Unity ...

  2. Unity Shader入门精要--第4 章 学习Shader 所需的数学基础:点和矢量

    Unity系列文章目录 文章目录 Unity系列文章目录 前言 点和矢量 4.3.1 点和矢量的区别 参考 前言 点(point)是n 维空间(游戏中主要使用二维和三维空间)中的一个位置,它没有大小. ...

  3. Unity Shader入门精要——第3章 Unity Shader基础

    Unity Shader入门精要读书笔记系列 第1章 欢迎来到Shader的世界 第2章 渲染流水线 第3章 Unity Shader基础 文章目录 Unity Shader入门精要读书笔记系列 前言 ...

  4. Unity Shader入门精要第七章 基础纹理之遮罩纹理

    Unity系列文章目录 文章目录 Unity系列文章目录 前言 一.实践 参考 前言 遮罩纹理(mask texture)是本章要介绍的最后一种纹理,它非常有用,在很多商业游戏中 都可以见到它的身影. ...

  5. Unity Shader入门精要第四章:学习Shader 所需的数学基础--坐标空间

    Unity系列文章目录 文章目录 Unity系列文章目录 前言 一.4.6.1 为什么要使用这么多不同的坐标空间 二.4.6.3 顶点的坐标空间变换过程 4.6.4 模型空间 4.6.6 观察空间 4 ...

  6. Unity Shader入门精要第七章 基础纹理 凹凸映射之在世界空间下计算

    Unity系列文章目录 文章目录 Unity系列文章目录 前言 一.pandas是什么? 二.使用步骤 2.Unity 中的法线纹理类型 参考 前言 现在,我们来实现第二种方法,即在世界空间下计算光照 ...

  7. Unity Shader入门精要第七章 基础纹理渐变纹理

    Unity系列文章目录 文章目录 Unity系列文章目录 前言 一.渐变纹理是什么 参考 前言 尽管在一开始,我们在渲染中使用纹理是为了定义一个物体的颜色,但后来人们发现,纹理 其实可以用于存储任何表 ...

  8. TA课程笔记01——光照(主要为shader入门精要第六章)

    //老师在这里简单的简述了一下渲染流水线,我在之前的图形学笔记中很详细的讲述了渲染流水线的过程,便不再赘述 //因为老师很详细的将几种常见的光照模型都讲了一遍,但都是美术方向,前面的案例也都缺少代码, ...

  9. Unity Shader入门精要 第2章 读书笔记

    第2章  渲染流水线 注意:图片的来源基本来自作者冯乐乐的GitHub,感谢作者分享 https://github.com/candycat1992/Unity_Shaders_Book 什么是Sha ...

最新文章

  1. 利用vue-resource模拟百度下拉列表
  2. Jmeter之ForEach控制器(配合正则表达式使用)
  3. Redis中的Cluster总结
  4. 7-12(图) 社交网络图中结点的“重要性”计算(30 分)
  5. 【Python】time内置模块处理时间信息
  6. 建筑公司木地板WordPress企业网站模板
  7. 吴恩达深度学习 —— 2.12 向量化的更多例子
  8. Java讲课笔记05:运算符与表达式
  9. Graph Embedding图嵌入
  10. linux重启mysql一直_linux正确重启MySQL的方法
  11. 8. PHP7 安装
  12. 一文细数73个Vision transformer家族成员
  13. 三端食堂售饭管理系统
  14. 关于代码家(干货集中营)共享android端知识点综合整理
  15. 什么是3D建模?一文帮助小白了解建模全流程!
  16. 今天拿到软件设计师证书
  17. mac idea Tomcat 内网映射解决方案
  18. 环境在线监测监控系统
  19. Python实战——VAE的理论详解及Pytorch实现
  20. SAP HANA首次在农夫山泉成功上线

热门文章

  1. 2021年上半年网络工程师真题详解(全套)
  2. 用Python实现在微信头像右上角加上红底白字的数字,类似于微信未读信息数量那种提示效果
  3. Cloneable接口的作用与深入理解深度克隆与浅度克隆
  4. ImageMagick处理图像
  5. 每日学术速递4.17
  6. java 白盒测试与黑盒测试_什么是白盒测试和黑盒测试?有什么区别?
  7. 零售商场无线覆盖解决方案
  8. STM32F103ZE串口笔记
  9. es(elasticsearch)查询数据出现异常Result window is too large, from + size must be less than or equal to
  10. Hadoop基准测试