前面的文章讲的都是shader高级知识和运用,后面开始讲述shader基础知识,包括理论和语法。
学习shader之前有必要了解什么是渲染流水线的,渲染流水线的最终目的在于生成或者说是渲染一张二维纹理,即我们在电脑屏幕上看到的所有效果,它的输入是一个虚拟摄像机、一些光源、一些shader以及纹理等。
一、综述
要学会怎么使用shader,首先要了解shader是怎么工作的,shader仅仅是渲染流水线的一个环节,想要让我们的shader发挥作用,我们就需要它在渲染流水线中扮演了怎样的角色。
1.1什么是渲染流水线
渲染流水线的工作任务在于由一个三维场景出发、生成(渲染)一张二维图像。计算机需要从一系列的顶点数据、纹理等信息出发,把这些信息最终转换成一张人眼可以看到的图像,而这个工作通常是由CPU和GPU共同完成的。
渲染流程一般分为三个阶段:应用阶段、几何阶段、光栅化阶段。下图显示了三个概念阶段之间的关系:
1.应用阶段:
这个阶段由我们的应用主导,通常由CPU实现。在这一阶段中,开发者有三个任务:首先准备好场景数据,例如摄像机的位置、视椎体、场景中包含了哪些模型、使用了哪些光源等等;其次为了提高渲染性能,我们往往需要做一个粗粒度剔除工作,把那些不可见的物体剔除出去,这样就不需要再移交给几何阶段进行处理;最后设置好每个模型的渲染状态。这些渲染状态包括但不限于它使用的材质(漫反射颜色、高光反射颜色)、使用的纹理、使用的shader等。这一阶段最重要的输出是渲染所需的几何信息,即渲染图元。通俗来讲渲染图元可以是点、线、三角面等。这些渲染图元将会被传递给下一个阶段——几何阶段。
2.几何阶段:
几何阶段用于处理所有和我们要绘制的几何相关的事情。例如决定要绘制的图元是什么,怎样绘制它们,在哪里绘制它们,这一阶段通常在GPU上进行。
几何阶段负责和每个渲染图元打交道,进行逐顶点,逐多边形的操作。这个阶段可以进一步分成更小的流水线阶段。几何阶段的一个重要任务就是把顶点坐标变换到屏幕空间中,再交给光栅器进行处理。通过对输入的渲染图元进行多步处理后,这一阶段将会输出屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等相关信息,并传递给下一个阶段。
3.光栅化阶段:
这一阶段将会使用上个阶段传递的数据来产生屏幕上的像素,并渲染出最终的图像。这一阶段也是在GPU上运行,光栅化的任务主要是决定每个渲染图元中哪些像素应该被绘制在屏幕上,它需要对上一个阶段得到的逐顶点数据(例如纹理坐标、顶点颜色等)进行插值,然后再进行逐像素处理。光栅化阶段也可以分成更小的流水线阶段。
注意:这里的流水线是概念流水线,是为了给一个渲染流程进行基本的功能划分而提出来的,下面要介绍的GPU流水线,则是硬件真正用于实现上述概念的流水线。
二、CPU和GPU的之间的通信
渲染流水线的起点是CPU,即应用阶段。应用阶段大致可分为下面3个阶段:
(1)把数据加载到显存中。
(2)设置渲染状态。
(3)调用Draw Call。
2.1 把数据加载到显存中
所有渲染所需的数据都需要从硬盘中加载到系统内存中,然后网格和纹理等数据又被加载到显卡上的存储空间——显存中,这是因为显卡对于显存的访问速度更快,而且大多数显卡对于RAM没有直接访问的权利。
当把数据加载到显存中后,RAM中的数据就可以移除了。但对于一些数据来说,CPU仍然需要访问他们(例如希望CPU可以访问网格数据来进行碰撞检测),那么我们就可能不希望这些数据被移除,因为从硬盘加载到RAM的过程是十分耗时的。
在这之后,开发者还需要通过CPU来设置渲染状态,从而指导GPU如何进行渲染工作。
2.2 设置渲染状态
渲染状态的通俗解释是:这些状态定义了场景中的网格是怎样被渲染的。例如使用哪个顶点着色器/片元着色器、光源属性、材质等。如果我们没有更改渲染状态,那么所有网格都将使用同一种渲染状态。
在准备好上述所有工作后,CPU就需要调用一个渲染命令(就是Draw Call)告诉GPU数据准备好了,可以按照我的设置来渲染了。