赞
踩
3D文件请放在public文件下,或者使用网络地址
<template>
<div>
<div id="colorBar" style="display: none" ref="colorBar">
{{ activeFloor }}
</div>
<div id="body" ref="body" style="position: relative"></div>
<div id="showWindow" v-if="showWindow.name">
<p>当前选中信息:</p>
<p>类型:{{ showWindow.type }}</p>
<p>名称:{{ showWindow.name }}</p>
<p
v-if="showWindow.type == 'Floor' && !showWindow.isShow"
style="text-align: center"
>
<button @click="showFloor">查看</button>
</p>
<button v-if="showWindow.isShow" @click="showAll">返回</button>
</div>
</div>
</template>
<script>
// 方式 1: 导入整个 three.js核心库
import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
export default {
name: "HelloWorld",
props: {
names: { type: Array, default: () => ["肾", "足部", "大脑"] },
sex: { type: Number, default: () => 1 }, //0 女 1 男
},
watch: {
sex() {
alert(1);
this.loaderModel();
},
},
data() {
return {
scene: null,
camera: null,
light: null,
model1: null, //模型
clock: null, //时钟
loader: null, //加载器
controls: null, //控制器
// 渲染器
renderer: null,
// 动画数量
animateLength: 0,
// 相机轨迹动画
nowCameraAnimation: null,
cameraMixer: null,
cameraAnimations: [],
// 模型动画
model1Animations: null,
mixer: null,
nowanimation: [], //当前动画
floorAnimation: [], //楼层动画
uuid: null, //当前选中楼层
activeFloor: null,
activeType: "",
lastTime: 0,
currentFrameRate: 0,
//是否正在旋转
isXuanzhuan: false,
otherColor: [], //保存楼层历史颜色
raycaster: null, //点击位置
Vector2: null, //(二维向量)的类
// 右侧窗口信息
showWindow: {
type: "",
name: "",
isShow: false,
},
};
},
filters: {
formatName(name) {
return name.split("_")[2];
},
},
mounted() {
// 相机
this.setCamera();
// 场景
this.scene = new THREE.Scene();
this.setScene();
// 灯光
this.setLoght();
// // 渲染器
this.renderer = new THREE.WebGLRenderer({
alpha: true, //遣染器活明
antialias: true, //抗据货
precision: "highp",
});
this.renderer.shadowMap.enabled = true;
// 设置渲染器尺寸
this.renderer.setSize(
this.$refs.body.offsetWidth,
this.$refs.body.offsetHeight
);
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
this.$refs.body.appendChild(this.renderer.domElement);
// 添加点击事件
this.addJH();
// 设置控制器
this.setControls();
this.loader = new GLTFLoader();
// 加载模型
this.loaderModel(1);
// this.setLine(1, 0, 0, "#f00");
// this.setLine(0, 1, 0, "green");
// this.setLine(0, 0, 1, "#000");
this.clock = new THREE.Clock();
this.raycaster = new THREE.Raycaster();
this.Vector2 = new THREE.Vector2();
// 添加一个地板
// this.addBackground();
this.animate();
},
methods: {
loaderModel(scale = 1) {
let url = "";
if (this.sex === 0) {
url = "/Female_1.2.gltf";
} else {
url = "/Male_1.2.gltf";
}
this.loader.load(
url,
(gltf) => {
console.log(gltf);
gltf.scene.traverse((object) => {
console.log(object.name);
if (
object.name != "血管" &&
object.name != "血管2" &&
object.name != "静脉" &&
object.name != "动脉" &&
object.name
) {
console.log(object.name, this.names.includes(object.name));
if (this.names.includes(object.name)) {
var material = new THREE.MeshBasicMaterial({
color: "#f00",
transparent: true,
opacity: 0.5,
});
object.material = material;
object.isIll = true;
} else {
// 创建一个透明的材质
var customMaterial = new THREE.ShaderMaterial({
uniforms: {
// 在这里定义uniform变量,用于控制透明度、边缘和反光效果
edgeColor: { value: new THREE.Vector3(1.0, 1.0, 1.0) }, // 边缘颜色
edgeThickness: { value: 0.05 }, // 边缘厚度
reflectionStrength: { value: 0.9 }, // 反光强度
},
vertexShader: `
varying vec3 vNormal;
void main() {
vNormal = normal;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
varying vec3 vNormal;
uniform vec3 edgeColor;
uniform float edgeThickness;
uniform float reflectionStrength;
void main() {
// 计算透明度
float alpha = 0.5; // 透明度的基础值
// 计算边缘效果
float edge = fwidth(length(vNormal));
alpha *= smoothstep(0.0, edgeThickness, edge);
// 计算反光效果
vec3 reflection = reflect(normalize(-vNormal), vec3(0.455, 0.749, 0.890));
float reflectionIntensity = max(dot(reflection, normalize(-vNormal)), 0.0);
vec3 reflectionColor = reflectionStrength * reflectionIntensity * edgeColor;
// 最终颜色设置为"#74bfe3"
vec3 finalColor = vec3(0.455, 0.749, 0.890); // "#74bfe3"的RGB值
finalColor += reflectionColor; // 添加反光效果
// 设置透明度
gl_FragColor = vec4(finalColor, alpha);
}
`,
transparent: true,
});
object.material = customMaterial;
object.isIll = false;
}
}
if (object.name == "骨架") {
object.visible = false;
}
});
this.model1 = gltf.scene;
this.scene.add(this.model1);
this.model1.scale.set(scale, scale, scale);
this.mixer = new THREE.AnimationMixer(this.model1);
this.model1.position.set(0, 0, 0);
this.model1Animations = gltf.animations;
// 获取相机的动画剪辑数组
this.cameraAnimations = gltf.animations;
// 获取到楼层的动画
this.floorAnimation = gltf.animations
.filter((item) => item)
.sort((a, b) => a.name.split("_")[1] - b.name.split("_")[1])
.reverse();
},
undefined,
function (e) {
console.error(e);
}
);
},
setCamera() {
// PerspectiveCamera 相机
// fov — 摄像机视锥体垂直视野角度
// aspect — 摄像机视锥体长宽比
// near — 摄像机视锥体近端面
// far — 摄像机视锥体远端面
// Vector3 三维向量(标记为x、y和z)
// this.camera.lookAt(new THREE.Vector3(0, 1, 0));
// this.cameraMixer = new THREE.AnimationMixer(this.camera);
this.camera = new THREE.PerspectiveCamera(
75,
this.$refs.body.offsetWidth / this.$refs.body.offsetHeight,
0.1,
1000
);
this.camera.position.set(0, 0, 2);
},
setScene() {
this.scene.background = new THREE.Color("#000");
// 一个fog实例定义了影响场景中的每个物体的雾的类型。默认值为null。
// this.scene.fog = new THREE.Fog("#000", 20, 1000);
// this.scene.position.x -= 2;
this.scene.position.y -= 1;
},
setControls(type = false) {
if (type) {
this.controls.dispose();
return;
}
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
},
setLoght() {
// 从上方照射的白色平行光,强度为 0.5。
// 创建平行光源
this.light = new THREE.DirectionalLight(0xffffff, 2);
this.light.position.set(-10, 10, 20).normalize();
// this.light.castShadow = true; // default false
this.scene.add(this.light);
const light = new THREE.AmbientLight(0x404040, 0.5); // 柔和的白光
this.scene.add(light);
},
animate() {
// currentTime
// // 更新动画时间
// var delta = this.clock.getDelta();
// const deltaTime = currentTime - this.lastTime; // 计算距离上一帧的时间间隔,转换为秒
// this.lastTime = currentTime; // 更新上一帧的时间戳
// const currentFrameRate = 1 / deltaTime;
// if (this.mixer) {
// this.nowanimation.forEach((item) => {
// // 检查动画是否已经结束
// let sumTime = item.getClip().duration;
// if (!item.paused && item.time >= sumTime - currentFrameRate) {
// // 暂停动画
// item.paused = true;
// item.time = sumTime;
// this.animateLength--;
// }
// });
// this.mixer.update(delta);
// }
// if (this.cameraMixer && this.nowCameraAnimation) {
// this.cameraMixer.update(delta);
// }
this.controls.update();
// this.CSS2DRenderer.render(this.scene, this.camera);
this.renderer.render(this.scene, this.camera);
requestAnimationFrame(this.animate);
},
setLine(x, y, z, color) {
const dir = new THREE.Vector3(x, y, z);
//normalize the direction vector (convert to vector of length 1)
dir.normalize();
const origin = new THREE.Vector3(0, 0, 0);
const length = 10;
const arrowHelper = new THREE.ArrowHelper(dir, origin, length, color);
this.scene.add(arrowHelper);
},
addJH() {
this.renderer.domElement.addEventListener(
"mousedown",
this.onMouseDown,
false
);
// this.renderer.domElement.addEventListener(
// "mousemove",
// this.onMouseMove,
// false
// );
// this.renderer.domElement.addEventListener(
// "mouseup",
// this.onMouseUp,
// false
// );
},
onMouseDown(event) {
// 在这里处理鼠标点击事件的逻辑
this.Vector2.set(
(event.clientX / this.$refs.body.clientWidth) * 2 - 1,
-(event.clientY / this.$refs.body.clientHeight) * 2 + 1
);
// 射线投射
this.raycaster.setFromCamera(this.Vector2, this.camera);
// 获取与射线相交的对象列表
const intersects = this.raycaster.intersectObjects(this.scene.children);
// 遍历相交对象列表
for (let i = 0; i < intersects.length; i++) {
const intersect = intersects[i];
if (intersect.object.isIll) {
const name = intersect.object.name;
this.$emit("organOnclick", name);
}
}
},
onMouseMove() {
// 在这里处理鼠标点击事件的逻辑
// console.log("点击了 Three.js 元素", event);
},
onMouseUp() {
// 在这里处理鼠标点击事件的逻辑
},
// console.log("点击了 Three.js 元素", event);
changeColor(name, type = "name") {
this.model1.traverse((child) => {
if (type == "name") {
for (let index = 0; index < this.otherColor.length; index++) {
const element = this.otherColor[index];
if (element.name == child.name) {
child.material = element.material;
this.otherColor.splice(index, 1);
index--;
}
}
if (name && child.name == name) {
this.otherColor.push({
name: name,
material: child.material.clone(),
});
child.material = new THREE.MeshBasicMaterial({
color: "#f00",
transparent: true,
opacity: 0.5,
});
}
}
});
this.renderer.render(this.scene, this.camera);
},
getRandomColor() {
var letters = "0123456789ABCDEF";
var color = "#";
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
console.log(color);
return color;
},
addBackground() {
// // 创建地面的几何体;
var dgeometry = new THREE.PlaneGeometry(100, 100); // 根据需要指定地面的宽度和长度
dgeometry.rotateX(-Math.PI / 2);
// 创建地面的材质
var dmaterial = new THREE.MeshBasicMaterial({ color: "#333" }); // 根据需要指定地面的颜色或纹理
// 创建地面的网格对象
var ground = new THREE.Mesh(dgeometry, dmaterial);
ground.receiveShadow = true;
// 将地面网格对象添加到场景中
this.scene.add(ground);
},
},
};
</script>
<style scoped>
#body {
width: 100%;
height: 100%;
position: absolute;
top: 0;
overflow: hidden;
}
#btns {
position: fixed;
right: 5px;
top: 20px;
width: 100px;
display: flex;
justify-content: center;
flex-direction: column;
}
#colorBar {
width: 50px;
height: 10px;
font-size: 16px;
color: #f00;
}
#showWindow {
position: absolute;
z-index: 2;
right: 5px;
top: 5px;
background-color: #fff;
border-radius: 4px;
padding: 5px;
}
#showWindow p {
text-align: left;
}
</style>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。