赞
踩
three.js是一款基于原生web GL封装通用Web 3D引擎,在小游戏、产品展示、物联网、数字孪生、智慧城市园区、机械、建筑、全景看房、GIS等各个领域基本上都有three.js的身影。
Three.js中文网提供Three.js、WebGL视频课程http://www.webgl3d.cn/
npm install three@0.157.0 -S
1)准备模型,在public下新建model文件夹,把模型放到model文件夹下
2)复制three.js 依赖包自带的draco文件夹复制到public下
draco的位置如下:node_modules->three->examples->jsm->libs->draco
将该文件夹复制到public下
3)准备hdr纹理贴图,如果不需要纹理贴图的可以忽略
- <template>
- <div>
- <!-- 进度条 -->
- <div class="pro-box">
- <el-progress v-if="showProcess" :stroke-width="22" :percentage="loadProcess" />
- </div>
- </div>
- </template>
-
- <script setup>
- import {
- ref,
- getCurrentInstance,
- watch
- } from "vue";
- import {
- tag,
- labelRenderer
- } from "@/label.js";
-
- import * as THREE from "three";
- import {
- OrbitControls
- } from "three/examples/jsm/controls/OrbitControls";
- import {
- GLTFLoader
- } from "three/examples/jsm/loaders/GLTFLoader"; // 加载gltf模型
- import {
- DRACOLoader
- } from "three/examples/jsm/loaders/DRACOLoader"; // 解压gltf模型
- import {
- GUI
- } from "three/examples/jsm/libs/lil-gui.module.min.js";
- import {
- RGBELoader
- } from "three/examples/jsm/loaders/RGBELoader"; // 加载hdr环境光照
-
- // import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer"; // 后期处理
- // import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass"; // 后期处理
- // import gsap from "gsap";
-
- // ******************
- const {
- proxy
- } = getCurrentInstance();
- const loadProcess = ref(0);
- const showProcess = ref(true);
- const labelBox = ref(tag());
- const modelObj = ref(null);
-
- // 进度条
- watch(loadProcess, (value) => {
- if (value == 100) {
- setTimeout(() => {
- showProcess.value = false;
- }, 2000);
- }
- });
- // ******************
-
-
-
-
- // 初始化场景
- // const model = ref(null);
- const scene = new THREE.Scene();
-
- // 初始化相机
- const camera = new THREE.PerspectiveCamera(
- 75,
- window.innerWidth / window.innerHeight,
- 0.1,
- 1000
- );
- camera.position.set(8, 5, 4);
- camera.lookAt(0, 0, 0); // 相机观察目标
- camera.updateProjectionMatrix(); //更新投影矩阵
- // 初始化渲染器
- const renderer = new THREE.WebGLRenderer({
- // 设置抗锯齿
- antialias: true,
- });
- renderer.setSize(window.innerWidth, window.innerHeight);
- document.body.appendChild(renderer.domElement);
-
- // 设置色调映射
- renderer.outputEncoding = THREE.sRGBEncoding;
- renderer.toneMapping = THREE.ACESFilmicToneMapping;
- renderer.toneMappingExposure = 1;
- renderer.shadowMap.enabled = true; //允许阴影
-
- // AxesHelper: 辅助观察的坐标系
- const axesHelper = new THREE.AxesHelper(10);
- scene.add(axesHelper);
-
- // 初始化相机轨道控制器
- const controls = new OrbitControls(camera, renderer.domElement);
- controls.enableDamping = true;
-
- // 加载环境纹理
- let rebgLoader = new RGBELoader();
- rebgLoader.load("./textues/sky2.hdr", function (texture) {
- // 设置球形纹理映射
- texture.mapping = THREE.EquirectangularReflectionMapping;
- scene.background = texture;
- scene.environment = texture;
- });
-
- // 初始化加载器draco
- const dracoLoader = new DRACOLoader();
- // 设置draco路径
- dracoLoader.setDecoderPath("./draco/");
- const gltfLoader = new GLTFLoader();
- // 设置gltf加载器draco解码器
- gltfLoader.setDRACOLoader(dracoLoader);
-
- // 加载模型
- gltfLoader.load("./model/jc.glb", function (gltf) {
- const model = gltf.scene;
- model.traverse((child) => {
- if (child.isMesh) {
- child.material.side = THREE.DoubleSide; // 模型双面渲染
- child.castShadow = true; // 光照是否有阴影
- child.receiveShadow = true; // 是否接收阴影
- child.frustumCulled = false;
- }
- });
- model.position.set(0, -3, 0);
- scene.add(model);
-
- // gui
- const gui = new GUI();
- gui.add(model.position, "x").name("模型坐标轴x位置");
- gui.add(model.position, "y").name("模型坐标轴y位置");
- gui.add(model.position, "z").name("模型坐标轴z位置");
- }, function (xhr) {
- // 计算加载进度
- const percent = xhr.loaded / xhr.total * 100;
- loadProcess.value = parseInt(percent);
- // console.log('加载进度------', parseInt(loadProcess.value),parseInt(percent));
- });
-
- // 点光源
- const pointLight = new THREE.PointLight(0xffffff, 10); //光源颜色 光照强度
- pointLight.decay = 0.0; //设置光源不随距离衰减 默认2.0
- pointLight.position.set(5, 10, 0); // 点光源位置
- pointLight.castShadow = true;
- scene.add(pointLight);
-
- // 环境光设置
- const ambient = new THREE.AmbientLight(0xffffff, 1);
- scene.add(ambient);
- // 添加平行光
- const light = new THREE.DirectionalLight(0xffffff, 1);
- light.position.set(0, 60, 10);
- light.target.position.set(0, 4, -50);
- scene.add(light);
-
- // 平行光辅助观察
- // const lightHelper = new THREE.DirectionalLightHelper(light, 1000);
- // scene.add(lightHelper);
- // lightHelper.position.set(300, 200, 120);
-
- function render() {
- requestAnimationFrame(render);
- renderer.render(scene, camera);
- controls.update();
- }
-
- render();
-
- document.body.addEventListener("click", function (event) {
- const px = event.offsetX;
- const py = event.offsetY;
- const Sx = event.clientX; //鼠标单击位置横坐标
- const Sy = event.clientY; //鼠标单击位置纵坐标
- //屏幕坐标转WebGL标准设备坐标
- // 这里的window.innerWidth和window.innerHeight其实是容器的宽高,并且默认按照(0,0)来计算的,需要考虑偏移的位置(px,py)
- const x = (Sx / renderer.domElement.clientWidth) * 2 - 1;
- const y = -(Sy / renderer.domElement.clientHeight) * 2 + 1;
- //创建一个射线投射器Raycaster
- const raycaster = new THREE.Raycaster();
- //通过鼠标单击位置标准设备坐标和相机参数计算射线投射器`Raycaster`的射线属性.ray
- raycaster.setFromCamera(new THREE.Vector2(x, y), camera); //参数: 标准化的二维坐标 相机
- //返回.intersectObjects()参数中射线选中的网格模型对象
- // 未选中对象返回空数组[],选中一个数组1个元素,选中两个数组两个元素
- const intersects = raycaster.intersectObjects(scene.children); //参数 计算范围
- console.log(intersects, '----------intersects')
- if (intersects.length > 0) {
- modelObj.value = intersects[0].object;
- // console.log(intersects[0], '--------------intersects[0]')
- labelBox.value.position.set(intersects[0].point.x, intersects[0].point.y, intersects[0].point.z);
- // labelBox.value.element.innerHTML = modelObj.value.name + '哈哈哈哈哈';
- labelBox.value.element.innerHTML = `
- <div>
- <div>
- <div>名称:${modelObj.value.name}</div>
- <div>提示:哈哈哈哈哈</div>
- </div>
- </div>
- `;
- labelBox.value.element.style.visibility = 'visible';
- } else {
- labelBox.value.element.style.visibility = 'hidden'; //没有选中mesh,隐藏标签
- }
- scene.add(labelBox.value);
- labelRenderer.render(scene, camera); // 标签渲染
- });
- </script>
-
- <style scoped>
- * {
- margin: 0;
- padding: 0;
- }
-
- canvas {
- width: 100vw;
- height: 100vh;
- position: fixed;
- left: 0;
- top: 0;
- }
-
- .pro-box {
- position: absolute;
- left: 50px;
- top: 30px;
- width: 500px;
- height: 10px;
- }
- </style>
label.js
- import {
- CSS2DRenderer,
- CSS2DObject,
- } from "three/examples/jsm/renderers/CSS2DRenderer.js";
- import labelBg from "@/assets/tips-border.png"; // 导入标签背景
-
- // 创建一个HTML标签
- function tag() {
- // 创建div元素(作为标签)
- var div = document.createElement("div");
- div.style.visibility = "hidden";
- div.innerHTML = "";
- div.style.width = "200px";
- div.style.height = "100px";
- div.style.padding = "5px 10px";
- div.style.color = "#fff";
- div.style.fontSize = "16px";
- div.style.position = "absolute";
- // div.style.backgroundColor = "rgba(25,25,25,0.5)";
- div.style.background = `rgba(25,25,25,0.5) url(${labelBg})no-repeat center center`;
- div.style.backgroundSize = "100% 100%";
- div.style.borderRadius = "5px";
- //div元素包装为CSS2模型对象CSS2DObject
- var label = new CSS2DObject(div);
- div.style.pointerEvents = "none"; //避免HTML标签遮挡三维场景的鼠标事件
- // 设置HTML元素标签在three.js世界坐标中位置
- // label.position.set(x, y, z);
- return label; //返回CSS2模型标签
- }
-
- // 创建一个CSS2渲染器CSS2DRenderer
- var labelRenderer = new CSS2DRenderer();
- labelRenderer.setSize(window.innerWidth, window.innerHeight);
- labelRenderer.domElement.style.position = "absolute";
- // // 避免renderer.domElement影响HTMl标签定位,设置top为0px
- labelRenderer.domElement.style.top = "0px";
- labelRenderer.domElement.style.left = "0px";
- // //设置.pointerEvents=none,以免模型标签HTML元素遮挡鼠标选择场景模型
- labelRenderer.domElement.style.pointerEvents = "none";
- document.body.appendChild(labelRenderer.domElement);
-
- export { tag, labelRenderer };
这世界很喧嚣,做你自己就好
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。