在现实生活里,我们看到一个场景是立体的,整个一个场景如果通过电脑模拟出来首先要知道这个场景都有什么组成。
比如在一个场景里,我看到了一张桌子。所以桌子是在场景里的。然后在分析桌子,桌子是有形状和木头材质组成。所以形状和材质是桌子的属性。形状体又有大小的属性,材质有颜色等属性。在然后桌子是在场景的某个位置的,也就是桌子和场景是通过位置连接的。还有一个关键点,就是我看到这个物体这个视角,也是因为有观察点才能看到当前这个场景,才能有距离感等,所以一个场景里的一个重要点,就是视角。最后把这些模拟到虚拟场景里
一、创建一个场景(首先创建最外层,如果是2d的这一步相当于准备好画布)
const scene = new THREE.Scene();//创建场景
二、创建场景里的"桌子"(物体)
物体由两种属性组成,材质和形状
1)、创建形状
官方给出的形状api有
const geometry = new THREE.BoxGeometry(100,100,100);//创建了一个长宽高为100的正方体
2)、创建材质
官方给出的材质分类有
const material = new THREE.MeshBasicMaterial({color:0xff0000,//0xff0000设置材质颜色为红色})
有了形状和材质,将它们合成物体
3)、合成物体
const mesh = new THREE.Mesh(geometry, material)//网格模型对象Mesh
有了物体,就要把物体放在某个位置上
mesh.position.set(0,0,0)//把物体放在原点上
三、把物体和位置放进场景里,组成虚拟场景
scene.add(mesh)//把物体放进场景里
现在场景的所有组成已经有了,就像在一个房间里放着一个桌子,不管你看不看它就在哪里,但是如果要看到,就需要有视角才能看到
四、创建视角(创建相机)
const camera = new THREE.PerspectiveCamera(
45, //视角
window.innerWidth / window.innerHeight, //宽高比
0.1, //近平面
1000 //远平面
);
相机四个属性
fov:视角,可以理解为视野的角度,默认值是50,越小看到的物体看着就会越大,越大则看到的物体越小,想象一个聚光手电筒照在一个物体上,只能看清有光的那部分,所以在视图上就会显得物体更大,如果是一个散光的手电筒,则会看清楚全貌甚至还可以看到周围环境,同样大的视图里需要展示的东西就更多,所以物体就会变小。
设置20的时候的视角
设置100的时候的视角
aspect:宽高比,设置宽高比后视图里所有的物体的宽高比都会按照这个来,一般是用窗口的宽高比。如果是特定的div,则用div的宽高比。
正常窗口宽高比
窗口不变,放大宽度后的宽高比
near/far:近/远截面距离,只有在两个截面内的物体才可以显示出来。可以理解为两面不透明但是可以穿越的次元壁,如果物体迈过任一墙面,则迈过去的部分就不可见。默认值是0.1,2000。
五、设置人站的位置(相机位置)和观察点
camera.position.set(200, 200, 200);
camera.lookAt(0,0,0)//视线对焦点位(没发现有啥用)
六、创建渲染器
现在我们场景已经创建好,场景里的物品也已经创建好了,但是需要渲染出来才可以被看到,不然只是存在,但是看不到。可以理解为,我们有了相机,有了要拍摄的景物,要让景物变成照片变成可见的,就需要按下快门这个操作。渲染器就是这个操作。
const renderer = new THREE.WebGLRenderer({ antialias: true });
antialias:抗锯齿
1)、设置canvas画布尺寸.setSize()
renderer.setSize(window.innerWidth, window.innerHeight); //设置three.js渲染区域的尺寸(像素px)
2)、渲染.render()
渲染器的渲染方法,执行拍照
renderer.render(scene, camera);
3)、画布(底片),将拍摄到的场景添加到画布上.domElement
document.body.appendChild(render.domElement)//也可以是具体的dom节点,vue的话需要使用ref。
///
<div ref="webgl" style="margin-top: 200px;margin-left: 100px;"></div>
document.getElementById('webgl').appendChild(renderer.domElement);
完整代码:
<template>
<div
ref="webglContainer"
id="webgl"
style="width: 800px; height: 500px; position: relative"
></div>
<!-- 注意这里要有结束标签 -->
</template>
<script setup>
import { ref, onMounted } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 创建 ref 来引用 DOM 元素
const webglContainer = ref(null);
// 创建实例
const scene = new THREE.Scene();
const geometry = new THREE.BoxGeometry(100, 100, 100);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 0, 0);
// 添加坐标轴
const axesHelper = new THREE.AxesHelper(200); // 参数是轴的长度
scene.add(axesHelper); // 将坐标轴添加到场景中
scene.add(mesh);
// 相机配置
const width = 800; // 宽度
const height = 500; // 高度
const camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 2000);
camera.position.set(200, 200, 200);
camera.lookAt(0, 0, 0);
// 创建渲染器对象
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height); // 设置渲染区域的尺寸
// 在 onMounted 生命周期钩子中将渲染器的 canvas 添加到 DOM
onMounted(() => {
if (webglContainer.value) {
console.log(webglContainer.value);
webglContainer.value.appendChild(renderer.domElement);
}
});
// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 动画循环
function animate() {
requestAnimationFrame(animate);
controls.update(); // 更新控制器
renderer.render(scene, camera); // 渲染场景
}
animate();
</script>
<style>
canvas {
position: absolute;
display: block;
width: 100%; /* 修改为100% */
height: 100%; /* 修改为100% */
}
</style>