文章主要是加载了一个模型,并对其进行了一些美化操作
主要加载了2个模型,一个是头发,一个是脸,因为模型是电脑下载下来的,内部没有其他节点,所以对于更改模型颜色不生效
<template>
<div class="root">
<div id="box"></div>
</div>
</template>
<script>
// 方式 1: 导入整个 three.js核心库
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import {
DirectionalLight,
HemisphereLight,
PlaneGeometry,
MeshPhongMaterial,
Mesh,
Vector2,
Vector3,
Raycaster,
Color
} from "three";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
export default {
data() {
return {
scene: null,
camera: null,
renderer: null,
box: null,
gltf: null,
composer: null
};
},
async mounted() {
this.box = document.getElementById("box");
this.init();
this.animate();
},
methods: {
// 初始化
init() {
this.getScene();
this.getCamera();
this.getRender();
this.getModel();
this.addLight();
this.addShadow();
this.controlsCamera();
document.body.addEventListener("click", this.changeSkinColor, false);
},
// 4.Animate -渲染场景
animate() {
requestAnimationFrame(this.animate);
this.renderer.render(this.scene, this.camera);
if (this.composer) {
this.composer.render();
}
},
// 创建场景
getScene() {
// 1.Scene-创建场景
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0xb3cefb);
this.scene.fog = new THREE.Fog(this.scene.background, 1, 100); //雾化效果
},
// 照相机
getCamera() {
//2. Camera 第一个参数是视野角度 第二个参数是长宽比 接下来的两个参数是近截面(near)和远截面(far)
this.camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
1,
1000
);
this.camera.position.set(0, 0, 15);
},
// 渲染器
getRender() {
// 3.renderer - 渲染器
this.renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
});
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth, window.innerHeight);
// 首先渲染器开启阴影
this.renderer.shadowMap.enabled = true;
this.renderer.outputEncoding = THREE.sRGBEncoding;
this.box.appendChild(this.renderer.domElement);
},
// 加载模型
getModel() {
const loader = new GLTFLoader();
loader.load(
// resource URL
"../../../static/tails.glb",
// called when the resource is loaded
gltf => {
this.gltf = gltf;
// 模型太小,会导致页面看不见模型,缩放模型可显示
gltf.scene.scale.set(5, 5, 5);
// 模型Mesh开启阴影
gltf.scene.traverse(obj => {
if (obj.isMesh) {
obj.castShadow = true;
obj.receiveShadow = true;
}
});
this.scene.add(gltf.scene);
},
// called while loading is progressing
function(xhr) {
// console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
},
// called when loading has errors
function(error) {
console.log("An error happened", error);
}
);
loader.load(
// resource URL
"../../../static/HeartEyesFace.glb",
// called when the resource is loaded
gltf => {
this.gltf = gltf;
// 模型太小,会导致页面看不见模型,缩放模型可显示
gltf.scene.scale.set(150, 150, 150);
gltf.scene.position.set(0.1,0.9,0);
// 模型Mesh开启阴影
gltf.scene.traverse(obj => {
if (obj.isMesh) {
obj.castShadow = true;
obj.receiveShadow = true;
}
});
this.scene.add(gltf.scene);
},
// called while loading is progressing
function(xhr) {
// console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
},
// called when loading has errors
function(error) {
console.log("An error happened", error);
}
);
},
// 添加灯光
addLight() {
// 平行光是沿着特定方向发射的光。.光颜色 光照的强度。缺省值为1
const directionalLight = new DirectionalLight(0xffffff, 1);
directionalLight.position.set(-4, 5, 20);
this.scene.add(directionalLight);
// 光源直接放置于场景之上,光照颜色从天空光线颜色渐变到地面光线颜色。
let hemisphereLight = new HemisphereLight(0xffffff, 0xffffff, 0.4);
hemisphereLight.position.set(0, 5, 0);
// 光源开启阴影
directionalLight.castShadow = true;
directionalLight.shadow.mapSize = new Vector2(1024, 1024);
// 模型身上很多条纹状黑线,因为渲染阴影中产生了伪影
directionalLight.shadow.bias = -0.001; // value 自行调节
this.scene.add(directionalLight);
this.scene.add(hemisphereLight);
},
// 添加阴影 阴影显示需要光源和投射地(阴影显示的地方),有了光源差投射地,所以创建一个地板,让阴影投射至地板上
addShadow() {
// 生成一个平面几何
let floorGeometry = new PlaneGeometry(5000, 5000, 1);
// 给平面添加材质
let floorMaterial = new MeshPhongMaterial({
color: 0x77f28f,
shininess: 0
// wireframe: true
});
let floor = new Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -0.5 * Math.PI;
floor.position.y = -2.1;
// 地板接受阴影开启
floor.receiveShadow = true;
this.scene.add(floor);
// 添加影子 对光源、渲染器、模型、地板进行设置
},
// 换肤功能 想要实现各个部件换肤功能,我们需要选中部件,修改选中部件材质来达到我们换肤的功能。
changeSkinColor(ev) {
// Raycaster 光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)。
// 1.选中不见,在场景中加入射线检测就可以了
let raycaster = new Raycaster(); //鼠标移入过了什么物体
let pointer = new Vector2();
// 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
pointer.x = (ev.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(ev.clientY / window.innerHeight) * 2 + 1;
// 通过摄像机和鼠标位置更新射线
raycaster.setFromCamera(pointer, this.camera);
// 计算物体和射线的焦点 gltf.scene.children只需要获取模型的位置,如果scene则是获取全部相交点,模型+阴影
let intersects = raycaster.intersectObjects(
this.gltf.scene.children,
true
);
let selectedObjects = null;
// 红 绿 蓝 骚粉
let colocs = [0xffa500, 0x008000, 0x87ceeb, 0xff1493];
console.log("intersects: ", intersects);
if (intersects.length > 0) {
selectedObjects = intersects[0].object;
selectedObjects.material.color.set(0x87ceeb);
}
// 2.设置颜色或者纹理 修改材质颜色
// let newMaterial = selectedObjects.material.clone();
// newMaterial.needsUpdate = true;
// newMaterial.color = new Color("#FFC0CB"); //重新修改颜色
// selectedObjects.material = newMaterial;
// 3.添加选中效果 给选中的加线&高亮
this.composer = new EffectComposer(this.renderer);
let renderPass = new RenderPass(this.scene, this.camera);
this.composer.addPass(renderPass);
let outlinePass = new OutlinePass(
new Vector2(window.innerWidth, window.innerHeight),
this.scene,
this.camera
);
outlinePass.renderToScreen = true;
outlinePass.edgeStrength = 5; //粗
outlinePass.edgeGlow = 2; //发光
outlinePass.visibleEdgeColor.set("#130AF2"); // 设置显示的颜色
this.composer.addPass(outlinePass);
},
// 旋转控制
controlsCamera() {
let controls = new OrbitControls(this.camera, this.renderer.domElement);
controls.enablePan = false;
controls.maxPolarAngle = Math.PI / 2;
controls.minPolarAngle = Math.PI / 3;
controls.enableZoom = false;
controls.update();
}
}
};
</script>
<style lang="less" scoped>
.root {
background: white;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
</style>
最终效果图