【Threejs】代码+图文带你快速上手

前言

 📫 大家好,我是南木元元,热衷分享有趣实用的文章,希望大家多多支持,一起进步!

 🍅 个人主页:南木元元

上篇文章中,我们已经介绍了three.js,并使用vite搭建了three.js的开发环境,实现了第一个3D场景。本文就来继续分享一下three.js的相关知识,通过代码+图文带你快速上手three.js。


目录

Threejs三维坐标系

轨道控制器OrbitControls

让物体动起来

添加光源

加载纹理

集成Gui工具

dat.gui.js库

引入dat.gui.js

基本使用

结语


Threejs三维坐标系

three.js使用的是右手坐标系,这源于OpenGL默认情况下,也是右手坐标系。x轴正方向向右,y轴正方向向上,z轴由屏幕从里向外。

为了便于观察,threejs允许我们创建一个辅助坐标系:

// 创建辅助观察坐标系:红色为x轴,绿色为y轴,蓝色为z轴
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);

效果如下:

  • 渲染优化

上述会看到明显的锯齿,我们可以通过给渲染器设置一些配置,改善渲染效果。

// 创建渲染器,设置渲染器锯齿属性antialias为true表示开启抗锯齿
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置设备像素比为当前屏幕对应的设备像素比,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

完整代码:

import * as THREE from "three";

// 创建场景
const scene = new THREE.Scene();
// 创建透视相机
const fov = 45;
const aspect = window.innerWidth / window.innerHeight;
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// 定位相机
camera.position.set(200, 300, 200);
camera.lookAt(0, 0, 0);

// 创建立方体(几何+材质)
const geometry = new THREE.BoxGeometry(10, 10, 10);
const material = new THREE.MeshNormalMaterial();
const mesh = new THREE.Mesh(geometry, material);
// 添加到场景
scene.add(mesh);

// 创建辅助观察坐标系:红色为x轴,绿色为y轴,蓝色为z轴
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);

// 创建渲染器,设置渲染器锯齿属性antialias为true表示开启抗锯齿
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置设备像素比为当前屏幕对应的设备像素比,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 渲染
renderer.render(scene, camera);

上面x轴和z轴的方向和预期不一致是因为我们这里把相机定位到了(200, 300, 200)导致的。为了方便调试与观察,我们可以使用轨道控制器(OrbitControls)控制相机的位置。

轨道控制器OrbitControls

轨道控制器可以改变相机在空间坐标系中的位置,进而方便从不同的角度观察物体。

  • 旋转:拖动鼠标左键
  • 缩放:滚动鼠标中键
  • 平移:拖动鼠标右键

效果:

1.导入组件

OrbitControls是一个附加组件,在使用之前需要先导入。

// 导入轨道控制器
import { OrbitControls } from "three/addons/controls/OrbitControls.js";

2.创建控制器

// 创建相机轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);

3.使用OrbitControls

OrbitControls本质上就是改变相机的参数如坐标位置,改变坐标后,需要重新渲染。

// 监听鼠标、键盘事件, 如果OrbitControls改变了相机参数,则重新渲染
controls.addEventListener("change", function () {
  renderer.render(scene, camera); 
}); 

完整代码:

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

// 创建场景
const scene = new THREE.Scene();
// 创建透视相机
const fov = 45;
const aspect = window.innerWidth / window.innerHeight;
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// 定位相机
camera.position.set(200, 300, 200);
camera.lookAt(0, 0, 0);

// 创建立方体(几何+材质)
const geometry = new THREE.BoxGeometry(10, 10, 10);
const material = new THREE.MeshNormalMaterial();
const mesh = new THREE.Mesh(geometry, material);
// 添加到场景
scene.add(mesh);

// 创建辅助观察坐标系:红色为x轴,绿色为y轴,蓝色为z轴
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);

// 创建渲染器,设置渲染器锯齿属性antialias为true表示开启抗锯齿
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置设备像素比为当前屏幕对应的设备像素比,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 渲染
renderer.render(scene, camera);

// 创建相机轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 监听鼠标、键盘事件, 如果OrbitControls改变了相机参数,则重新渲染
controls.addEventListener("change", function () {
  renderer.render(scene, camera); 
}); 

让物体动起来

接下来,我们添加一个旋转的动画,让物体动起来。

//循环渲染
function animation() {
  // 改变角度, 每次绕y轴旋转0.01弧度
  mesh.rotateY(0.01); 
  // 重新渲染
  renderer.render(scene, camera);

  // 下一帧渲染回调
  requestAnimationFrame(animation);
}
animation();

上述代码实现了一个周期性的渲染,不断旋转角度和重绘。核心是requestAnimationFrame这个方法, 它告诉浏览器你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。实现效果:

完整代码:

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

// 创建场景
const scene = new THREE.Scene();
// 创建透视相机
const fov = 45;
const aspect = window.innerWidth / window.innerHeight;
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// 定位相机
camera.position.set(200, 300, 200);
camera.lookAt(0, 0, 0);

// 创建立方体(几何+材质)
const geometry = new THREE.BoxGeometry(10, 10, 10);
const material = new THREE.MeshNormalMaterial();
const mesh = new THREE.Mesh(geometry, material);
// 添加到场景
scene.add(mesh);

// 创建辅助观察坐标系:红色为x轴,绿色为y轴,蓝色为z轴
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);

// 创建渲染器,设置渲染器锯齿属性antialias为true表示开启抗锯齿
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置设备像素比为当前屏幕对应的设备像素比,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 创建相机轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 循环渲染
function animation() {
  // 改变角度, 每次绕y轴旋转0.01弧度
  mesh.rotateY(0.01); 
  // 更新轨道控制器,以响应用户交互
  controls.update();
  // 重新渲染
  renderer.render(scene, camera);

  // 下一帧渲染回调
  requestAnimationFrame(animation);
}
animation();

添加光源

three.js中的光照分为两种类型:

  • 直接光照:从光源出来并击中物体的光线。
  • 环境光:间接照明方式,即从场景中所有物体反弹的光线。

其中直接光照有三种重要的光源:点光源PointLight、平行光DirectionalLight、聚光灯SpotLight。

// 点光源
var point = new THREE.PointLight(0xffffff);
point.position.set(400, 200, 300); 
scene.add(point); 
// 环境光
var ambient = new THREE.AmbientLight(0xffffff);
scene.add(ambient);

上面代码中,添加了一个直接照射的点光源PointLight和间接的环境光AmbientLight。需要注意的是,threejs提供的网格材质,有的受光照影响,有的不受光照影响。

所以我们这里把材质改为MeshStandardMaterial。

const material = new THREE.MeshStandardMaterial();

添加光照后的效果:

加载纹理

除了光照外,对几何体材质添加纹理是常见的操作,我们接下来给立方体添加纹理。

// 引入图片
import texture from "../images/texture.jpg";
// 初始化纹理加载器
const textloader = new THREE.TextureLoader();
// 加载纹理
const material = new THREE.MeshStandardMaterial({
  map: textloader.load(texture),
});

效果:

集成Gui工具

dat.gui.js库

如果每次通过修改代码的方式修改属性值效率太低,这里我们可以集成一种Gui工具dat.gui.js,通过UI交互界面来修改方便很多。

dat.gui.js库:一个前端js库,用于快速创建控制三维场景的UI交互界面。

引入dat.gui.js

我们可以通过npmgithub方式获得dat.gui.js库,当然为了学习方便,threejs官方案例扩展库中也提供了gui.js,可以直接使用。

// 引入dat.gui.js的一个类GUI
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

基本使用

创建一个GUI对象,你可以看到浏览器右上角多了一个交互界面。

// 初始化gui
const gui = new GUI();

add()方法可以快速创建一个UI交互界面,比如一个拖动条,用来改变一个js对象属性的属性值。

gui.add(cube.position, 'x').min(-10).max(10).step(1)

这里我们可以针对三种基础变换(平移、旋转、缩放)做一个分组。

// 初始化gui
const gui = new GUI();
// 平移
const guiPosition = gui.addFolder("平移");
guiPosition.add(mesh.position, "x").min(-10).max(10).step(1);
guiPosition.add(mesh.position, "y").min(-10).max(10).step(1);
guiPosition.add(mesh.position, "z").min(-10).max(10).step(1);
// 旋转
const guiRotation = gui.addFolder("旋转");
guiRotation.add(mesh.rotation, "x").min(-Math.PI).max(Math.PI).step(0.01);
guiRotation.add(mesh.rotation, "y").min(-Math.PI).max(Math.PI).step(0.01);
guiRotation.add(mesh.rotation, "z").min(-Math.PI).max(Math.PI).step(0.01);
// 缩放
const guiScale = gui.addFolder("缩放");
guiScale.add(mesh.scale, "x").min(1).max(10).step(1);
guiScale.add(mesh.scale, "y").min(1).max(10).step(1);
guiScale.add(mesh.scale, "z").min(1).max(10).step(1);

效果:

完整代码:

import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
// 引入dat.gui.js的一个类GUI
import { GUI } from "three/addons/libs/lil-gui.module.min.js";
// 引入图片
import texture from "../images/texture.jpg";

// 创建场景
const scene = new THREE.Scene();
// 创建透视相机
const fov = 45;
const aspect = window.innerWidth / window.innerHeight;
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// 定位相机
camera.position.set(200, 300, 200);
camera.lookAt(0, 0, 0);

// 创建立方体(几何+材质)
const geometry = new THREE.BoxGeometry(10, 10, 10);
// 初始化纹理加载器
const textloader = new THREE.TextureLoader();
// 加载纹理
const material = new THREE.MeshStandardMaterial({
  map: textloader.load(texture),
});
const mesh = new THREE.Mesh(geometry, material);

// 初始化gui
const gui = new GUI();
// 平移
const guiPosition = gui.addFolder("平移");
guiPosition.add(mesh.position, "x").min(-10).max(10).step(1);
guiPosition.add(mesh.position, "y").min(-10).max(10).step(1);
guiPosition.add(mesh.position, "z").min(-10).max(10).step(1);
// 旋转
const guiRotation = gui.addFolder("旋转");
guiRotation.add(mesh.rotation, "x").min(-Math.PI).max(Math.PI).step(0.01);
guiRotation.add(mesh.rotation, "y").min(-Math.PI).max(Math.PI).step(0.01);
guiRotation.add(mesh.rotation, "z").min(-Math.PI).max(Math.PI).step(0.01);
// 缩放
const guiScale = gui.addFolder("缩放");
guiScale.add(mesh.scale, "x").min(1).max(10).step(1);
guiScale.add(mesh.scale, "y").min(1).max(10).step(1);
guiScale.add(mesh.scale, "z").min(1).max(10).step(1);

// 添加到场景
scene.add(mesh);

// 点光源
var point = new THREE.PointLight(0xffffff);
point.position.set(400, 200, 300);
scene.add(point);
//环境光
var ambient = new THREE.AmbientLight(0xffffff);
scene.add(ambient);

// 创建辅助观察坐标系:红色为x轴,绿色为y轴,蓝色为z轴
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);

// 创建渲染器,设置渲染器锯齿属性antialias为true表示开启抗锯齿
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置设备像素比为当前屏幕对应的设备像素比,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 创建相机轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
//循环渲染
function animation() {
  // 改变角度, 每次绕y轴旋转0.01弧度
  mesh.rotateY(0.01);
  // 更新轨道控制器,以响应用户交互
  controls.update();
  // 重新渲染
  renderer.render(scene, camera);

  // 下一帧渲染回调
  requestAnimationFrame(animation);
}
animation();

结语

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏✍️评论支持一下博主~     

  • 85
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 125
    评论
好的,以下是一个简单的HTML+JS的图文排版的示例: ```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>图文排版示例</title> <style> .article { width: 800px; margin: 0 auto; font-family: Arial, sans-serif; } .image { width: 400px; float: left; margin: 20px; } .text { width: 320px; float: left; margin: 20px; } h1 { font-size: 28px; color: #333; margin-top: 0; } p { font-size: 16px; color: #666; line-height: 1.5; } </style> </head> <body> <div class="article"> <div class="image"> <img src="https://picsum.photos/400/300" alt="示例图片"> </div> <div class="text"> <h1>这是一个标题</h1> <p>这是一段文字,用于说明这张图片的内容。Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed suscipit diam euismod odio commodo, vel pretium ex luctus. Vivamus sit amet euismod velit. Sed mattis non elit vitae bibendum. Nullam vel mauris ut enim maximus posuere. Vestibulum volutpat, turpis at malesuada scelerisque, est leo sollicitudin tellus, ac rutrum leo enim ut turpis. Phasellus vel felis tincidunt, bibendum enim in, mollis quam. Sed vel sapien ac ipsum ultrices tincidunt. Maecenas in lacus vel enim hendrerit commodo vel at lectus. Duis vehicula sapien in metus viverra aliquam. Nam in turpis vel mi pharetra euismod.</p> </div> <div style="clear: both;"></div> </div> <script> // 如果需要使用JS添加更多的文章内容,可以在这里编写代码 </script> </body> </html> ``` 这个示例中,我们定义了一个宽度为800px的容器,并将图片和文字分别放置在左右两侧。使用CSS中的浮动和清除浮动,可以实现这种效果。我们还使用了一些基本的CSS样式来设置标题和段落的字体大小、颜色和行高。在JS代码段中,您可以添加更多的文章内容,例如更多的图片、视频或其他元素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 125
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南木元元

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值