最近在自学three.js但是由于社区及中文文档的不健全,互联网上的许多文章教程里很多api都弃用了,导致踩了许多坑,故打算记录一下自己的踩坑过程,以加深印象。 废话不多说,开始整活!
本项目开发使用 "vue@3.3.11", "three@0.159.0",
threeJs的三大马车
场景Scene 、相机Camera 、渲染器Renderer 以后你看到的全部有关于threeJs的东西都是由这三个基本元素构成的,建议背下这三个单词因为以后你会经常见到它们。
Scene 场景
全部3d对象的载体,一个三维空间对象及其内部的全部元素。作为整个3D世界的容器,后续你所有需要进行渲染的对象、模型、灯光、粒子系统、雾效等元素都要添加到场景内才会生效。
js
//创建场景
var scene = new THREE.Scene();
Camera 相机
相机决定了我们在哪看,怎么看,正所谓横看成岭侧成峰,所以相机很重要。 THREE.PerspectiveCamera
对象,这是Three.js中用于模拟人眼视角的相机类型,适用于大多数3D场景。
参数详解:
fov
:代表相机的视场角(Field of View, FOV) ,以度数表示。这是一个角度值,决定了相机镜头能看到的水平视野范围。数值越大,视野越广,看到的场景范围越宽;数值越小,视野越窄,看到的场景更聚焦、纵深感更强。在这个例子中,FOV设为45度,是一个常见的中间值,兼顾了视野宽度与深度感。aspect
:计算得到的是纵横比(Aspect Ratio) ,即画布宽度与高度的比例。这个比例决定了相机投影出的图像在水平和垂直方向上的拉伸或压缩程度,确保渲染出的图像能够适配浏览器窗口的当前尺寸。保持正确的纵横比有助于避免画面变形。这里使用浏览器窗口的实时宽度除以高度,确保相机适应窗口尺寸的变化(如用户调整窗口大小)。near
:代表相机的近裁剪平面距离(Near Clipping Plane) 。这是从相机位置开始,距离相机最近的一个平面。在此平面之内的物体不会被渲染,任何比这个距离更近的物体都将被裁剪掉。设置合适的近裁剪平面距离有助于避免深度冲突和精度损失。这里设置为0.1,表示距离相机0.1个单位(具体单位由开发者定义,如米、厘米或自定义单位)以内的物体将不被渲染。far
:代表相机的远裁剪平面距离(Far Clipping Plane) 。这是从相机位置开始,距离相机最远的一个平面。在此平面之外的物体同样不会被渲染,任何比这个距离更远的物体都将被裁剪掉。设置合适的远裁剪平面距离可以提高渲染效率,因为远处的物体细节通常较少,且过多的深度信息会占用额外的内存。这里设置为1000,表示距离相机1000个单位以外的物体将不被渲染。
js
//设置相机
var camera = new THREE.PerspectiveCamera(
45, //fov
window.innerWidth / window.innerHeight, //aspect
0.1, //near
1000 //far
);
camera.position.set(-20, 30, 20); //设置相机位置在点(-20, 30, 20),这里用到的单位是米
camera.lookAt(scene.position); // 使用lookAt()函数将相机指向场景的中心,也就是坐标系原点(0,0,0)
Renderer 渲染器
把相机所拍摄到的东西渲染输出到页面的canvas中
js
//构造渲染器
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0x272822));// 设置背景色
renderer.setSize(window.innerWidth, window.innerHeight);//设置渲染器的尺寸
function render() {
renderer.render(scene, camera); //执行渲染操作
}
render();
现在我们就完成了一个最基本的threejs案例,但是我们任然什么什么都看不到,因为我们刚才所创建的世界是一个空世界,里面啥都没有所以什么都看不到,我们得再加一个观测对象 在加观测对象之前我们先学习一下2个three.js的辅助工具
辅助工具
AxesHelper 三维坐标系
把虚拟世界的三维坐标系具象显示出来,可辅助开发。用于简单模拟3个坐标轴的对象。红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴,3条直线所对应的方向为正方向,交点为原点(0,0,0)
js
// 向场景中添加坐标轴
var axes = new THREE.AxesHelper(100);//100是自定义的坐标系显示的长度
scene.add(axes);
OrbitControls 相机控件
一个改变相机参数的控件,可由鼠标来控制相机,实现模型的360度旋转预览效果,和改变相机能看到的视野范围,滚轮上滑相机向前移动,下滑相机向后移动,鼠标点按拖动可控制相机以原点为中心往对应的方向旋转
js
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
var controls = new OrbitControls(camera, renderer.domElement); //创建控件对象
controls.addEventListener("change", render); //监听鼠标、键盘事件,render是上面的render()
好现在我们开始加观测对象,我们添加一个基本的长方体对象,他是我们常用的几何体对象之一,后续我们再详细展开。
js
// 创建一个方块(cube)对象
const geometry = new THREE.BoxGeometry(10, 10, 10); //创建一个长宽高分别为10的长方体对象
//创建高光网格材质,材质就是物体的皮肤
const material = new THREE.MeshPhongMaterial({
color: 0x1e80ff, //颜色
transparent: true, //开启材质透明
opacity: 0.6 //透明度,范围0-1 效果同css opacity
// wireframe: true //以线框展示
});
//使用geometry的形状和material的材质新建一个网格对象,表示一个虚拟的物体
const box = new THREE.Mesh(geometry, material);
scene.add(box); //将物体添加到场景中,物体默认的位置是坐标系原点
此时我们看到的是一个黑色的正方体,此时不是我们配置的物体颜色失效,而是我们得三维场景中没有光源,所以正方体是黑色的,就像天黑了,不开灯什么都是黑色的,现在我们给场景加上光源
js
const light = new THREE.DirectionalLight(0xffffff, 3); //新建平行光对象,颜色为白色,强度为3
light.position.set(-300, -200, -100); //设置光源位置
scene.add(light);
//由于光只能照亮物体的一侧,为了观察方便我们在对应点在增加一个光源
const light2 = new THREE.DirectionalLight(0xffffff, 3);
light2.position.set(300, 200, 100);
scene.add(light2);
至此一个完整的环境及模型搭建案例到此结束,只要你能把第一个3D案例搞明白,后面学习就会非常顺利了
js
<script setup>
import { onMounted, ref } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
var scene = new THREE.Scene();
onMounted(() => {
init();
// addMap();
});
async function init() {
// 设置相机
var camera = new THREE.PerspectiveCamera(
45, //for
window.innerWidth / window.innerHeight, //aspect
0.1, //near
1000 //far
);
// 确定相机位置
camera.position.set(-20, 30, 20); //设置相机位置在点(-20, 30, 20),这里用到的单位是米
camera.lookAt(scene.position); // 使用lookAt()函数将相机指向场景的中心,也就是坐标系原点(0,0,0)
/**
* 光源设置
*/
const light = new THREE.DirectionalLight(0xffffff, 3); //新建平行光对象,颜色为白色,强度为3
light.position.set(-300, -200, -100); //设置光源位置
scene.add(light);
//由于光只能照亮物体的一侧,为了观察方便我们在对应点在增加一个光源
const light2 = new THREE.DirectionalLight(0xffffff, 3);
light2.position.set(300, 200, 100);
scene.add(light2);
// 构造渲染器,这里使用WebGL渲染器
var renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
// 设置背景色
renderer.setClearColor(new THREE.Color(0x272822));
renderer.setSize(window.innerWidth, window.innerHeight);
// 向场景中添加坐标轴
var axes = new THREE.AxesHelper(100);
scene.add(axes);
const geometry = new THREE.BoxGeometry(10, 10, 10); //创建一个长方体对象
//创建高光网格材质,材质就是物体的皮肤
const material = new THREE.MeshPhongMaterial({
color: 0x1e80ff, //颜色
transparent: true, //开启材质透明
opacity: 0.6 //透明度,范围0-1 效果同css opacity
// wireframe: true //以线框展示
});
const box = new THREE.Mesh(geometry, material); //使用geometry的形状和material的材质新建一个网格对象,表示一个虚拟的物体
scene.add(box); //将物体添加到场景中,物体默认的位置是坐标系原点
function render() {
renderer.render(scene, camera); //执行渲染操作
}
render();
var controls = new OrbitControls(camera, renderer.domElement); //创建控件对象
controls.addEventListener("change", render); //监听鼠标、键盘事件
document.getElementById("threejs-example").appendChild(renderer.domElement);
}
</script>
<template>
<div id="threejs-example"></div>
</template>
<style>
</style>
最后文章若有错误之处,还请评论区斧正,感谢!