计算机图形学-着色

深度

世界中有许多物体,我们应该根据远近来确定那些部分应该被画到屏幕上。

画家算法

画家算法先绘制远处的模型的三角形面,再绘制近处的三角形面,近处的会覆盖掉远处的。三角形的深度为三个顶点的平均深度(视图空间的Z值)。然后从远到近进行排序,依次着色。

但是画家算法在处理循环遮挡情况下存在问题,而且排序开销较大,不主要使用。

深度缓冲 Z-Buffer

对于每一个像素都记录它的最小深度(使用重心坐标进行深度插值),同时需要帧缓冲FrameBuffer用于记录该像素的经过着色后的颜色。最后FrameBuffer就是这一帧的实际画面。

着色是向物体应用材质的过程。

Blinn-Phong光照模型

物体表面发出的光包括:

  • 镜面高光(specular highlights)
  • 漫反射(diffuse reflection)
  • 环境光照(ambient lighting)
  • 视线方向 – v
  • 表面法线 – n
  • 光照方向 – l
  • 表面参数,颜色、粗糙度等等

着色首先不考虑阴影。

漫反射

光照到物体表面,光线会向四周均匀地散射出去。

表面接受到光线的强度与光线与表面的夹角有关。接受到的能量与该夹角的余弦值成正比。

假如传播过程中光照能量不损耗,那么各个球面4πr24\pi r^{2}上的能量相等。所以,一个点光照的强弱与它距离光源距离的平方成反比。

综上,可得漫反射光照的计算公式:

Ld=kdIr2max(0,nl)L_{d} = k_{d} \frac{I}{r_{2}} \max(0, n \cdot l)

其中,kdk_{d}是漫反射系数,在0到1之间,表示亮度与颜色,II为光照强度,rr为与光源的距离,nn为表面法线,ll为入射方向。

随着K值变大,着色效果如下图所示。

高光

光几乎镜面反射。当观察向量和反射向量足够接近时,就会出现高光现象。我们可以利用半程向量来判断观察向量与反射向量的夹角。

半程向量:

h=v+l|v+l|h=\frac{v+l}{\lvert v+l \rvert}

高光:

Ls=ksIr2max(0,nh)pL_{s}=k_{s}\frac{I}{r^{2}} \max(0, n \cdot h)^{p}

其中,ksk_{s}是高光影响因素。高光计算中不考虑入射角度与接收能量的关系,即不需要乘max(0,nl)\max(0, n \cdot l)。半程向量夹角处有p次方是为了缩小高光区域。实际上,只有3度左右的范围能看到高光。一般,p大约在100到200之间。

环境光照

真实的环境光照非常复杂。Blinn-Phong光照模型假设所有地方的环境光是相同的。

La=kaIaL_{a}=k_{a} I_{a}

环境光被简化为常数。

最后,将三者相加即可得到最终光照效果。

L=kdIr2max(0,nl)+ksIr2max(0,nh)p+kaIaL=k_{d} \frac{I}{r_{2}} \max(0, n \cdot l)+k_{s}\frac{I}{r^{2}} \max(0, n \cdot h)^{p}+k_{a} I_{a}

着色频率

三个球分别对应:对平面着色、对顶点着色(使用差值计算内部颜色)、对像素(使用差值计算内部法线)着色

平面着色 Shade each triangle (flat shading)

  • 三角形是一个面,有一条法线
  • 对光滑平面来说效果并不好

高洛德着色 Shade each vertex (Gouraud shading)

  • 通过顶点差值计算像素的颜色
  • 每个顶点有一条法线

古罗着色 Shade each pixel (Phong shading)

为每个像素应用着色模型

通过顶点差值计算像素的法线

顶点法线

最精确的求顶点法线的方式是通过几何体来求。也可以简化为从周围相邻的面的法线取平均值。

图形管线

图形管线是图形渲染的全流程。主要包含顶点着色(MVP变换)->光栅化->片元着色等阶段。其中,顶点着色器与片元着色器部分是可编程的。

纹理映射

任何一个三维物体的表面其实是二维的,纹理就是三维物体表面再开后的二维面。模型上的每个三角形顶点都会被分配给纹理上的一个坐标(u,v)。u、v始终在0到1之间。

至于怎么映射、以及纹理如何设计,我们不关心。

重心坐标

通过重心坐标可以做线性插值,其中可以是顶点的位置、材质坐标、颜色、法向量等等属性。

注意到,三维空间内三角形的重心坐标在投影后,不一定是二维投影的重心坐标。所以应该在三维空间做插值,再投影到二维上去;而不是先投影到二维,再做插值。

详细见:https://xuanlin.life/computer-graphics-rasterization.html

应用纹理

把纹理应用到物体过程为:

  • 对于每个采样点(像素中心),可以通过重心坐标插值得到纹理对应坐标。原来我们只知道顶点对应的坐标,现在通过插值得到所有采样点的了。
  • 获取材质坐标的颜色,并设置该采样点的漫反射系数Kd即可。

纹理放大

因为虽然UV是连续的,但是UV对应的纹理贴图是离散的。当纹理图片分辨率过低,不同UV可能对应的实际纹理像素是相同的,那么就会出现多个像素点仍然对应一个(u,v)坐标,并将该(u,v)坐标下颜色信息复制给该像素点,这样形成的图片边界过渡就会非常不自然,形成一个个的小格子。

对于纹理放大,有三种做法:

最近像素

直接选取距离uv坐标最近的像素。

双线性插值

双线性插值就是选取周围4个像素做插值计算出该uv的颜色。

事实上,在纹理被放大和缩小时都可以使用nearest、bilinear或bicubic的做法。这称为纹理滤波(texture filtering)。

纹理放大

屏幕上一个像素所覆盖的区域,映射回纹理空间后,纹理分辨率过高,可能覆盖了一大片纹理区域,包含了成百上千个纹素,就会出现一个像素点对应多个(u,v)坐标。但屏幕像素渲染时,我们却只在这个像素的中心取一个点,通过插值得到一个(u,v)坐标,然后去纹理上采一个点的颜色。这就相当于用一个点的信息来概括一整片区域,完全忽略了区域内的变化,这必然导致走样(比如远处地面的摩尔纹)。

所以,我们需要一个获取一个范围内纹理的像素的平均信息,这就是Mipmap。

Mipmap的额外空间占用仅比原来多1/3。

计算mipmap等级

在光栅化时,三角形内部的每一个屏幕像素 (x, y) ,都会被插值出一个独特的 (u, v)。这本身就定义了两个连续函数:u = f(x, y)与v = g(x, y)。

当这个小方块映射到纹理上时,它的形状可以用纹理坐标(u,v)对屏幕坐标(x,y)的偏导数来近似描述。

  • du/dx、dv/dx:在屏幕x方向上移动一个像素,纹理坐标u和v的变化量。
  • du/dy、dv/dy:在屏幕y方向上移动一个像素,纹理坐标u和v的变化量。

简单做法:光栅化时,是把像素编成一个2×2的小组(quad)来并行处理的。 在这个2×2的组里,四个像素的屏幕坐标和纹理坐标都是已知的。于是:

dudx=u1u0dvdy=U2u0\begin{align} \frac{du}{dx} = u_{1}-u_{0} \\ \frac{dv}{dy} = U_{2}-u_{0} \end{align}

有了这两个向量,我们就能估算像素在纹理空间覆盖区域的“大小”。为了安全和简单,通常取这两个向量长度的最大值作为特征长度 L。

表示屏幕上的1个像素,大约对应了纹理上 L×L 个纹素的范围。

Mipmap的每一层都是上一层长宽各缩小一半,即面积变为1/4。第0层是原始分辨率,第1层是原来的1/2,第D层是原来的12D\frac{1}{2}^{D}

D=log2LD=\log_{2}{L}

三线性插值

计算出连续值 D 后,直接取整会造成Mipmap层之间的接缝。因此实践中都会用三线性插值:

  1. 取 D 的整数部分 D_floor 和 D_ceil = D_floor + 1。
  2. 分别在层级 D_floor 和 D_ceil 上做双线性插值,得到两个颜色值。
  3. 再根据 D 的小数部分,把这两个颜色值做线性混合。

这样就能得到平滑过渡的结果。

各向异性过滤

上述前提的假设是屏幕像素在纹理上的“脚印”是一个正正方形。但现实中,由于透视投影和斜视角,脚印常常被拉得很长。比如一堵向远方延伸的墙,屏幕上一个像素对应在纹理上的区域可能是一个扁长的矩形。

当我们强行用最大边长 L把它当正方形处理时:

  • 选的Mipmap层级 D = log2(L) 会过高(因为L是长边,远比短边大)。
  • 结果就是,一个原本应该很清晰、沿着墙面延伸的纹理,在短边方向上被过度模糊了。

首先要获取屏幕x、y方向的纹理坐标变化率向量,组成一个雅可比矩阵:

J=(dudxdvdxdudydvdy)J = \begin{pmatrix} \frac{du}{dx} & \frac{dv}{dx} \\ \frac{du}{dy} & \frac{dv}{dy} \end{pmatrix}

对矩阵JTJJ^{T}J求特征值,其中最大值为λ_major,最小值为λ_minor。它们的平方根就正比于两个轴的长度。将它们开方得L_major与L_minor。

各项异性比例N= L_major / L_minor。个值决定了我们需要沿长轴做多少次采样来覆盖整个椭圆。硬件通常限制在一个上限值(如x16),也就是我们常说的“x4各向异性过滤”、“x16各向异性过滤”。

Mipmap等级:D=log2(L_minor)D = \log_{2}{(L\_minor)}。注意,这里用的是短轴,从而保证了在这个变化最平缓、信息最密集的方向上,我们选到了足够高分辨率的纹理层,不会过度模糊。

以像素映射到纹理空间的那个中心点 (u0, v0) 为基准,沿着计算出的长轴方向,向两边均匀撒下 N 个采样点,而短轴方向只采样一个点。其总长度

待完成~

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇