山东大学计算机图形学期末复习9——CG12上

CG12上

  • 几何管线末端:顶点已组装成基本图元(Primitives,如点、线、三角形)。

    主要任务:

    • 裁剪:视锥体是一个三维空间区域(由近裁剪面、远裁剪面和侧面组成),超出该区域的图元需要被裁剪或剔除,以避免浪费计算资源。

      光栅化:将裁剪后的图元转化为屏幕上的像素(称为片段,Fragments)。这一过程也叫扫描转换(Scan Conversion),因为它涉及将连续的几何形状映射到离散的像素网格。

Cohen-Sutherland 2D线段裁剪算法概述

Cohen-Sutherland算法是计算机图形学中的一个经典算法,主要用于对2D图形进行裁剪。该算法通过简单的区域编码和逻辑运算高效地判断哪些线段与裁剪窗口相交,从而确定是否需要进行裁剪。通过这种方法,算法能在不计算每个交点的情况下迅速排除不需要裁剪的线段。

暴力裁剪与其问题

在暴力裁剪方法中,线段需要与裁剪窗口的四条边进行交点计算,检查每条边是否与线段相交。这意味着对于每条线段,我们需要多次计算交点,这会带来较大的计算负担,特别是在处理大量线段时。由于交点计算涉及浮点数除法,效率较低,因此我们需要一种更高效的方法。

Cohen-Sutherland算法的基本思路

Cohen-Sutherland算法通过区域编码(Outcode)来快速判断线段与裁剪窗口的关系。裁剪窗口由四条边界线定义,分别是:

  • x = x min x = x_{\text{min}} x=xmin(左边界)
  • x = x max x = x_{\text{max}} x=xmax(右边界)
  • y = y min y = y_{\text{min}} y=ymin(下边界)
  • y = y max y = y_{\text{max}} y=ymax(上边界)

算法的核心思想是利用区域编码来对每个线段的两个端点进行标记,并根据这些编码值来判断线段是否在窗口内、完全在窗口外,或者部分与窗口相交。通过这种方式,算法避免了不必要的交点计算,显著提高了效率。

区域编码(Outcode)

区域编码是一个4位二进制数,每一位表示端点相对于裁剪窗口四条边的情况:

  • 第1位:表示端点是否在 y max y_{\text{max}} ymax 以上
  • 第2位:表示端点是否在 y min y_{\text{min}} ymin 以下
  • 第3位:表示端点是否在 x max x_{\text{max}} xmax 右侧
  • 第4位:表示端点是否在 x min x_{\text{min}} xmin 左侧

通过这种编码,我们可以快速判定端点的位置,并根据这些信息判断线段的裁剪情况。

Cohen-Sutherland算法的步骤

  1. 计算区域编码:对线段的两个端点计算区域编码。
  2. 直接接受:如果两个端点的编码都为0000,说明线段完全在窗口内,可以直接接受,无需裁剪。
  3. 直接拒绝:如果两个端点的编码通过按位与运算(AND)得到非零值,说明线段完全在窗口外,可以直接丢弃。
  4. 需要裁剪:如果线段的两个端点一个在窗口内,另一个在窗口外,或者两个端点都在窗口外但位于不同区域,那么需要计算交点。
具体的裁剪步骤
  • 步骤1:计算两端点的区域编码。如果两端点的编码都是0000,接受线段;如果按位与(AND)后不为0,直接拒绝该线段。
  • 步骤2:如果需要裁剪,选择一个在窗口外的端点,计算它与窗口边界的交点。
  • 步骤3:将交点替代外侧端点,重新计算该端点的区域编码,并返回步骤1。如果仍需要裁剪,则继续迭代。

Cyrus-Beck算法

Cyrus-Beck算法思想-CSDN博客

Cyrus-Beck 算法是一个用于线段与凸多边形裁剪的经典算法,适用于图形学中各种裁剪问题。算法的核心思想是利用向量运算和参数化技术来判断线段与多边形边的交点,从而高效地裁剪出符合条件的线段。我们将根据以下步骤来详细讲解其原理与实现。

1. 线段与多边形的交点计算

首先,我们将线段通过参数方程表示。线段 P 0 P 1 P_0P_1 P0P1 的方程可以写成:
P ( t ) = P 0 + t ( P 1 − P 0 ) P(t) = P_0 + t (P_1 - P_0) P(t)=P0+t(P1−P0)

其中, P 0 P_0 P0 和 P 1 P_1 P1 分别是线段的起点和终点, t t t 是参数,表示线段上的任意一点。当 t t t 从 0 变化到 1 时, P ( t ) P(t) P(t) 代表从 P 0 P_0 P0 到 P 1 P_1 P1 的整条线段。

对于多边形的每条边,我们也可以将其表示为参数方程。因此,求解交点,也就是求解t,需要把线段的方程带入到交点的等式当中。见下图:

2. 向量点积与线段与边的关系判断

Cyrus-Beck 算法利用向量点积来判断线段端点相对于多边形边的位置。对于每条多边形边,我们有一个法向量 N \mathbf{N} N,它垂直于该边。线段上的任意一点 P ( t ) P(t) P(t) 相对于该边的位置可以通过计算以下表达式来判断:
N ⋅ ( P ( t ) − A ) \mathbf{N} \cdot (P(t) - A) N⋅(P(t)−A)

其中, A A A 是多边形边上的一个点, N \mathbf{N} N 是该边的法向量, P ( t ) P(t) P(t) 是当前线段上的点。

  • 如果 N ⋅ ( P ( t ) − A ) > 0 \mathbf{N} \cdot (P(t) - A) > 0 N⋅(P(t)−A)>0,则点 P ( t ) P(t) P(t) 位于多边形边的外侧。
  • 如果 N ⋅ ( P ( t ) − A ) = 0 \mathbf{N} \cdot (P(t) - A) = 0 N⋅(P(t)−A)=0,则点 P ( t ) P(t) P(t) 在多边形边上或边的延长线上。
  • 如果 N ⋅ ( P ( t ) − A ) < 0 \mathbf{N} \cdot (P(t) - A) < 0 N⋅(P(t)−A)<0,则点 P ( t ) P(t) P(t) 位于多边形的内侧。

通过这一方法,Cyrus-Beck 算法能够快速判断一个线段是否完全在多边形内,或者是否需要进一步进行裁剪。

3. 从起点组里选择最大值,从终点组中选择最小值来作为线段和多边形实际的交点
4. 伪代码
复制代码
for (k edges of clipping polygon)
{
    solve Ni·(p1-p0);
    solve Ni.(p0-Ai);
    if ( Ni·(p1-p0) == 0) //parallel with the edge
    {
        if ( Ni.(p0-Ai) < 0 )
            break; /linvisible
        else
            go to next edge;
    }
    else // Ni·(p1-p0) != 0
    {
        solve ti;
        if ( Ni (p1-p0) < 0)
                te = min(1, min(ti, te));
        else
                ts = max(0, max(ti, ts));
    }
}

解析:

目标与方法总览

我们不想用暴力法去求线段与每条边的交点,而是想用数学的方法判断线段与边界的关系

Cyrus-Beck 算法的做法是:

➤ 用 参数 t ∈ [0, 1] 来表示线段上的点:
  • 线段用参数方程表示:
    P ( t ) = P 0 + t ( P 1 − P 0 ) P(t) = P_0 + t(P_1 - P_0) P(t)=P0+t(P1−P0)

    • 当 t = 0 t = 0 t=0 时, P ( t ) = P 0 P(t) = P_0 P(t)=P0
    • 当 t = 1 t = 1 t=1 时, P ( t ) = P 1 P(t) = P_1 P(t)=P1
    • 当 t ∈ ( 0 , 1 ) t \in (0, 1) t∈(0,1) 时, P ( t ) P(t) P(t) 是在线段之间的点
➤ 遍历多边形的每一条边,根据边的法向量 N i N_i Ni,判断:
  • 线段是从边的外部进入(潜在进入点 PE)
  • 还是从内部离开(潜在离开点 PL)
关键思路:如何判断是进入还是离开?

我们有三样东西:

  • d ⃗ = P 1 − P 0 \vec{d} = P_1 - P_0 d =P1−P0:线段方向向量
  • N ⃗ i \vec{N}_i N i:多边形某一条边的外法向量
  • A i A_i Ai:该边上的某个点

计算两个点积:

含义
N ⃗ i ⋅ d ⃗ \vec{N}_i \cdot \vec{d} N i⋅d 判断线段是否与边平行/进入/离开
N ⃗ i ⋅ ( P 0 − A i ) \vec{N}_i \cdot (P_0 - A_i) N i⋅(P0−Ai) 判断起点是在边的哪一侧
伪代码结构分析

我们现在一步步解释这段伪代码是怎么构建的。

初始化:准备裁剪
复制代码
for (k edges of clipping polygon)
  • 遍历多边形的每条边(比如 6 边形就要处理 6 次)
Step 1:计算两个点积
复制代码
solve Ni·(p1 - p0);   // 线段方向与边的法向量的点积
solve Ni·(p0 - Ai);   // 起点到边上的点,与边的法向量点积
  • 第一项告诉我们线段与边是否平行 或者是否穿过边界
  • 第二项判断起点在边的哪一侧
Step 2:判断平行情况
复制代码
if (Ni·(p1 - p0) == 0)
{
    if (Ni·(p0 - Ai) < 0)
        break; // Line is completely outside polygon
    else
        go to next edge;
}

如果线段与边平行:

  • 并且在外侧:直接结束,线段不可见(完全在外侧)
  • 在内侧或边上:继续检查下一条边
Step 3:计算交点参数 t i t_i ti

如果线段不平行,就有可能与该边相交:

复制代码
solve ti;

我们要求解:
t i = N ⃗ i ⋅ ( A i − P 0 ) N ⃗ i ⋅ ( P 1 − P 0 ) t_i = \frac{ \vec{N}_i \cdot (A_i - P_0) }{ \vec{N}_i \cdot (P_1 - P_0) } ti=N i⋅(P1−P0)N i⋅(Ai−P0)

这个公式就是将 P ( t ) P(t) P(t) 代入边的平面方程,求得交点参数 t t t。

Step 4:分类交点类型(PE 或 PL)
复制代码
if (Ni·(p1 - p0) < 0)
    te = min(1, min(ti, te)); // 潜在离开点
else
    ts = max(0, max(ti, ts)); // 潜在进入点
  • 如果法向量朝着线段的前进方向(dot product < 0),说明线段会离开 多边形 => 更新离开点 te
  • 如果法向量背对线段的方向(dot product > 0),说明线段从这里进入 多边形 => 更新进入点 ts

我们要在所有边中,找到:

  • 最大的进入点 t s t_s ts
  • 最小的离开点 t e t_e te
最后输出裁剪结
复制代码
if (ts > te)
    return nil; // 不可见,进入在离开之后
else
    return P(ts), P(te); // 可见线段部分

如果进入点 t s ts ts 比离开点 t e te te 晚,说明线段只在外部滑过,没有进入 => 剪裁结果为空。

否则,返回的是裁剪后保留在线段上的区间 [ t s , t e ] [ts, te] [ts,te],即:
P ( t s ) = P 0 + t s ( P 1 − P 0 ) P ( t e ) = P 0 + t e ( P 1 − P 0 ) P(t_s) = P_0 + t_s (P_1 - P_0) \\ P(t_e) = P_0 + t_e (P_1 - P_0) P(ts)=P0+ts(P1−P0)P(te)=P0+te(P1−P0)

Liang-Barsky Algorithm

一种特殊情况,即多边形为矩形。参考这篇写得极好的博客,这里不做介绍了。

[OpenGL\]计算机图形学:直线裁剪算法中Cohen-Sutherland算法和Liang-Barsky算法_opengl梁友栋裁剪算法例题-CSDN博客](https://blog.csdn.net/pleasecallmewhy/article/details/8393445) ##### 裁剪窗口的边界 我们考虑的窗口是矩形,由四条边组成: * 左边界: x = x L x = x_L x=xL * 右边界: x = x R x = x_R x=xR * 下边界: y = y B y = y_B y=yB * 上边界: y = y T y = y_T y=yT *** ** * ** *** ##### 线段参数表示法 我们把线段表示为参数形式: P ( t ) = ( x ( t ) , y ( t ) ) = ( x 1 + t Δ x , y 1 + t Δ y ) , 0 ≤ t ≤ 1 P(t) = (x(t), y(t)) = (x_1 + t \\Delta x, y_1 + t \\Delta y), \\quad 0 \\le t \\le 1 P(t)=(x(t),y(t))=(x1+tΔx,y1+tΔy),0≤t≤1 其中: Δ x = x 2 − x 1 , Δ y = y 2 − y 1 \\Delta x = x_2 - x_1, \\quad \\Delta y = y_2 - y_1 Δx=x2−x1,Δy=y2−y1 我们希望找到参数 t t t 的范围,使得 P ( t ) P(t) P(t) 落在矩形窗口内。 *** ** * ** *** ##### 与每条边相交的条件推导 我们来看看线段与窗口的**每条边**相交的条件: *** ** * ** *** ##### 与左边界 x = x L x = x_L x=xL 相交 我们想找出线段进入或离开左边界的点。 设当前点为: P ( t ) = ( x 1 + t Δ x , y 1 + t Δ y ) P(t) = (x_1 + t \\Delta x, y_1 + t \\Delta y) P(t)=(x1+tΔx,y1+tΔy) 条件: x ( t ) ≥ x L ⇒ x 1 + t Δ x ≥ x L x(t) \\ge x_L \\quad \\Rightarrow \\quad x_1 + t \\Delta x \\ge x_L x(t)≥xL⇒x1+tΔx≥xL 变形: t ≥ x L − x 1 Δ x 当 Δ x \> 0 ⇒ 进入点 t \\ge \\frac{x_L - x_1}{\\Delta x} \\quad \\text{当} \\quad \\Delta x \> 0 \\Rightarrow \\text{进入点} t≥ΔxxL−x1当Δx\>0⇒进入点 如果 Δ x \< 0 \\Delta x \< 0 Δx\<0,方向相反,说明是**离开点**。 我们定义如下两个变量: * r k = − Δ x r_k = -\\Delta x rk=−Δx * s k = x 1 − x L s_k = x_1 - x_L sk=x1−xL 于是 t k = s k r k = x 1 − x L − Δ x t_k = \\frac{s_k}{r_k} = \\frac{x_1 - x_L}{- \\Delta x} tk=rksk=−Δxx1−xL *** ** * ** *** ##### 与右边界 x = x R x = x_R x=xR 相交 推导类似: x ( t ) ≤ x R ⇒ x 1 + t Δ x ≤ x R ⇒ t ≤ x R − x 1 Δ x x(t) \\le x_R \\quad \\Rightarrow \\quad x_1 + t \\Delta x \\le x_R \\Rightarrow t \\le \\frac{x_R - x_1}{\\Delta x} x(t)≤xR⇒x1+tΔx≤xR⇒t≤ΔxxR−x1 记: * r k = Δ x r_k = \\Delta x rk=Δx * s k = x R − x 1 s_k = x_R - x_1 sk=xR−x1 * t k = s k r k t_k = \\frac{s_k}{r_k} tk=rksk *** ** * ** *** ##### 与下边界 y = y B y = y_B y=yB 同理,要求: y ( t ) ≥ y B ⇒ t ≥ y B − y 1 Δ y y(t) \\ge y_B \\quad \\Rightarrow \\quad t \\ge \\frac{y_B - y_1}{\\Delta y} y(t)≥yB⇒t≥ΔyyB−y1 记: * r k = − Δ y r_k = -\\Delta y rk=−Δy * s k = y 1 − y B s_k = y_1 - y_B sk=y1−yB * t k = s k r k t_k = \\frac{s_k}{r_k} tk=rksk *** ** * ** *** ##### 与上边界 y = y T y = y_T y=yT 同理: y ( t ) ≤ y T ⇒ t ≤ y T − y 1 Δ y y(t) \\le y_T \\Rightarrow t \\le \\frac{y_T - y_1}{\\Delta y} y(t)≤yT⇒t≤ΔyyT−y1 记: * r k = Δ y r_k = \\Delta y rk=Δy * s k = y T − y 1 s_k = y_T - y_1 sk=yT−y1 * t k = s k r k t_k = \\frac{s_k}{r_k} tk=rksk ##### 综合表示 我们整理得到: { r 1 = − Δ x , s 1 = x 1 − x L r 2 = Δ x , s 2 = x R − x 1 r 3 = − Δ y , s 3 = y 1 − y B r 4 = Δ y , s 4 = y T − y 1 ⇒ t k = s k r k , k = 1 , 2 , 3 , 4 \\begin{cases} r_1 = -\\Delta x, \& s_1 = x_1 - x_L \\\\ r_2 = \\Delta x, \& s_2 = x_R - x_1 \\\\ r_3 = -\\Delta y, \& s_3 = y_1 - y_B \\\\ r_4 = \\Delta y, \& s_4 = y_T - y_1 \\\\ \\end{cases} \\quad\\Rightarrow\\quad t_k = \\frac{s_k}{r_k}, \\quad k = 1,2,3,4 ⎩ ⎨ ⎧r1=−Δx,r2=Δx,r3=−Δy,r4=Δy,s1=x1−xLs2=xR−x1s3=y1−yBs4=yT−y1⇒tk=rksk,k=1,2,3,4 * 如果 r k \< 0 r_k \< 0 rk\<0:表示线段**进入**区域(进入点) * 如果 r k \> 0 r_k \> 0 rk\>0:表示线段**离开**区域(离开点) * 如果 r k = 0 r_k = 0 rk=0 且 s k \< 0 s_k \< 0 sk\<0:线段平行并**在边界外**,不可见 * 如果 r k = 0 r_k = 0 rk=0 且 s k ≥ 0 s_k \\ge 0 sk≥0:线段平行但**在边界内**,处理其他边

相关推荐
平和男人杨争争6 小时前
山东大学计算机图形学期末复习2——CG01至CG03
图形渲染
平和男人杨争争11 小时前
山东大学计算机图形学期末复习7——CG11上
图形渲染
顾子茵1 天前
计算机图形学基础--Games101笔记(一)数学基础与光栅化
笔记·图形渲染
GISer_Jing1 天前
前端图形渲染 html+css、canvas、svg和webgl绘制详解,各个应用场景及其区别
前端·html·图形渲染
龙湾开发2 天前
计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 10.增强表面细节(一)过程式凹凸贴图
c++·笔记·学习·3d·图形渲染
像素工坊可视化2 天前
WebGL 开发前沿:探索未来图形渲染的新可能
图形渲染·webgl
六bring个六4 天前
qtcreater配置opencv
c++·qt·opencv·计算机视觉·图形渲染·opengl
龙湾开发5 天前
计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 09.天空和背景
c++·笔记·学习·3d·图形渲染
龙湾开发8 天前
计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 05.纹理贴图
c++·笔记·学习·3d·图形渲染·贴图