Skip to main content

3.1 着色技术基础

在第1章讲述渲染方程的时候,我们说明了渲染方程是一个费雷德霍姆第二类方程,为了方便,我们重写一个简化的渲染方程如下:

Lo(p,v)=Le(p,v)+Ωf(l,v)Li(p,l)cosθidωi L_o(p,\mathbf{v})=L_e(p,\mathbf{v})+{\rm \int}_\Omega f(\mathbf{l},\mathbf{v})\otimes L_i(p,\mathbf{l})\cos{\theta_i}{\rm d}\omega_i

(式1

这里Lo(p,v)L_o(p,\mathbf{v})表示点pp处沿v\mathbf{v}方向的辐射亮度,Le(p,v)L_e(p,\mathbf{v})表示点pp处沿v\mathbf{v}方向自发光的辐射亮度,Li(p,l)L_i(p,\mathbf{l})表示沿l\mathbf{l}方向射向pp点的辐射亮度,f(l,v)f(\mathbf{l},\mathbf{v})表示pp点处的BRDF函数。

对于方程(1),其完整的解通常需要使用一些迭代的方法来计算,本书后面会讨论多种解这个方程的方法。但是很显然,这样一个方程并不是对GPU友好的,我们不能直接将它放入着色器中求解,着色器中要处理的公式,它必须是能够直接根据材质参数计算出最终结果的,也就是说着色器中不能包含未知的参数。

因此,正如将在后面的一些章节中看到的那样,实时渲染方法中通常将渲染方程分解为多个部分,并使每个部分能够以各种方式形成着色器中的一个参数,最终着色方程可以直接根据这些参数(它们通常都是某种程度上的近似值)计算出一个像素点的最终颜色。这些参数可能是一个包含间接光照的球谐函数,一个包含远距离环境反射的环境贴图,或者是一个光源的阴影贴图等等。它们可能以预处理的方式提前在预处理阶段计算出来,也可能实时地使用光栅化技术来计算某个量,不管怎样,这些参数使得最终在着色器中我们可以使用一个公式计算出最终的颜色值。

有了这些材质参数,着色方程可以被写成一个只包含最基本的几个变量的形式,渲染方程的各个部分可以根据这些最基本的变量以及材质参数计算出来(例如通过观察方向和物体表面的法线方向就可以确定入射光的方向从而对环境贴图进行采样),这个包含基本变量的着色方程如下:

Lo(v)=k=1nfshade(ELk,lk,v,n,cdiff,cspec,m) L_{o}(\mathbf{v})=\sum^{n}_{k=1}f_{\rm shade}(E_{L_k},\mathbf{l}_k,\mathbf{v},\mathbf{n},\mathbf{c}_{\rm diff},\mathbf{c}_{\rm spec},m)

(式2

这里的\sum加数形式表示辐射亮度Lo(v)L_o(\mathbf{v})是所有光源的累积贡献,ELkE_{L_k}表示第kk个光源的辐射照度,lk\mathbf{l}_k表示入射光方向矢量,v\mathbf{v}表示观察方向矢量,n\mathbf{n}表示表面法线矢量,cdiff\mathbf{c}_{\rm diff}cspec\mathbf{c}_{\rm spec}分别表示表面的漫反射折射率和高光反射折射率,mm表示某个高光模型(例如Blinn-Phong模型)中的高光扩散系数(specular spread factor)或者粗糙度。

这里有两个变量是随着光源的变化而变化的:即入射光方向矢量lk\mathbf{l}_k和光源辐射照度ELkE_{L_k},对于辐射照度ELkE_{L_k},它一般可以通过光源参数中的辐射强度II和距离递减函数求得,即:E=Icosθfdist(r)E=I\cos{\theta}f_{\rm dist}(r),参见第1.3节的内容。需要注意的是,公式(2)仅考虑点光源和直线光线,它们可以很直接地计算出辐射照度ELkE_{L_k}的值,对于其他光源如环境贴图,在渲染中通常使用单独的渲染通道来处理,此时,利用式(2)中的变量仍然能够满足计算辐射照度的条件(例如环境贴图需要的入射光方向)。

有了如式(2)这样直接的着色方程表述,对一个物体表面进行着色也就是在着色器中执行该式计算的过程。最简单的方式就是在OpenGL渲染管线中对场景中的所有几何体的顶点进行光栅化,然后在片元着色器中执行式(2)的计算,不过不幸的是,这种方法虽然简单却有很大的缺陷导致其性能很低,因此工程师们针对渲染管线建立一些不同的基础架构,如延迟着色,它们用来改善直接光栅化技术的性能问题,这些架构方法正是本章要讨论的内容,要深入理解这些架构我们首先需要了解光栅化技术存在的一些问题。