Three.js Journey (notes)

news2025/3/13 15:07:30

Ref

Three.js中文网

Three.js Journey — Learn WebGL with Three.js

Part 1 first-threejs-project

1. build tools

①vite vs webpack

  do a bunch of things like optimizations,cache breaking,source mapping,running a local server,etc.

build tool

vite (most appreciated):

faster to install, faster to run, and less prone to bugs.

webpack (widely used):

It’s widely used, it has a great community and you can do a lot with it. 

初始化速度

在开发模式下通过原生 ES 模块(ESM)来服务应用,可以按需编译未缓存的模块,从而大大减少了首次启动时间在开发服务器启动时会将整个应用程序打包,这可能导致较慢的初始化速度,尤其是在大型项目中

热更新

非常快速的热更新体验,因为它只重新加载修改过的模块,而不是整个页面热更新机制通常需要更多的配置,并且在某些情况下可能不如 Vite 快速或可靠

生态系统和插件

插件数量和成熟度目前可能还不及 Webpack拥有庞大的社区支持和丰富的插件库,可以满足各种复杂的构建需求

构建性能

使用 Rollup 来进行打包,对于大多数现代 JavaScript 应用来说,这可以提供更快的构建速度和更小的输出文件构建性能可以通过多种优化手段得到提升,但在默认配置下,它的构建速度可能会比 Vite 慢一些

兼容性

主要针对现代浏览器和基于 ESM 的开发流程,对旧版浏览器的支持需要额外配置支持老旧浏览器的项目\更多定制化功能

.To add a dependency to a Node.js project, in the terminal, run npm install vite

2.Terminal

①Launch VSCode and press CMD + J (MacOS) or CTRL + J (Windows) to open the integrated terminal.

②目录查看

③CTRL + C to stop the server

3. Node.js

① node.js——a dependency manager that will fetch the packages we need.

 NPM stands for Node Package Manager and is a dependency manager that will fetch the packages we need such as Three.js, Vite, and the various libraries we are going to use in the course.

  • In order to run Vite, you need to have Node.js installed on your computer.
  • Node.js enables running JavaScript on your computer outside of a browser. It’s great to run various tools like Vite, it has been around for many years and is very popular.
  • If you don’t know if Node.js is already installed or which version is installed run node -v from your terminal.
  • If the answer tells you that the node command isn’t recognised, then it’s not installed.
  • If it’s installed, the answer will contain the current version. Make sure it’s updated to the last version. Vite currently works with version 14.18 and above, but I recommend you always have the LTS (Long Term Support) version of Node.js installed.
  • To install or update Node.js, go to Node.js — Run JavaScript Everywhere, download the “LTS” and install it with the default settings.
  • Close your Terminal (MacOS) or Command Prompt (Windows), re-open it, and run node -v again to check the version.

4. Create a Node.js project

(1)npm init -y

By running npm init -y, NPM will create a package.json that will contain the minimal information needed to run a Node.js project.

  • 创建一个名为 package.json 的文件,该文件是项目的配置文件,包含了项目的元数据(如名称、版本、描述等)、依赖项列表以及脚本命令等信息。
  • 在执行 npm init 时,npm 会引导用户输入一系列问题(例如项目名称、版本号、入口文件等),根据用户的回答生成或更新 package.json 文件。
  • 可以通过传递参数简化这个过程,比如使用 npm init --yes 或 npm init -y 来自动接受所有默认值,快速创建 package.json
  • The package.json has been updated and now contains an array of dependencies written in the "dependencies" properties with numbers corresponding to the version with a level of tolerance.
  • package-lock.json has been written and contains information about the dependencies and the exact versions that have been installed in your project without tolerance.

(2)npm install

  • 安装在 package.json 文件中的 dependencies 和 devDependencies 列表中指定的包,并将它们下载到项目的 node_modules 目录下。
  • 如果没有指定具体的包名,npm install 会读取 package.json 并安装所有列出的依赖。
  • 如果指定了包名(例如 npm install <package-name>),则会安装该包及其依赖,并将其添加到 dependencies 或 devDependencies 中(取决于是否使用了 -D 或 --save-dev 标志)。
  • 还可以使用 --save 或 -S(现在已默认行为)来保存安装的包作为生产依赖,或者使用 -D 或 --save-dev 来保存为开发依赖。
  • npm init 主要用于创建或更新 package.json 文件,定义项目的元数据和初始依赖结构。
  • npm install 主要用于安装项目依赖,确保开发者拥有运行和构建项目所需的所有外部库。

(3)node_modules/

  This folder contains the project dependencies and you should never modify things inside that folder. Besides containing the vite dependency as the vite/ folder, it also features the other dependencies that are being used by vite.

  If you want to share your beautiful Three.js website with another developer, you’ll want to remove the node_modules folder and share the rest of the project. The other developer will retrieve your project and run a command that will automatically re-create the node_modules folder according to what is written in package.json

Note that package-lock.json is optional, but if it’s present in the project, the other developer will get the exact same versions of the dependencies as you with no tolerance.

5. Basic website

We have our Node.js project, we have Vite added as a dependency.

In package.json, replace the "scripts" part with the following:

{
  // ...
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  },
  // ...
}

  By having those two “scripts”, we can now run those dev and build commands from the terminal and those commands will trigger vite and vite build respectively by using the vite/ dependency from the node_modules/ folder.

①npm run dev (final step)

  To run the dev script, in the terminal, run npm run dev.

   Vite should display a URL looking something like http://localhost:5173/. Copy it and paste it into your browser to open it like any website.

P.S. vite.config.js vs package.json

  • vite.config.js:

    • 仅在使用 Vite 构建工具的上下文中起作用,它不是通用的 JavaScript 项目配置文件。
    • 是 Vite 构建工具的配置文件,主要用于定义与构建过程相关的设置,如插件使用、环境变量、服务器配置、优化选项等。
    • 帮助开发者自定义和扩展 Vite 的行为,以适应项目的特定需求。
  • package.json:

    • package.json 中的 scripts 字段常常包含启动 Vite 开发服务器或执行 Vite 构建的命令;是一个广泛使用的标准文件,适用于所有的 Node.js 和 JavaScript 项目,不限于使用 Vite 或任何其他特定的构建工具。
    • 是一个 npm(Node Package Manager)或 yarn 的项目描述文件,用于记录项目的元数据信息,例如名称、版本、作者、许可证等。
    • 列出项目的依赖项(dependencies)和开发依赖项(devDependencies),以及脚本命令(scripts)来执行各种任务,比如启动开发服务器、构建生产版本、运行测试等,确保所有开发者在安装项目时都能获得相同版本的库。

vite.config.js 更像是一个具体的构建配置文件,而 package.json 则更像是一个项目的全局管理和描述文件。


1.Three.js加载

①npm install three

(1) script标签方式引入three.js

three.js库可以在threejs官方文件包下面的build目录获取到。

<script src="./build/three.js"></script>
//随便输入一个API,测试下是否已经正常引入three.js
console.log(THREE.Scene); 
(2) ES6 import方式引入

  import * as THREE from 'three'

  will import all core classes of Three.js inside the THREE variable.

  除了three.js核心库以外,在threejs文件包中examples/jsm目录下,还可以看到各种不同功能的扩展库。

// 引入扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 引入扩展库GLTFLoader.js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
// 扩展库引入——旧版本,比如122, 新版本路径addons替换了examples/jsm
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

(3)  type="importmap"配置路径
<!-- 具体路径配置,你根据自己文件目录设置,我的是课件中源码形式 -->
<script type="importmap">
    {
		"imports": {
			"three": "../../../three.js/build/three.module.js"
		}
	}
</script>

The three/ folder should have been added to the node_modules/ folder and both package.json and package-lock.json now contain references to it.


2. 4 Basic elements

Renderer

In index.html, instead of the <h1>, create the <canvas> element before you load the scripts and give it a class

在index.html中:

<canvas class="webgl"></canvas>

Create a canvas variable at the start of the code, then fetch and store in it the element we created in the HTML using document.querySelector(...).

在script.js中: 

Create a canvas variable at the start of the code, then fetch and store in it the element we created in the HTML using document.querySelector(...).

// Canvas
const canvas = document.querySelector('canvas.webgl')

// ...

// Renderer
const renderer = new THREE.WebGLRenderer({
    canvas: canvas
})
renderer.setSize(sizes.width, sizes.height)

renderer.render(scene, camera)

  • Scene

  a container in which we put objects , models, particles , lights , etc.

①const scene = new THREE.Scene()
  • Obects

  can be many things

const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
//设置网格模型在三维空间中的位置坐标,默认是坐标原点
mesh.position.set(0,10,0);

scene.add(mesh); 
  • Camera

  can have multiple cameras but usually only use one

const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height)
camera.position.z = 3
scene.add(camera)

Part 2 Transform objects 

4 properties to transform objects in our scene

  • position (to move the object)
  • scale (to resize the object)
  • rotation (to rotate the object)
  • quaternion (to also rotate the object; more about that later)

Move objects

The position possesses 3 essential properties, which are xy, and z. Those are the 3 necessary axes to position something in a 3D space.

The direction of each axis is purely arbitrary, and it can vary according to the environment. In Three.js, we usually consider that the y axis is going upward, the z axis is going backward, and the x axis is going to the right.

As for the meaning of 1 unit, it's up to you. 1 can be 1 centimeter, 1 meter, or even 1 kilometer. I recommend that you adapt the unit to what you want to build. If you're going to create a house, you probably should think of 1 unit as 1 meter.

You can play around with the position property of your mesh and try to guess where the cube will get (try to keep it in the screen).

Make sure to do that before you call the render(...) method or you will render the mesh before moving it.

mesh.position.x = 0.7
mesh.position.y = - 0.6
mesh.position.z = 1

 The position property is not any object. It's an instance of the Vector3 class. While this class has an x, a y, and a z property, it also has many useful methods.

console.log(mesh.position.length())

console.log(mesh.position.distanceTo(camera.position))

console.log(mesh.position.normalize())

// To change the values, instead of changing x, y and z separately, you can also use the set(...) method:
mesh.position.set(0.7, - 0.6, 1)

 Axes helper

The AxesHelper will display 3 lines corresponding to the xy and z axes, each one starting at the center of the scene and going in the corresponding direction.

To create the AxesHelper, instantiate it and add it to the scene right after instantiating that scene. You can specify the length of the lines as the only parameter. We are going to use 2:

/**
 * Axes Helper

The green line corresponds to the y axis. 
The red line corresponds to the x axis and 
there is a blue line corresponding to the z axis 
but we can't see it because it's perfectly aligned with the camera.

 */
const axesHelper = new THREE.AxesHelper(2)
scene.add(axesHelper)

Scale objects

mesh.scale.x = 2
mesh.scale.y = 0.25
mesh.scale.z = 0.5

Rotate objects 

In native JavaScript, you can end up with an approximation of π using Math.PI.

mesh.rotation.x = Math.PI * 0.25
mesh.rotation.y = Math.PI * 0.25

 We can change this order by using the reorder(...) method object.rotation.reorder('YXZ')

Quaternion

The quaternion property also expresses a rotation, but in a more mathematical way, which solves the order problem.

We will not cover how quaternions work in this lesson but keep in mind that the quaternion updates when you change the rotation. This means that you can use any one of the two as you please.

Look at this!

Object3D instances have an excellent method named lookAt(...) that lets you ask an object to look at something. The object will automatically rotate its -z axis toward the target you provided. No complicated maths needed.

You can use it to rotate the camera toward an object, orientate a cannon to face an enemy, or move the character's eyes to an object.

The parameter is the target and must be a Vector3. You can create one just to try it:

camera.lookAt(new THREE.Vector3(0, - 1, 0))
camera.lookAt(mesh.position)

Scene graph

At some point, you might want to group things. Let's say you are building a house with walls, doors, windows, a roof, bushes, etc.

When you think you're done, you become aware that the house is too small, and you have to re-scale each object and update their positions.

A good alternative would be to group all those objects into a container and scale that container.

You can do that with the Group class.

/**
 * Objects
 */
const group = new THREE.Group()
group.scale.y = 2
group.rotation.y = 0.2
scene.add(group)

const cube1 = new THREE.Mesh(
    new THREE.BoxGeometry(1, 1, 1),
    new THREE.MeshBasicMaterial({ color: 0xff0000 })
)
cube1.position.x = - 1.5
group.add(cube1)

const cube2 = new THREE.Mesh(
    new THREE.BoxGeometry(1, 1, 1),
    new THREE.MeshBasicMaterial({ color: 0xff0000 })
)
cube2.position.x = 0
group.add(cube2)

const cube3 = new THREE.Mesh(
    new THREE.BoxGeometry(1, 1, 1),
    new THREE.MeshBasicMaterial({ color: 0xff0000 })
)
cube3.position.x = 1.5
group.add(cube3)

Part 3 Animations

The screen you are looking at runs at a specific frequency. We call that a frame rate. The frame rate mostly depends on the screen, but the computer itself has limitations. Most screens run at 60 frames per second. If you do the maths, that means about a frame every 16ms. But some screens can run much faster, and when the computer has difficulties processing things, it'll run more slowly.


requestAnimationFrame will execute the function you provide on the next frame. But then, if this function also uses requestAnimationFrame to execute that same function on the next frame, you'll end up with your function being executed on each frame forever which is exactly what we want.

Create a function named tick and call this function once. In this function, use window.requestAnimationFrame(...) to call this same function on the next frame:

/**
 * Animate
 */
const tick = () =>
{
    // Update objects
    mesh.rotation.y += 0.01

    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

Adaptation to the framerate 

First, we need a way to measure time. In native JavaScript, you can use Date.now() to get the current timestamp:

const time = Date.now()

The timestamp corresponds to how much time has passed since the 1st of January 1970 (the beginning of time for Unix). In JavaScript, its unit is in milliseconds.

What you need now is to subtract the current timestamp to that of the previous frame to get what we can call the deltaTime and use this value when animating objects:

/**
 * Animate
 */
let time = Date.now()

const tick = () =>
{
		// Time
    const currentTime = Date.now()
    const deltaTime = currentTime - time
    time = currentTime

    // Update objects
    mesh.rotation.y += 0.01 * deltaTime

    // ...
}

tick()

Using Clock

You simply have to instantiate a Clock variable and use the built-in methods like getElapsedTime(). This method will return how many seconds have passed since the Clock was created.

You can use this value to rotate the object:

/**
 * Animate
 */
const clock = new THREE.Clock()

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()

    // Update objects
    mesh.rotation.y = elapsedTime

    // ...
}

tick()

 You can also use it to move things with the position property. If you combine it with Math.sin(...) you can get a pretty good result:

/**
 * Animate
 */
const clock = new THREE.Clock()

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()

    // Update objects
    mesh.position.x = Math.cos(elapsedTime)
    mesh.position.y = Math.sin(elapsedTime)

    // ...
}

tick()

 And obviously, you can use those techniques to animate any Object3D like the camera:

/**
 * Animate
 */
const clock = new THREE.Clock()

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()

    // Update objects
    camera.position.x = Math.cos(elapsedTime)
    camera.position.y = Math.sin(elapsedTime)
    camera.lookAt(mesh.position)

    // ...
}

tick()

 Using a library

Sometimes you'll want to animate your scene in a very specific way that will require using another library. There are tons of animation libraries, but a very famous one is GSAP.

To add GSAP to our project, we can use the dependency manager provided with Node.js called npm.

In your terminal (while the server is not running or by using another terminal window on the same folder), run npm install gsap@3.12

The @3.12 forces the version. We use this version because it was the one used when writing the lesson, but you can try the latest version if you want by removing @3.12.

GSAP is now available in the node_modules/ folder, and we can import it in our script.js:

import './style.css'
import * as THREE from 'three'
import gsap from 'gsap'

// ...

 There are many ways of using GSAP, and we could dedicate an entire course to it, but it is not the goal of this course. We will simply create a tween to test things out. If you already know how to use GSAP, it works the same with Three.js.

Comment the code related to the previous animations but keep the tick function with the render. Then you can create what we call a tween (an animation from A to B) using gsap.to(...):

/**
 * Animate
 */
gsap.to(mesh.position, { duration: 1, delay: 1, x: 2 })

const tick = () =>
{
    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

 GSAP has a built-in requestAnimationFrame, so you don't need to update the animation by yourself, but still, if you want to see the cube moving, you need to keep doing the renders of your scene on each frame.

Part 4 Camera

ArrayCamera

The ArrayCamera is used to render your scene multiple times by using multiple cameras. Each camera will render a specific area of the canvas. You can imagine this looking like old school console multiplayer games where we had to share a split-screen.

StereoCamera

The StereoCamera is used to render the scene through two cameras that mimic the eyes in order to create what we call a parallax effect that will lure your brain into thinking that there is depth. You must have the adequate equipment like a VR headset or red and blue glasses to see the result.

CubeCamera

The CubeCamera is used to get a render facing each direction (forward, backward, leftward, rightward, upward, and downward) to create a render of the surrounding. You can use it to create an environment map for reflection or a shadow map. 

OrthographicCamera

The OrthographicCamera is used to create orthographic renders of your scene without perspective. It's useful if you make an RTS game like Age of Empire. Elements will have the same size on the screen regardless of their distance from the camera.

PerspectiveCamera

The PerspectiveCamera is the one we already used and simulated a real-life camera with perspective.

We are going to focus on the OrthographicCamera and the PerspectiveCamera.

As we saw earlier, the PerspectiveCamera class needs some parameters to be instantiated, but we didn't use all the possible parameters. Add the third and fourth parameters:

const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 1, 100)

Field of view

The first parameter called field of view corresponds to your camera view's vertical amplitude angle in degrees. If you use a small angle, you'll end up with a long scope effect, and if you use a wide-angle, you'll end up with a fish eye effect because, in the end, what the camera sees will be stretched or squeezed to fit the canvas.

As for choosing the right field of view, you'll have to try things out. I usually use a field of view between 45 and 75.

Aspect ratio

The second parameter is called aspect ratio and corresponds to the width divided by the height. While you might think that it's obviously the canvas width by the canvas height and Three.js should calculate it by itself, it's not always the case if you start using Three.js in very specific ways. But in our case, you can simply use the canvas width and the canvas height.

I recommend saving those values in an object because we are going to need them multiple times:

const sizes = {
    width: 800,
    height: 600
}

 Near and far

The third and fourth parameters called near and far, correspond to how close and how far the camera can see. Any object or part of the object closer to the camera than the near value or further away from the camera than the far value will not show up on the render.

You can see that like in those old racing games where you could see the trees pop up in the distance.

While you might be tempted to use very small and very large values like 0.0001 and 9999999 you might end up with a bug called z-fighting where two faces seem to fight for which one will be rendered above the other.

https://twitter.com/FreyaHolmer/status/799602767081848832

https://twitter.com/Snapman_I_Am/status/800567120765616128

Try to use reasonable values and increase those only if you need it. In our case, we can use 0.1 and 100.

OrthographicCamera

The OrthographicCamera differs from the PerspectiveCamera by its lack of perspective, meaning that the objects will have the same size regardless of their distance from the camera.

The parameters you have to provide are very different from the PerspectiveCamera.

Instead of a field of view, you must provide how far the camera can see in each direction (leftrighttop and bottom). Then you can provide the near and far values just like we did for the PerspectiveCamera.

Comment the PerspectiveCamera and add OrthographicCamera . Keep the position update and lookAt(...) call:

const camera = new THREE.OrthographicCamera(- 1, 1, 1, - 1, 0.1, 100)

As you can see, there is no perspective, and the sides of our cube seem parallel. The problem is that our cube doesn't look cubic.

That is due to the values we provided for the leftrighttop, and bottom which are 1 or - 1, meaning that we render a square area, but that square area will be stretched to fit our rectangle canvas and our canvas isn't a square.

We need to use the canvas ratio (width by height). Let's create a variable named aspectRatio (just like the PerspectiveCamera) and store that ratio in it:

const aspectRatio = sizes.width / sizes.height
const camera = new THREE.OrthographicCamera(- 1 * aspectRatio, 1 * aspectRatio, 1, - 1, 0.1, 100)

 Custom controls

// Camera
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 1, 1000)

// const aspectRatio = sizes.width / sizes.height
// const camera = new THREE.OrthographicCamera(- 1 * aspectRatio, 1 * aspectRatio, 1, - 1, 0.1, 100)

// camera.position.x = 2
// camera.position.y = 2
camera.position.z = 3
camera.lookAt(mesh.position)
scene.add(camera)

 What we want to do now is control the camera with our mouse. First of all, we want to know the mouse coordinates. We can do that using native JavaScript by listening to the mousemove event with addEventListener.

The coordinates will be located in the argument of the callback function as event.clientX and event.clientY:

// Cursor
window.addEventListener('mousemove', (event) =>
{
    console.log(event.clientX, event.clientY)
})

 We could use those values, but I recommend adjusting them. By adjusting, I mean to have a 1 amplitude and that the value can be both negative and positive.

If we only focus on the x value, that would mean that:

  • if your cursor is on the far left of the canvas, you should get - 0.5
  • if your cursor is at the center of the canvas, you should get 0
  • if your cursor is at the far right of the canvas, you should get 0.5

While this is not mandatory, it helps to have clean values like that.

Just like the size variable, we will create a cursor variable with default x and y properties and then update those properties in the mousemove callback:

// Cursor
const cursor = {
    x: 0,
    y: 0
}

window.addEventListener('mousemove', (event) =>
{
    cursor.x = event.clientX / sizes.width - 0.5
    cursor.y = event.clientY / sizes.height - 0.5

    console.log(cursor.x, cursor.y)
})

 Dividing event.clientX by sizes.width will give us a value between 0 and 1 (if we keep the cursor above the canvas) while subtracting 0.5 will give you a value between - 0.5 and 0.5.

You now have the mouse position stored in the cursor object variable, and you can update the position of the camera in the tick function:

const tick = () =>
{
    // ...

    // Update camera
    camera.position.x = cursor.x
    camera.position.y = cursor.y

    // ...
}

As you can see, it's working but the axes movements seem kind of wrong. This is due to the position.y axis being positive when going upward in Three.js but the clientY axis being positive when going downward in the webpage.

You can simply invert the cursor.y while updating it by adding a - in front of the whole formula (don't forget the parentheses):

window.addEventListener('mousemove', (event) =>
{
    cursor.x = event.clientX / sizes.width - 0.5
    cursor.y = - (event.clientY / sizes.height - 0.5)
})

 Built-in controls

DeviceOrientationControls

DeviceOrientationControls will automatically retrieve the device orientation if your device, OS, and browser allow it and rotate the camera accordingly. You can use it to create immersive universes or VR experiences if you have the right equipment.

FlyControls

FlyControls enable moving the camera like if you were on a spaceship. You can rotate on all 3 axes, go forward and go backward.

FirstPersonControls

FirstPersonControls is just like FlyControls, but with a fixed up axis. You can see that like a flying bird view where the bird cannot do a barrel roll. While the FirstPersonControls contains "FirstPerson," it doesn't work like in FPS games.

PointerLockControls

PointerLockControls uses the pointer lock JavaScript API. This API hides the cursor, keeps it centered, and keeps sending the movements in the mousemove event callback. With this API, you can create FPS games right inside the browser. While this class sounds very promising if you want to create that kind of interaction, it'll only handle the camera rotation when the pointer is locked. You'll have to handle the camera position and game physics by yourself.

OrbitControls

OrbitControls is very similar to the controls we made in the previous lesson. You can rotate around a point with the left mouse, translate laterally using the right mouse, and zoom in or out using the wheel.

TrackballControls

TrackballControls is just like OrbitControls but there are no limits in terms of vertical angle. You can keep rotating and do spins with the camera even if the scene gets upside down.

TransformControls

TransformControls has nothing to do with the camera. You can use it to add a gizmo to an object to move that object.

DragControls

Just like the TransformControls, DragControls has nothing to do with the camera. You can use it to move objects on a plane facing the camera by drag and dropping them.

We will only use the OrbitControls but feel free to test the other classes.

OrbitControls

The OrbitControls class is part of those classes that are not available by default in the THREE variable. That decision helps to reduce the weight of the library. And this is where our Vite template comes in.

The OrbitControls class may not be available in the THREE variable; it is still located in the dependencies folder. To import it, you must provide the path from inside the /node_modules/ folder, which is /three/examples/jsm/controls/OrbitControls.js:

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

For it to work, you must provide the camera and the element in the page that will handle the mouse events as parameters:

// Controls
const controls = new OrbitControls(camera, canvas)

 If we want the OrbitControls to look above the cube by default, we just have to increase the y property.But this won’t work just like that because we need to tell the OrbitControl to update itself. And we can do that by calling the update method right after:

controls.target.y = 2
controls.update()

Damping

If you read the documentation of OrbitControls there are mentions of damping. The damping will smooth the animation by adding some kind of acceleration and friction formulas.

To enable damping, switch the enableDamping property of controls to true.

In order to work properly, the controls also needs to be updated on each frame by calling controls.update(). You can do that on the tick function:

// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true

// ...

const tick = () =>
{
    // ...

    // Update controls
    controls.update()

    // ...
}

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

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

相关文章

【玩转OCR】 | 腾讯云智能结构化OCR在多场景的实际应用与体验

文章目录 引言产品简介产品功能产品优势 API调用与场景实践图像增强API调用实例发票API调用实例其他场景 结语相关链接 引言 在数字化信息处理的时代&#xff0c;如何高效、精准地提取和结构化各类文档数据成为了企业和政府部门的重要需求。尤其是在面对海量票据、证件、表单和…

c# VS2022安装教程

换了部电脑&#xff0c;重新安装vs2022&#xff0c;做个记录给自己以后方便看 官网下载地址&#xff1a;下载 Visual Studio Tools - 免费安装 Windows、Mac、Linux 官网下载vs2022社区版&#xff0c;安装包 双击后点击继续&#xff0c;需要全程联网 随后出现这个界面并勾选…

Bluetooth Spec【0】蓝牙核心架构

蓝牙核心系统由一个主机、一个主控制器和零个或多个辅助控制器组成蓝牙BR/ EDR核心系统的最小实现包括了由蓝牙规范定义的四个最低层和相关协议&#xff0c;以及一个公共服务层协议&#xff1b;服务发现协议&#xff08;SDP&#xff09;和总体配置文件要求在通用访问配置文件&a…

PCA降维MATLAB代码解释及应用场景

代码整体功能概述 这段代码主要实现了以下几个功能&#xff1a;首先读取两个 CSV 文件中的数据&#xff0c;对数据进行归一化处理后合并&#xff0c;接着绘制原始数据的散点图进行可视化展示&#xff0c;然后应用主成分分析&#xff08;PCA&#xff09;算法对合并后的数据进行…

深入理解Redis

1.数据结构类型 数据结构-SDS-简单动态字符串 Redis构建了一种新字符串结构,称为简单动态字符串(Simple Dynamic String),简称SDS。 Redis未直接使用C语言的字符串,如:char* s = "hello",本质是字符数组: {h, e, l, l, o, \0}。因为C语言字符串存在很多问题…

数字后端培训项目Floorplan常见问题系列专题续集1

今天继续给大家分享下数字IC后端设计实现floorplan阶段常见问题系列专题。这些问题都是来自于咱们社区IC后端训练营学员提问的问题库。目前这部分问题库已经积累了4年了&#xff0c;后面会陆续分享这方面的问题。 希望对大家的数字后端学习和工作有所帮助。 数字后端项目Floor…

【TaskBasics】- KRTS C++示例精讲(3)

TaskBasics示例讲解 目录 TaskBasics示例讲解结构说明 项目打开请查看【BaseFunction精讲】。 结构说明 TaskBasics&#xff1a;应用层程序&#xff0c;主要用于人机交互、数据显示、内核层数据交互等&#xff1b; TaskBasics.h &#xff1a; 数据定义TaskBasics.cpp&#xff…

jenkins集成工具(一)部署php项目

目录 什么是CI 、CD Jenkins集成工具 一、Jenkins介绍 二、jenkins的安装和部署 环境部署 安装jenkins 安装gitlab 配置镜像源进行安装 修改密码 安装git工具 上传测试代码 Jenkins部署php项目wordpress 发布php代码 安装插件 测试代码发布 实现发布成功发送邮件…

在交叉编译中,常见的ELF(elf)到底是什么意思?

ELF 是 Executable and Linkable Format 的缩写&#xff0c;中文翻译为“可执行与可链接格式”。它是一种通用的文件格式&#xff0c;主要用于存储可执行文件、目标文件&#xff08;编译后的中间文件&#xff09;、动态库&#xff08;.so 文件&#xff09;以及内存转储文件&…

图神经网络_图嵌入_Struc2Vec

0 背景 之前的node embedding方式&#xff0c;都是基于近邻关系&#xff0c;但是有些节点没有近邻&#xff0c;也有结构相似性。如图中的u、v节点。 struc2vec算法适用于捕获结构相似性。 1 相似度&#xff08;距离&#xff09;计算 1.1 公式 f k ( u , v ) f k − 1 ( u …

Linux内核中Typec CC检测原理及主从模式切换原理

一&#xff0c;Typec CC引脚定义 可以看到&#xff0c;数据传输主要有TX/RX两组差分信号&#xff0c;CC1和CC2是两个关键引脚&#xff0c;作用很多&#xff1a; • 探测连接&#xff0c;区分正反面&#xff0c;区分DFP和UFP&#xff0c;也就是主从 • 配置Vbus&#xff0c;有US…

uniapp通过v-if进行判断时,会出现闪屏?【已解决】

1.问题&#xff1a;按钮切换时&#xff0c;通过v-if来判断&#xff0c;会出现闪烁情况&#xff0c;影响用户体验 2.v-if 闪烁问题可能的原因 ‌条件切换频繁‌&#xff1a;如果 v-if 指令的条件在短时间内频繁切换&#xff0c;会导致元素不断被销毁和重新创建&#xff0c;从而…

webrtc获取IceCandidate流程

在WebRTC(Web Real-Time Communication)中,ICECandidate是一个关键概念,它用于描述在建立点对点(P2P)连接时可以考虑的潜在通信端点。以下是关于WebRTC中ICECandidate的详细解释: 一、ICECandidate的定义 ICECandidate对象通常包含以下关键属性: foundation:用于唯一…

计算机网络习题(第1章 概论 第2章 数据通信基础)

第1章 概论 1、计算机网络 2、互联网 3、计算机网络体系结构 分层模型 OSI/RM 7层模型 TCP/IP 5层模型 协议、PDU、SDU、SAP等术语 数据封装&#xff08;计算&#xff09; 第2章 数据通信基础 1、数据通信系统组成 2、主要性能指标 数据传输速率 码元速率 时延 3…

Springboot项目下面使用Vue3 + ElementPlus搭建侧边栏首页

Springboot项目下面、在html 页面 Vue3 ElementPlus 搭建侧边栏首页 1、效果图 2、static 文件下面的项目结构 3、代码实现 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>首页</title><…

【C++11】可变参数模版

目录 一、可变参数模版 1.1 基本语法及原理 1.2 包拓展 1.3 empalce系列接口 一、可变参数模版 之前我们在C语言中就学过可变参数&#xff0c;但是模版类型是固定的&#xff0c;怎么变呢&#xff1f;这里c11就给出了可变参数模版 1.1 基本语法及原理 C11支持可变参数模…

FFmpeg在python里推流被处理过的视频流

链式算法处理视频流 视频源是本地摄像头 # codinggbk # 本地摄像头直接推流到 RTMP 服务器 import cv2 import mediapipe as mp import subprocess as sp# 初始化 Mediapipe mp_drawing mp.solutions.drawing_utils mp_drawing_styles mp.solutions.drawing_styles mp_holis…

我的秋招总结

我的秋招总结 个人背景 双非本&#xff0c;985硕&#xff0c;科班 准备情况 以求职为目的学习Java的时间大概一年。 八股&#xff0c;一开始主要是看B站黑马的八股文课程&#xff0c;背JavaGuide和小林coding还有面试鸭。 算法&#xff0c;250&#xff0c;刷了3遍左右 项目&…

C++:单例模式

创建自己的对象&#xff0c;同时确保对象的唯一性。 单例类只能有一个实例☞静态成员static☞静态成员 必须类外初始化 单例类必须自己创建自己的唯一实例 单例类必须给所有其他对象提供这一实例 静态成员类内部可以访问 构造函数私有化☞构造函数私有外部不能创建&#x…

RBTree(红黑树)

目录 红黑树的概念 红黑树的性质 红黑树节点的定义 红黑树的插入 1. 按照二叉搜索的树规则插入新节点 2. 检测新节点插入后&#xff0c;红黑树的性质是否造到破坏 红黑树的检测 红黑树的删除 红黑树和AVL树的比较 红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&…