01.模版测试(Stencil Test):与之相关的是模版缓冲(Stencil Buffer).
02.模版测试更高级的用法:渲染阴影,轮廓渲染等
03.深度测试(Depth Test):这个测试同样是可以高度配置的.比较函数是小于等于的关系,即如果这个片元的深度值大于等于当前深度缓冲区中的值,那么就会舍弃它.透明效果和深度测试以及深度写入的关系非常密切.
04.合并需要解决的问题:是使用这次渲染得到的颜色完全覆盖掉之前的结果,还是进行其他处理?
05.混合(Blend)操作:简化版的混合操作的流程图
图片来源于转载
06.当模型的图元经过了上面层层计算和测试后,就会显示到我们的屏幕上.我们的屏幕显示的就是颜色缓冲区中的颜色值.但是,为了避免我们看到那些正在进行光栅化的图元,GPU 会使用双重缓冲(Double Buffering) 的策略.这意味着,对场景的渲染是在幕后发生的,既在后置缓冲(Back Buffer)中.一旦场景已经被渲染到了后置缓冲中, GPU 就会交换后置缓冲区和前置缓冲区(Front Buffer)中的内容,而前置缓冲区是之前显示在屏幕上的图像.由此,保证了我们看到的图像总是连续的.
01.图像编程接口(如 OpenGL 和 DirectX)
02.封装性会导致编程自由度下降
2.4.1 什么是 OpenGL / DirectX
01.显卡驱动(Graphics Driver)发送渲染命令
02.比喻:显卡驱动就是显卡的操作系统
03.开发者可以通过图像编程接口发出渲染命令,这些渲染命令也被称为 Draw Call.
04.一个显卡除了有图像处理单元 GPU 外,还拥有自己的内存,这个内存通常被称为显存(Video Random Access Memory, VRAM)
05.CPU,OpenGL/DirectX,显卡驱动和GPU之间的关系
图片来源于转载01.着色器的可编程性在于,我们可以使用一种特定的语言来编写程序,就好比我们可以用 C# 来写游戏逻辑一样.
02.在可编程管线出现之前,为了编写着色器代码,开发者们学习汇编语言.为了给开发者们打开更方便的大门,就出现了更高级的着色语言(Shading Language).
03.常见的着色语言有:<1> DirectX 的 HLSL(High Level Shading Language)
<2> OpenGL 的 GLSL(OpenGL Shading Language)
<3> NVIDIA 的 CG(C for Graphic)
以上这些都是"高级(High - Level)"语言
01.Draw Call 本身的含义很简单,就是 CPU 调用图像编程接口.以命令 GPU 进行渲染的操作.
02.解决 CPU 和 GPU 并行工作的解决方法就是使用一个命令缓冲区(Command Buffer).
03.批处理(Batching)的方法:一个很显然的优化想法就是把很多小的 Draw Call 合并成一个大的 Draw Call,这就是批处理的思想.批处理技术更加适合于那些静态的物体,例如不会移动的大地,石头等,对于这些静态物体我们只需要合并一次即可.动态物体也可以进行批处理,但是由于动态物体是不断运动的,因此每一帧都需要重新进行合并然后再发送给 GPU ,这对空间和时间都会造成一定的影响.
图片来自于转载
04.为了减少 Draw Call 的开销,有两点需要注意:<1> 避免使用大量很小的网格.一定要使用大量很小的网格,考虑是否可以合并它们
<2> 避免使用过多的材质.尽量在不同的网格之间公用同一个材质
01.固定函数的流水线(Fixed - Function Pipeline),也简称为固定管线,通常是指在较旧的 GPU 上实现的渲染流水线.
01.Shader所在的阶段就是渲染流水线的一部分
02.Shader就是: <1> GPU 流水线上一些可高度编程的阶段,而由着色器编译出来的最终代码是会在 GPU 上运行的(对于固定管线的渲染来说,着色器有时等同于一些特定的渲染设置)
<2> 有一些特定类型的着色器,如顶点着色器,片元着色器等
<3> 依靠着着色器我们可以控制流水线中的渲染细节,例如用顶点着色器来进行顶点变换以及传递数据,用片元着色器来进行顶点变换以及传递数据,用片元着色器来进行逐像素的渲染.
01.在 Unity 中,我们需要配合使用材质(Material)和 Unity Shader 才能达到需要的效果
最常见的流程:<1> 创建一个材质;
<2> 创建一个 Unity Shader,并把它赋给上一步中创建的材质;
<3> 把材质赋给要渲染的对象;
<4> 在材质面板中调整 Unity Shader 的属性,以得到满意的效果
01.一个单独的 Unity Shader 是无法发挥任何作用的,它必须和材质结合起来,才能发生神奇的"化学反应"!
02.Unity Shader 本质上就是一个文本文件.
01."计算机科学中的任何问题都可以通过增加一层抽象来解决."--大卫,惠勒
01.伪代码中我们见到了一些 ShaderLab 的语义,如 Properties,SubShader,Fallback 等.这些语义定义了 Unity Shader 的结构,从而帮助 Unity 分析该 Unity Shader 文件,以便进行正确的编译.
01.每个 Unity Shader 文件的第一行都需要通过 Shader 语义来指定该 Unity Shader 的名字.这个名字由一个字符串来定义.通过在字符串中添加斜杠("/"),可以控制 Unity Shader 在材质面板中出现的位置
01.Properties 语义块中包含了一系列属性(property),这些属性将会出现在材质面板中
02.声明这些属性是为了在材质面板中能够方便地调整各种材质属性.需要使用每个属性的名字(Name),在 Shader 中访问它们.这些属性的名字通常由一个下划线开始.
03.显示的名称(display name) :是出现在材质面板上的名字
04.类型(PropertyType) :需要为每个属性指定它的类型
图片来源于转载05.为了在 Shader 中可以访问到属性.我们需要在 CG 代码片中定义和这些属性类型相匹配的变量.
06.即使我们不在 Properties 语义块中声明这些属性,也可以直接在 CG 代码片中定义变量.
07.Properties 语义块的作用仅仅是为了让这些属性可以出现在材质面板中.
01.每一个 Unity Shader 文件可以包含多个 SubShader 语义块,但最少要有一个.
02.当 Unity Shader 被加载时, Unity 会扫描所有的 SubShader 语义块,然后选择第一个能够在目标平台上运行的 SubShader.如果都不支持的话,Unity 就会使用 Fallback 语义指定的 Unity Shader.
03.常见渲染状态设置选项
04.SubShader 的标签 : SubShader 的标签( Tags )是一个键值对(Key/Value Pair),它的键和值都是字符串类型.
05.标签的结构 以及 SubShader 的标签块支持的标签类型
06.UsePass:可以使用该命令来复用其他 Unity Shader 中的 Pass
07.GrabPass:该 Pass 负责抓取屏幕并将结果存储在一张纹理中,以用于后续的 Pass 处理
01.Fallback 指令,紧跟在各个 SubShader 语义块后面.它的作用是:如果上面所有的 SubShader 在这块显卡上都不能运行,那么就使用这个最低级的 Shader.
01.使用 CustomEditor 语义来扩展编辑界面
02.使用 Category 语义来对 Unity Shader 中的命令进行分组
Page 33