《Unity Shader入门精要》笔记02 第1章+第2章

本笔记基于《Unity Shader入门精要》基础篇的第1章和第2章,详细介绍了渲染流水线的各个阶段,包括应用阶段、几何阶段和光栅化阶段,以及CPU与GPU间的通信和GPU流水线的工作流程。内容涵盖顶点着色器、片元着色器的作用以及模板测试、深度测试、混合等逐片元操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基础篇 第1章+第2章

——本系列是基于人民邮电出版社《Unity Shader入门精要》(冯乐乐著 )的自学Unity Shader笔记,如果您发现了本文的纰漏,还望不吝指正。

基础篇

  • 第1章 欢迎来到Shader的世界
  • 第2章 渲染流水线
  • 第3章 Unity Shader基础
  • 第4章 学习Shader所需的数学基础

第1章 欢迎来到Shader的世界

有人说,程序员的三大浪漫是编译原理、操作系统和图形学

学习Shader对比学习C#这样的编程语言

  • 学习Shader需要牵扯到整个渲染流程,Shader只是整个渲染流程中的一个子部分,想要学习它,我们就需要了解整个渲染流程是如何进行的
  • Shader更多地是面向GPU的工作方式,所以它的一些语法对我们来说并不那么直观

第2章 渲染流水线

如果你没有了解过渲染流水线的工作流程,就永远无法说自己对Shader已经入门。

1. 什么是渲染流水线

使用流水线的好处在于可以提高单位时间的生产量

渲染流水线的工作任务:由一个三维场景出发、生成(或者说渲染)一张二维图像

渲染流程分成3个阶段:

  • 应用阶段 (Application Stage)
  • 几何阶段(Geometry Stage)
  • 光栅化阶段(Rasterizer Stage)

2-1

1.1 应用阶段 (Application Stage)

这一阶段最重要的输出是渲染所需的几何信息,即渲染图元(rendering primitives)。通俗来讲,渲染图元可以是点、线、三角面等。这些渲染图元将会被传递给下一个阶段——几何阶段

通常由CPU负责实现

3个主要任务:

  • 准备好场景数据
  • 粗粒度剔除(culling)工作
  • 设置好每个模型的渲染状态
1.2 几何阶段(Geometry Stage)

几何阶段的一个重要任务就是把顶点坐标变换到屏幕空间中,再交给光栅器进行处理。通过对输入的渲染图元进行多步处理后,这一阶段将会输出屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等相关信息,并传递给下一个阶段。

通常在GPU上运行

几何阶段负责和每个渲染图元打交道,进行逐顶点、逐多边形的操作。这个阶段可以进一步分成更小的流水线阶段

1.3 光栅化阶段(Rasterizer Stage)

光栅化的任务主要是决定每个渲染图元中的哪些像素应该被绘制在屏幕 上。它需要对上一个阶段得到的逐顶点数据(例如纹理坐标、顶点颜色等)进行插值,然后再进行逐像素处理。光栅化阶段也可以分成更小的流水线阶段。

通常在GPU上运行

这一阶段将会使用上个阶段传递的数据来产生屏幕上的像素,并渲染出最终的图像。

2. CPU和GPU之间的通信

从应用阶段到几何阶段,CPU输出渲染图元至GPU

应用阶段分为3个阶段:

  • 把数据加载到显存中
  • 设置渲染状态
  • 调用Draw Call
2.1 把数据加载到显存中

所有渲染所需的数据都需要从硬盘(HDD)中加载到系统内存(RAM)中。然后,网格和纹理等数据又被加载到显卡上的存储空间显存(VRAM)中

2.2 设置渲染状态

这些状态定义了场景中的网格是怎样被渲染的

2.3 调用Draw Call

Draw Call就是一个命令,它的发起方是CPU,接收方是GPU

这个命令仅仅会指向一个需要被渲染的图元(primitives)列表,,而不会再包含任何材质信息

3. GPU流水线

当GPU从CPU那里得到渲染命令后,就会进行一系列流水线操作,最终把图元渲染到屏幕上

GPU渲染的过程(几何阶段和光栅化阶段)就是GPU流水线

2-2
GPU的渲染流水线实现

每个阶段GPU提供了不同的可配置性或可编程性:

  • 绿色表示该流水线阶段是完全可编程控制的
  • 黄色表示该流水线阶段可以配置但不是可编程的
  • 蓝色表示该流水线阶段是由GPU固定实现的,开发者没有任何控制权。
  • 实线表示该shader必须由开发者编程实现,虚线表示该Shader是可选的
3.1 顶点着色器

接收由 应用阶段 加载到 显存(VRAM) 中,再由 Draw Call 指定的顶点数据作为输入

顶点着色器(Vertex Shader)是完全可编程的

顶点着色器完成:

  • 坐标变换(把顶点坐标从模型空间转换到齐次裁剪空间)
  • 计算和输出顶点的颜色(如逐顶点光照)
  • 还可以输出后续阶段所需的数据
    2-3

例:在顶点着色器中会看到类似下面的代码:

o.pos = mul(UNITY_MVP, v.position);	//把顶点坐标转换到齐次裁剪坐标系下

曲面细分着色器(Tessellation Shader)是一个可选的着色器,它用于细分图元。

几何着色器(Geometry Shader)同样是一个可选的着色器,它可以被用于执行逐图元的着色操作,或者被用于产生更多的图元。

3.2 裁剪

裁剪(Clipping)是可配置,不可编程的,是硬件上的固定操作,但我们可以自定义一个裁剪操作来对这一步进行配置

裁剪完成:

  • 将那些不在摄像机视野内的顶点裁剪掉,并剔除某些三角图元的面片

2-4
只有在单位立方体的图元才需要被继续处理。因此,完全在单位立方体外部的图元(红色三角形)被舍弃,完全在单位立方体内部的图元(绿色三角形)将被保留。和单位立方体相交的图元(黄色三角形)会被裁剪,新的顶点会被生成,原来在外部的顶点会被舍弃

3.3 屏幕映射

屏幕映射(Screen Mapping)是不可配置不可编程的

屏幕映射完成:

  • 把每个图元的坐标转换到屏幕坐标系(Screen Coordinates)中

屏幕坐标系是一个二维坐标系,它和Z坐标一起构成了一个窗口坐标系(Window Coordinates)。这些值会一起被传递到光栅化阶段。

2-5
屏幕映射将 x、y 坐标从(-1, 1)范围转换到屏幕坐标系中

*OpenGL 把屏幕的左下角当成最小的窗口坐标值,而DirectX则定义了屏幕的左上角为最小的窗口坐标值。如果发现得到的图像是倒转的,那么很有可能就是这个原因造成的。如下图所示:

2-6
在OpenGL中其(0, 0)点在左下角,而在DirectX中其(0, 0)点在左上角

3.4 三角形设置

三角形设置(Triangle Setup)是不可配置不可编程的

三角形设置完成:

  • 计算光栅化一个三角网格所需的信息
3.5 三角形遍历

三角形遍历(Triangle Traversal)是不可配置不可编程的

三角形遍历完成:

  • 检查每个像素是否被一个三角网格所覆盖

如果被覆盖的话,就会生成一个片元(fragment)。这个阶段也被称为扫描变换(Scan Conversion)

2-7
三角形遍历的过程,片元中的状态是对三个顶点的信息进行插值得到的

这一步的输出就是得到一个片元序列。需要注意的是,一个片元并不是真正意义上的像素, 而是包含了很多状态的集合,这些状态用于计算每个像素的最终颜色。这些状态包括了(但不限 于)它的屏幕坐标、深度信息,以及其他从几何阶段输出的顶点信息,例如法线、纹理坐标等。

3.6 片元着色器

片元着色器(Fragment Shader)是完全可编程的

片元着色器完成:

  • 输出是一个或者多个颜色值,完成很多重要的渲染技术,其中最重要的技术之一就是纹理采样

片元着色器仅可以影响单个片元

3.7 逐片元操作

逐片元操作(Per-Fragment Operations)是可配置,不可编程的

逐片元操作(Per-Fragment Operations)是OpenGL中的说法,在DirectX中,这一阶段被称为输出合并阶段(Output-Merger)

逐片元操作完成:

  • 决定每个片元的可见性
  • 如果一个片元通过了所有的测试,就需要把这个片元的颜色值和巳经存储在颜色缓冲区中的颜色进行合并,或者说是混合。

逐片元操作对每一个片元进行操作

2-8
逐片元操作阶段所做的操作:只有通过了所有的测试后,新生成的片元才能和颜色缓冲区中已经存在的像素颜色进行混合,最后再写入颜色缓冲区中(对于大多数GPU来说,它们会尽可能在执行片元着色器之前就进行这些测试 )

• 模板测试

模板测试通常用于限制渲染的区域。还有一些更高级的用法,如渲染阴影、轮廓渲染等。

• 深度测试

透明效果和深度测试以及深度写入的关系非常密切
2-9

• 混合

对于不透明物体,可以关闭混合(Blend)操作。

对于半透明物体,可以开启混合操作来让这个物体看起来是透明的

*混合很像 Photoshop中对图层的操作:每一层图层可以选择混合模式,混合模式决定了该图层和下层图层的混合结果,而我们看到的图片就是混合后的图片。

2-10

3.8 总结

虽然渲染流水线比较复杂,但Unity作为一个非常出色的平台为我们封装了很多功能。更多时候,我们只需要在一个Unity Shader设置一些输入、编写顶点着色器和片元着色器、设置一些状态就可以达到大部分常见的屏幕效果。

在学习过程中发现有些设置或代码无法理解,可以不断查阅本章内容,相信会有更深的理解。

4. 一些术语

4.1 OpenGL与DirectX

一个显卡制作商为了让他们的显卡可以同时和OpenGL、DirectX合作,就必须提供支持OpenGL和DirectX接口的显卡驱动。

OpenGL和DirectX都是图像应用编程接口,它们在硬件的基础上实现了一层抽象,架起了上层应用程序和底层GPU的沟通桥梁

DirectX是微软的多媒体编程接口,在Windows的平台下,配合支持DX的高端显卡能把游戏场景的特效等等发挥得淋漓尽致,而OpenGL是一个跨平台的编程接口,是硬件无关的编程接口。

2-9
CPU、OpenGL/DirectX、显卡驱动 和 GPU 之间的关系

1).我们的应用程序运行在CPU上

2).应用程序可以通过调用OpenGL或DirectX的图形接口将渲染所需的数据,存储在显存中的特定区域

3).开发者可以通过图像编程接口发出渲染命令(DrawCall),它们将会被显卡驱动翻译成GPU能够理解的代码,进行真正的绘制

4.2 HLSL、GLSL、CG

着色语言(Shading Language)是专门用于编写着色器的,常见的着色语言有 DirectX 的 HLSL (High Level Shading Language)、OpenGL 的 GLSL (OpenGL Shading Language)以及 NVIDIA 的 CG (C for Graphic)

  • GLSL
    优点:跨平台性,它可以在Windows、Linux、Mac甚至移动平台等多种平台上工作
    缺点:GLSL 是依赖硬件,而非操作系统层级的,不同硬件对GLSL的编译实现不尽相同,这可能会造成编译结果不一致的情况
  • HLSL
    优点:不同的硬件,同一个着色器的编译 结果也是一样的(前提是版本相同)
    缺点:平台相对比较有限,几乎完全是微软自已的产品,如Windows、Xbox 360、PS3等
  • CG
    优点:真正意义上的跨平台。它会根据平台的不同,编译成相应的中间语言
    缺点:可能无法完全发挥出OpenGL的最新特性
4.3 Draw Call

Draw Call本身的含义很简单,就是CPU调用图像编程接口,如 OpenGL 中的 glDrawElements 命令或者 DirectX 中的 DrawIndexedPrimitive 命令,以命令GPU进行渲染的操作。

Draw Call中造成性能问题的是CPU

  • CPU和GPU实现并行工作
    使用一个命令缓冲区(Command Buffer)

2-10
命令缓冲区: CPU通过图像编程接口向命令缓冲区中添加命令,而GPU从中读取命令并执行。命令缓冲区中的命令有很多种类,而Draw Call是其中一种。黄色方框内的命令就是Draw Call,而红色方框内的命令用于改变渲染状态。

	命令缓冲区包含了一个命令队列,由CPU向其中添加命令,而由GPU从中读取命令,添加和读取的过程是互相独立的
  • Draw Call多了会影响帧率

     在每次调用Draw Call之前,CPU需要向GPU发送很多内容,
     而GPU的渲染能力是很强的,因此渲染速度往往快于CPU提交命令的速度。
     如果Draw Call的数量太多,CPU就会把大量时间花费在提交Draw Call上,造成CPU的过载
    
  • 如何减少Draw Call
    批处理(Batching)——把很多小的DrawCall合并成一个大的Draw Call(更加适合于那些静态的物体)

     避免使用大量很小的网格。当不可避免地需要使用很小的网格结构时,考虑是否可以合并它们。	
     避免使用过多的材质。尽量在不同的网格之间共用同一个材质。
    

2-11
利用批处理,CPU在RAM把多个网格合并成一个更大的网格,再发送给GPU,然后在一个Draw Call中渲染它们。但要注意的是,使用批处理合并的网格将会使用同一种渲染状态。也就是说,如果网格之间需要使用不同的渲染状态,那么就无法使用批处理技术

4.4 固定管线渲染

固定函数的流水线(Fixed-Function Pipeline),也简称为固定管线,通常是指在较旧的GPU上实现的渲染流水线。这种流水线只给开发者提供一些配置操作,但开发者没有对流水线阶段的完全控制权。

固定管线着色器是很古老显卡的编程着色器,现在很少使用,画质不好

扩展阅读

OpenGL和DirectX的渲染流水线的实现细节:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值