携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
相信想要做3D可视化的同学都了解过three.js并且学习过,而学习的第一途径,就是官方文档。不得不说,官方文档真的很简单粗暴,属于看完就跟没看一样(当然大佬除外
但是又不能就不学了吧,只能对官方例子下手了,以下是我用vue写官方例子的一些代码和理解,请各位大佬路过指教一下(实在不知道怎么学啊!
vue引入three.js
创建vue项目的步骤我就省略了(这方面教程遍地都是
- 安装three.js
npm install three
- 容器id先写好
<template>
<div class="item">
<div id="THREE7"></div>
</div>
</template>
- 引入three.js、GUI、CinematicCamera
GUI是three.js社区的一个UI库,用来对模型进行交互控制,使用非常的方便和简单
CinematicCamera是一个相机插件,可以用来设置焦点
import * as THREE from "three";
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
import { CinematicCamera } from "three/examples/jsm/cameras/CinematicCamera.js";
- mounted方法写一个,initThreejs()定义的变量下面会一一解释
Vector2():创建一个二维向量
mounted() {
this.initThreejs();
},
methods: {
initThreejs() {
let camera, scene, raycaster, renderer;
const mouse = new THREE.Vector2();
let INTERSECTED;
init();
animate();
}
}
init():
- 相机设置
CinematicCamera(角度,摄像机视锥体的长宽比)
setLens 设置相机离物体的远近程度,数字越小,离得越远;初始化相机焦距相关
position.set 设置相机位置
camera = new CinematicCamera(
60,
(window.innerWidth - 210) / window.innerHeight,
1,
1000
);
camera.setLens(5);
camera.position.set(2, 1, 500);
- 场景设置
scene.background:设置场景背景颜色
scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f0f0);
- 光源设置
AmbientLight:环境光(白色,光照强度为0.3)
DirectionalLight:平行光(白色,光照强度0.35),模拟太阳光
scene.add(new THREE.AmbientLight(0xffffff, 0.3));
const light = new THREE.DirectionalLight(0xffffff, 0.35);
light.position.set(1, 1, 1).normalize(); // 理解为从(0,0,0)->(1,1,1)的方向的光
scene.add(light);
- 创建网格模型
BoxGeometry(长,宽,高):创建立方体
Mesh(模型,材质):创建网格模型
这里是创建了1500个颜色随机位置随机的立方体的网格模型
const geometry = new THREE.BoxGeometry(20, 20, 20);
for (let i = 0; i < 1500; i++) {
const object = new THREE.Mesh(
geometry,
new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })
);
object.position.x = Math.random() * 800 - 400;
object.position.y = Math.random() * 800 - 400;
object.position.z = Math.random() * 800 - 400;
scene.add(object);
}
- 光线投射(用于进行鼠标拾取)
raycaster = new THREE.Raycaster();
- 创建渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth - 201, window.innerHeight);
document.getElementById("THREE7").appendChild(renderer.domElement);
- 鼠标移动事件监听
在原本的例子上改了:clientX,clientY => offsetX,offsetY
document.getElementById("THREE7").addEventListener("mousemove", onDocumentMouseMove);
function onDocumentMouseMove(event) {
event.preventDefault();
mouse.x = (event.offsetX / (window.innerWidth - 201)) * 2 - 1;
mouse.y = -(event.offsetY / window.innerHeight) * 2 + 1;
}
- GUI的使用
camera.setLens:初始化相机焦距相关
const effectController = {
focalLength: 15,
fstop: 2.8,
showFocus: false,
focalDepth: 3,
};
const matChanger = function () {
for (const e in effectController) {
if (e in camera.postprocessing.bokeh_uniforms) {
camera.postprocessing.bokeh_uniforms[e].value =
effectController[e];
}
}
camera.postprocessing.bokeh_uniforms["znear"].value = camera.near;
camera.postprocessing.bokeh_uniforms["zfar"].value = camera.far;
camera.setLens(
effectController.focalLength,
camera.frameHeight,
effectController.fstop,
camera.coc
);
effectController["focalDepth"] =
camera.postprocessing.bokeh_uniforms["focalDepth"].value;
};
const gui = new GUI();
gui.add(effectController, "focalLength", 1, 135, 0.01).onChange(matChanger);
gui.add(effectController, "fstop", 1.8, 22, 0.01).onChange(matChanger);
gui.add(effectController, "focalDepth", 0.1, 100, 0.001).onChange(matChanger);
gui.add(effectController, "showFocus", true).onChange(matChanger);
matChanger();
animate():
function animate() {
requestAnimationFrame(animate, renderer.domElement);
render();
}
render():
setFromCamera(在标准化设备坐标中鼠标的二维坐标,射线来源的相机):通过摄像机和鼠标位置更新射线
intersectObjects:检查所有模型(上面增加的1500个)和射线的相交部分。该方法返回一个包含有交叉部分的数组,返回结果时,相交部分将按距离进行排序,最近的位于第一个。
-
distance:射线投射原点和相交部分之间的距离
-
object:相交的物体
camera.focusAt(焦距):修改相机的焦距
INTERSECTED.material.emissive.getHex():object本身的颜色
INTERSECTED.material.emissive.setHex(0xff0000):设置对象为红色
camera.renderCinematic(scene, renderer):使用这个方法渲染场景, 代替renderer.render(scene, camera)
整体的逻辑是通过鼠标和摄像机的位置得到一根射线,距离最近的对象显示为红色,其他对象显示其本身的颜色。
function render() {
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, false);
if (intersects.length > 0) {
const targetDistance = intersects[0].distance;
camera.focusAt(targetDistance);
if (INTERSECTED != intersects[0].object) {
if (INTERSECTED)
INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
INTERSECTED = intersects[0].object;
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex(0xff0000);
}
} else {
if (INTERSECTED)
INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
INTERSECTED = null;
}
if (camera.postprocessing.enabled) {
camera.renderCinematic(scene, renderer);
} else {
scene.overrideMaterial = null;
renderer.clear();
renderer.render(scene, camera);
}
}