一起学 WebGL:绘制三角形

news2024/11/26 15:30:44

大家好,我是前端西瓜哥。画了好几节课的点,这次我们来画三角形了。

三角形可太重要了,再复杂的三维模型都是由一个个小三角形组合而成,越多越精细越真实。

绘制三角形

这次绘制三角形,要绘制的点就有三个了,不再是一个。为此我们需要用到缓存区对象(buffer object)。

通过缓存区对象,我们可以一次性向顶点着色器传入多个顶点数据。

Float32Array

首先我们来用 Float32Array 数组保存需要用到的三个点的位置信息。

// 顶点数据
const vertices = new Float32Array([
  // 第一个点
  0, 0.5,
  // 第二个点
  -0.5, -0.5,
  // 第三个点
  0.5, -0.5
]);

为了更简单一些,这里先不考虑 z 维度,每个顶点只用了 x 和 y 分量。到时候传递到顶点着色器的 gl_Position 时,z 会自动填充默认的 0。

Float32Array 是一个比较特殊的 JavaScript 数组,最初也是为了和 WebGL 配合使用的,它可以创建一个 32 位浮点数的强类型数组。

普通的 JS 数组没有类型的概念,数组元素可能是数字、字符串、对象的混合体,传给 WebGL,要处理也麻烦,不太合适。

需要注意的是这种强类型数组的 API 和普通数组不一样,比如不能用 push 方法。

缓冲区对象的创建和数据写入

// 创建缓冲区对象
const vertexBuffer = gl.createBuffer();
// 绑定缓冲区对象到上下文
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向缓冲区对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

首先是用 gl.createBuffer 方法创建一个缓冲区对象。

然后用 gl.bindBuffer(target, buffer) 将缓存区绑定到 gl 上下文指定目标(gl.ARRAY_BUFFER)中。target 参数还有另一个值是 gl.ELEMENT_ARRAY_BUFFER,这个我们后面章节讲如何绘制立方体的时候会用到哈。

buffer 参数就是刚刚创建的缓冲区对象。

最后往缓冲区对象写入我们刚刚的数组数据。最后一个参数 gl.STATIC_DRAW,表示数据写入后不会再被修改,去绘制 静态 场景。

绑定到顶点着色器上

接着就是让缓冲区对象对接上顶点着色器的变量。

// 获取 a_Position 变量地址
const a_Position = gl.getAttribLocation(gl.program, "a_Position");
// 将缓冲区对象分配给 a_Position 变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 允许访问缓存区
gl.enableVertexAttribArray(a_Position);

首先通过 gl.getAttribLocation 拿到顶点着色器中声明的 a_Position 变量的地址。

然后是比较复杂的 gl.vertexAttribPointer 方法。参数说明:

  1. location,待分配的 attribute 变量地址;
  2. size,每个顶点分量分量个数,就是一次从中取几个作为一个顶点数据,不够的补缺省值;
  3. type,指定数据格式,gl.FLOAT 表示使用的是 Float32Array 的类型;
  4. normalize,是否将浮点数归一化到 [0, 1] 或 [-1, 1] ;
  5. stride,相邻两个顶点之间的字节数,以后用数组保存多种信息数据时会用到;
  6. offset,指定缓冲区对象中的偏移量。

目前我们只需要知道 location 和 size 就行了。最后两个参数会在绘制颜色渐变的三角形用到,这里不细说。

最后是用调用 gl.enableVertexAttribArray(a_Position),表示 a_Position 变量对应的缓冲区被开启了。开启后,我们就不能再用原来的 gl.vertexAttrib3f 来传递数据了,WebGL 会从缓存区一个个拿。如果你想关闭分配,可以调用

绘制

/*** 绘制 ***/
// 清空画布,并指定颜色
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);

最后是清空画布,然后绘制三角形。

这里 gl.drawArrays 方法的第一个 mode 参数换成了 gl.TRIANGLES(三角形图元),不再是原来的 gl.POINTS。

绘制效果:

如果用原来的 gl.POINTS,并设置好 gl_PointSize 指定点大小,则会绘制出下面的效果:

代码

下面是完整的代码实现。

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");

const vertexShaderSrc = `
attribute vec4 a_Position;
void main() {
 gl_Position = a_Position;
 gl_PointSize = 10.0;
}
`;

const fragmentShaderSrc = `
void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;

/**** 渲染器生成处理 ****/
// 创建顶点渲染器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSrc);
gl.compileShader(vertexShader);
// 创建片元渲染器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSrc);
gl.compileShader(fragmentShader);
// 程序对象
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
gl.program = program;

// 顶点数据
const vertices = new Float32Array([
  // 第一个点
  0,
  0.5,
  // 第二个点
  -0.5,
  -0.5,
  // 第三个点
  0.5,
  -0.5
]);

// 创建缓存对象
const vertexBuffer = gl.createBuffer();
// 绑定缓存对象到上下文
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向缓存区写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

// 获取 a_Position 变量地址
const a_Position = gl.getAttribLocation(gl.program, "a_Position");
// 将缓冲区对象分配给 a_Position 变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

// 允许访问缓存区
gl.enableVertexAttribArray(a_Position);

/*** 绘制 ***/
// 清空画布,并指定颜色
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);

在线 demo:

https://codesandbox.io/s/gbh1xf?file=/index.js

结尾

我是前端西瓜哥,欢迎关注我,学习更多 WebGL 知识。

下一节,我们讲讲 WebGL 支持绘制的图元有哪些,可以通过怎么的模式绘制点、线、三角形。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/429221.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言之 单链表1(simply linked list)

单链表 链表优点: 1.按需申请空间,需要就申请,不需要就释放 2.头部或中间插入数据,不需要挪动数据 3.不存在空间浪费 缺点: 1.每次存放一个数据,到要存一个指针去链接后面的数据节点 2.不支持随机访问&a…

让你的ChatGPT更加强大——200+小白用来解锁ChatGPT高级功能的提示(Prompts)

让你的ChatGPT更加强大——200小白用来解锁ChatGPT高级功能的提示(Prompts)使用说明标签筛选关键词搜索展示区复制语言切换常见问题为什么提示词用英文?中文搜索出错输出虚假信息提示词不好用为什么执着于 ChatGPT?最后参考博客其…

Vue.js 2.0 单文件组件

Vue.js 2.0 单文件组件介绍 在很多Vue项目中,我们使用 Vue.component 来定义全局组件,紧接着用 new Vue({ el: #container }) 在每个页面内指定一个容器元素。 这种方案在只是使用 JavaScript 增强某个视图的中小型项目中表现得很好。然而在更复杂的项…

对象树、QT的坐标系、信号和槽机制

目录 1、QT中什么是对象树 2、QT的坐标系 3、信号和槽机制 3.1、信号槽的理解 3.2、信号槽的工作原理 3.3、信号槽的使用 3.3.1、系统的信号和槽 3.3.2、自定义信号和槽函数 3.3.3、信号和槽函数之间的参数传递 3.3.4、信号和槽的注意 1、QT中什么是对象树 在创建 QO…

重构·改善既有代码的设计.04之重构手法(下)完结

1. 前言 本文是代码重构系列的最后一篇啦。前面三篇《重构改善既有代码的设计.01之入门基础》、《重构改善既有代码的设计.02之代码的“坏味道”》、《重构改善既有代码的设计.03之重构手法(上)》介绍了基础入门,代码异味,还有部…

如何在ubuntu上搭建minio

由于腾讯的对象存储服务器(COS)的半年免费试用期已过,所以寻思鼓捣一下minio,试着在自己的服务器上搭建一套开源的minio对象存储系统。 单机部署基本上有以下两种方式。 一、直接安装 最基础的一种单机安装,这里不做…

记录-js基础练习题

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 隔行换色(%): window.onload function() {var aLi document.getElementsByTagName(li);for(var i 0; i < aLi.length; i){if(i%2 1){aLi[i].style.background #bfa;}} } <ul><li>…

微搭低代码学习之基础操作

低代码开发平台&#xff0c;是一种方便产生应用程序的平台软件&#xff0c;软件会开发环境让用户以图形化接口以及配置编写程序&#xff0c;而不是用传统的程序设计作法。此平台可能是针对 某些种类的应用而设计开发的&#xff0c;例如数据库、业务过程、以及用户界面。这类平台…

STM32锁芯片,解锁方法(J-LINK)

在烧写STM32时&#xff0c;不小心把usb口给弄掉了&#xff0c;然后就尴尬了&#xff0c;芯片被锁了。 烧写的时候报错&#xff0c;如下&#xff1a; 然后就搜了一下&#xff0c;大致有两个方法&#xff0c; 第一种需要通过接3.3V到板子上BOOT0重新上电第二种是直接通过软件…

Golang语言Windows环境搭建(Visual Studio Code)

一、Golang语言简介 二、Windows环境搭建 1、软件下载 Golang语言安装包官网下载地址&#xff1a;https://golang.google.cn/dl/ Visual Studio Code编辑器下载&#xff1a;https://code.visualstudio.com/ 2、Golang安装及环境变量配置 2.1 Golang语言安装包 双击安装…

可配置物料-文章资料分享

可配置物料项目一般很少用到&#xff0c;用到就是要命&#xff0c;推荐一下之前查资料收集的资料&#xff0c;分享给大家。感谢这位大佬收集的文章&#xff01; Variant Configuration (LO-VC) - Product Lifecycle Management - Support Wikihttps://archive.sap.com/document…

5V的LDO电源的WCCA分析-可靠性分析计算过程

WCCA(WorstCase Circuit Analysis)分析方法是一种电路可靠性分析设计技术&#xff0c;用来评估电路中各个器件同时发生变化时的性能&#xff0c;用于保证设计电路在整个生命周期的都可以可靠工作。通过WCCA分析&#xff0c;验证在上述参数在其容差范围内发生变化时&#xff0c;…

C++模板基础(八)

数值模板参数与模板模板参数 ● 模板可以接收&#xff08;编译期常量&#xff09;数值作为模板参数 – template class Str; template<int a> int fun(int x) {return x a; }int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);std::cout << fun…

相机SD卡无法读取提示格式化 相机SD卡无法读取怎么修复

相机SD卡中储存着的照片和视频&#xff0c;承载着我们美好的回忆。因为相机SD卡的容量有限&#xff0c;我们会定期对SD卡中的数据进行云盘备份&#xff0c;然后清理相机SD卡中的数据。在打开相机SD卡时&#xff0c;可能会遇到SD卡无法读取的情况。那么&#xff0c;相机SD卡无法…

Umi‘s Friends 冠名 VC MeetUP 酒会,圆满举办!

香港加密新政的整体目的是令虚拟资产交易明确化和合法化&#xff0c;通过不断完善的监管框架&#xff0c;促进香港虚拟资产行业的可持续和负责任地发展。在加强合规和持牌经营的监管思路下&#xff0c;长期审慎合规经营的老牌机构和项目&#xff0c;显然将获得先发优势。随着香…

开放式蓝牙耳机哪个好,分享几款舒适性高的开放式蓝牙耳机

开放式耳机的兴起是近几年来才出现的新概念&#xff0c;开放式耳机也是近几年来才开始流行起来&#xff0c;在我看来开放式耳机的兴起是科技进步的产物。随着蓝牙耳机技术和设备的发展&#xff0c;蓝牙耳机也越来越普及&#xff0c;但是也给用户带来了很多困扰。而开放式耳机就…

前端学习:HTML链接

目录 一、HTML超链接&#xff08;链接&#xff09; 二、HTML链接语法 三、target属性 target属性值展示 四、name属性 五、补充 关于创建电子邮件链接时如何发送邮件内容 在进行抄送时&#xff0c;需要使用关键字&#xff1a;cc 在进行密送时&#xff0c;需要使用关键字&a…

2.rabbitmq-linux安装

目录 一.环境准备 二.安装 一.环境准备 1.RabbitMQ版本 和 Erlang 版本兼容性关系 https://www.rabbitmq.com/which-erlang.html 2.官方安装包下载地址 【erlang下载地址】&#xff1a;https://github.com/rabbitmq/erlang-rpm/releases/tag/v21.3.1【rabbitmq下载地址】&a…

初识视觉SLAM(视觉SLAM十四讲ch1~ch2的收获与总结)

视觉SLAM14讲ch1和ch2的学习视觉SLAM14讲ch1和ch2的学习前言&#xff1a;一、SLAM是什么二、视觉SLAM14讲学习前的基础三、初步了解1. 小萝卜的例子2. 了解一些视觉SLAM的框架3. 一些数学问题的表述视觉SLAM14讲ch1和ch2的学习 前言&#xff1a; 开始学习视觉SLAM&#xff0c…

MMDetection 3.x 自定义数据集

最近在调研目标检测方面的技术实现&#xff0c;发现关于 MMDetection 自定义数据集的文章&#xff0c;多是就 2.x 版本进行描述的&#xff0c;而关于 3.x 版本的则比较少。有些文章在制作COCO数据集的时候&#xff0c;需要修改多处源代码&#xff0c;而这是没有必要的。本文主要…