UnityShader——基础篇之渲染流水线

渲染流水线

综述

什么是渲染流水线

  渲染流水线的工作任务在于由一个三维场景出发、生成(或者说渲染)一张二维图像,换句话说,计算机需要从一系列的顶点数据、纹理等信息出发,把这些信息最终转换成一张人眼可以看到的图像,而这个工作原理通常是由CPU和GPU共同完成的

  《Real-Time Rendering, Third Edition》中讲一个渲染流程分成3个阶段:应用阶段、集合阶段、光栅化阶段

在这里插入图片描述

  • 应用阶段
    • 这个阶段是由应用主导的,因此通常由CPU负责实现,开发者具有这个阶段的绝对控制权
    • 在这个阶段,开发者有三个主要任务:
      • 首先,准备好场景数据,例如摄像机的位置、视椎体,场景中包含了哪些模型、使用了哪些光源等等
      • 其次,为了提高渲染性能,往往需要做一个粗粒度剔除(culling)工作,以把那些不可见的物体剔除出去,这样就不需要再移交给几何阶段进行处理
      • 最后,设置好每个模型的渲染状态,这些渲染状态包括但不限于它使用的材质(漫反射颜色、高光反射颜色)、使用的纹理、使用的Shader等
    • 这个阶段最重要的输出是渲染所需的几何信息,即渲染图元,通俗来讲,渲染图元可以是点、线、三角面等,这些渲染图元会被传递给下一个阶段——集合阶段
    • 由于由开发者主导这个阶段,因此应用阶段的流水线化是由开发者决定的,可以参考以下阅读物:
      • Akenine-Möller T,Haines E,Hoffman N.Real-time rendering[M].CRC Press,2008
      • Wloka M.Batch,Batch,Batch:What does it really mean?[C]//Presentation at game developers conference 2003
  • 几何阶段
    • 用于处理所有要回值得几何相关的事情,例如,决定需要绘制的图元是什么,怎样绘制它们,这一阶段通常由GPU负责
    • 几何阶段负责和每个渲染图元打交道,进行逐顶点、逐多边形的操作,这个阶段可以进一步分成更小的流水线阶段
    • 几何阶段的一个重要任务是把顶点坐标变换到屏幕空间中,再交给光栅器进行处理。通过对输入的渲染图元进行多步处理后,这一阶段将会输出屏幕空间的二位顶点坐标,每个顶点对应的深度值、着色等相关信息,并传递给下一个阶段
  • 光栅化阶段
    • 这一阶段将会使用上个阶段传递的数据来产生屏幕上的像素,并渲染出最终的图像
    • 和上一个阶段类似,光栅化阶段也可以分成更小的流水线阶段

CPU和GPU之间的通信

渲染流水线的起点是CPU,即应用阶段。应用阶段大致可分为下面3个阶段

  1. 把数据加载到显存中
  2. 设置渲染状态
  3. 调用Draw Call

把数据加载到显存中

  所有渲染所需的数据都需要从硬盘 (Hard Disk Drive,HDD) 中加载到系统内存 (Random Access Memory, RAM) 中,然后,网格和纹理等数据又被加载到显卡上的存储空间——显存 (Video Random Access Memory, VRAM) 中。这是因为,显卡对于先存档访问速度更快,而且大多数显卡对于RAM没有直接的访问权限

在这里插入图片描述

  需要注意的是,真是渲染中需要加载到显存中的数据往往比上图所示复杂很多,例如:顶点的位置信息、法线方向、顶点颜色、纹理坐标等

  当把数据加载到显存中后,RAM中的数据就可以移除了,但对于一些数据来说,CPU仍然需要访问他们(例如,我们希望CPU可以访问网络数据来进行碰撞检测),那么可能就不希望这些数据被移除,因为从硬盘加载到RAM的过程是十分耗时的

  在这之后,开发者还需要通过CPU来设置渲染状态,从而“指导”GPU如何进行渲染工作

设置渲染状态

  通俗解释为:这些状态定义了场景中的网络是怎样被渲染的。例如:使用哪个顶点着色器(Vertex Shader)/片元着色器(Fragment Shader)、光源属性、材质等
  如果没有改变渲染状态,那么所有的网格都将使用同一种渲染状态

在这里插入图片描述

  在准备好上述所有工作后,CPU就需要调用一个渲染命令来告诉GPU数据已经准备好,按照设置进行渲染,这个渲染命令就是Draw Call

调用Draw Call

  Draw Call就是一个命令,他的发起方是CPU,接收方是GPU,这个命令仅仅会指向一个需要被渲染的图元(primitives)列表,而不会再包含任何材质信息——这是因为我们在上一个阶段中完成了

在这里插入图片描述

  当给定了一个Draw Call时,GPU就会根据渲染状态(例如材质、纹理、着色器等)和所有输入的顶点数据来进行计算,最终输出成屏幕上显示的那些票联的像素,而这个计算过程,就是GPU流水线

GPU流水线

概述

  几何阶段和光栅化阶段可以分成若干更小的流水线阶段,这些流水线阶段由GPU来实现,每个阶段GPU提供了不同的可配置型或可编程性,如图:

在这里插入图片描述

  从图中可以看出,GPU的渲染流水线接受顶点数据作为输入,这些顶点数据是由应用阶段加载到显存中,再由Darw Call指定的,这些数据随后被传递给顶点着色器

  顶点着色器Vertex Shader)是完全可编程的,它通常用于实现顶点的空间变换、顶点着色等功能

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

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

  裁剪Clipping),这一阶段的目的是讲那些不在摄像机视野的顶点裁剪掉,并剔除某些三角图源的面片,这个阶段是可配置的,例如,使用自定义的裁剪平面来配置裁剪区域。也可以通过指令控制裁剪三角图元的正面还是背面

  屏幕映射Screen Mapping),这一阶段是不可配置和编程的,它负责把每个图元的坐标转换到屏幕坐标系中

  三角形设置Triangle Setup)和三角形遍历Triangle Traversal)阶段也都是固定函数(Fixed-Function)的阶段

  片元着色器Fragment Shader)则是完全可编程的,它用于逐片源(Per-Fragment)的着色操作

  逐片源操作Per-Fragment Operation)阶段负责执行很多重要的操作,例如修改颜色、深度缓冲、进行混合等,他不是可编程的,但具有很高的可配置性

顶点着色器

  顶点着色器(Vertex Shader)是流水线的第一个阶段,他的输入来自于CPU顶点着色器的处理单位是顶点,也就是说,输入进来的每个节点都会调用一次顶点着色器

  顶点着色器本身不可以创建或者销毁任何顶点,而且无法得到顶点和顶点之间的关系

  顶点着色器需要完成的工作主要有:坐标变换和逐顶点光照,除了这两个主要任务外,顶点着色器还可以输出后续阶段所需的数据,如下图,展示了在顶点着色器中对定点位置进行坐标变换并计算顶点颜色的过程

在这里插入图片描述

  坐标变换,就是对顶点的坐标(即位置)进行某种变换。顶点着色器可以在这一步中改变顶点的位置,这在顶点动画中是非常有用的,比如可以通过改变顶点位置模拟水面,布料等

  注意:无论在顶点着色器中怎样改变顶点的位置,一个最基本的顶点着色器必须完成的一个工作——把顶点坐标从模型空间转换到齐次裁剪空间

  像顶点着色器中的代码

o.pos = mul(UNITY_MVP,v.position);

  这句代码的功能,就是把顶点坐标转换到齐次裁剪坐标系下,接着通常再由硬件做透视除法后,最终得到归一化的设备坐标(Normalized Device Coordinates,NDC),如下图示例,展示了这样的转换过程

在这里插入图片描述

  注意

  • 图中给出的坐标范围是OpenGL同时也是Unity使用的NDC,他的z分量范围在[-1,1]之间
  • 在DirectX中,NDC的z分量范围是[0,1]。
  • 顶点着色器可以有不同的输出方式。最常见的输出路径是光栅化之后交给片元着色器进行处理
  • 在现代的Shader Model中,它还可以把数据发送给曲面细分着色器或几何着色器

裁剪

  因为摄像机的视野范围很有可能不会覆盖所有的场景物体,所以,那些不在摄像机视野范围的物体不需要被处理,所以裁剪被提出来了

  一个图元和摄像机视野的关系有3种:完全在视野内、部分在视野内、完全在视野外、

  • 完全在视野内的图元就继续传递给下一个流水线阶段
  • 完全在视野外的图元不会继续向下传递,因为它们不会被渲染
  • 部分在视野内的图元需要进行一个处理,这就是裁剪

  由于已知在NDC下的定点位置,即定点位置在一个立方体内,因此裁剪就变得很简单:只需要将图元裁剪到单位立方体内,如图

在这里插入图片描述

  和顶点着色器不同,这一步是不可编程的,即无法通过编程来控制裁剪的过程,而是硬件的固定操作,但可以自定义一个裁剪操作来对这一步进行配置

屏幕映射

  这一步输入的坐标是三维坐标系下的坐标(范围在单位立方体内)

  屏幕映射Screen Mapping)的任务是把每个图元的x和y坐标转换到屏幕坐标系Screen Coordinates)下,屏幕坐标系使一个二维坐标系,它用于显示画面的分辨率有很大关系

  原理:

    假设,需要把场景渲染到一个窗口上,窗口的范围是从最小的窗口坐标( x 1 , y 1 x_1,y_1 x1,y1)到最大的窗口坐标( x 2 , y 2 x_2,y_2 x2,y2),其中 x 1 < x 2 x_1<x_2 x1<x2 y 1 < y 2 y_1<y_2 y1<y2。由于输入的坐标范围在 -1 到 1,因此,这个过程是一个缩放的过程,如图所示:

在这里插入图片描述

  输入的z坐标会怎么样?

    屏幕映射不会对输入的z坐标做任何处理。实际上,屏幕坐标系和z坐标一起构成了一个坐标系叫做窗口坐标系Window Coordinates),这些值会一起被传递到光栅化阶段

  屏幕映射得到的屏幕坐标决定了这个顶点对应屏幕上哪个像素以及距离这个像素有多远

  注意:

    屏幕坐标系在OpenGL和DirectX之间的差异问题。OpenGL把屏幕的左下角当成最小的窗口坐标值,而DirectX则定义了屏幕的左上角为最小的窗口坐标值,如图:

在这里插入图片描述

    造成这种差异的原因:微软的窗口都使用了这样的坐标系统,因为这和我们的阅读方式是一致的:从左到右、从上到下,而且很多图像文件也是按照这样的格式进行存储的

三角形设置

  从这一步开始进入光栅化阶段,光栅化的阶段有两个最重要的目标:计算每个图元覆盖了哪些像素,以及为这些像素计算它们的颜色

  光栅化阶段的第一个流水线阶段是三角形设置Trangle Setup)。这个阶段会计算光栅化第一个三角网格所需的信息。具体来说,上一个阶段输出的都是三角网格的顶点,即得到的是三角网格每条边的两个端点。但如果要得到整个三角网格对像素的覆盖情况,就必须计算每条边上的像素坐标

  为了能够计算边界像素的坐标信息,就需要得到三角形便捷的表示方式,一个计算三角网格表示数据的过程就叫做三角形设置

  它的输出是为了给下一个阶段做准备

三角形遍历

  三角形遍历Triangle Traversal)阶段将会检查每个像素是否被一个三角网格所覆盖,如果覆盖的话,就会生成一个片元fragment

  这样找到哪些像素被三角网格覆盖的过程就是三角形遍历

  这个阶段也被称为扫描变换Scan Conversion

  三角形遍历阶段会根据上一个阶段的计算结果来判断一个三角网络覆盖了哪些像素,并使用三角网格3个顶点的顶点信息对整个覆盖区域的像素进行差值,如图所示:

在这里插入图片描述

  这一步的输出就是得到一个片元序列

  注意:

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

片元着色器

  片元着色器Fragment Shader)是另一个非常重要的可编程着色器阶段。在DirectX中,片元着色器被称为像素着色器Pixel Shader),但片元着色器使一个更合适的名字,因为此时的片元并不是一个真正意义上的像素

  光栅化阶段市集上并不会影响屏幕上每个像素的颜色值,而是会产生一系列的数据信息,用来表达一个三角网络是怎样覆盖每个像素的。而每个片元就负责存储这样一系列数据。真正会对像素产生影响的阶段是逐片元操作Per-Fragment Operations

  片元着色器的输入是上一个阶段对顶点信息差值得到的结果,更具体来说,是根据那些从顶点着色器中输出的数据差值得到的。而他的输出是一个或者多个颜色值,如图所示:

在这里插入图片描述

  这一阶段可以完成很多重要的渲染技术,其中最重要的技术之一就是纹理采样

  为了在片元着色器中进行纹理采样,通常会在顶点着色器阶段输出每个顶点对应的纹理坐标,然后经过光栅化阶段对三角网格的3个顶点对应的纹理坐标进行差值之后,就可以得到其覆盖的片元的纹理坐标

  局限:

  • 它仅可以影响单个片元。也就是说,当执行片元着色器时,他不可以将自己的任何结果直接发送给它的邻居们
  • 有一个情况例外,片元着色器可以访问到导数信息(gradient,或者说是derivative)

逐片元操作

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

  目的:合并

  这一阶段有几个主要任务:

  • 决定每个片元的可见性。这涉及了很多测试工作,例如深度测试、模板测试等
  • 如果一个片元通过了所有的测试,就需要把这个片元的颜色值和已经存储在颜色缓冲区中的颜色进行合并,或者说是混合

  说明:

  • 逐片元操作阶段是高度可配置性的,即可以设置每一步的操作细节

  这个阶段首先需要每个片元的可见性问题。这需要进行一系列测试。如图所示:

在这里插入图片描述

  测试的过程实际是个比较复杂的过程,而且不同的图形接口(例如OpenGL和DirectX)的实现细节也不尽相同

  两个最基本的测试——深度测试和模板测试的实现过程,如图所示:

在这里插入图片描述

  模板测试Stencil Test),与之相关的是模板缓冲。实际上,模板缓冲和颜色缓冲、深度缓冲几乎是一类东西。如果开启了模板测试,GPU会首先读取(使用读取掩码)模板缓冲区中该片元位置的模板值,然后将该值和读取(使用读取掩码)到的参考值(reference value)进行比较,这个比较函数可以由开发者指定的

  例如小于时舍弃该片元,或者大于等于时舍弃该片元。如果这个片元没有通过测试,该片元就会被舍弃。

  不管一个片元有没有通过模板测试,都可以根据模板测试和下面的深度测试结果来修改模板缓冲区,这个修改操作也是由开发者指定的。开发者可以设置不同结果下的修改操作

  例如,在失败时模板缓冲区保持不变,通过时将模板缓冲区中对应位置的值加1等。

  模板测试通常用于限制渲染的区域。

  模板测试更高级的用法,如渲染阴影、轮廓渲染等


  深度测试Depth Test),如果一个片元通过了模板测试,那么就会进行深度测试。这个测试是可以高度配置的

  如果开启了深度测试,GPU会把该片元的深度值和已经存在于深度缓冲区中的深度值进行比较。这个比较函数也是可由开发者设置的,例如小于时舍弃该片元,或者大于等于时舍弃该片元。通常这个比较函数是小于等于的关系,即如果这个片元的深度值大于等于当前深度缓冲区的值,那么就会舍弃它

  因为只显示出离摄像机最近的物体,而那些被其他物体遮挡的就不需要出现在屏幕上,如果这个片元没有通过这个测试,它就会被舍弃。

  和模板测试不同的是,如果一个片元没有通过深度测试,他就没有权利更改深度缓冲区的值,如果它通过了测试,开发者还可以指定是否要用这个片元的深度值覆盖掉元用的深度值,这是通过开启/关闭深度写入来做到的

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


  合并功能,如果片元通过了上面所有测试,它就可以来到合并功能

  为什么需要合并?

  • 这里所讨论的渲染过程使一个物体接着一个物体划到屏幕上的。而每个像素的颜色信息被存储在一个名为颜色缓冲的地方
  • 因此,当执行这次渲染时,颜色缓冲中往往已经有了上次渲染之后的颜色结果
  • 是使用这次渲染的道德颜色完全覆盖掉之前的结果还是进行其他处理,这就是合并要处理的问题

  混合操作,对于不同命物体,开发可以关闭混合操作,这样片元着色器计算得到的颜色值就会直接覆盖掉颜色缓冲区中的像素值。

  对于半透明物体,就需要混合操作来让这个物体看起来是透明的,流程如图所示:

在这里插入图片描述

  混合操作也可以高度配置:开发者可以选择开启/关闭混合功能

  如果没有开启混合功能,就会直接使用片元的颜色覆盖掉颜色缓冲区的颜色,而这也是无法得到透明效果的原因

  如果开启了混合,GPU会去除源颜色和目标颜色,将两种颜色进行混合。源颜色指的是片元着色器得到的颜色值,而目标颜色则是已经存在于颜色缓冲区中的颜色值,之后就会使用一个混合函数来进行混合操作,这个混合操作函数通常和透明通道息息相关,例如根据透明通道的值进行相加、相见、相乘等


  以上的测试顺序并不是唯一的,虽然从逻辑上来说这些测试是在片元着色器之后进行的,但对于大多数GPU来说,它们会尽可能在执行片元着色器之前就进行这些测试

  当GPU在片元着色器终于计算出片元的颜色之后,结果发现这个片元根本没有通过检验,也就是说这个片元被舍弃了,就浪费了计算成本,如图所示

在这里插入图片描述

  在Unity给出的渲染流水线中,他给出的深度测试是在片元着色器之前,这种将深度测试提前执行的技术通常也被称为Eraly - Z技术

  如果将这些测试提前,其姜堰结果可能会与片元着色器中的一些操作冲突

  比如:如果在片元着色器进行了透明度测试,而这个片元没有通过透明度测试,会在着色器中调用API来手动将其舍弃掉,这样就会导致GPU无法提前执行各种测试

  因此,现代的GPU会判断片元着色器中的操作是否和提前测试发生冲突,如果有冲突,就会禁用提前测试,这样的话,性能就会下降,因为有更多的片元需要处理了


  当模型的图元经过了上面所有的计算和测试后,就会显示到屏幕上,屏幕显示的就是颜色缓冲区的颜色值

  但是,为了避免看到那些正在进行光栅化的图元,GPU会使用双重缓冲Double Buffering)的策略,这意味着,对场景的渲染是在幕后发生的,即在后置缓冲Back Buffer)中,一旦场景已经被渲染到了后置缓冲中,GPU就会交换后置缓冲区和前置缓冲Front Buffer)中的内容,而前置缓冲区是之前显示在屏幕上的图像。

  因此保证了看到的图像是连续的

容易困惑的地方

什么是OpenGL / DirectX

  如果要开发者直接访问GPU是一件非常麻烦的事情,可能需要和各种寄存器、显存打交道,而图像编程接口在这些硬件的基础上实现了这一层抽象

  OpenGL和DirectX就是这些图像应用编程接口,这些接口用于渲染二维或三维图形,架起了上层应用程序和底层GPU的沟通桥梁

  一个应用程序向这些接口发送渲染命令,而这些接口会依次向显卡驱动(Graphics Driver)发送渲染命令,这些显卡驱动是真正知道如何和GPU通信的角色,正是它们把OpenGL或者DirectX的函数调用翻译成了GPU能够听懂的语言,同时它们也负责把纹理等数据转换成GPU所支持的格式,如图所示:

在这里插入图片描述

  由图可以看出,应用程序可以通过调用OpenGL或DirectX的图形接口将渲染所需的数据,如顶点数据、纹理数据、材质参数等数据存储在显存中的特定区域。随后,开发者可以通过图像编程接口发出渲染命令,这些渲染命令也被称为Draw Call,他们将会呗显卡驱动翻译成GPU能够理解的代码,进行真正的绘制

  一个显卡除了有图像处理单元GPU外,还拥有自己的内存,这个内存通常被称为显存Video Random Access Memory,VRAM)。GPU可以在显存中存储任何数据,但对于渲染来说一些数据类型是必须的,例如用于屏幕显示的图像缓冲、深度缓冲等

  因为显卡驱动的存在,几乎所有的GPU都既可以和OpenGL合作,也可以和DirectX一起工作。从显卡的角度出发,市集上他只需要和显卡驱动打交道就可以了

  显卡驱动负责和图像编程接口以及GPU打交道。

什么是HLSL、GLSL、Cg

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

  这些语言都是高级语言,但只是对于汇编语言来说的,而不像是C#对于C的高级。

  这些语言会被编译成与机器无关的汇编语言,也被称为中间语言(Intermediate Language,IL),这些中间语言再交给显卡驱动来翻译成真正的机器语言,即GPU可以理解的语言


  GLSL的有点在于它的跨平台性,可以在Windows、Linux、Mac甚至移动平台等多种平台上工作,这种跨平台性是因为OpenGL没有提供着色器编译器,而是由显卡驱动来完成着色器的编译工作

  只要显卡驱动支持对GLSL的编译它就可以运行

  好处在于:GLSL完全依赖硬件,而非操作系统层级的。

  但这也意味着GLSL的编译结果将取决于硬件供应商,但是不同的硬件对GLSL的编译实现不尽相同,这可能会造成编译结果不一致的情况,因为这完全取决于供应商的做法


  HLSL,是由微软控制的着色器的编译,就算使用了不同的硬件,同一个着色器的编译结果也是一样的(前提是版本相同)

  但是,支持HLSL的平台相对比较有限,几乎完全是微软自己的产品,如Windows、Xbox 360等

  原因就是在其他平台上没有可以编译HLSL的编译器


  Cg是真正意义的跨平台,他会根据平台的不同,编译成相应的中间语言。

  Cg语言的跨平台性很大原因取决于与微软的合作,这也导致Cg语言的语法和HLSL非常相像,Cg语言可以无缝移植成HLSL代码

  缺点是可能无法发挥出OpenGL的最新特性

什么是Draw Call

  Draw Call本身的含义很简单,就是CPU调用图像编程接口,以命令GPU进行渲染的操作

  一个误区是:Draw Call中造成性能问题的是GPU,认为GPU上的状态切换是耗时的,其实不是,真正造成这个原因的是CPU

CPU和GPU是如何实现并行工作的?

  如果没有流水线化,那么CPU需要等到GPU完成上一个渲染任务才能再次发送渲染命令,但这种方法会造成效率低下

  因此,需要让CPU和GPU可以并行工作,而解决方法就是使用一个命令缓冲区Command Buffer

  命令缓冲区包含了一个命令队列,向CPU向其中添加命令,而由GPU从中读取命令,添加和读取的过程是相互独立的。

  命令缓冲区使得CPU和GPU可以像话独立工作,当CPU需要渲染一些对象,她可以向命令缓冲区中添加命令,而当GPU完成了上一次的渲染任务后,它就可以从命令队列中再取出一个命令并执行

  命令缓冲区中的命令有很多种,Draw Call只是其中一种,其他命令还有改变渲染状态等(例如改变使用的着色器,使用不同的纹理等)。如图

在这里插入图片描述

为什么Draw Call多了会影响速率

  在每次调用Draw Call之前,CPU需要向GPU发送很多内容,包括数据、状态和命令等

  在这一阶段,CPU需要完成很多工作,例如检查渲染状态等,而一旦完成了这些工作,GPU就可以开始本次的渲染,而GPU的渲染能力是很强的,因此渲染速度往往快鱼CPU提交命令的速度

  如果Draw Call数量太多,CPU就会把大量的时间花费在提交DrawCall上,造成CPU的过载,如图

在这里插入图片描述

如何减少Draw Call

  批处理Batching),把很多小的Draw Call合并成一个大的Draw Call,这就是批处理的思想,如图

在这里插入图片描述

  需要注意的是,由于需要在CPU的内存中合并网格,合并的过程是需要消耗时间的

  批处理技术更加适合于那些静态的物体,例如大地、石头等,对于这些静态物体只需要合并后一次即可

  批处理也可以对动态物体进行处理,但是这些物体是不断运动的,每一帧都需要重新合并然后发送给GPU,这对空间和时间都会造成一定的影响

  游戏开发过程中,为了减少Draw Call的开销,有两点需要注意

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

什么是固定管线渲染

  固定函数的流水线Fixed-Function Pipeline),也简称为固定管线,通常是指在较旧的GPU上实现的渲染流水线

  这种流水线只给开发者提供一些配置操作,但开发者没有对流水线阶段的完全控制权

  固定管线通常提供了一系列接口,这些接口包含了一个函数入口点(Function Entry Points)集合,这些函数入口点会匹配GPU上的一个特定的逻辑功能。开发者通过这些接口来控制渲染流水线,也就是说,固定渲染管线是只可配置的管线

  3中最常见的图像接口从固定管线向可编程管线进化的版本

3D API最后支持固定管线的版本第一个支持可编程管线的版本
OpenGL1.52.0
OpenGL ES1.12.0
DirectX7.08.0
  • 20
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值