当前位置: 首页 > news >正文

(十四)WebGL纹理坐标初识

纹理坐标是 WebGL 中将 2D 图像(纹理)应用到 3D 物体表面的重要概念。在 WebGL 中,纹理坐标通常使用一个二维坐标系,称为 uv 坐标,它们决定了纹理图像如何映射到几何体上。理解纹理坐标的核心就是明白它们如何将二维纹理贴图应用到三维物体的表面。

文章目录

      • 纹理坐标的基本概念
      • 纹理坐标的作用
      • 代码示例:立方体纹理映射
        • 1. HTML 文件
        • 2. JavaScript 部分:WebGL 纹理坐标示例
      • 3. 代码解析
      • 4. 纹理坐标的作用

纹理坐标的基本概念

纹理坐标(u, v)是一个标准的二维坐标系统,用于描述纹理图像中每个像素(即纹理元素,Texel)的位置。uv 是纹理的归一化坐标,即它们的值通常位于 [0, 1] 区间:

  • u坐标:控制纹理图像的水平位置,u = 0 是纹理的最左侧,u = 1 是纹理的最右侧。
  • v坐标:控制纹理图像的垂直位置,v = 0 是纹理的底部,v = 1 是纹理的顶部。

例如,在纹理坐标 (u=0.5, v=0.5) 处表示纹理图像的中心。
在这里插入图片描述

纹理坐标的作用

在 WebGL 中,每个顶点都有一个对应的纹理坐标。顶点着色器负责将这些坐标传递到片元着色器,片元着色器利用纹理坐标来从纹理图像中获取相应的像素颜色,从而将图像“映射”到几何体表面。

代码示例:立方体纹理映射

下面是一个带有纹理坐标的立方体的简单例子,展示了如何将纹理图像映射到立方体的每个面。

1. HTML 文件
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebGL 纹理坐标示例</title>
</head>
<body><canvas id="webgl-canvas" width="500" height="500"></canvas><script src="main.js"></script>
</body>
</html>
2. JavaScript 部分:WebGL 纹理坐标示例
// 获取 WebGL 上下文
const canvas = document.getElementById('webgl-canvas');
const gl = canvas.getContext('webgl');// 立方体顶点坐标 (x, y, z)
const vertices = new Float32Array([-0.5, -0.5, -0.5, // 前面0.5, -0.5, -0.5,0.5,  0.5, -0.5,-0.5,  0.5, -0.5,-0.5, -0.5,  0.5, // 后面0.5, -0.5,  0.5,0.5,  0.5,  0.5,-0.5,  0.5,  0.5,
]);// 立方体的纹理坐标 (u, v)
// 每个顶点的纹理坐标。注意 u/v 范围是 [0, 1]
const texCoords = new Float32Array([0.0, 0.0,  1.0, 0.0,  1.0, 1.0,  0.0, 1.0,  // 前面0.0, 0.0,  1.0, 0.0,  1.0, 1.0,  0.0, 1.0,  // 后面0.0, 0.0,  1.0, 0.0,  1.0, 1.0,  0.0, 1.0,  // 左面0.0, 0.0,  1.0, 0.0,  1.0, 1.0,  0.0, 1.0,  // 右面0.0, 0.0,  1.0, 0.0,  1.0, 1.0,  0.0, 1.0,  // 上面0.0, 0.0,  1.0, 0.0,  1.0, 1.0,  0.0, 1.0,  // 下面
]);// 立方体的索引,用于绘制每个面
const indices = new Uint16Array([0, 1, 2, 0, 2, 3,4, 5, 6, 4, 6, 7,0, 1, 5, 0, 5, 4,1, 2, 6, 1, 6, 5,2, 3, 7, 2, 7, 6,3, 0, 4, 3, 4, 7
]);// 创建并绑定缓冲区
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);const texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW);const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);// 创建着色器程序
const vertexShaderSource = `attribute vec4 a_position;attribute vec2 a_texCoord;varying vec2 v_texCoord;void main() {gl_Position = a_position;v_texCoord = a_texCoord;}
`;const fragmentShaderSource = `precision mediump float;varying vec2 v_texCoord;uniform sampler2D u_texture;void main() {gl_FragColor = texture2D(u_texture, v_texCoord);}
`;// 编译着色器并链接程序
function compileShader(type, source) {const shader = gl.createShader(type);gl.shaderSource(shader, source);gl.compileShader(shader);if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {console.error('Shader compilation failed', gl.getShaderInfoLog(shader));}return shader;
}const vertexShader = compileShader(gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = compileShader(gl.FRAGMENT_SHADER, fragmentShaderSource);const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {console.error('Program linking failed', gl.getProgramInfoLog(shaderProgram));
}// 使用着色器程序
gl.useProgram(shaderProgram);// 获取属性和统一变量的位置
const positionLocation = gl.getAttribLocation(shaderProgram, 'a_position');
const texCoordLocation = gl.getAttribLocation(shaderProgram, 'a_texCoord');
const textureLocation = gl.getUniformLocation(shaderProgram, 'u_texture');// 绑定位置数据
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLocation);// 绑定纹理坐标数据
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(texCoordLocation);// 创建并绑定纹理
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
const image = new Image();
image.onload = () => {gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);gl.generateMipmap(gl.TEXTURE_2D);
};
image.src = 'your-texture-image.jpg';  // 这里使用你自己的纹理图片路径// 清除画布并绘制
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);// 绘制立方体
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);

3. 代码解析

  • 纹理坐标(texCoords):每个顶点都有对应的纹理坐标,texCoords

数组定义了每个面四个顶点的纹理坐标。在这个例子中,(u, v) 范围是 [0, 1],代表纹理图像的左下角 (0, 0) 到右上角 (1, 1)。

  • 着色器

    • 顶点着色器:将顶点位置和纹理坐标传递到片元着色器。
    • 片元着色器:根据纹理坐标,从纹理图像中采样颜色。
  • 纹理加载:在 image.onload 中加载图片,并将其绑定为 WebGL 的纹理。在加载完成后,WebGL 会自动生成多级渐远纹理(mipmap),提高纹理渲染的效率和质量。

  • 绘制:使用 gl.drawElements 绘制立方体的每个面。每个面由两个三角形组成,纹理坐标确保了纹理图像正确地映射到每个面。

4. 纹理坐标的作用

纹理坐标使得 WebGL 能够将一个 2D 图像(纹理)正确地映射到 3D 对象的表面。当你需要将图片或其他 2D 图像添加到你的 3D 模型时,理解和使用纹理坐标是非常重要的。纹理坐标的映射关系决定了纹理如何在模型表面分布,例如,是否是平铺、是否有旋转、是否有缩放等。

相关文章:

(十四)WebGL纹理坐标初识

纹理坐标是 WebGL 中将 2D 图像&#xff08;纹理&#xff09;应用到 3D 物体表面的重要概念。在 WebGL 中&#xff0c;纹理坐标通常使用一个二维坐标系&#xff0c;称为 uv 坐标&#xff0c;它们决定了纹理图像如何映射到几何体上。理解纹理坐标的核心就是明白它们如何将二维纹…...

【机器学习】制造业转型:机器学习如何推动工业 4.0 的深度发展

我的个人主页 我的领域&#xff1a;人工智能篇&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;&#x1f44d;点赞 收藏❤ 引言 在当今科技飞速发展的时代&#xff0c;制造业正经历着前所未有的变革&#xff0c;工业4.0的浪潮席卷而来。工业4.0旨在通过将…...

Nginx安装配置Mac使用Nginx访问前端打包项目

目录 Linux安装环境变量配置 WinMac安装基本配置 Mac使用Nginx访问前端项目常用命令 Linux 官网&#xff1a;https://nginx.org/ 中文官网&#xff1a;https://nginx.p2hp.com/ 安装 http://nginx.org/en/download.html 1). 安装依赖包 由于nginx是基于c语言开发的&#x…...

国自然面上项目|基于组合机器学习算法的病理性近视眼底多模态影像资料自动化定量分析研究|基金申请·25-01-18

小罗碎碎念 今天和大家分享一个面上项目&#xff0c;资助年限为2020&#xff5e;2023&#xff0c;直接费用为55万。 病理性近视致盲问题严峻&#xff0c;机制和诊疗策略尚待探索。本项目基于前期积累的大量影像资料和算法开发工作&#xff0c;计划构建标准影像数据库&#xff0…...

03_UI自适应

因为Canvas大小是始终和屏幕一致的 所以设置Canvas的屏幕大小 通常设置为1920 * 1080 又因为屏幕的图像及按钮如果想适配各种显示屏需要锁定长或者宽&#xff0c; 之后利用钉子将其他图像利用创建空节点定在左右或者上下两侧 比如unity编辑器通常是锁定宽的&#xff0c;那我…...

Python在DevOps中的应用:自动化CI/CD管道的实现

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门&#xff01; 解锁Python编程的无限可能&#xff1a;《奇妙的Python》带你漫游代码世界 在现代软件开发中&#xff0c;DevOps理念的引入极大地提升了开发与运维的协作效率&#xff0c;而持续集成&#xff08…...

API接口技术推动电商数据处理的自动化

在当今数字化浪潮中&#xff0c;电商行业正以前所未有的速度发展。API&#xff08;Application Programming Interface&#xff0c;应用程序编程接口&#xff09;接口技术在这一过程中扮演着至关重要的角色。API接口作为连接不同系统和服务的关键桥梁&#xff0c;通过其自动化处…...

Nginx反向代理架构介绍

Nginx反向代理架构是一种强大的服务器架构模式&#xff0c;它位于用户和原始服务器之间&#xff0c;接收用户的请求并将其转发到一个或多个后端服务器&#xff0c;然后将从后端服务器获取的响应返回给用户&#xff0c;就好像这些内容都是由代理服务器本身直接提供的一样。以下是…...

.Net Core微服务入门系列(一)——项目搭建

系列文章目录 1、.Net Core微服务入门系列&#xff08;一&#xff09;——项目搭建 2、.Net Core微服务入门全纪录&#xff08;二&#xff09;——Consul-服务注册与发现&#xff08;上&#xff09; 3、.Net Core微服务入门全纪录&#xff08;三&#xff09;——Consul-服务注…...

WPF 实现可视化操作数据库的程序全解析

在软件开发中&#xff0c;实现对数据库的可视化操作能极大提升开发效率和用户体验。借助 WPF&#xff08;Windows Presentation Foundation&#xff09;强大的界面开发能力&#xff0c;我们可以打造出功能丰富、交互友好的数据库操作程序。本文将详细介绍如何使用 WPF 搭建一个…...

python mysql库的三个库mysqlclient mysql-connector-python pymysql如何选择,他们之间的区别

三者的区别 1. mysqlclient 特点&#xff1a; 是一个用于Python的MySQL数据库驱动程序&#xff0c;用于与MySQL数据库进行交互。 依赖于MySQL的本地库&#xff0c;因此在安装时需要确保系统上已安装了必要的依赖项&#xff0c;如libmysqlclient-dev等。 性能较好&#xff0c…...

如何将数据库字符集改为中文,让今后所有的数据库都支持中文

最后一行有我自己的my.ini文件 数据库输入中文数据时会变为乱码&#xff0c; 这个时候&#xff0c;我们为每个数据库设置字符集&#xff0c;太过于麻烦&#xff0c;为数据库单独设置重启后又会消失 Set character_set_database’utf8’; Set character_set_server’utf8’; …...

Low-Level 大一统:如何使用Diffusion Models完成视频超分、去雨、去雾、降噪等所有Low-Level 任务?

Diffusion Models专栏文章汇总:入门与实战 前言:视频在传输过程中常常因为各种因素(如恶劣天气、噪声、压缩和传感器分辨率限制)而出现质量下降,这会严重影响计算机视觉任务(如目标检测和视频监控)的性能。现有的视频修复方法虽然取得了一些进展,但通常只能针对特定的退…...

EAMM: 通过基于音频的情感感知运动模型实现的一次性情感对话人脸合成

EAMM: 通过基于音频的情感感知运动模型实现的一次性情感对话人脸合成 1所有的材料都可以在EAMM: One-Shot Emotional Talking Face via Audio-Based Emotion-Aware Motion Model网站上找到。 摘要 尽管音频驱动的对话人脸生成技术已取得显著进展&#xff0c;但现有方法要么忽…...

Docker Compose的使用

文章首发于我的博客&#xff1a;https://blog.liuzijian.com/post/docker-compose.html 目录 Docker Compose是什么Docker Compose安装Docker Compose文件Docker Compose常用命令案例&#xff1a;部署WordPress博客系统 Docker Compose是什么 Docker Compose是Docker官方的开源…...

[STM32 HAL库]串口空闲中断+DMA接收不定长数据

一、空闲中断 STM32的串口具有空闲中断&#xff0c;什么叫做空闲呢&#xff1f;如何触发空闲中断呢&#xff1f; 空闲&#xff1a;串口发送的两个字符之间间隔非常短&#xff0c;所以在两个字符之间不叫空闲。空闲的定义是总线上在一个字节的时间内没有再接收到数据。触发条件…...

三、华为交换机 Hybrid

一、Hybrid功能 Hybrid口既可以连接普通终端的接入链路&#xff08;类似于Access接口&#xff09;&#xff0c;又可以连接交换机间的干道链路&#xff08;类似于Trunk接口&#xff09;。它允许多个VLAN的帧通过&#xff0c;并可以在出接口方向将某些VLAN帧的标签剥掉&#xff0…...

如何通过 Apache Airflow 将数据导入 Elasticsearch

作者&#xff1a;来自 Elastic Andre Luiz 了解如何通过 Apache Airflow 将数据导入 Elasticsearch。 Apache Airflow Apache Airflow 是一个旨在创建、安排&#xff08;schedule&#xff09;和监控工作流的平台。它用于编排 ETL&#xff08;Extract-Transform-Load&#xff0…...

Android Studio:Linux环境下安装与配置

更多内容&#xff1a;XiaoJ的知识星球 Android Studio&#xff1a;Linux环境下安装与配置 1.安装JDK2.安装Android Studio2.1 获取安装包2.2 安装&#xff08;1&#xff09;配置环境变量&#xff1a;&#xff08;2&#xff09;运行安装&#xff1a;&#xff08;3&#xff09;配…...

token是用来鉴权的,那session是用来干什么的?

在Web应用和API设计中&#xff0c;鉴权与会话管理是两个核心概念&#xff0c;它们对于确保用户身份的安全性和维护用户会话状态至关重要。Token和Session是两种常用的鉴权与会话管理机制&#xff0c;它们各自具有独特的工作原理和适用场景。下面是对Token和Session的详细解析及…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...