Shader、Draw Call和渲染管线(Rendering Pipeline)

翻阅了不少资料,也作了很多笔记,决定仍是对渲染进行一个总结,以巩固所学的东西。web

 

《Real-Time Rendering, Third Edition》   (PDF的配图连接)将一个渲染流程分为三个阶段:编程

应用阶段(PApplication Stage)、几何阶段(Geometry Stage)、光栅化阶段(Rasterizer Stage)dom

 

我借用《Unity Shader入门精要》的网页贴图来讲明ide

概念流水线.png-16.9kB

GPU流水线.png-82.2kB

颜色表示了不一样阶段的可配置性或可编程性:绿色表示该流水线阶段是彻底可编程控制的,黄色表示该流水线阶段能够配置但不是可编程的,蓝色表示该流水线阶段是由GPU固定实现的,开发者没有任何控制权。实线表示该shader必须由开发者编程实现,虚线表示该Shader是可选的。测试

 

顶点着色器(Vertex Shader)、片元着色器(Fragment Shader)是咱们编写 Shader最经常使用的二个。另外二个曲面细分着色器(Tessellation Shader)、几何着色器(Geometry Shader)都是可选着色器。优化

 

既然提到了着色器(Shader),那什么是Shader呢this

A shader is a piece of code, that is executed on the GPU. The engine feeds it with 3d model vertices, textures and other information, and gets back from it pixel colours.
-- from What is a Shader?翻译

翻译过来:Shader 就是运行在GPU上的一段代码,引擎提供给它3D的模型顶点、纹理和其它信息,并获取返回的像素颜色。3d

 

Draw Call又是什么呢code

A draw call is a command to render one mesh. It is given by the CPU. It is received by the GPU. The command only points to a mesh which shall be rendered and doesn’t contain any material information since these are already defined via the render state. The mesh resides at this point in the memory of your graphic card (VRAM).

-- from renderhell-book1

image

有兴趣能够观看下面这个视频,很形象和生动。

视频地址:

https://data.simonschreibt.de/gat049/cpu_calls_gpu.webm

https://data.simonschreibt.de/gat049/commandbuffer_communication_chunk.webm

 

实际上,Draw Call就是一个命令,它的发起方是CPU,接收方是GPU。这个命令仅仅会指向一个须要被渲染的图元(primitives)列表,而不会再包含任何材质信息(这些信息已经在渲染状态中被定义了),此时网格是驻留在显存(Video Random Access Memory)中的。

 

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

 

只看文字难免过于抽象,我以前在网上看到一张图,描述的挺不错。

各着色器

 

而实际的工做比这个要复杂不少,包括:坐标转换、透视、裁剪等一系列操做。

RTR3.02.03

所上图所示,须要历经:坐标转换(模型空间 –> 齐次裁剪空间),逐顶点光照,透视除法(归一化的设备坐标 —— Normalized Device Coordinates, NDC),裁剪,屏幕映射。

 

这里再多解释一下,何为齐次裁剪空间。齐次裁剪空间是一个中心点是坐标原点的立方体,xyz取值范围是[-1, 1]。使用一个4x4的齐次变换矩阵将点从摄像机坐标空间变换到齐次裁剪空间,将顶点的深度值z保存在顶点通过变换获得的齐次坐标的w份量中。最后,把顶点在齐次空间中的坐标经过将x,y,z份量除以w份量的方式,将齐次坐标转为NDC。

 

为何须要使用NDC呢?为了适配屏幕的多分辨率问题。

归一化坐标中,两个轴其中一个轴的范围是由0至1(但不能两个都是0~1),并且能轻易缩放至不一样分辨率下的像素单位。假设把y轴的范围设置为0.0 ~ 1.0,当使用4:3长宽比时,x轴的范围就是0.0至1.333(=4/3),而16:9时x轴的范围则是0.0 ~ 1.777(=16/9),这样就不会出现拉伸了。

 

 

 

 

 

接下来就进入光栅化阶段了。

接收顶点信息,进行适当的转换后,对顶点进行插值处理,而后对三角形进行遍历,检查每一个网格是否被三角形覆盖,若是被覆盖就会生成一个片元。以后交给片元着色器(在DirectX中,也称为像素着色器 Pixel Shader)。

 

通过片元着色器(Fragment Shader)处理后,获得一个或者多个颜色值(以下图所示)。

 

 

以后进入逐片元操做(Per-Fragment Operations),会通过模板测试(Stencil Test)、深度测试(Depth Test)、混合(Blend)等一系列操做,最后获得的结果(一系列颜色值)存放到帧缓冲区,供GPU进行屏幕的更新。

 

 

大致的渲染过程就如上所述。中间忽略了很多信息,如坐标转换(主要使用矩阵、四元数,矩阵就是映射),还有投影(正交/平行投影、透视投影),以及光照模型(各类贴图和法线、切线等)。整个过程当中裁剪不只仅只发生在应用阶段,背面剔除、遮挡剔除(光栅化阶段)都使用了裁剪。也没有说起为何是减小Draw Call,以及如何优化。

 

先大致理解整个脉络,中间每个点均可以单独写不少内容,可是总的流程是这样的。对整个渲染流程,时刻要在脑海中有一个比较清晰的认识和了解,否则很容易被各类转换和模型给搞晕,不知道当前处于什么阶段,输入是什么,输出又是什么,接下来要怎么走。

相关文章
相关标签/搜索