持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
写在前面
本文研究使用vue+threejs对物体进行勾边操作。
我们在选中物体时,需要给物体勾边,表示其被选中的状态。
最终演示gif如下:
完整代码说明
- 创建一个id容器,用于展示threejs
<template>
<div class="item">
<div id="THREE34"></div>
</div>
</template>
- 引入需要用到的threejs模块
- OrbitControls: 轨道控制器,用于界面随鼠标操作而移动、缩放、转换视角
- ThreeMFLoader: .3mf后缀模型导入模块,用它加载.3mf文件模型
- GUI: threejs的一个UI工具,用来控制例如:颜色的变化
- EffectComposer:后期处理模块
- RenderPass:必须,用于渲染
- OutlinePass:勾边功能模块
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { ThreeMFLoader } from "three/examples/jsm/loaders/3MFLoader.js";
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";
- 初始化threejs的方法写在mounted()中,因为需要根据id获取元素
mounted() {
this.initThreejs();
},
- initThreejs方法
首先创建threejs必须的:相机、场景、灯光和渲染器,并设置场景背景颜色、相机的位置、灯光的类型颜色及位置。
再用PlaneGeometry创建一个平面,进行90度旋转后ground.rotation.x = -Math.PI / 2;作为地面。
接着通过ThreeMFLoader加载.3mf文件的模型,并将模型添加到场景中scene.add(object);。
然后就是本文的重点,勾边效果的实现:
(1) 创建一个后期处理composer = new EffectComposer(renderer);
(2) 将RenderPass和OutlinePass都添加到后期处理链中composer.addPass(renderPass);``composer.addPass(outlinePass);
(3) 对添加的两个处理链的参数进行修改,参数的说明在代码中也有注释。
后期处理是threejs用来提供进行后期处理的一个模块,其中一个后期处理可以用来完成勾边功能:OutlinePass
打印outlinePass,有很多属性,有兴趣的可以一个个研究,下面是一些常用的
- visibleEdgeColor:勾边的颜色
- edgeStrength:勾边锐度 Number
- edgeGlow:边缘辉光 Number
- pulsePeriod:边缘闪烁 Number
- edgeThickness:边缘厚度 Number
到此时整个勾边效果已经写完了,我这里用GUI加了个改变勾边颜色的功能,具体代码都在createPanel方法中,且都有注释。
最后就是整个场景的渲染 composer.render();
initThreejs() {
/**
* 相机,场景,渲染器
*/
let camera, scene, renderer;
/**
* 模型,gui设置
*/
let model, settings;
/**
* 后期处理,勾选功能模块
*/
let composer, outlinePass;
init();
function init() {
// 场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0x8cc7de); // 场景背景颜色
scene.fog = new THREE.Fog(0xa0a0a0, 10, 500); // 边缘雾化
// 相机,这里创建了一个透视相机
camera = new THREE.PerspectiveCamera(
35,
(window.innerWidth - 201) / window.innerHeight,
1,
500
);
camera.position.set(-100, 80, 100); // 相机位置
scene.add(camera);
// 半球光
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444); // 天空的光的颜色是0xffffff,地面的光的颜色是0x444444
hemiLight.position.set(0, 100, 0); // 灯光位置
scene.add(hemiLight);
// 平行光
const dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(-0, 40, 50);
dirLight.castShadow = true; // 是否产生阴影
dirLight.shadow.camera.top = 50;
dirLight.shadow.camera.bottom = -25;
dirLight.shadow.camera.left = -25;
dirLight.shadow.camera.right = 25;
dirLight.shadow.camera.near = 0.1;
dirLight.shadow.camera.far = 200;
dirLight.shadow.mapSize.set(1024, 1024);
scene.add(dirLight);
// 渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth - 201, window.innerHeight);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.shadowMap.enabled = true; // 是否渲染阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 阴影类型
document.getElementById("THREE34").appendChild(renderer.domElement);
// 地面
const ground = new THREE.Mesh(
new THREE.PlaneGeometry(1000, 1000),
new THREE.MeshPhongMaterial({ color: 0x999999, depthWrite: false })
);
ground.rotation.x = -Math.PI / 2;
ground.position.y = 11;
ground.receiveShadow = true;
scene.add(ground);
/**
* 后期处理
* 这是threejs用来提供进行后期处理的一个模块,其中一个后期处理可以用来完成勾边功能
* RenderPass是必须的
* OutlinePass就是勾边功能的主要模块
* 这里先将OutlinePass加入后期处理链中,后面在createPanel() 中会用到
* 打印outlinePass,有很多属性,有兴趣的可以一个个研究,下面是一些常用的
* - visibleEdgeColor:勾边的颜色
* - edgeStrength:勾边锐度 Number
* - edgeGlow:边缘辉光 Number
* - pulsePeriod:边缘闪烁 Number
* - edgeThickness:边缘厚度 Number
*/
composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
outlinePass = new OutlinePass(
new THREE.Vector2(window.innerWidth - 201, window.innerHeight),
scene,
camera
);
outlinePass.visibleEdgeColor.set(new THREE.Color(0xffffff));
outlinePass.edgeStrength = 3;
outlinePass.edgeThickness = 1;
composer.addPass(outlinePass);
// 模型加载
const loader = new ThreeMFLoader();
loader.load("models/models/3mf/truck.3mf", function (object) {
object.quaternion.setFromEuler(new THREE.Euler(-Math.PI / 2, 0, 0)); // z-up conversion
object.traverse(function (child) {
child.castShadow = true; // 设置模型有阴影
});
model = object;
scene.add(object);
createPanel();
animate();
});
// 控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.addEventListener("change", render);
controls.minDistance = 50; // 能够将相机向内移动多少
controls.maxDistance = 200; // 能够将相机向外移动多少
controls.minPolarAngle = 0; // 能够旋转角度的下限
controls.maxPolarAngle = Math.PI / 2; // 能够旋转角度的上限
controls.enablePan = false; // 启用或禁用摄像机平移
controls.target.set(0, 20, 0); // 控制器焦点
controls.update();
}
function createPanel() {
const panel = new GUI({ width: 310 });
const folder1 = panel.addFolder("物体效果");
settings = {
勾边: false,
勾边颜色: "0xffffff",
};
// outlinePass的selectedObjects属性存储的模型都会通过该方法进行勾边处理
folder1.add(settings, "勾边").onChange((val) => {
if (val) {
outlinePass.selectedObjects = [model];
} else {
outlinePass.selectedObjects = [];
}
});
folder1.addColor(settings, "勾边颜色").onChange(function (value) {
outlinePass.visibleEdgeColor.set(value);
});
folder1.open();
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
// 注意,这里就是composer.render();而不是renderer.render(scene, camera);了
composer.render();
}
},
写在最后
以上就是勾边功能的实现,但是有个问题需要解决,勾边的颜色会带点透明度,显得颜色很淡,不知道在哪里可以修改。