Three.js 入门——核心概念和坐标系理解

Three.js 是什么?

一个封装了 WebGL 的库,简化 WebGL 的使用

WebGL vs OpenGL

OpenGL 主要被认为是一种 API(应用程序编程接口),它为我们提供了大量可用于操作图形和图像的函数,主要用 C语言编写的。

然而,OpenGL 本身并不是一个 API,而是一个规范,规定了每个函数如何执行和输出,至于具体实现,是由各个厂商的开发者根据自己的硬件特性开发出相应的 API。市场上,OpenGL 大都是显卡、GPU 、浏览器厂商来实现的,例如 Google。

WebGL 是一种用于在Web浏览器中展示 3D 图形的技术,是基于 OpenGL ES 2.0 的 Javascript API。也就是,通过浏览器提供的接口,我们可以使用底层的 OpenGL 库。

OpenGL ES 是 OpenGL 为了满足嵌入式设备需求而开发的一个特殊版本,是它的子集。但和 OpenGL 还是有差别,并不是只取其中一部分。

基础的 Three.js 应用结构

要写一个 Three.js 应用常用用到的四个核心概念,包括渲染器(Renderer)、场景(Scene)、相机(Camera)、网格(Mesh)。
在这里插入图片描述

渲染器 Renderer

将在指定相机角度下看到的 scene 画面绘制到浏览器上,也就是将摄像机的三维场景渲染成一个二维场景。

如果涉及到动态的场景,例如旋转,需要创建循环渲染 requestAnimationFrame,它会以每秒 60 次的频率来绘制场景

var renderer = new THREE.WebGLRenderer({ antialias: true });
// 将渲染器的像素比设置为设备像素比
renderer.setPixelRatio(window.devicePixelRatio);
// 将渲染器的大小设置为窗口的内部宽度(window.innerWidth)和高度(window.innerHeight)
//  如果为 1/2 ,视图将缩小一半
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置动画循环
document.body.appendChild(renderer.domElement);function renderFunc() {requestAnimationFrame(renderFunc); // 循环渲染renderer.render(scene, camera);
}renderFunc()

场景 Scene

场景是一个容器,可以将所有我们想渲染的物体都装在里面

 var scene = new THREE.Scene();scene.add(cube) // 把cube装入场景里,初始位置为 (0,0,0)

相机 Camera

在浏览器里渲染的景象就是从相机视角拍出来的。
相机的位置、视角不同,拍到的风景也不同,只能拍到相机投影范围内的景物
相机的类型有两种,透视摄像机(PerspectiveCamera)、正投影相机(OrthographicCamera)

透视摄像机 PerspectiveCamera

近大远小,距离相机近的地方大,距离相机远的地方小
在这里插入图片描述

var camera = new THREE.PerspectiveCamera(fov, aspect, near, far);

PerspectiveCamera 的参数说明,如下图所示:

  • fov:视场。可以理解为睁眼时,下眼皮到上眼皮睁开的角度。假如是 90 度,那么两个眼皮距离中间的角度分别是 45 度。假如是 180 度,那么物体会变小,因为在整个可视区的占比变小了。
  • aspect:长宽比。整个窗口的长宽比,一般是 window.innerWidth / window.innerHeight
  • near:近面。距离相机多近的距离开始渲染场景。
  • far:远面。定义相机可以看多远。

近面到远面的范围,为相机可以看到的范围,出了这个范围,则不可见。

在这里插入图片描述

正投影相机 OrthographicCamera

正投影相机的所有对象渲染的尺寸都一样。因此它不关心使用什么样的长宽比和视角。
在这里插入图片描述

var camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far)

OrthographicCamera 的参数说明,如下图所示:

  • left:左边界。可视范围的左平面,超出这个范围的不会被渲染。
  • right:右边界。可被渲染的最右面。
  • top:上边界。可被渲染的最上面。
  • bottom:下边界。可被渲染的最下面。
  • near:近面。距离相机的位置,从该位置开始渲染
  • far:远面。基于相机的位置,一直渲染到场景中的这一点

只有在范围 left、right、top、bottom、near、far 范围内的可见。超出该范围不可见。

在这里插入图片描述

网格 Mesh

可以理解为用一种特定的材质(Material)来绘制的一个特定的几何体(Geometry)

var cubeGeometry = new THREE.BoxGeometry(4, 4, 4); // 绘制立方体
var cubeMaterial = new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true}); // 绘制材质
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial); // 用该材质绘制后的立方体
scene.add(cube); // 添加到场景中

坐标系转换

坐标如下所示
在这里插入图片描述
可通过创建坐标系辅助对象,来展示坐标系。红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.

var coneAxesHelper = new THREE.AxesHelper(size); // size 为轴的线段长度

右手坐标系

Three.js 使用的是右手坐标系,x 轴朝右,y 轴朝上,z 轴朝向自己。

在这里插入图片描述

围绕轴旋转

拇指指向轴正方向,四指弯曲的方向为旋转正方向。
在这里插入图片描述

如下图所示,将 rotation 的 y 值,从 0 度改为 50 度,物体沿着 y 轴逆时针旋转

在这里插入图片描述
物体旋转 50 度:
在这里插入图片描述

editor 编辑器

使用 Three.js 源码 的 editor编辑器,可以让我们更直观的理解 Three.js 中的物体的坐标系位置。

将项目跑起来,点击到对应的目录即可。

由此可看出,相机被添加到场景中时的初始位置是 (0,0,0)
在这里插入图片描述
相机朝向 Z 轴的负方向
在这里插入图片描述

一个简单的 Three.js 应用

HTML 代码:

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>My first three.js app</title><style>body { margin: 0; }</style></head><body><script type="module" src="/main.js"></script></body>
</html>

Javascript 代码:

import * as THREE from 'three';const scene = new THREE.Scene(); // 场景
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); // 透视摄像机const renderer = new THREE.WebGLRenderer(); // 渲染器
renderer.setSize( window.innerWidth, window.innerHeight ); // 设置渲染器的大小
document.body.appendChild( renderer.domElement ); // 将渲染器元素添加到 HTML 文档中const geometry = new THREE.BoxGeometry( 1, 1, 1 ); // 创建一个立方体几何
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); // 创建材质
const cube = new THREE.Mesh( geometry, material ); // 用材质绘制后的立方体
scene.add( cube ); // 添加到场景中camera.position.z = 5; // 设置相机的位置,因为相机朝向 Z 轴负方向。往后推,这样才能看得到全局function animate() {requestAnimationFrame( animate ); // 循环渲染cube.rotation.x += 0.01; // 立方体沿 x 轴旋转cube.rotation.y += 0.01; // 立方体沿 y 轴旋转renderer.render( scene, camera ); // 渲染
}animate(); 

换成优雅的类封装

把所有的代码放在同一个页面里,会越堆越多,阅读体验不好。因此可按照功能拆分成函数。进一步可放到类里调用。

以下代码仅提供思路。

// 调用game = new Game("canvas.webgl");game.start();// 封装
export class Game {width!: number; // 视图宽度height!: number; // 视图高度aspect!: number; // 视图宽高比...constructor(selector: string) {if (Game.instance) {return Game.instance;}Game.instance = this;this.selector = selector;this.initConfig();this.initTime();this.initResource();this.initContainer();this.initScene();this.initCamera();this.initRenderer();...}initConfig() {this.width = window.innerWidth;this.height = window.innerHeight;this.aspect = this.width / this.height;}initScene() {this.gameScene = new GameScene();}initCamera() {this.gameCamera = new GameCamera();}...
}

参考资料,也为不错的学习资料。

《Three.js 开发指南》
Three.js 专栏
Three.js-docs
Three.js-manual

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

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

相关文章

python辅助QQ登入

python辅助QQ登入 import pyautogui import time import random from pyautogui import ImageNotFoundException# 生成随机等待时间&#xff0c;范围在1到3秒之间 random_time random.uniform(1, 3)def find_and_click(image_path, moveFalse, execute_nextTrue):try:image_l…

【QT学习】7.事件,把文本显示在页面中(文本可变),鼠标指针切换,鼠标左键右键按下,qt设置背景样式

0.创建项目&#xff0c;事件的创建 1.事件的位置 2.这就是多态&#xff0c;子类重写父类函数&#xff0c;子类调用子类函数&#xff0c;也可以调用父类函数。但同函数名 1.要求&#xff1a;文本显示在页面中&#xff08;文本可变&#xff09; 1.文本显示在页面的核心代码 主要步…

vue3通过事件总线不同组件之间传递消息(两个组件可以没有任何关系)

首先在main.js中定义 const app createApp(App) app.config.globalProperties.$eventBus new mitt() // 创建全局事件总线对象然后在发送事件的组件中写&#xff08;js和ts导入方式不太一样&#xff0c;用法一样&#xff09; <script setup> import {getCurrentInsta…

DRF requets源码分析

【四】requets源码分析 【1】查看request传递的数据 &#xff08;1&#xff09;视图层 编写传输数据的接口查看request方法的参数 class BookAPIView(APIView):def get(self, request, *args, **kwargs):return Response({body: request.body, data: request.data, post: r…

【Web】DASCTF X GFCTF 2022十月挑战赛题解

目录 EasyPOP hade_waibo EasyLove BlogSystem EasyPOP 先读hint.php sorry.__destruct -> secret_code::secret() exp: $anew sorry(); $bnew secret_code(); $a->password"suibian"; $a->name"jay"; echo serialize($a); 真暗号啊&…

web项目中jsp页面不识别el表达式

如果使用el表达式出现下图问题 ** 解决办法 ** 这是因为maven创建项目时&#xff0c;web.xml头部声明默认是2.3&#xff0c;这个默认jsp关闭el表达式 修改web.xml文件开头的web-app的版本 <?xml version"1.0" encoding"UTF-8"?> <web-app x…

Vue3 Reactive和Ref

当你在使用Vue 3时&#xff0c;reactive 和 ref 是两个常用的响应式API。它们都是用来跟踪状态变化并在UI中进行响应式更新的。 1. ref ref 用于创建一个响应式的基本数据类型变量&#xff0c;例如数字、字符串等。它返回一个带有 .value 属性的对象&#xff0c;该属性包含了…

Python爬取猫眼电影票房 + 数据可视化

目录 主角查看与分析 爬取可视化分析猫眼电影上座率前10分析猫眼电影票房场均人次前10分析猫眼电影票票房占比分析 主角查看与分析 爬取 对猫眼电影票房进行爬取&#xff0c;首先我们打开猫眼 接着我们想要进行数据抓包&#xff0c;就要看网站的具体内容&#xff0c;通过按F12…

Postman之安装

Postman工具之介绍与安装 Postman是什么&#xff1f;Postman有几种安装方式&#xff1f; Postman是什么&#xff1f; postman是一款http客户端的模拟器&#xff0c;它可以模拟发出各种各样的网络请求&#xff0c;用于接口测试。 Postman有几种安装方式&#xff1f; 两种&…

4.17 网络编程

思维导图 select实现TCP并发服务器 #include <myhead.h> #define SER_IP "192.168.125.26" #define SER_PORT 8888int main(int argc, const char *argv[]) {int sfd socket(AF_INET,SOCK_STREAM,0);if(sfd -1){perror("socket error");return -1…

hvv准备ing

常见的SQL注入&#xff1f;sqlmap的常用命令和功能&#xff1f;SQLMAPAPI怎么使用&#xff1f;sqlmap --os-shell 原理&#xff1a;在数据交互中&#xff0c;前端的数据传入到后台处理 时&#xff0c;由于后端没有做严格的判断&#xff0c;导致其传入的“数 据”拼接到SQL语句…

c++程序员通用成长规划

一、长期计划 要有一个长期的学习计划&#xff0c;确定学习方向&#xff0c;拆分为各个模块&#xff0c;每天学习多少&#xff0c;根据实际情况灵活调整&#xff0c;一切以当前实际工作为主后续发展为辅&#xff0c;并且要坚持。这里的坚持不是强制的&#xff0c;比如今天有事…

Spring Boot深度解析:是什么、为何使用及其优势所在

在Java企业级应用开发的漫长历史中&#xff0c;Spring框架以其卓越的依赖注入和面向切面编程的能力&#xff0c;赢得了广大开发者的青睐。然而&#xff0c;随着技术的不断进步和项目的日益复杂&#xff0c;传统的Spring应用开发流程逐渐显得繁琐和低效。为了解决这一问题&#…

基于Java+SpringBoot+Mybaties-plus+Vue+elememt 小区物业管理系统 的设计与实现

一.项目介绍 系统分为管理员 和 业主 两块&#xff1a; 管理员点击进入到系统操作界面&#xff0c;可以对首页、业主信息管理、管理员信息管理、 楼栋和房屋信息管理、物业费管理、地下停车位管理、公告信息管理、报修信息管理、 投诉管理以及个人信息等功能模块 …

libftdi1学习笔记 5 - SPI Nor Flash

目录 1. 初始化 2. CS控制例子 3. 读ID 3.1 制造商 3.2 容量大小 3.3 设置IO类型 3.3.1 setQSPIWinbond 3.3.2 setQSPIMxic 3.3.3 setQSPIMicrochip 3.3.4 setQSPIMicron 4. 写保护 5. 等待空闲 6. 擦除扇区 7. 页编程 8. 页读 9. 写 10. 读 11. 验证 基于M…

cesium加载高层级离线影像地图瓦片(天地图、19级Arcgis)

实际加载效果如图&#xff1a; 1、下载离线地图瓦片方式&#xff08;多种任选其一&#xff0c;个人倾向于Qgis工具&#xff09;&#xff1a; 方式1、采用第三方下载工具如&#xff1a;91卫图、水经注、全能电子地图下载器、bigemap等等。&#xff08;这些有的下载层级不够&…

python中什么是元类?

已知__class__是实例对象的属性&#xff0c;代表其所属的类。我们现在执行一段代码&#xff1a; class A:passa A() print(a.__class__) b 20 print(b.__class__) c xxx print(c.__class__)结果为 <class __main__.A> <class int> <class str>即&#xf…

Spring Boot:Web应用开发之登录与退出的实现

Spring Boot 前言实现登录功能配置拦截器 实现退出功能 前言 登录与退出功能作为 Web 应用中的基础且重要的组成部分&#xff0c;直接关系到用户的安全和隐私保护。通过实现登录与退出功能&#xff0c;可以对用户的身份进行验证和授权&#xff0c;确保只有合法的用户才能访问特…

Qwen1.5大语言模型微调实践

在人工智能领域&#xff0c;大语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;的兴起和广泛应用&#xff0c;为自然语言处理&#xff08;NLP&#xff09;带来了前所未有的变革。Qwen1.5大语言模型作为其中的佼佼者&#xff0c;不仅拥有强大的语言生成和理…

Pytorch——训练时,冻结网络部分参数的方法

一、原理&#xff1a; 要固定训练网络的哪几层&#xff0c;只需要找到这几层参数(parameter)&#xff0c;然后将其 .requires_grad 属性设置为 False 即可。 二、代码&#xff1a; # 根据参数层的 name 来进行冻结 unfreeze_layers ["text_id"] # 用列表 # 设置冻…