图形学入门自学笔记——lec7【光栅化】

光栅化:对几何阶段传递过来的屏幕空间的顶点信息进行离散化处理

  • 屏幕坐标
  • 法向量
  • 顶点颜色
  • 纹理坐标

最终生成屏幕像素,渲染出图像。
在这里插入图片描述
相机坐标里的三角形经过透视变换,投影到屏幕坐标

主要问题(Visibility):

  • 确定像素被哪些投影三角形覆盖
  • 计算像素的最终颜色

这阶段的算法结果输出到FrameBuffer

Visibility Problem

光栅化算法

  1. 把一个空间三角形投影到屏幕坐标

v ′ . x = n ∗ v . x / v . z v ′ . y = n ∗ v . y / v . z v ′ . z = v . z v'.x=n*v.x/v.z\\ v'.y=n*v.y/v.z\\ v'.z=v.z v.x=nv.x/v.zv.y=nv.y/v.zv.z=v.z(简化为直接保留z)
在这里插入图片描述

在这里插入图片描述在这里插入图片描述
2. 思路:对屏幕上每个像素,检查是否位于投影三角形内
在这里插入图片描述
在这里插入图片描述
考虑多个三角形有重叠的情形:Z-Buffer算法

  • 维护FrameBuffer大小的二维数组Z-Buffer
  • 对于被投影三角形覆盖的像素点,只更新离相机更近的点

光线投射:

  • 对于屏幕每个像素生成一条射线
  • 对场景里每个三角形,查找和射线相交的最近的三角形

Triangle Setup 三角形设置

已知图元顶点颜色和别的信息

  • 组装图元(点,线,三角形)
  • 需要计算三角形边方程(slope)

GPU内部采用三角形网格

  • 任意多边形都可以拆分成三角形
  • 三点共面
  • 容易判断点在三角形内
  • 三角形内容易插值
  • 纹理映射会有问题

Triangle Traversal 三角形遍历

遍历三角形图元,检查覆盖的像素点
在这里插入图片描述
反锯齿(Anti-Alias)

光栅化算法

点光栅化

在这里插入图片描述

  • 最近点:映射到距离最近的像素点,但动画时会跳动
  • 取整点
  • 取整点,保留4bit,看做Sub pixel。移动时计算保留位再取整

在这里插入图片描述

直线光栅化

DDA数值微分算法:

  • y = k x + b y=kx+b y=kx+b
  • ∣ k ∣ < 1 |k|<1 k<1时,从起点开始画起,每次 x ′ = x + 1 , y ′ = y + k x'=x+1, y'=y+k x=x+1,y=y+k,并将 y y y四舍五入,得到新的 x x x y y y就是像素点应该画的地方。
  • ∣ k ∣ > 1 |k|>1 k>1时,从起点开始画起,每次 y = y + 1 , x = x + 1 / k y=y+1, x=x+1/k y=y+1,x=x+1/k,并将 x x x四舍五入,得到新的 x x x y y y就是像素点应该画的地方

缺点:每个点都需要浮点加

Bresenham中点画线算法:

  • v 0 ( x 0 , y 0 ) → v 1 ( x 1 , y 1 ) v_0(x_0,y_0) \to v_1(x_1,y_1) v0(x0,y0)v1(x1,y1)和斜率 k = y 1 − y 0 x 1 − x 0 < 1 k=\frac{y_1-y_0}{x_1-x_0}<1 k=x1x0y1y0<1
  • 格点 x x x每次增加1,格点 y y y有可能增加1
    在这里插入图片描述
    高效实现(化为整数计算)
  • 线方程 f ( x , y ) = ( x 1 − x 0 ) y − ( y 1 − y 0 ) x + b ′ f(x,y)=(x_1-x_0)y-(y_1-y_0)x+b' f(x,y)=(x1x0)y(y1y0)x+b
  • 判断 d e l t a = f ( x + 1 , y + 0.5 ) < 0 delta=f(x+1,y+0.5)<0 delta=f(x+1,y+0.5)<0
    • 初始值 f ( x 0 + 1 , y 0 + 0.5 ) f(x_0+1,y_0+0.5) f(x0+1,y0+0.5)
    • 情况一: d e l t a < 0 delta<0 delta<0
      • y = y + 1 y=y+1 y=y+1
      • d e l t a ′ = f ( x + 2 , y + 1.5 ) = d e l t a + ( x 1 − x 0 ) − ( y 1 − y 0 ) delta'=f(x+2,y+1.5)=delta+(x_1-x_0)-(y_1-y_0) delta=f(x+2,y+1.5)=delta+(x1x0)(y1y0)
    • 情况二: d e l t a ≥ 0 delta\geq 0 delta0
      • d e l t a ′ = f ( x + 2 , y + 0.5 ) = d e l t a − ( y 1 − y 0 ) delta'=f(x+2,y+0.5)=delta-(y_1-y_0) delta=f(x+2,y+0.5)=delta(y1y0)
        在这里插入图片描述

曲线光栅化

圆光栅化

在这里插入图片描述
在这里插入图片描述
只需要计算八分之一圆弧,其余可对称

在这里插入图片描述
M点在圆弧外,则y-1,否则y不变

高效实现(化为整数计算)
圆方程: f ( x , y ) = y 2 + x 2 − r 2 f(x,y)=y^2+x^2-r^2 f(x,y)=y2+x2r2

  • 判断 d e l t a = f ( x + 1 , y − 0.5 ) ≥ 0 delta=f(x+1,y-0.5)\ge 0 delta=f(x+1,y0.5)0
    • 初始值 f ( 1 , r − 0.5 ) = 1.25 − r f(1,r-0.5)=1.25-r f(1,r0.5)=1.25r
    • 情况一: d e l t a < 0 delta<0 delta<0
      • d e l t a ′ = f ( x + 2 , y − 0.5 ) = d e l t a + 2 x + 3 delta'=f(x+2,y-0.5)=delta+2x+3 delta=f(x+2,y0.5)=delta+2x+3
    • 情况二: d e l t a ≥ 0 delta\ge 0 delta0:
      • y = y − 1 y=y-1 y=y1
      • d e l t a ′ = f ( x + 2 , y − 1.5 ) = d e l t a + 2 ( x − y ) + 5 delta'=f(x+2,y-1.5)=delta+2(x-y)+5 delta=f(x+2,y1.5)=delta+2(xy)+5

插值算法

如何判断像素点是否在三角形图元内:

  1. 扫描线
    覆盖范围内每行的最左、最右边界点
    在这里插入图片描述在这里插入图片描述

  2. 根据像素的中心位置,检查是否在三角形内

  • 只看三角形的Bounding Box内的像素点
  • 只画三角形的左边和上边
    在这里插入图片描述

设计Edge Function

  • 测试点 P 在线 V 0 V 1 的左(右)侧 测试点P在线V_0V_1的左(右)侧 测试点P在线V0V1的左(右)侧
  • ∣ ∣ V 0 P → × V 0 V 1 → ∣ ∣ = ∣ ∣ V 0 P → ∣ ∣ ] ⋅ ∣ ∣ V 0 V 1 → ∣ ∣ ⋅ s i n θ ||\overrightarrow{V_0P}\times \overrightarrow{V_0V_1}||=||\overrightarrow{V_0P}||]\cdot||\overrightarrow{V_0V_1}||\cdot sin\theta ∣∣V0P ×V0V1 ∣∣=∣∣V0P ∣∣]∣∣V0V1 ∣∣sinθ
  • 不考虑Z轴

(向量叉积的正负可以判断向量夹角是顺时针还是逆时针,也可以表示向量构成的平行四边形的面积)
在这里插入图片描述

  • 按顺时针对三角形边测试,只有内部的点满足三次测试都是+
    在这里插入图片描述

片元(像素)着色

  • 所属图元的顶点信息
  • 通过插值计算光照(阴影、明暗…)
  • 纹理采样

在这里插入图片描述
重心坐标
在这里插入图片描述
根据三角形面积决定插值的参数

Edge Function

  • 判断点是否在三角形内部只需要计算正负
  • 可以计算面积,返回值是向量构成的三角形面积2倍

在这里插入图片描述
邻接三角形共同边容易被渲染两次

避免边界点渲染两次:

  • 遵循Top-Left规则,只渲染左边和上边
    在这里插入图片描述

遮挡剔除算法

渲染大量三角形图元,会有很多遮挡可能

  • 靠近摄像机的物体被渲染
  • 剔除被挡住的物体
  • 视角变动后,遮挡关系变化
  • GPU里通过Z-Buffer实现

Hidden Surface Removal算法

光纤投射 RayCasting

从每一个像素射出一条射线,找到最接近的物体
在这里插入图片描述

画家算法

先画后面的物体,再画前面的物体
在这里插入图片描述
无法处理:在这里插入图片描述

沃诺克算法

  • 分治法:四分屏幕空间
  • 细分的子空间只存在简单的前后关系
  • 使用画家算法
  • 在曲面和抗锯齿中很有用
    在这里插入图片描述

BSP Tree

分治法:用超平面递归细分空间
在每个存在前后关系的子空间里使用画家算法
可以处理带透明度的遮挡
不用每个像素点都计算Z并检测
在这里插入图片描述

Z-Buffer算法

从像素的细度考虑前后关系

  • 创建和FrameBuffer大小一样的二维数组
  • 每个元素代表对应像素的已知深度数据
  • 每遍历一个三角形
    • 获取覆盖像素的深度,比较Z-Buffer里对应的已知深度
    • 如果小于,则同时更新深度值和颜色值
      在这里插入图片描述在这里插入图片描述

深度插值

在这里插入图片描述

正确的插值公式是:
Z = 1 α Z 0 + β Z 1 + γ Z 2 Z=\frac{1}{\frac{\alpha}{Z_0}+\frac{\beta}{Z_1}+\frac{\gamma}{Z_2}} Z=Z0α+Z1β+Z2γ1
推导:
在这里插入图片描述
相当于对1/z插值

对投影三角形正确插值,需要透视纠正:重心坐标再除以对应的z值

z插值: 1 z = α 1 z 0 + β 1 z 1 + γ 1 z 2 \frac{1}{z}=\alpha\frac{1}{z_0}+\beta\frac{1}{z_1}+\gamma\frac{1}{z_2} z1=αz01+βz11+γz21
别的属性插值,比如颜色、纹理坐标: c z = α c 0 z 0 + β c 1 z 1 + γ c 2 z 2 \frac{c}{z}=\alpha\frac{c_0}{z_0}+\beta\frac{c_1}{z_1}+\gamma\frac{c_2}{z_2} zc=αz0c0+βz1c1+γz2c2

Z-Buffer in OpenGL

  • Z-Buffer初始值设为极大值
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)
  • 绘制屏幕三角形时
    • 比较覆盖像素的深度值和Z-Buffer中对应的深度值
    • 如果新像素的深度值更小,则更新为新像素的颜色
  • 使用Z-Buffer:
  1. 先激活深度测试
    glutInitDisplayMode(GLUT_DEPTH | …);
    glEnable(GL_DEPTH_TEST);
  2. 每次绘制窗口时,先清理Z-Buffer数据
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

逐片元操作

  • 片元(像素)可见性
    • 深度测试Depth Test
    • 透明度测试Alpha Test
    • 模板测试Stencil Test
  • 混合(Blending)
    • 测试通过的片元和FrameBuffer里的值混合
  • 最后通过FrameBuffer输出到屏幕

锯齿形边界Anti-Alias

采样:采样前先做模糊(或滤波)
在这里插入图片描述在这里插入图片描述

采样

采样产生的问题:

  • 锯齿问题(Jaggies)- sampling in space

  • 摩尔纹问题(Moire)- 规律性细节采样不够
    在这里插入图片描述

  • 车轮效应 - 采样频率低
    在这里插入图片描述

超采样Super Sampling

每个像素点细分为m*m像素点。以2*2为例
在这里插入图片描述
最后取每个像素块内所有像素点颜色均值

在这里插入图片描述

多重采样Multi-Sampling

  • 避免每个采样点都计算颜色
  • 颜色计算只按照中心点来
  • 覆盖两个点则相当于50%
    在这里插入图片描述

Multi-Sample Antialiasing in OpenGL

多重采样抗锯齿(MSAA)

  1. 设置glut
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_MULTISAMPLE)
  2. 设置多采样
    //Set multisampling
    glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
    glEnable(GL_MULTISAMPLE);

Anti-Alias in OpenGL

  1. 激活混合
    glEnable(GL_BLEND)
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  2. 设置反锯齿
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
    glEnable(GL_LINE_SMOOTH);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值