threejs之贴图原理

news2025/2/24 15:17:34
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

// 创建场景
const scene = new THREE.Scene();

// 创建相机
const camera = new THREE.PerspectiveCamera(
  45, // 视角
  window.innerWidth / window.innerHeight, // 宽高比
  0.1, // 近平面
  1000 // 远平面
);

// 创建渲染器
const renderer = new THREE.WebGLRenderer({
  antialias: true, // 抗锯齿
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 设置相机位置
camera.position.z = 15;
camera.position.y = 12;
camera.position.x = 12;
camera.lookAt(0, 0, 0);

// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

// 添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
// controls.autoRotate = true;

// 渲染函数
function animate() {
  controls.update();
  requestAnimationFrame(animate);
  // 渲染
  renderer.render(scene, camera);
}
animate();

// 监听窗口变化
window.addEventListener("resize", () => {
  // 重置渲染器宽高比
  renderer.setSize(window.innerWidth, window.innerHeight);
  // 重置相机宽高比
  camera.aspect = window.innerWidth / window.innerHeight;
  // 更新相机投影矩阵
  camera.updateProjectionMatrix();
});

// 创建纹理加载器
let textureLoader = new THREE.TextureLoader();
// 加载纹理
let texture = textureLoader.load("./texture/uv_grid_opengl.jpg");
texture.colorSpace = THREE.SRGBColorSpace;
texture.minFilter = THREE.LinearMipMapLinearFilter;

// 设置纹理包裹方式为 ClampToEdgeWrapping
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;

const geometry = new THREE.BufferGeometry();
const plane = new THREE.Mesh(
  geometry,
  new THREE.MeshBasicMaterial({
    map: texture,
  })
);

let uv = new THREE.Float32BufferAttribute(
  [
    0,
    0, // 左下角顶点的UV坐标

    1,
    0, // 右下角顶点的UV坐标

    1,
    1, // 右上角顶点的UV坐标

    0,
    1, // 左上角顶点的UV坐标
  ],
  2
);
let position = new THREE.Float32BufferAttribute(
  [
    -5,
    -5, // 左下角顶点的UV坐标
    0,

    5, // 右下角顶点的UV坐标
    -5,
    0, 

    5,
    5, // 右上角顶点的UV坐标
    0,

    -5, // 左上角顶点的UV坐标
    5,
    0,
  ],
  3
);
const index = new THREE.Uint16BufferAttribute(
  [
    0,
    1,
    2, // 第一个三角形
    0,
    2,
    3, // 第二个三角形
  ],
  1
);

geometry.setIndex(index);
geometry.setAttribute('uv',uv)
geometry.setAttribute('position',position)
scene.add(plane);

uv的取值范围是从0到1的,uv的设置和position的设置顺序有关。在上述例子中position的设置是逆时针设置的,所以uv的设置也要逆时针设置才能正确取样。
在这里插入图片描述

在这里插入图片描述
接下来我们再把BufferGeometry替换成PlaneGeometry

// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

// 创建场景
const scene = new THREE.Scene();

// 创建相机
const camera = new THREE.PerspectiveCamera(
  45, // 视角
  window.innerWidth / window.innerHeight, // 宽高比
  0.1, // 近平面
  1000 // 远平面
);

// 创建渲染器
const renderer = new THREE.WebGLRenderer({
  antialias: true, // 抗锯齿
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 设置相机位置
camera.position.z = 15;
camera.position.y = 12;
camera.position.x = 12;
camera.lookAt(0, 0, 0);

// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

// 添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
// controls.autoRotate = true;

// 渲染函数
function animate() {
  controls.update();
  requestAnimationFrame(animate);
  // 渲染
  renderer.render(scene, camera);
}
animate();

// 监听窗口变化
window.addEventListener("resize", () => {
  // 重置渲染器宽高比
  renderer.setSize(window.innerWidth, window.innerHeight);
  // 重置相机宽高比
  camera.aspect = window.innerWidth / window.innerHeight;
  // 更新相机投影矩阵
  camera.updateProjectionMatrix();
});

// 创建纹理加载器
let textureLoader = new THREE.TextureLoader();
// 加载纹理
let texture = textureLoader.load("./texture/uv_grid_opengl.jpg");
texture.colorSpace = THREE.SRGBColorSpace;
texture.minFilter = THREE.LinearMipMapLinearFilter;

// 设置纹理包裹方式为 ClampToEdgeWrapping
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;

const geometry = new THREE.PlaneGeometry(10,10);
const plane = new THREE.Mesh(
  geometry,
  new THREE.MeshBasicMaterial({
    map: texture,
  })
);

scene.add(plane);

我们再来看看此时的position、uv、index设置:

position的取值如下:
{
    "itemSize": 3,
    "type": "Float32Array",
    "array": [
        -5,
        5,
        0,
        5,
        5,
        0,
        -5,
        -5,
        0,
        5,
        -5,
        0
    ],
    "normalized": false
}
uv取值如下:
{
    "itemSize": 2,
    "type": "Float32Array",
    "array": [
        0,
        1,
        1,
        1,
        0,
        0,
        1,
        0
    ],
    "normalized": false
}
index的取值如下:
{
    "itemSize": 1,
    "type": "Uint16Array",
    "array": [
        0,
        2,
        1,
        2,
        3,
        1
    ],
    "normalized": false
}

uv的值的顺序取决于position的设置顺序。比如position的设置顺序是从[-5,5,0]->[5,5,0]->[-5,-5,0]->[5,-5,0],所以uv的设置顺序是左上角->右上角->左下角->右下角

对于index的设置如果你按照逆时针的顺序定义顶点索引,渲染引擎会认为这是一个正面的三角形,会正确计算法线和光照效果。如果你按照顺时针的顺序定义顶点索引,渲染引擎会认为这是一个背面的三角形,可能会导致不正确的光照效果。但是texture会默认flipY为true,所以我们看到的是逆时针的,其实本来是顺时针。

在这里插入图片描述
如果我们设置为顺时针,看到正面其实是没有图像的,因为图像在反面。
在这里插入图片描述
可以看出z轴是背向我们的。
在这里插入图片描述
geometry.setIndex(new THREE.Uint16BufferAttribute([0,1,2,2,3,1],1)),如果我们设置index的值为一半顺时针,一半逆时针那么就会出现正面有一半反面有一半,我们始终只能看到一半。
z轴朝向我们是正面。
在这里插入图片描述
z轴背对我们是反面。
在这里插入图片描述

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

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

相关文章

使用Java JDBC连接数据库

在Java应用程序中,与数据库交互是一个常见的任务。Java数据库连接(JDBC)是一种用于在Java应用程序和数据库之间建立连接并执行SQL查询的标准API。通过JDBC,您可以轻松地执行各种数据库操作,如插入、更新、删除和查询数…

有没搞错!花了大价钱的激光孔设计性能竟然不如普通通孔?

高速先生成员--黄刚 老话说得好,一分耕耘一分收获,又或者另外一句,有什么付出就会得到多少收获。我们都不会去怀疑这些话的正确性。但是把这两句话用到PCB领域中,用了好的加工工艺后,PCB板的性能就一定会比用普通工艺要…

视频素材免费下载素材库哪里有?推荐8个高清无水印素材网

在这个数字化时代,无论是专业的内容创作者还是日常的社交媒体使用者,我们都会寻找高质量的素材来丰富我们的作品或帖子。从令人震撼的摄影作品到高分辨率的视频素材,再到生动的GIF和必需的设计元素,素材的需求无处不在。 视频素材…

XXL-JOB完全开发手册(一篇学会XXL-JOB所有知识点)

目录 1、什么是XXL-JOB 1.1、XXL-JOB简介 1.2、XXL-JOB构成 调度模块(调度中心): 执行模块(执行器): 任务: 1.3、XXL-JOB总结 ​编辑 2、XXL-JOB原理 2.1、执行器的注册和发现 2.2、调度中心调用…

基于华为ensp的企业网络规划(新版)

第一章 项目概述 1.1 项目总体描述 假设某大型公司总部在北京、在重庆设置分部,总部和分部均有研发部、市场部、财务部等部门,现在要求进行网络规划与设计,实现分部和总部能够进行网络连通。为了保证数据安全,在总部和分部之间可…

教师怎么发成绩才不会被投诉

在当今社会,教育的重要性日益凸显,而学生的成绩作为衡量教育效果的重要指标之一,备受家长和学生的关注。然而,教师在发布成绩时,稍有不慎就可能引发家长的不满和投诉。那么,教师该如何发成绩才能避免被投诉…

【Spring Cloud Gateway】路由配置uri三种方式及区别

websocket配置方式 ws:// 或 wss:// 开头的 URI,表示配置的是支持 Websocket 协议的目标地址。 这种方式适用于需要与客户端建立长连接、实现双向通信的场景,比如实时消息推送、即时聊天等。 使用 Websocket 配置方式可以让 Spring Cloud Gateway 能够…

【物联网】Modbus 协议及应用

Modbus 协议简介 QingHub设计器在设计物联网数据采集时不可避免的需要针对Modbus协议的设备做相关数据采集,这里就我们的实际项目经验分享Modbus协议 简介 Modbus由MODICON公司于1979年开发,是一种工业现场总线协议标准。1996年施耐德公司推出基于以太…

虚拟机VMware上 centos7 的网络配置

第一步:权限的切换 由普通用户切换到管理者/超级用户 用户名为:root 密码为:自己安装 linux 时第一次设置的密码 su -root管理者/超级用户的命令提示符是“#”,普通用户的命令提示符是“$”。当看到你的命令提示符为“$”时&…

《世界之外》玩家闹上315,乙游打响维权大战

315维权微博的评论区,竟然被举报网易的玩家占领了。 玩家举报网易乙游《世界之外》虚假宣传侵害消费者权益,在游戏中设置排行榜和专属商店将玩家分为三六九等,诱导玩家消费氪金,强烈要求网易打开退款通道。 目前大批玩家举报的举…

如何在三个简单步骤中为对象检测标注图像

初始通过彻底清洗和处理原始图像数据来奠定有效对象检测注释的基础。选择适合的工具、方法和清晰的注释过程指南来建立注释工作空间。通过在图像中划定对象并附上类别标签来执行注释,随后进行细致的核验,以确保数据集的精确性和完整性。 图像注释是计算…

klipper源码分析之simulavr测试

分析Klipper源码,有时需要结合下位机一起分析,这样才能更加全面的了解Klipper的工作原理。如果手头上有打印机主板,电脑当做上位机运行Klipper,这样是比较方便。如果手头上没有打印机主板,可以用simulavr模拟AVR下位机…

蓝桥杯备赛_python_DFS搜索算法_刷题学习笔记

1.是什么 沿着一条路径一直搜索下去,在无法搜索时,回退到刚刚访问过的节点。并且每个节点只能访问一次。本质上是持续搜索,遍历了所有可能的情况,必然能得到解。 流程是一个树的形式,每次一条路走到黑。 目的主要是达到…

外包2月,技术退步惊现!大专生逆袭大厂,全靠这份神秘资料!

大家好,我是一名大专生,19年通过校招进入湖南某软件公司,从事功能测试工作已近4年。今年8月,我意识到长期舒适的环境让我变得不思进取,技术停滞不前,甚至因此失去了谈了2年的女朋友。我下定决心&#xff0c…

数据容器-list-Python

师从黑马程序员 列表的定义语法 注:列表可以一次存储多个数据,且可以为不同的数据类型,支持嵌套 my_list["itheima","chengxuyuan","python"] print(my_list) print(type(my_list))#元素类型不受限 my_list[&…

明微电源适配器AC-DC降压型SSR/Buck/Buck-Boost驱动芯片SM7012

电源适配器是一种将市电转换成直流电的装置,常用于为电子设备提供稳定的电源。AC-DC降压型SSR(固态继电器)、Buck、Buck-Boost是三种不同的电源转换技术。 1. SSR(固态继电器):SSR是一种电子开关&#xff…

Linux之shell文本编辑工具sed

华子目录 sed概念工作原理 基本语法格式参数定位符操作 输出文本范例文本定址符和操作符连用搭配正则使用规定语法 文本替换范例文件格式示例&示例\u大写字母\L小写字母\b确定单词边界 配置实例使用替换实现删除 删除文本示例 插入文本格式示例 注意 sed概念 sed是一个非交…

libVLC windows开发环境搭建

1.简介 LibVLC是一个强大的开源库,它构成了VLC媒体播放器的核心部分。 LibVLC提供了一系列的功能接口,使得VLC能够处理流媒体的接入、音频和视频输出、插件管理以及线程系统等核心任务。 跨平台性:VLC作为一个跨平台的多媒体播放器&#x…

源码部署LAMP架构

LAMP 文章目录 LAMP1. lamp简介2. web服务器工作流程2.1 cgi与fastcgi2.2 httpd与php结合的方式2.3 web工作流程 3. LAMP平台构建3.1 安装httpd3.2 安装mysql3.3 安装php3.4 验证 1. lamp简介 有了前面学习的知识的铺垫,今天可以来学习下第一个常用的web架构了。 …

网络编程:多点通信+域套接字

一、多点通信 1.网络属性 getsockopt和setsockopt int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); 功能:获取或设置套接字…