WebGL 同时使用多幅纹理

目录

前言

​编辑

示例代码

颜色矢量的分量乘法来计算两个纹素最终的片元颜色

注册事件响应函数:loadTexture(),最后一个参数是纹理单元编号。

请求浏览器加载图像:

配置纹理:loadTexture()函数。该函数的核心部分代码如下所示: 

需要注意的是


前言

WebGL可以同时处理多幅纹理,纹理单元就是为了这个目的而设计的。本例程序在矩形上重叠粘贴两幅纹理图像。下图显示了本例运行效果,两张纹理图像在矩形上的混合效果如下。

下图中的两幅图分别显示了示例程序用到的两幅纹理图像。为了说明WebGL具有处理不同纹理图像格式的能力,本例故意使用了两种不同格式的图像(左侧jpg,右侧gif)。 

示例代码

// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'attribute vec2 a_TexCoord;\n' +'varying vec2 v_TexCoord;\n' +'void main() {\n' +'  gl_Position = a_Position;\n' +'  v_TexCoord = a_TexCoord;\n' +'}\n';// 片元着色器
var FSHADER_SOURCE ='#ifdef GL_ES\n' +'precision mediump float;\n' +'#endif\n' +'uniform sampler2D u_Sampler0;\n' +'uniform sampler2D u_Sampler1;\n' +'varying vec2 v_TexCoord;\n' +'void main() {\n' +'  vec4 color0 = texture2D(u_Sampler0, v_TexCoord);\n' +'  vec4 color1 = texture2D(u_Sampler1, v_TexCoord);\n' +'  gl_FragColor = color0 * color1;\n' +'}\n';function main() {var canvas = document.getElementById('webgl');var gl = getWebGLContext(canvas);if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to intialize shaders.');return;}var n = initVertexBuffers(gl);gl.clearColor(0.0, 0.0, 0.0, 1.0);// 配置纹理if (!initTextures(gl, n)) {console.log('Failed to intialize the texture.');return;}
}function initVertexBuffers(gl) {var verticesTexCoords = new Float32Array([// 顶点坐标和纹理坐标-0.5,  0.5,   0.0, 1.0,-0.5, -0.5,   0.0, 0.0,0.5,  0.5,   1.0, 1.0,0.5, -0.5,   1.0, 0.0,]);var n = 4; // 顶点数量var vertexTexCoordBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;var a_Position = gl.getAttribLocation(gl.program, 'a_Position');gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);gl.enableVertexAttribArray(a_Position);  // Enable the assignment of the buffer objectvar a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);gl.enableVertexAttribArray(a_TexCoord);  // Enable the buffer assignmentreturn n;
}function initTextures(gl, n) {// 创建纹理缓冲区对象var texture0 = gl.createTexture(); var texture1 = gl.createTexture();// 获取u_Sampler1和u_Sampler2的存储位置var u_Sampler0 = gl.getUniformLocation(gl.program, 'u_Sampler0');var u_Sampler1 = gl.getUniformLocation(gl.program, 'u_Sampler1');var image0 = new Image();var image1 = new Image();// 注册事件响应函数,在图像加载完成后调用image0.onload = function(){ loadTexture(gl, n, texture0, u_Sampler0, image0, 0); };image1.onload = function(){ loadTexture(gl, n, texture1, u_Sampler1, image1, 1); };// 告诉浏览器开始加载图像image0.src = '../resources/sky.jpg';image1.src = '../resources/circle.gif';return true;
}
// 标记纹理单元是否已经就绪
var g_texUnit0 = false, g_texUnit1 = false; 
function loadTexture(gl, n, texture, u_Sampler, image, texUnit) {gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);// 沿反转图像// 激活纹理单元if (texUnit == 0) {gl.activeTexture(gl.TEXTURE0);g_texUnit0 = true;} else {gl.activeTexture(gl.TEXTURE1);g_texUnit1 = true;}// 绑定纹理对象到目标上(先绑定到纹理单元后指定纹理类型绑定到目标上)gl.bindTexture(gl.TEXTURE_2D, texture);   // 配置纹理参数gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);// 设置纹理图像gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);// 将纹理单元编号传递给取样器gl.uniform1i(u_Sampler, texUnit);   gl.clear(gl.COLOR_BUFFER_BIT);// 图像加载是异步的,我们无法预测哪一张纹理被加载完成,只有两幅都加载好,程序才开始绘制if (g_texUnit0 && g_texUnit1) {gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);  }
}

首先,让我们来看一下片元着色器,本例用到了两幅纹理,那么就需要两个定义uniform变量

然后,在片元着色器的main()函数中,我们从两个纹理中取出颜色,分别存储在变量color0和color1中

颜色矢量的分量乘法来计算两个纹素最终的片元颜色

使用两个纹素来计算最终的片元颜色(gl_FragColor)有多种可能的方法。示例程序使用的是颜色矢量的分量乘法——两个矢量中对应的分量相乘作为新矢量的分量,如下图所示。这很好理解。在GLSL ES中,只需要将两个vec4变量简单相乘一下就可以达到目的。 

第55、56行创建了两个纹理缓冲区对象,变量名的后缀(texture0中的 “0” 和 texture1中的 “1”)对应对应着纹理单元的编号(纹理单元0和纹理单元1),此外uniform变量(第68,69行)与image对象(第70,71行)也采用了类似的命名方式

注册事件响应函数:loadTexture(),最后一个参数是纹理单元编号。

请求浏览器加载图像:

配置纹理:loadTexture()函数。该函数的核心部分代码如下所示: 

需要注意的是

在loadTexture()函数中,我们无法预测哪一幅纹理图像先被加载完成,因为加载的过程是异步进行的。只有当两幅纹理图像都完成加载时,程序才会开始绘图。为此,我们定义了两个全局变量g_texUnit0和g_texUnit1来指示对应的纹理是否加载完成(第81行)。 

这些变量都被初始化为false(第81行)。当任意一幅纹理加载完成时,就触发onload事件并调用响应函数loadTexture()。该函数首先根据纹理单元编号0或1来将g_texUnit0或g_texUnit1赋值为true(第85行)。换句话说,如果触发本次onload事件的纹理的编号是0,那么0号纹理单元就被激活了,并将g_texUnit0设置为true;如果是1,那么1号纹理单元被激活了,并将g_texUnit0设置为ture

接着,纹理单元编号texUnit被赋给了uniform变量(第99行)。注意texUnit是通过gl.uniform1i()方法传入着色器的。在两幅纹理图像都完成加载后,WebGL系统内部的状态就如下图所示。

loadTexture()函数的最后通过检查g_texUnit0和g_texUnit1变量来判断两幅图像是否全部完成加载了(第103行)。如果是,就开始执行顶点着色器,在图形上重叠着绘制出两层纹理,如本文第一张图所示。 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis中至关重要的关系映射----全方面介绍

目录 一 对于映射的概念 1.1 三种关系映射 1.2 resultType与resultMap的区别 resultType: resultMap: 二,一对一关联查询 2.1 嵌套结果集编写 2.2 案例演示 三,一对多关联查询 3.1 嵌套结果集编写 3.3 案例演示 四&…

SpringBoot + Prometheus + Grafana 打造可视化监控

SpringBoot Prometheus Grafana 打造可视化监控 文章目录 SpringBoot Prometheus Grafana 打造可视化监控常见的监控组件搭配安装Prometheus安装Grafana搭建SpringBoot项目引入依赖示例:监控SpringBoot内置Tomcat线程池的情况grafana创建监控看板 后台SpringBoot服务添加自…

【IBMMQ】搭建测试队列

一、安装IBMMQ 网上有教程,可以学习 我用的IBMMQ7.5,安装教程 二、创建测试队列 进入工作台: 右击队列管理器,新建队列管理器 写队列管理器名称 点击下一步 点击下一步 点击下一步 端口默认为1414,建议换一个 注…

多线程快速入门

线程与进程区别 每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行。也可以把它理解为代码运行的上下文。所以线程基本上是轻量级的进程,它负责在单个程序里…

Linux之查看so/bin依赖(三十一)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…

【Redis】3、Redis主从复制、哨兵、集群

Redis主从复制 主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master),后者称为从节点(Slave);数据的复制是单向的,只能由主节点到从节点。 默认情况下,每台Redis服务器…

linux————ansible

一、认识自动化运维 自动化运维: 将日常IT运维中大量的重复性工作,小到简单的日常检查、配置变更和软件安装,大到整个变更流程的组织调度,由过去的手工执行转为自动化操作,从而减少乃至消除运维中的延迟,实现“零延时”…

时序预测 | MATLAB实现ELM极限学习机时间序列预测未来

时序预测 | MATLAB实现ELM极限学习机时间序列预测未来 目录 时序预测 | MATLAB实现ELM极限学习机时间序列预测未来预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.MATLAB实现ELM极限学习机时间序列预测未来; 2.运行环境Matlab2018及以上,data为数…

【Redis专题】RedisCluster集群运维与核心原理剖析

目录 课程内容一、Redis集群架构模型二、Redis集群架构搭建(单机搭建)2.1 在服务器下新建各个节点的配置存放目录2.2 修改配置(以redis-8001.conf为例) 三、Java代码实战四、Redis集群原理分析4.1 槽位定位算法4.2 跳转重定位4.3 …

Ansible数组同步至Shell脚本数组中

1、ansible中定义数组,我以 ccaPojectList 数组为例子,如下图数组内容 2、需要写一个j2模板的Shell脚本,在j2模板的Shell脚本中引用ansible的 ccaPojectList 数组,大致如下图: {% for item in ccaPojectList %} "{{ item }…

探索程序员需要掌握的算法?

文章目录 一:引言二:常见算法介绍三:重点算法总结 🎉欢迎来到数据结构学习专栏~探索程序员需要掌握的算法? ☆* o(≧▽≦)o *☆嗨~我是IT陈寒🍹✨博客主页:IT陈寒的博客🎈该系列文章…

【漏洞库】Fastjson_1.2.47_rce

文章目录 漏洞描述漏洞编号漏洞评级影响版本漏洞复现- 利用工具- 漏洞环境- 漏洞扫描- 漏洞验证- 深度利用- GetShell- EXP 编写 漏洞挖掘- 寻找入口点- 指纹信息 修复建议- 漏洞修复 漏洞原理 漏洞描述 Fastjson是阿里巴巴公司开源的一款json解析器,其性能优越&am…

TCP服务器使用多路复用

启用复用的作用? 解决linux系统中的io阻塞问题,让多个阻塞io接口可以一起执行。无需开启线程,节省系统资源。 linux系统中的阻塞io有哪些? scanf、read管道、eadTcp套接字、acppet接收连接请求 有以下两种方式实现多路复用&am…

单位固定资产应该怎么管理

对于单位固定资产的管理,更是需要我们以创新的方式,以科技的手段,以严谨的态度来对待。那么,单位固定资产应该如何进行有效的管理呢? 建立一个完善的资产管理系统  我们需要建立一个完善的资产管理系统。这个系统应…

JS如何判断一个变量是否为数组类型?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用 Array.isArray() 方法⭐ 使用 instanceof 操作符⭐ 使用 Object.prototype.toString.call() 方法⭐ 使用 Array.from() 方法⭐ 使用 Array.prototype.isArray 属性(不推荐)⭐ 写在最后 ⭐ 专栏简介 前端入门之…

使用rpm重新安装包

#查询 rpm -qa | grep cloudstack #卸载 rpm -e cloudstack-agent-4.18.0.0-1.x86_64 #安装 rpm -ivh cloudstack-agent-4.18.0.0-1.x86_64.rpm

Mysql同步数据到Doris的踩坑过程

问题背景 由于项目需要,需要把多个Mysql数据库的数据同步到Doris数据库,然后利用Doris强调的计算和查询能力,来满足业务需求。有关Doris可以查看它的官方文档来了解它。 seatunnel的使用到放弃 缘起 从《第十届GIAC全球互联网架构大会》了…

《Chain-of-Thought Prompting Elicits Reasoning in Large Language Models》全文翻译

《Chain-of-Thought Prompting Elicits Reasoning in Large Language Models》- Chain-of-Thought Prompting Elicits Reasoning in Large Language Models 论文信息摘要1. 介绍2. 思维链提示3. 算术推理3.1 实验设置3.2 结果3.3 消融研究3.4 思想链的稳健性 4. 常识推理5. 符号…

win11本地连接没了怎么办

很多用户在使用win11系统时发现自己的网络连接没有了,遇到这种情况的话,我们应该怎么处理呢?我们可以尝试打开网络图标,下面就是小编整理出的教程,大家一起看看吧。 win11本地连接没了怎么办 方法一: 1、…

Python图像融合处理和 ROI 区域绘制基础

文章目录 一、图像融合二、图像 ROI 区域定位三、图像属性3.1 shape3.2 size3.3 dtype四、图像通道分离及合并4.1、split()函数4.2 merge()函数五、图像类型转换一、图像融合 图像融合通常是指多张图像的信息进行融合,从而获得信息更丰富的结果,能够帮助人们观察或计算机处理…