Real-Time Rendering 第三章 GPU架构以及着色器

从历史上来讲,图形加速开始于扫描三角形覆盖的像素,并显示出像素差值的颜色,也就是GPU真正用力的地方,包括访问图片的数据,并把材质的数据应用到物体表面,现在添加的一些用于差值和z-test的硬件提供了内置的功能,来检测该像素的可见性,因为这些功能经常使用,所以交给指定的硬件,专门来处理,依次来提高性能.渲染管线中的很多功能都对应了特定的图形处理硬件 ,所以要比CPU的速度更快

第一代消费级的GPU芯片,包括顶点处理是英伟达在1999年发布的的GeForce256(NVIDIA’s GeForce256) ,并且发明了一个术语 graphics processing unit (GPU),用来和之前的芯片区分

GPU 处理的速度快,得益于它可以高度并行处理任务,它有自定义的硅单元专门用于执行z-buffer,能够快速的访问贴图的数据和其它的一些缓存数据(buffer),并找到三角形覆盖的像素。

 一个 shader core(渲染核)是一个很小的 处理器processor ,可以执行单独的任务,比如把顶点的位置从局部坐标转换到屏幕坐标,或者计算片元的颜色,每一帧都有成千上万个三角形被传递到屏幕上,每一秒都有上百万个shader 的调用, 也就是说,运行的是shader program的单独实例,每一个shader的调用,都是一个shader的实例

延迟是所有处理器processors需要面对的问题,访问数据需要大量的时间,要访问的数据离处理器越远,延迟就越长,存在内存中的数据要比存在本地中的数据访问起来慢,访问延迟,也就是等待数据返回的时候,处理器是停滞的,这降低了处理器的性能

3.1 Data-Parallel Architectures 数据并行处理架构

不同的处理器有不同的策略来降低延迟

比如,CPU 有多个处理器,用来处理各种各样的的数据架构和大型代码库,但是每一个处理器都是以串行的方式处理,后来有了SIMD(single instruction mutiply data)

为了进一步缩小延迟,很多CPU芯片都有本地缓存,把下次要访问的数据,放在缓存中;还有一些其它的方式,比如branch prediction、instruction reordering、register renaming、 cache prefetching [715]

GPU采用了不同的方法,GPU中包含了成千上万个处理器,也叫做渲染核(shader core),GPU是一个流处理器 stream processor,依次处理类似的数据, similar data 类似的数据,比如顶点,像素,GPU可以大规模并行处理这些数据,另一个很重要的元素是这些处理之间尽可能的保持独立,这样就不需要访问邻近像素或顶点的数据,也不用共享可写内存位置,但是有时会需要访问邻近点的数据,这样就会产生潜在的延迟,因为该处理器需要等待另一个处理器完成处理.

吞吐量(throughput)用来衡量GPU处理数据的速率,快速的处理数据是有代价的,因为很少有GPU芯片可以缓存数据和控制逻辑,所以通常每一个shader core 的延迟要比CPU还要高.

GPU 为了优化数据访问的速度,采用了寄存器的方式。shader中的一些算术运算可以在使用寄存器中的值

Registers 是本地数据,可以快速访问,没有延迟,比如,一个模型需要被绘制到屏幕上,需要知道像素中,贴图的颜色.贴图是一个完全独立的数据源,不是片元着色器本地内存的一部分,并且访问贴图是很经常的事,从内存中读取数据是一个缓慢的过程,这是GPU就会陷入停滞,直到返回贴图的颜色值

为了解决这个,我们为每一个片元在本地寄存器中提供一些存储空间,这样处理器就不用等待返回了,而是处理下一个片元,转向下一个处理器是非常快的. 第二个片元也会遇到同样的问题,然后用同样的解决方法,最终所有的片元都被执行完了,这时第一个片元的颜色值返回了,这样着色器就能继续处理了。

这种架构大大缩短了GPU的延迟时间,GPU通过把执行逻辑和数据分离,进一步优化了这种设计,这种方式叫做(SIMD), 也就在是固定数量的着色器上,执行相同的命令, SIMD 的优点就是可以用更少的硅单元处理数据和转换,用我们现在的术语说,每一个片元着色器的调用都是一个thread,它和CPU的线程不一样,它包含一些输入值所占的内存和本地寄存器的内存.  使用同一个shader program 的线程被绑定到同一个组group内,英伟达称之为warps,一个warps 通常由8到64个shader core 来执行,如果使用SIMD-processing处理 。每一个thread映射到一个 SIMD 通道

thread->warps->SIMD(8-64)shader core(processor),也就是由原来的一对一变成了一对多

假如我们有2000个thread,英伟达的GPU上的一个warps.包含32个线程,则需要  2000/32 = 62.5 个warps,warps的执行就和processor执行差不多,shader program 片元着色器在32个处理器上同时执行,当同时需要查找内存数据的时候,warps会切换到下一个warps,而不是陷入停滞

warps切换要比我们单一处理器的切换要快,因为在切换的时候,线程上没有数据的访问,每一个线程都有自己的寄存器,每一个warp都会记录当前正在执行的指令,切换只是把当前的shader core 指向另一组线程,直到所有的warps执行完.

在上面的简单例子中,从内存中访问贴图的数据可能会造成warp的切换,在现实中,warp的切换可能在更短的延迟内就发生了,因为切换的代价实在是太小了,所以可以很频繁,当然还有其它的技术来优化执行,但是 warp-swapping 是所有GPU最主要的缩短延迟的方法 ,这其中有几个元素会影响处理器的速度,比如如果有很少的thread 需要执行,相对应的warps也会很少,这样就会增加延迟的时间,所以这个GPU的能力也有关,需要硬件支持更多的thread

着色器程序的架构也是影响效率的一个重要的因素,一个主要的因素就是一个线程可以拥有的寄存器的数量,上面的例子中,我们假设GPU可以同时执行2000个线程,与每个线程关联的着色程序需要的寄存器越多,驻留在GPU中的线程就越少,因此warp也就越少,因此延迟也越高, 常驻GPU的warp的数量称为占有率 occupancy.占有率越高,代表有更多的线程可以去处理数据,这样空闲就没有空闲的线程了,低占有率表示性能也越低, 也就是GPU的线程总数是一定的,你用不用都是那些,要尽可能的把这些线程全利用起来,每一个线程的寄存器越多,则需要的线程就少了,空闲的线程就会增多. 第二个影响延迟因素就是 访问内存的频率,不访问最快

另一个影响整体效率的因素就是动态分支dynamic branching, 通过声明if 或者循环,比如:如果着色器程序遇到了一个if,如果所有的线程都进入同一个分支,wrap 可以继续进行,因为所有的线程的进度都是一样的,但是,如果有一些线程,进入了不同的分支,这个warp 就会执行两个分支,然后丢弃其中一个线程不需要的结果,这个问题叫做线程分散thread divergence,当一小部分线程需要执行循环或者需要执行if分支,而其它线程不需要时, 这样其它的线程就会停滞

所有gpu都实现了这些架构思想,导致系统受到严格的限制,但GPU的计算能力却是巨大的.

上图是整个流水线的流程

3.2 GPU Pipeline Overview 流水线预览

GPU 实现了 geometry processing、rasterization、pixel processing 阶段,它们又被分为几个硬件阶段,每个阶段具有不同程度的可配置性或可编程性.

我们在这里描述的是GPU的逻辑模型,作为程序员的你可以通过API看到它. 至于逻辑模型也就是物理模型的实现,取决于硬件供应商. 在logical model 逻辑模型中,具有固定功能的阶段,是通过向邻近的可编程阶段添加命令来执行的,渲染管线中单独的程序,会被划分成被多个子单元执行的元素,或者完全被一个单独的pass 执行.  logical model 可以帮助我们理解影响性能的原因,但它不是GPU实现管线的方式

顶点着色器完全可编程

clipping、triangle setup、 triangle traversal通过拥有固定功能的硬件执行. Screen mapping 受窗口和视口设置的影响,

pixel shader 完全可编程. 

merger stage 高度可配置

3.3. The Programmable Shader Stage

现代的着色器程序使用统一的shader 设计,这也意味着 vertex, pixel, geometry, tessellation-related shaders 共享同一套程序模型,它们有着相同的指令集结构 instruction set architecture (ISA). 实现这种模型的处理器叫做 common-shader core in DirectX, 拥有这个核心的 GPU 就被叫做拥有统一的shader架构,这种架构的目的是处理器可以适用于不同的角色, 比如, 例如,一组由小三角形组成的网格比由两个三角形组成的大正方形需要更多的顶点着色器处理. 一个GPU拥有独立 separate pools of vertex and pixel shader cores ,意味着如何能让这些cores 始终性能最大化,分配的工作都是严格分配的,unified shader cores,GPU可以平衡这一点

DirectX’s 使用High-Level Shading Language (HLSL)变成 , OpenGL 使用(GLSL). DirectX’s HLSL 可以被编译成中间机器语言,编译成中间语言意味着着色器程序可以被编译,并且离线存储,而不用实时编译,中间语言通过驱动为不同的GPU转化成不同的指令集,所以着色器程序,就是一个一个的指令,让GPU执行

最近本的数据类型是32位的单精度浮点型向量或者标量,在现代gpu上,支持32位整数和64位浮点数. Floating point vectors 一般用来描述 positions (xyzw), normals, matrix rows, colors (rgba), 或者 texture coordinates (uvwq). Integers一般用来计数 counters、 indices、bitmasks.

一次draw call 调用图形API绘制一组primitives, 这是就是走渲染流水线,执行shader 程序,每一个可编程的shader 阶段包含两种类型的输入:统一的输入uniform inputs,在整个draw call过程中保持常量(可以在draw call 之间改变)、可变的输入varying input,来自于三角形顶点或者栅格化的数据,比如,pixel shader 可能提供一个光源的颜色,当作常量,三角形的位置,当作变量,贴图就是一中特殊的常量,可以被看成任意大的数据数组.

用于常量存储的寄存器要比用于变量存储的寄存器大的多,因为变量的输入和输出需要为每个顶点单独存储,而常量是整体存储

常量 uniform inputs 只存储一次,并在整个draw call 中可以被重复使用,虚拟机也会根据需要生成临时的寄存器, 所有类型的寄存器都可以在临时寄存器中使用整数值进行数组索引。

Shaders 支持两种流程控制,一种是静态控制 Static flow ,它通过常量uniform input 控制分支. 这意味着代码流在draw call 中是不变的. 静态控制最大的好处就是允许相同的shader,可以用在不同的情形当中,比如根据灯光数量的不同,这不会发生线程分散thread divergence,因为所有的warp 都是走的统一的调用,动态控制Dynamic flow 基于变量varying inputs,也就是每一个片元可以执行不同的代码,这要比静态控制消耗更多的性能

3.5 The Vertex Shader

vertex shader 是第一个阶段,在这之前要准备一些数据,比如一个物体可以用一个位置数组和一个颜色数组来表示.

一个三角面可以用一组顶点(包含在三角面上的位置)来表示,除了位置,顶点还有其它的属性,比如颜色和uv,还有表面法线,在渲染的时候,三角面通过用被用来表示曲面,顶点法线表示了该表面的朝向,而不仅仅只表示该三角面的朝向

vertex shader 是处理三角面的第一个阶段triangle mesh. 但是描述三角形面的数据是不可用的,只处理传入的顶点

它可以修改,生成,忽略和三角形相关的地顶点,比如颜色,法线,uv,位置。通常vertex shader 把顶点从局部坐标转换到齐次裁剪坐标下,至少应该输出该顶点的位置

顶点着色器阶段不可以创建或者销毁顶点,一个顶点着色器生成的结果不能传递给另一个顶点,因为顶点之间是独立的,GPU上任何数量的处理器 都可以并行的处理顶点流。

在顶点着色器程序执行之前,输入数据流已经给准备好了

下面是vertex shader 的一些效果,比如顶点动画,剪影渲染,还包括:

  • Object generation, 对象生成,对已有的对象进行形变

  • 使用蒙皮skin和形变技术morphing 给人的身体和脸部做动画

  • Procedural deformations,程序化形变,比如飘舞的旗帜,衣服,或者水流

  • Particle creation,粒子生成,把简单的点生成一个带有区域的网格

  • Lens distortion镜头扭曲、heat haze热浪效果、 water ripples水波纹、page curls页面卷曲、和其它一些效果,通过使用整个framebuffer的内容作为纹理,进行变形

  • 使用顶点数据,应用地形的高度

  •  vertex shader 顶点着色器的输出有几种方式,一般是传递给渲染管线的下一阶段,还有一些GPU可以传递给曲面细分着色器,几何着色器,或者存储在内存中,供CPU、GPU访问

3.6 The Tessellation Stage

tessellation stage 曲面细分阶段可以渲染曲面,它是可选择的,在 DirectX 11上首次被提出, OpenGL 4.0 和 OpenGL ES 3.2也支持

使用曲面细分着色器的有点:

除了节省内存,它也可以避免在角色变形动画时,从CPU到GPU造成的瓶颈,这些表面可以通过生成一定数量的三角形面,而不用一开始就准备大量的三角面,从而提高渲染性能 ,比如,如果一个球离镜头很远,只需要渲染很少的面,离得近了,有需要很多的面,这种控制细节的能力,同时也决定了该App的性能,就是根据不同的情形来改变三角形面的数量.

曲面细分阶段也分为三个阶段:在 DirectX中,分别是 hull shader、tessellator、 domain shader. OpenGL 中 hull shader用来控制曲面细分(tessellation control shader) , domain shader 来评估曲面细分结果. 在OpenGL中,固定功能的曲面细分器,也叫做primitive generator

曲面细分开始阶段,把primitive patch 传递给 hull shader,它有几个控制曲面细分的点组成,首先它告诉 tessellator 应该生成多少个三角形,分别配置什么数据,第二 处理每个控制曲面细分的点,同时,hull shader可以修改输入的primitive patch ,根据需要增加或者移除控制点, hull shader输出修改后的控制点的数据,传递给domain shader

tessellator 在渲染管线中是一个固定功能的阶段,只能用在 tessellation shaders中.它的任务是生成新的顶点,传递给domain shader 处理,hull shader 会传递给它曲面细分的类型,比如三角形,四边形或者等值线 triangle, quadrilateral, or isoline. Isolines 一些线条纹,有时候用来生成头发, hull shader 还会传递给它另一个很重要的值,就是细分的等级,它包括两种类型,  inner 和 outer edge. inner factors 决定了多少细分发生在三角形或者四边形的内部, outer factors决定了外部的边缘split的程度 . 通过单独的控制内部和外部,我们可以让相邻曲面的边缘在曲面细分中匹配, 而不用管内部是如何细分的we can have adjacent curved surfaces’ edges match in tessellation, regardless of how the interiors are tessellated. 边缘匹配可以避免边缘裂缝,Matching edges avoids cracks or other shading artifacts where patches meet.  给顶点分配重心坐标(第22.8节),这是指定所需曲面上每个点的相对位置的值。

 hull shader也会输出一个patch,一组控制点的位置,然而,通过给 tessellator 发送的outer tessellation level 为0或者为负值,从而让tessellator 忽略这个patch,否则 tessellator 会生成一个网格然后传递给domain shader. 控制点可以用来计算最终生成的mesh

传递给hull shader的patch ,很少或者不会有更改, hull shader也可以使用patch的目测距离或者屏幕大小来计算曲面细分的等级,你在屏幕上小,就不会细分很多个,一般用来地形渲染 terrain rendering [466]. 可供选择的,hull shader 可以简单的传递一组app提供的固定的值给所有的patch, tessellator 执行一个固定的功能,就是生成顶点,并给出它们的位置,以及组成的三角形面或者线(isonline),为了计算效率,数据放大步骤在着色器之外执行. domain shader 根据每个点的重心位置(坐标),用来计算patch的位置,法线,和uv

 

 

3.7 The Geometry Shader

geometry shader 可以把 primitives 转换成其他类型的 primitives, 一些是曲面细分做不到的,比如,一个三角形网格,可以通过为每个三角形边创建一条线,从而转换成线框模式 wireframe view 同理,这些线也可以被四边形面替代.  geometry shader 在DirectX 10的时候被提出,它在曲面细分着色器之后执行,是可选择的,需要 Shader Model 4.0, OpenGL 3.2 和 OpenGL ES 3.2 也支持

 geometry shader 输入是单独的对象以及它涉及到的顶点,这些对象通常由三角形条纹或者几何线段,或者简单的点组成,特别的,三角形外的三个额外的顶点可以被传入,如上图 . DirectX 11 和 Shader Model 5.0, 你可以传递至多32个控制点, 但是和曲面细分阶段相比,还是微不足道的

geometry shader 处理primitive 输出0个或者多个顶点,这些顶点被当成点,折线,或者三角形条纹, Note that no output at all can be generated by the geometry shader. 以此来说,一个网格可以通过修改顶点,增加,删除primitive来修改

geometry shader 被设计用来修改输入的数据或者进行有限的负值,比如,一个面可以被复制成六份,以此来模拟cube map的六个面,它也可以用来生成级联阴影贴图,来提高阴影的质量, 其它利用geometry shader的算法,包括根据一个点的数据,生成不同大小的粒子,或者找出物体的边。

DirectX 11 增加了可以使用geometry shader实例instance的功能, geometry shader 实例 可以在任何输入primitive上运行一定的时间

geometry shader 可以至多输出四个stream,其中一个可以被传递到渲染管线的下一个阶段,所有的streams 都可以选择性的传递到 output render target。

geometry shader 可以保证primitive 输出的顺序和输入时的顺序一致,但是这耗费性能,因为如果多个并行运行的shader core ,它们访问输入和修改输入数据的顺序不固定,就要结果就要保存下来,并且排好序,所以不要在一个pass 中大量的复制 创建图形

调用draw call 之后,只有渲染管线上的三个位置可以在GPU上执行,After a draw call is issued, there are only three places in the pipeline where work can be created on the GPU: rasterization, the tessellation stage, and the geometry shader. 在这其中,当考虑到 resources 和 memory needed,geometry shader是最不可预测的. 在实践中,很少用到geometry shader,它并不能充分发挥GPU的长处

3.7.1 Stream Output

stream output 的想法在Shader Model 4.0中提出来,在顶点被  vertex shader ( optionally, the tessellation and geometry shaders)之后,可以输出到一个stream中,比如 一个有顺序的数组,除了被传递到管线的下一阶段,还可以完全关闭管线,然后GPU 就会作为一个非图形流处理器来使用 non-graphical stream processor.

以这种方式处理的数据,可以通过管线返回,允许迭代访问处理,这种类型的操作对模拟水流或其它的粒子效果很有用 ,thus allowing iterative processing. 也可以用来skin a model and then have these vertices available for reuse (Section 4.4).

Stream 输出的数据只包括浮点型数据,所以可以很明显的看到所占内存, Stream output 的操作对象是 primitives, 而不直接是顶点 vertices. 如果网格沿着管道发送,每个三角形都会生成自己的一组输出顶点。在原始网格中共享的任何顶点都将丢失. 因此,更经典的方法是把顶点作为一个point primitive  传递.  OpenGL 中stream output 阶段叫做transform feedback, 因为更更多的是顶点变换,然后返回它们以供进一步处理

3.8 The Pixel Shader

这一节在渲染过程中相对来说比较固定,比如不可编程,但可配置,每一个三角形决定了覆盖了哪些像素,这些三角形顶点的值包了括存储在z-buffer中的深度值

pixel shader 程序中声明了在三角形中顶点值差值的方式,是线性插值还是其它的,一般的,我们使用透视矫正差值 perspective-correct interpolation,这样两个物体在世界空间下位置离得越远,像素距离就越远,还有其它的一些差值的方式,比如, 屏幕空间差值screen-space interpolation,不会考虑透视投影。

从程序上来说,顶点着色器输出的数据,经过三角形或者线差值后,输入到片元着色器上,随着GPU的发展,还可以传递其它的数据,比如片元的屏幕位置,还有一个标记,用来表示三角形哪个面是可见的,这个标记对在单个pass 中区别渲染正面和反面非常有用,片元着色器的输出通常是片元的颜色,也可以输出片元的深度值,深度值在光栅化阶段产生,片元着色器可以修改它, stencil buffer 的值通常是不可更改的,只是传递给合并阶段,DirectX 11.3 允许修改stencil  buffer的值,雾的计算和alpha test 从合并阶段转移到片元着色器阶段了。

现在片元着色器不仅仅为每个片元生成颜色和深度值,还可以生成更多的值,存储在不同的buffer 中,每个buffer都叫一个 render target. Render targets通常x 和y 都是相同的,一些架构需要每一个render target 都有一样的 bit depth. GPU不同, 支持render targets 的数量也不同,一般是4-8个.

即使有这些限制, MRT 功能仍然是一个有用的、执行渲染算法更高效的功能,一个pass 可以生成一个color image ,存储在 一个render target 中,把对象的ID 存在另个一个target中,世界空间下的距离存在另一个target中, 这种能力还产生了一种不同类型的渲染管道,称为延迟着色,

第一个pass 负责收集数据,第二个pass 负责应用光照,计算最终的效果

pixel shader’的限制是他只能在片元阶段,往render target 里写入数据,而且不能读取邻近像素的数据,意味着不能访问邻近像素的着色结果,只能修改像素的结果,但是解决方法就是我们上面说的MRT ,延迟渲染,第一个pass先准备数据,第二个pass 再处理数据,这样就能访问临近像素的数据了

有一个例外,就是在计算渐变或者导数(gradient or derivative information.)的时候,可以立马访问临近像素的数据.  像素着色器提供了沿着屏幕轴x和y方向上的每个像素的任何插值值的变化量。现代 GPUs通过 一个 2 × 2的quad来实现这个功能,这种实现的方式,意味着像素之间的偏移值,不能通过动态流程控制访问,所有的片元必须执行一样的渲染指令

DirectX 11介绍了一种buffer类型,可以在任何位置写入,叫做 unordered access view (UAV). 一开始只有片元着色器可以使用,DirectX 11.1 所有的着色器都可以使用[146]. OpenGL 4.3 叫shader storage buffer object (SSBO).这个buffer 在线程之间共享

这种情况有时会产生冲突,因为线程之间没有加锁,GPUs 通过使用专门的原子单位dedicated atomic units来避免这种情况. 但是坏处就是shader 需要等待,等待其它线程对该buffer的操作完毕后再操作

虽然原子可以避免数据危险,但许多算法需要特定的执行顺序。比如, 先绘制一个透明的蓝色物体,在绘制一个透明的红色物体,在标准管线中,片段的结果在被处理之前先在合并阶段进行排序. DirectX 11.3介绍了Rasterizer order views (ROVs)方法排序, ROVs可以保证片元数据以正确的顺序访问,.  这大大增加了这些着色器可访问缓冲区的用处[327,328]。 ROVs 允许 pixel shader 自定义混合方法 blending methods, 因为它可以直接访问或者写入数据,因此不需要合并阶段了,代价就是,如果检测到一个无序的访问,着色器必须先等到先绘制的三角形处理完成才能继续

3.9 The Merging Stage

合并阶段,就是合并当前和之前的color buffer 和z-buffer的值,DirectX将这个阶段称为输出合并; 传统管线中,stencil-buffer 和 z-buffer 在这个阶段执行,如果片段是可见的,再颜色混合. 对于不透明的表面,不涉及真正的混合,因为片段的颜色只是替换先前存储的颜色。混合实际上用于透明物体(Section 5.5).

如果一个像素什么都处理完了,但是最终不需要被渲染,那么之前做的都是不必要的,为了避免这个,GPU在执行合并阶段之前,就执行一些合并测试,比如深度测试,这种技术叫 early-z [1220, 1542].但是如果存在 ,透明物体

3.10 The Compute Shader

GPU 不仅仅可以用来实现传统的渲染管线的功能,还可以作为非图形渲染芯片,执行不同的功能,比如对股票期权估值与深度学习的神经网络训练. 这种只利用GPU的计算功能,称之为 GPU computing. 把GPU当做一个并行处理器.

DirectX 11中 , compute shader 由 GPU computing组成,它不在渲染管线的指定位置执行,而是通常调用 graphics API执行,.它与顶点、像素和其他着色器一起使用. 它利用了和渲染管线中一样的processor值,和其它shader一样,输入顶点的数据,也会有warps 和thread,DirectX 11中出现了一个概念叫thread group, 由1 到1024 个threads组成 . 这些thread groups 通过xyz的坐标指定,主要是为了方便,每一个 thread group 都有一个小数量的内存,在所有的thread中共享,DirectX 11中,大小是 32 kB. Compute shaders通过 thread group执行,  这样组中的所有线程都可以保证并发运行[1971].

compute shaders一个很重要的有点就是可以直接访问 GPU生成的数据, 将数据从GPU发送到CPU会产生延迟,因此如果处理和结果可以保留在GPU上,则可以提高性能 [1403]. Post-processing, 通过修改渲染的图像达到效果,是 compute shaders的经典使用案例,共享内存意味着邻近线程之间可以共享线程处理的结果,使用compute shader 着色器,来决定图像上光照的分布和光的亮度的平均值,这比延迟渲染还要快,也就是渲染两个pass  [530].

Compute shaders 通常也能用于粒子系统 particle systems, 面部动画、剔除、贴图采样、提高深度的精确度、阴影渲染、景深等等.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值