百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

利用html canvas实践三角形光栅化

toyiye 2024-07-06 23:32 15 浏览 0 评论

在Bresenham画线算法及实践一文中讲到了如何将理想的直线绘制到由一个个离线的像素组成的屏幕上的过程,这个过程就是光栅化。本文介绍如何实现三角形的光栅化,绘制三角形和绘制线段不同,因为三角形不仅包含线段,还涉及三角形内部的着色问题。

理解三角形的绘制原理

1. 包围三角形的矩形框

首先,可以考虑把三角形限制在一个矩形框内,只要逐一遍历矩形内的像素坐标,位于三角形内的点才进行绘制。那么接下来就需要知道如何判定矩形内的点坐标是不是在三角形内,以及这个点颜色该如何确定。

2. 重心坐标

我们先来看如何判定一个点是不是在由a、b、c三个点组成的三角形内。如下图所示,如果我们把a看作坐标原点、向量ab为横坐标且作为单位向量、向量ac为纵坐标且作为单位向量,那么实际上就它们就构成了一个非正交坐标系,β为横坐标、γ为纵坐标。


根据向量的运算法则可以得出任意点p的坐标为

变换之后可以表示为

把1-β-γ简化为α(也就是a+β+γ=1),则p点的坐标公式又可简化为

当p位于三角形内时,(α、β、γ)被称为p点的重心坐标,且0<α、β、γ<1;如果其中一个为0则落在某一边上,两个为0则落在顶点上。

我们还知道通过(x0,y0)、(x1,y1)两点的直线方程可以表示为如下方程的形式,如fca表通过点a、c的直线方程,那么所有平行于ac所在直线的坐标fca(x,y)的值与β存在线性比例关系;同样的fab(x,y)与γ存在比例关系、fbc(x,y)与α存在比例关系。

据此线性关系,α、β、γ的值可以通过p点的(x,y)坐标计算如下:

注:本文重点偏向图形学的应用,相关数学公式建议能直观地理解其含义就行,如需详细推导过程建议参阅参考文献中的章节。

3. 颜色插值

确定了点是否在三角形内部之后,我们通过如下公式来内插出该点的颜色(c0,c1、c2为三角形三个点的颜色值)

使用canvas实现三角形绘制

我们使用html canvas来编写代码实现三角形的光栅化绘制,之所以选择canvas来实践图形学,是因为它不需要任何的环境准备、工具安装,只要打开浏览器和编辑器就可以开撸代码并观察效果了,而且canvas本身提供的方法我们也只用到设置单个像素的颜色就行了,用不到其他任何额外的方法。如果你对图形学感兴趣的话,你完全可以从绘制一个简单的点开始一步步地实践3D、动画等复杂的场景是如何一步步构建起来的。

废话不多说了,下面直接列出代码及显示的效果,关键代码已做了注释,示例中绘制了三个三角形,渲染效果如下:


index.html

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>三角形光栅化</title>

</head>

<body>

    <canvas id="canvas" width="780" height="780"></canvas>

    <script src="./triangle.js"></script>

</body>

</html>

triangle.ts,这里使用的是typescript语言编写,只需要执行tsc命令编译成js文件即可

interface Point {

    x: number;

    y: number;

}

  

interface Color {

    r: number;

    g: number;

    b: number;

    a: number;

}

  
  

function add(c1: Color, c2: Color): Color {

    return {

        r: c1.r + c2.r,

        g: c1.g + c2.g,

        b: c1.b + c2.b,

        a: c1.a + c2.a

    }

}

  

function scale(c1: Color, s: number): Color {

    return {

        r: c1.r * s,

        g: c1.g * s,

        b: c1.b * s,

        a: c1.a * s

    }

}

  

class Painter {

    canvas: HTMLCanvasElement;

    ctx: any;

  

    constructor(canvas: HTMLCanvasElement) {

        this.canvas = canvas;

        this.ctx = this.canvas.getContext("2d");

    }

  

    // 设置像素坐标(x,y)颜色为c

    setPixel(x: number, y: number, c: Color = {r:255,g:255,b:255,a:1.0}) {

        let c_str = "rgba(" + c.r + "," + c.g + "," + c.b + "," + c.a + ")";

        this.ctx.fillStyle = c_str;

        this.ctx.fillRect(x, y, 1, 1);

    }

  

    // 清空画布背景为黑色

    clear() {

        this.ctx.fillStyle = "0x000000";

        this.ctx.fillRect(0, 0, this.canvas.clientWidth, this.canvas.clientHeight);

    }

  

    // 绘制三角形

    drawTriangle(p0:Point, p1:Point, p2:Point,

        c0: Color={r: 255, g: 0, b: 0, a: 1},

        c1: Color={r: 0, g: 255, b: 0, a: 1},

        c2: Color={r: 0, g: 0, b: 255, a: 1}) {

        //        p2

        //      /    \

        //     /      \

        //    p0------p1

        let [x0, y0, x1, y1, x2, y2] = [p0.x, p0.y, p1.x, p1.y, p2.x, p2.y];

        // 获取包围三角形的矩形框

        let xmin = Math.min(x0, x1, x2);

        let xmax = Math.max(x0, x1, x2);

        let ymin = Math.min(y0, y1, y2);

        let ymax = Math.max(y0, y1, y2);

  

        // 经过两点的直线方程函数形式

        // f01(x, y) = (y0 ? y1)x + (x1 ? x0)y + x0y1 ? x1y0

        let f01 = (x, y) => (y0 - y1) * x + (x1 - x0) * y + x0 * y1 - x1 * y0;

        // f12(x, y) = (y1 ? y2)x + (x2 ? x1)y + x1y2 ? x2y1

        let f12 = (x, y) => (y1 - y2) * x + (x2 - x1) * y + x1 * y2 - x2 * y1;

        // f20(x, y) = (y2 ? y0)x + (x0 ? x2)y + x2y0 ? x0y2

        let f20 = (x, y) => (y2 - y0) * x + (x0 - x2) * y + x2 * y0 - x0 * y2;

        let fa = f12(x0, y0);

        let fb = f20(x1, y1);

        let fr = f01(x2, y2);

        // 遍历矩形框内的像素坐标

        for (let y = ymin; y < ymax; y++) {

            for (let x = xmin; x < xmax; x++) {

                // 计算α、β、γ的值

                let a = f12(x, y) / fa;

                let b = f20(x, y) / fb;

                let r = f01(x, y) / fr;

                // 计算α、β、γ的范围>=0表示在三角形内、边或顶点

                if (a >= 0 && b >= 0 && r >= 0) {

                    // 如果多个三角形有存在公共边的情况下,如果不加判断就会三角形公共边多次绘制的情况

                    // 这种公共边的情况我们只选择一个三角形的边进行绘制即可

                    // 为了避免多次绘制的问题(其实从效果上看不出区别),增加的下面的判断只是为了

                    // 只绘制与坐标(-1,-1)(选择的是一个不在屏幕中的点)在同一侧的那个三角形的边

                    if ((a > 0 || fa * f12(-1, -1) > 0) &&

                        (b > 0 || fb * f20(-1, -1) > 0) &&

                        (r > 0 || fr * f01(-1, -1) > 0)) {

                            // 计算颜色插值

                            let c = add(add(scale(c0, a), scale(c1, b)), scale(c2, r));

                            // 绘制像素颜色

                            this.setPixel(x, y, c);

                        }

                }

            }

        }

  

    }

}

  

var app = function() {

    var canvas = <HTMLCanvasElement> document.getElementById("canvas");

    var painter = new Painter(canvas);

    painter.clear();

    // 绘制中间三角形

    painter.drawTriangle({x: 290, y: 490}, {x: 490, y: 490}, {x: 390, y: 290});

    // 绘制右侧三角形

    painter.drawTriangle({x: 390, y: 290}, {x: 490, y: 490}, {x: 590, y: 190});

    // 绘制左侧三角形

    painter.drawTriangle({x: 190, y: 190}, {x: 290, y: 490}, {x: 390, y: 290});

}

app();

参考文献

[1]. 《fundamentals of computer graphics》9.1.2 Triangle Rasterization, P182;

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码