【十天自制软渲染器】DAY 03:画一个三角形(向量叉乘算法 & 重心坐标算法)

这篇博客介绍了如何利用向量叉乘和三角形重心坐标算法来绘制三角形,详细讲解了数学推导和代码实现,包括如何判断点与三角形的位置关系以及如何优化绘制效率。
摘要由CSDN通过智能技术生成

前面两天画了点和线,今天我们来画一个最简单也是最强大的面——三角形

本文主要讲解三角形绘制算法的推导和思路(只涉及到一点点的向量知识),最后会给出代码实现,大家放心的看下去就好。


本文源码 👉:toyRenderer-day3-draw-triangle

1.如何画一个三角形?

在正式开始这一小节前,我们先想一下如何利用上一节的画线算法绘制一个实心的三角形。

假设现在平面内有三个不共线的点组成一个三角形,我们可以利用上一节的直线算法轻易的连接三角形的三条边,这时候我们会生成一个空心的封闭的三角形。

那么这时候问题就转换为,如何把这个空心的三角形变为一个实心的三角形?

我想大家这时候已经有思路了,就是一行一行地扫描像素,把两个边界点之间的像素全部涂满上色就可以了。

day03_scanLineDrawTriangle

这个方法肯定是可以的,但是实现不是很优雅,也不是业内的主流实现方式。因为基于行扫描的算法不是本文的重点,所以详细的推导和代码实现就不提供了,感兴趣的同学可以自己尝试实现一下。

2.利用向量叉乘画三角形

开始本节前先简单复习一下向量叉乘的几何意义。

2.1 数学推导

在三维空间中,两个三维向量 a \mathbf {a} a b \mathbf {b} b 做叉乘,会得到一个和已知两个向量垂直的新向量 a × b \mathbf {a} \times \mathbf {b} a×b

既然叉乘产生的是一个新向量,那么它肯定有个方向,我们一般用右手定则来判断:将右手食指指向 a \mathbf {a} a 的方向、中指指向 b \mathbf {b} b 的方向,则此时拇指的方向即为 a × b \mathbf {a} \times \mathbf {b} a×b 的方向。

右手定则

综上所述,我们可以对向量叉乘做一个严谨的定义:

a × b = ∥ a ∥ ∥ b ∥ sin ⁡ ( θ )   n {\displaystyle \mathbf {a} \times \mathbf {b} =\|\mathbf {a} \|\|\mathbf {b} \|\sin(\theta )\ \mathbf {n} } a×b=absin(θ) n

其中 θ \theta θ 表示 a \mathbf {a} a b \mathbf {b} b 在它们所定义的平面上的夹角( 0 ∘ ≤ θ ≤ 18 0 ∘ 0^{\circ }\leq \theta \leq 180^{\circ} 0θ180)。 ∥ a ∥ {\displaystyle \|\mathbf {a} \|} a ∥ b ∥ \displaystyle \|\mathbf {b} \| b 是向量 a \mathbf {a} a b \mathbf {b} b 的模长,而 n \mathbf{n} n 则是一个与 a \mathbf {a} a b \mathbf {b} b 所构成的平面垂直的单位向量,方向由右手定则决定。


有上面的理论,我们就可以判断两个向量的相对位置:

  • a \mathbf {a} a 向量叉乘 b \mathbf {b} b 向量,如果值为,则表示 b \mathbf {b} b 向量在 a \mathbf {a} a 向量
  • a \mathbf {a} a 向量叉乘 b \mathbf {b} b 向量,如果值为,则表示 b \mathbf {b} b 向量在 a \mathbf {a} a 向量
  • a \mathbf {a} a 向量叉乘 b \mathbf {b} b 向量,如果值为,则表示 b \mathbf {b} b 向量与 a \mathbf {a} a 向量共线

会判断两条线的相对位置了,我们可以做个理论迁移,利用向量叉乘判断点和三角形的位置关系

例如下面这里例子,对于三角形 Δ A B C \Delta ABC ΔABC 来说,把三条边看作 A B → \overrightarrow{A B} AB B C → \overrightarrow{B C} BC C A → \overrightarrow{C A} CA 三条首尾相连的向量,平面内有一个点 P P P,我们通过向量叉乘来判断相对位置:

day03_cross_product

  • A B → × A P → \overrightarrow{A B} \times \overrightarrow{A P} AB ×AP ,值为,故 P P P A B AB AB 左侧
  • B C → × B P → \overrightarrow{B C} \times \overrightarrow{B P} BC ×BP ,值为,故 P P P B C BC BC 左侧
  • C A → × C P → \overrightarrow{C A} \times \overrightarrow{C P} CA ×CP ,值为,故 P P P A C AC AC 左侧

综合以上三个限制条件,我们可以判断 P P P Δ A B C \Delta ABC ΔABC 内。

如果上面三个计算中有值为负的情况,说明 P P P 在三角形外;如果有值为 0 的情况,说明 P P P 在三角形的边或顶点上。

2.2 代码实现

理论基础复习完了,我们就可以写代码了。代码实现相当简单,我们构建一个函数 crossProduct,传入三角形的三个顶点和平面上的任意一点 P P P,然后根据四个顶点构建出向量计算叉乘就可以了:

// 利用叉乘判断是否在三角形内部
Vec3i crossProduct(Vec2i *pts, Vec2i P) {
   
    // 构建出三角形 ABC 三条边的向量
    Vec2i AB(pts[1].x - pts[0].x, pts[1].y - pts[0].y);
    Vec2i BC(pts[2].x - pts[1].x, pts[2].y - pts[1].y);
    Vec2i CA(pts[0].x - pts[2].x, pts[0].y - pts[2].y);
    
    // 三角形三个顶点和 P 链接形成的向量
    Vec2i AP(P.x - pts[0].x, P.y - pts[0].y);
    Vec2i BP(P.x - pts[1].x, P.y - pts[1].y);
    Vec2i CP(P.x - pts[2].x, P.y - pts[2].y);
    
    return Vec3i(AB^AP, BC^BP, CA^CP);
}

代码非常的简单,我们跑一个简单的例子验证一下:

void drawSingleTriangle() {
   
    // 图片的宽高
    int width  = 200;
    int height = 200;

    TGAImage frame(width, height, TGAImage::RGB);
    Vec2i pts[3] = {
   Vec2i(10, 10), Vec2i(150, 30)
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值