一起学 WebGL:三角形加上渐变色

news2024/12/22 20:43:24

大家好,我是前端西瓜哥。之前教大家绘制一个红色的三角形,这次我们来画个有渐变的三角形。

本文为系列文章,请先阅读如何绘制红色三角形的文章:

《一起学 WebGL:绘制三角形》

原来的写法,颜色是在片元着色器中写死的,这次我们来像传顶点数据一样,声明一个颜色数据传递过去。

颜色需要在片元着色器中赋值给内部变量 gl_FragColor,但 attribute 动态类型却不能在片元着色器中使用。

这时候就要用到一个新的类型 varying 了。(意思为:“变化的“)

varying 用于从顶点着色器中将变量传递到片元着色器中

两个缓冲区对象的写法

着色器代码:

const vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
 gl_Position = a_Position;
 v_Color = a_Color;
}
`;

const fragmentShaderSrc = `
precision mediump float;
varying vec4 v_Color;
void main() {
  gl_FragColor = v_Color;
}
`;

这里我们需要在两种着色器中同时声明 varing 变量,后面的类型也必须是相同的 vec4,变量名也要一致,只能说是完全想通了。

顶点着色器中需要通过 v_Color = a_Color; 赋值。然后在片元着色器中,再将同步过来的 v_Color 赋值给 gl_FragColor。

然后是新增的颜色数组的声明,以及对应缓存区的创建。

/**** 颜色数据 ****/
// prettier-ignore
const colors = new Float32Array([
  1, 0, 0, // 红色
  0, 1, 0, // 绿色
  0, 0, 1, // 蓝色
])
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
const a_Color = gl.getAttribLocation(gl.program, "a_Color");
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Color);

贴一下完整代码:

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

const vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
 gl_Position = a_Position;
 v_Color = a_Color;
}
`;

const fragmentShaderSrc = `
precision mediump float;
varying vec4 v_Color;
void main() {
  gl_FragColor = v_Color;
}
`;

/**** 渲染器生成处理 ****/
// 创建顶点渲染器
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;

// 顶点数据
// prettier-ignore
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);

/**** 颜色数据 ****/
// prettier-ignore
const colors = new Float32Array([
  1, 0, 0, // 红色
  0, 1, 0, // 绿色
  0, 0, 1, // 蓝色
])
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
const a_Color = gl.getAttribLocation(gl.program, "a_Color");
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Color);

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

demo 地址:

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

渲染结果:

我们其实只是给三个顶点设置了红、绿、蓝三个颜色,然后 WebGL 会基于它们计算出中间的过内插颜色,将它们填充满三个点围成区域的像素点。

单缓冲区的实现

前面的实现用了两个缓冲区对象分别保存位置信息和颜色信息。

但实际上可以将它们组合在一起,让数据更紧凑放在一个缓冲区里。

浮点数数组为:

// prettier-ignore
const verticesColors = new Float32Array([
  0, 0.5, 1, 0, 0,  // 点 1 的位置和颜色信息
  -0.5, -0.5, 0, 1, 0,  // 点 2
  0.5, -0.5, 0, 0, 1,  // 点 3
]);

然后是和前一种写法有一些不同的地方:

// 每个数组元素的字节数
const SIZE = verticesColors.BYTES_PER_ELEMENT;

// 获取 a_Position 变量地址
const a_Position = gl.getAttribLocation(gl.program, "a_Position");
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, SIZE * 5, 0);
gl.enableVertexAttribArray(a_Position);

const a_Color = gl.getAttribLocation(gl.program, "a_Color");
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, SIZE * 5, SIZE * 2);
gl.enableVertexAttribArray(a_Color);

主要是 gl.vertexAttribPointer 方法的最后两个参数 stride 和 offset。

我们看下面这个:

gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, SIZE * 5, SIZE * 2);

stride 为 SIZE * 5(单位为字节,所以要乘以一个数组元素的字节大小),表示 5 个数组元素为一趟,然后 offset 为 SIZE * 2,表示从第 2 个元素,取 3 个元素作为这一趟的数据内容。

完整代码实现:

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

const vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
 gl_Position = a_Position;
 v_Color = a_Color;
}
`;

const fragmentShaderSrc = `
precision mediump float;
varying vec4 v_Color;
void main() {
  gl_FragColor = v_Color;
}
`;

/**** 渲染器生成处理 ****/
// 创建顶点渲染器
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;

// 顶点数据
// prettier-ignore
const verticesColors = new Float32Array([
  0, 0.5, 1, 0, 0,  // 点 1 的位置和颜色信息
  -0.5, -0.5, 0, 1, 0,  // 点 2
  0.5, -0.5, 0, 0, 1,  // 点 3
]);
// 每个数组元素的字节数
const SIZE = verticesColors.BYTES_PER_ELEMENT;

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

// 获取 a_Position 变量地址
const a_Position = gl.getAttribLocation(gl.program, "a_Position");
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, SIZE * 5, 0);
gl.enableVertexAttribArray(a_Position);

const a_Color = gl.getAttribLocation(gl.program, "a_Color");
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, SIZE * 5, SIZE * 2);
gl.enableVertexAttribArray(a_Color);

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

demo 地址:

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

结尾

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

本节讲了 varying 的能力:将顶点着色器中的变量传递给片元着色器。并演示了使用两个缓冲区对象,位置数据和颜色数据,以及将它们组合成一个缓冲区对象的实现。

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

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

相关文章

移动边缘计算意味着真正的5G时代已经来临

5G的承诺尚未实现,但现在宣布其失败还为时过早。DataBank首席执行官劳尔k马丁尼克(Raul K. Martynek)表示 ,真正的5G正在通过移动边缘计算实现,而数据中心将成为其中的核心。 在美国所有主要的移动运营商都在大力宣传他们在全美范围内提供无…

STM32-移植RTT

目录 Cubemx引入RTT资源新建工程生成工程 时钟选择选单片机引脚引脚搜索快速选中取消引脚选中引脚命名IO普通模式设置 串口串口基本配置串口DMA ADC采集ADC基本应用ADC_DMA RTT-shell指令定义RTTCOM调试串口J-Link RTT调试 教程shell指令RTT外设驱动使用1--串口添加 STM32_pwm …

玩元宇宙血亏后 蓝色光标梭哈AI也挺悬

蓝色光标2022年年度报告出炉,巨亏21.75 亿元,其中20.38亿亏损因商誉、无形资产及其他资产减值造成,而在实际亏损业务中,元宇宙占比不小。 蓝色光标在元宇宙领域的布局,主要通过三家子公司实施,分别为蓝色宇…

分布式文件系统HDFS的多问多答

分布式文件系统HDFS 简述HDFS的优缺点简述HDFS的体系结构请论述HDFS中SecondaryNameNode的作用和工作原理请论述HDFS写数据原理 简述HDFS的优缺点 HDFS的优良特性: ①兼容廉价的硬件设备。在成百上千台廉价服务器中存储数据,常会出现节点失效的情况&…

从浏览器输入url到页面加载(四)协议栈和套接字以及三次握手确认对于通信的作用

前言 上一节我们说到了域名对用户记忆的优点,但是IP对于路由器的优点,所以需要有DNS服务器提供域名与IP地址的转换,还说到了在前端开发中dns-prefetch域名预解析的好处。 本小节呢,我们会说一些不常用的知识点,如协议…

【社区图书馆】读《悲惨世界》有感

文章目录 故事简介经典重现价值取向我的思想 故事简介 《悲惨世界》是一部充满了悲剧的小说,故事首先由教堂展开,然后主要围绕着主人公冉阿让进行一系列的生动形象的描写,讲述了冉阿让悲惨的一生。 主人公冉阿让是一个诚实、善良的工人&…

100天涨薪4k,从功能测试到自动化测试,我整理的3000字超全学习指南

去年6月份,由于经济压力让我下定决心进阶自动化测试,已经24的我做了3年功能测试,坐标广州薪资定格在8k,可能是生活过的太安逸,觉得8000的工资也够了,但是生活总是多变的,女朋友的突然怀孕&#…

SpringBoot 整合WebService详解

1. 概述 WebService服务端是以远程接口为主的,在Java实现的WebService技术里主要依靠CXF开发框架,而这个CXF开发框架可以直接将接口发布成WebService。 CXF又分为JAX-WS和JAX-RS,JAX-WS是基于xml协议,而JAX-RS是基于Restful风格&…

OCR卡证识别

文章目录 前言一、DBNet多分类二、步骤1.训练、训练模型推理、模型转换2.通过推理模型进行推理 三、解决思路1、查看模型2、tools/infer/predict_det.py修改3、utility.py修改 总结 前言 最近涉及到了身份证识别,为了便于匹配识别结果的属性,如姓名、身…

(二) AIGC—Stable Difussion (1)

1. 前置知识 目前通用的图像生成模型一般包含三个组件: Text Encoder 根据文字生成向量生成模型 根据向量和Noise 生成 缩小版本的图像Image Decoder 根据小分辨率图像生成大分辨率图像 2. Text Encoder 文字的Encoder对于结果的影响很大,增大Diffusio…

华为p60系列超级快充 Turbo技术,轻松搞定充电困扰!

随着手机的功能越来越丰富,电量消耗也越来越快,当手机电量剩余20%时,是否有电量焦虑。为了满足大家快速充电的需求,华为P60系列配备了超级快充Turbo充电技术,让我们手机充电更快,用的更久,从此告…

Python爬虫解读

爬虫: Python爬虫是指利用计算机程序或者脚本自动抓取网站数据的一种行为,通常是为了提取网站数据或者进行数据分析等目的。 Python 爬虫可以分为手动爬虫和自动爬虫两种。手动爬虫是指完全由人工编写代码来实现的爬虫,这种方式需要编写大量的…

ES使用小结

ES使用总结 1.查询es全部索2.根据es索引查询文档3.查看指定索引mapping文件4.默认查询总数10000条5.删除指定索引文档6.删除所有数据包括索引7.設置窗口值8. logstash简单配置Logstash配置:logstash 控制台输出 9. filebenat配置 1.查询es全部索 localhost:9200/_c…

为什么说网络安全行业是IT行业最后的红利?

前言 2023年网络安全行业的前景看起来非常乐观。根据当前的趋势和发展,一些趋势和发展可能对2023年网络安全行业产生影响: 5G技术的广泛应用:5G技术的普及将会使互联网的速度更快,同时也将带来更多的网络威胁和安全挑战。网络安全…

DHCP 给内网客户端分配ip地址

~ 为 InsideCli 客户端网络分配地址,地址池范围: 192.168.0.110-192.168.0.190/24; ~ 域名解析服务器:按照实际需求配置 DNS 服务器地址选项; ~ 网关:按照实际需求配置网关地址选项; ~ 为…

JAVAWeb08-手动实现 Tomcat 底层机制+ 自己设计 Servlet

1. 前言 先看一个小案例, 引出对 Tomcat 底层实现思考 1.1 完成小案例 ● 快速给小伙伴完成这个小案例 0. 我们准备使用 Maven 来创建一个 WEB 项目, 老师先简单给小伙伴介绍一下 Maven 是什么, 更加详细的使用,我们还会细讲, 现在先使用一把 先创建…

【MySQL】带你了解MySQL 如何学习MySQL以及MySQL的用途以及意义

目录 1 MySQL的起源和发展 1.0.1 数据库管理系统 1.1 MySQL的起源 命名由来: 1.2 MySQL的发展历程 2 什么是MySQL? 2.1 数据库 2.1.1 我们之前存储数据的格式: 2.1.2 使用数据库的目的: 2.1.3 数据库分类 2.2 SQL语句 2…

STM32-HAL-串口的printf重定向

一、C语言的格式化输出 C语言的printf是一个标准库函数,用于将格式化的数据输出到标准的输出设备(通常是终端) 基本语法: int printf(const char *format, ...);其中的第一个参数const char *format表示输出格式,后面…

Kubernetes核心组件及资源介绍

文章目录 一、Kubernetes架构二、Kubernetes核心组件三、Kubernetes核心资源四、拓展1、Service和Ingress的区别是什么?2、Replicaset和Deployment的区别是什么?3、Deployment和Statefulset的区别是什么?4、Job和Cronjob的区别是什么&#xf…

【FAQ】统一扫码服务常见问题及解答

1.隐私政策是怎么样的?收集哪些信息? 关于Scan Kit的隐私政策及收集的信息,请查看SDK隐私安全说明。 Android:SDK隐私安全说明 iOS:SDK隐私安全说明 2.如何使用多码识别?多码模式下如何实现指定条码&am…