一起学 WebGL:纹理对象学习

news2024/11/17 2:25:06

大家好,我是前端西瓜哥,今天我们来了解 WebGL 的纹理对象(Texture)

纹理对象,是将像素(texels)以数组方式传给 GPU 的对象,常见场景是贴图,就是将图片的数据应用到 3D 物体上。

纹理对象创建和绑定

先创建纹理对象:

const texture = gl.createTexture(); // 创建纹理对象

然后绑定到纹理单元:

gl.bindTexture(gl.TEXTURE_2D, texture); // 将纹理对象绑定上去

填充方式

纹理是要贴到画布的某个区域上的,并不一定刚好设置一下填充方式。

纹理比绘制区域大,就要做缩放;纹理比绘制区域小,就要做放大;纹理没能完全填充绘制区域,就要在水平和垂直方向进行填充。

这些场景都需要对应设置不同的策略。

具体讲解看我的这篇文章:

《一起学 WebGL:绘制图片》

// 缩小和放大都都使用 “最近点采样”
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

纹理单元

WebGL 支持设置多个纹理单元(Texture Unit),即我们可以将多个图片放到多个单元中,然后进行切换。

就好像手里拿着不同的盖章,想印哪种图案就掏出哪个盖上去。

纹理单元是有上限的,至少要支持 8 个,主流浏览器一般支持 16 个。

具体支持几个,可通过下面代码获得。

gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) // 通常是 16

默认使用 0 号纹理单元,可通过下面这一行代码来切换纹理单元:

gl.activeTexture(gl.TEXTURE1); // 开启 1 号纹理单元

注意这个要 在将纹理对象绑定纹理单元之前 执行。

最后我们需要设置一下我们的纹理采样器选择使用哪个纹理单元:

gl.uniform1i(u_Sampler, 0); // 开启 0 号纹理对象

不主动调用这个方法,默认会使用 0 号纹理单元。

切换纹理单元是有一定的性能代价的,不建议你在短时间内不断地切换纹理单元。简单的渲染场景可忽略不计。

纯色纹理

画个纯纯的红色纹理。

// 红色
const data = new Uint8Array([
  255, 0, 0
]);

gl.texImage2D(
  gl.TEXTURE_2D, // 纹理目标,这里是二维纹理
  0, // 细节级别,0 表示最高级别
  gl.RGB, // 纹理内部格式,还支持其他的比如 gl.RGBA、LUMINANCE(流明)
  1, // 宽(宽高的单位为像素,且为 2 的 n 次幂)
  1, // 高
  0, // 是否描边。必须为 0(但 opengl 支持)
  gl.RGB, // 源图像数据格式
  gl.UNSIGNED_BYTE, // 纹素(单个像素)数据类型
  data // 数据数组,一个个像素点
);

主要注意的是,gl.texImage2D() 方法支持函数重载,有多种传入的参数的方式,注意分辨。具体看 官方文档。

这里选择使用 gl.RGB 格式,设置了一个 (255, 0, 0) 的红色颜色值。

最后我们成功画出一个纯红色块。

完整代码:

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

const vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;
void main() {
 gl_Position = a_Position;
 v_TexCoord = a_TexCoord;
}
`;

const fragmentShaderSrc = `
precision highp float;
uniform sampler2D u_Sampler;
varying vec2 v_TexCoord;
void main() {
  gl_FragColor = texture2D(u_Sampler, v_TexCoord);
}
`;

// 创建程序对象
createProgram(gl);

// 顶点坐标,纹理坐标
const verticesTexCoords = new Float32Array([
  // 左上点。
  // 左边两个是顶点;右边两个是纹理
  -0.5, 0.5, 0.0, 1,
  // 左下
  -0.5, -0.5, 0.0, 0.0,
  // 右上
  0.5, 0.5, 1, 1,
  // 右下
  0.5, -0.5, 1, 0.0,
]);
const FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;

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

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

// 传入纹理坐标位置信息
const a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
gl.enableVertexAttribArray(a_TexCoord);

/***** 纹理对象 *****/
const texture = gl.createTexture(); // 创建纹理对象
const u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler'); // 获取 u_Sampler 地址

// 记载图片

gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // 翻转纹路图像的 y 轴
gl.activeTexture(gl.TEXTURE0); // 开启 0 号纹理单元
gl.bindTexture(gl.TEXTURE_2D, texture); // 将我们的纹理对象绑定上去

// 配置纹理参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

// 【----关键代码---】配置纹理图像
const data = new Uint8Array([255, 0, 0, 0, 255, 255, 0, 255, 0, 0, 255, 0]);
gl.texImage2D(
  gl.TEXTURE_2D, // 纹理目标
  0, // 细节级别
  gl.RGB, // 纹理内部格式
  1,
  1,
  0,
  gl.RGB, // 源图像数据格式
  gl.UNSIGNED_BYTE, // 纹素数据类型
  data // 数据
);

gl.uniform1i(u_Sampler, 0); // 开启 0 号纹理对象

/****** 绘制 ******/
// 清空画布,并指定颜色
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制矩形,这里提供了 4 个点
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

/**** 封装的方法 ****/

function createProgram(gl) {
  /**** 渲染器生成处理 ****/
  // 创建顶点渲染器
  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;
}

线上 demo:

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

多个色块纹理

也可以同时设置多个色块。

const data = new Uint8Array([
  255, 0, 0, 255,   // 红色
  255, 255, 0, 255, // 黄色
  0, 0, 255, 255,  // 蓝色
  0, 255, 0, 255,  // 绿色
]);

gl.texImage2D(
  gl.TEXTURE_2D, // 纹理目标
  0, // 细节级别
  gl.RGBA, // 纹理内部格式
  2,
  2,
  0,
  gl.RGBA, // 源图像数据格式
  gl.UNSIGNED_BYTE, // 纹素数据类型
  data // 数据
);

创建了 2x2 4个像素大小的纹理,并制定了这个 4 个像素点的颜色,然后被放大绘制到指定区域上。

线上演示 demo:

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

图片纹理

图片纹理,需要加载玩图片,将图片对象绑定到纹理对象上。

// 将纹理图像分配给纹理对象
gl.texImage2D(
  gl.TEXTURE_2D,
  0, // 细节级别
  gl.RGB,
  gl.RGB,
  gl.UNSIGNED_BYTE,
  img // Image 实例
);

具体看我的这篇文章:

《一起学 WebGL:绘制图片》

结尾

纹理对象是很常用的一个对象,用于指定区域要填充的像素。

常见的是加载图片,把图片贴到三维的一个面上。也可以自己指定像素值。

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

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

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

相关文章

vue 根据word摸板导出word文档,并压缩为zip

yarn add jszip3.10.1 yarn add jszip-utils0.1.0 yarn add pizzip3.1.4 yarn add docxtemplater3.29.5 yarn add docxtemplater-image-module-free1.1.1 yarn add file-saver2.0.5 注意:这里的fileUrl必须是绝对路径,否则可能会报 is not zip的错误&…

当pytest遇上poium会擦出什么火花

当pytest遇上poium会擦出什么火花 首先,创建一个test_sample/test_demo.py 文件,写入下面三行代码。 def test_bing(page):page.get("https://www.bing.com")assert page.get_title "必应"不要问题 page 从哪里来,打开…

(LLM) 的所有知识;10分钟了解向量数据库;微软 Bing 可以识别图片了;

🦉 AI新闻 🚀 微软 Bing 可以识图」了,吊打 GPT-4? 摘要:微软 Bing 最新识图功能让用户可以上传图片并进行编程、做题、看病等操作,还能分析梗图笑点。然而在某些情况下表现不佳,例如无法数清…

技术分享 | i.MX8M Plus开发板 固定IP地址以及单网口多IP设置

以启扬IMX8MP开发板为例,给大家分享固定IP地址以及单网口多IP设置的步骤流程。 固定IP地址设置 20-wired.network 重启Network生效 网口多ip设置 对于一些网络管理的命令 connman设置(参考) imx8 yocto系统的init system使用systemd&#xff…

精选Java SSM 框架基础面试题

一、Spring面试题 1、Spring 在ssm中起什么作用? Spring:轻量级框架作用:Bean工厂,用来管理Bean的生命周期和框架集成。两大核心:1、IOC/DI(控制反转/依赖注入) :把dao依赖注入到service层,se…

STM32 GPIO 详解

0. 实验平台 基于STM32F407ZG 1. GPIO 简介 1.1 简介 GPIO全称:General Purpose Input Output,即通用输入输出端口,一般用来采集外部器件的信息或者控制外部器件工作,即输入输出 1.2 STM32 的 GPIO 特点 不同型号&#xff0…

SpringBatch从入门到实战(五):执行上下文和单步骤重启

一:执行上下文 1.1 Job Context 作业上下文 JobContext 绑定 JobExecution 执行对象,为Job作业执行提供执行环境(上下文)。 1.2 Step Context 步骤上下文 StepContext 绑定 StepExecution 执行对象,为Step步骤执行提供执行环境(上下文)。 …

【剑指offer专项突破版】栈篇——“C“

文章目录 前言一、后缀表达式题目分析思路分析代码 二、小行星碰撞题目分析思路分析代码 三、每日温度题目分析思路分析代码 四、直方图最大矩形面积题目分析思路分析代码 五、矩阵中最大的矩形题目分析思路分析代码 总结 前言 剑指offer专项突破版(力扣官网&#x…

IBM不藏私:深刻解析量子计算机的突破和机遇

​ 巴伐利亚科学部长Markus Blume在莱布尼茨超级计算中心与Dieter Kranzlmlle(左)一起观看量子计算机的部分构件。(图片来源:网络) 关于量子计算机的研究已进行了数十年,目前还尚未生产一台能够掀起计算革命…

Vue全家桶(一):Vue基础+Vue-Cli+Vue组件化+过渡动画

目录 1.Vue概述1.1 认识Vue1.2 Vue的两核心1.3 Vue的初体验1.4 Vue的生命周期 2. Vue-CLI (Command Line Interface)3. Vue基本使用3.1 传统开发模式对比3.2 Vue.js引入3.3 Vue.js 案例分析3.3.1 实例参数el、data、methods的写法 4. Vue模板语法4.1 插值语法 {{xxx}}4.2 指令语…

vue3+ts:shims-vue.d.ts

一、本文引子 uniapp(3.8.4.20230531) vue3 ts vite 项目 在搭建这个base项目的时候出现红素波浪线如图,代码运行正常,但是看起来很难受,于是各种查找,能找到的资料很少,可能和我提问不够准…

【备战秋招】每日一题:4月23日美团春招第一题:题面+题目思路 + C++/python/js/Go/java带注释

为了更好的阅读体检,为了更好的阅读体检,,可以查看我的算法学习博客第一题-申请奖学金 在线评测链接:P1245 题目内容 塔子哥是一个热爱学习的大学生,他的梦想是成为一名优秀的算法竞赛高手。为了实现自己的梦想,他需…

代理ip匿名原理及那些行业需要代理ip

互联网的高速发展,连带了代理ip也受到了更多人的使用,不同的行业都存在使用代理ip的情况,同时代理ip也以为匿名程度分成了高匿、普匿、透明代理,那么代理ip匿名的原理是什么呢?又有哪些行业需要代理ip呢?下…

flume环境配置-传输Hadoop日志(namenode或datanode日志)

解压文件 修改文件名 配置环境变量 执行flume-ng version 将flume-env.sh.template改名为flume-env.sh, 并修改其配置 启动Flume传输Hadoop日志 启动flume 解压文件 tar -zxvf apache-flume-1.9.0-bin.tar.gz -C /opt 修改文件名 mv apache-flume-1.9.0-b…

全面安全防护,加速企业创新发展——亚马逊云科技re:Inforce全球大会

亚马逊云科技re:Inforce 2023全球大会于当地时间2023年6月13日在美国加州安纳海姆拉开帷幕。在大会上,亚马逊云科技宣布推出十多项安全新服务及功能,下面就来一览本次大会的风采。 “Security is our top priority.” “安全是我们的首要优先级”&#…

Java 面向对象 | 详细知识图谱式讲解

💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! ☢Java入门 基础知识:了解 Java 基本语法、面向对象编程(OOP)概念、流程控制语句、数据类型、方法等基础知识。可以通过 Java 编程…

Nodejs七、身份认证

零、文章目录 Nodejs七、身份认证 1、Web 开发模式 (1)目前主流的 Web 开发模式 基于服务端渲染的传统 Web 开发模式基于前后端分离的新型 Web 开发模式 (2)服务端渲染的 Web 开发模式 服务器发送给客户端的 HTML 页面&…

HBase Shell操作HBase进行预分区

本文将介绍如何使用HBase Shell操作HBase进行预分区。预分区是指在创建表的时候,指定表的初始分区点,从而使表的数据能够均匀地分布在多个RegionServer上,提高读写性能和负载均衡。本文将使用HBase Shell命令,创建不同的预分区表&…

Allure在自动化测试中的应用

目录 前言: 01Allure的简介及使用 1、应用场景 02Allure与Pytest结合 1、添加测试步骤 2、添加主要功能模块描述 3、添加严重等级 03Allure集成Jenkins 1、Jenkins介绍和安装 2、Jenkins安装allure插件 前言: Allure是一种流行的测试报告框架…

Floating UI 使用经验分享 - Dialog

上文:Floating UI 使用经验分享 - Popover 在本文中,我将分享如何使用 Floating UI 来创建另一种常见的浮动 UI 组件——Dialog(对话框)。Dialog 是一个浮动元素,显示需要立即关注的信息,他会出现在页面内…