赞
踩
项目太丑,扎眼了,刚写好,不立刻记下笔记就忘了,自己优化 npm run serve
链接:https://pan.baidu.com/s/1fVZLTfuALxi4Gmpc6hGNfg
提取码:ee0e
复制这段内容后打开百度网盘手机App,操作更方便哦--来自百度网盘超级会员V3的分享
1.vue引入three 不用在main中做配置
import * as THREE from "three";
import { OBJLoader, MTLLoader } from "three-obj-mtl-loader";
import { CSS2DRenderer, CSS2DObject } from "three-css2drender";
const OrbitControls = require("three-orbit-controls")(THREE);
或者直接在package.json中dependencies和devDependencies对象中加入下面的
然后 npm install
"dependencies": {
"three-css2drender": "^1.0.0",
"three-obj-mtl-loader": "^1.0.3"
},
"devDependencies": {
"three": "^0.123.0",
"three-obj-mtl-loader": "^1.0.3"
},
听说不能直接赋值,vue会监听,也没研究那么深,人家竟然这么说,也就没定义值
data() { return { axes: "", group: "", scene: "", light: "", camera: "", controls: "", renderer: "", directionalLight: "", ambient: "", loader: "", //地板 glass_material: "", publicPath: process.env.BASE_URL, m1: "", m2: "", m3: "", mouse: "", objects: [], raycaster: "", getBoundingClientRect: {}, offsetWidth: "", offsetHeight: "", theta: 0, //相机旋转角度 cord1: {}, cord2: {}, cord3: {}, cord4: {}, selectObject: {}, //被点击的第一个元素 }; },
mounted() {
this.init();
this.loadObj();
this.animate();
},
这里是:因为我加载的obj模型是有四个子模块组成的打印的时候它的child有四个,才定义四个的
this.cord1 = new THREE.Object3D();
this.cord2 = new THREE.Object3D();
this.cord3 = new THREE.Object3D();
this.cord4 = new THREE.Object3D();
接下来:创建一个场景
this.scene = new THREE.Scene();
接下来:在three.js,可以利用THREE.Raycaster来达到点击与交互,即选择物体的操作。点击事件少不了的东西
this.raycaster = new THREE.Raycaster();
接下来:因为是小白,就给自己加载的模型放了个长500的坐标轴方便观看
this.scene.add(new THREE.AxesHelper(500));
接下来:得创建相机了,因为不想弄全屏的模型展示,做个800x800的,要是想做全屏的就用window.innerWidth/window.innnerHeight,因为800/800为1,担心不知道是哪个一,第二个参数
/**
* @param {
* 初始化相机
* }
*/
this.camera = new THREE.PerspectiveCamera(
1,
// window.innerWidth / window.innerHeight,
800/800,
1,
10000
);
接下来设置相机的位置还是什么东西,自己查查这个api就会有介绍,反正我不懂,瞎设置呗,然后就把相机add到scene(场景)中
this.camera.position.set(0, 1000, 1000);
this.camera.lookAt(new THREE.Vector3(0, 0, 0));
// this.camera.lookAt(0, 0, 0);
this.scene.add(this.camera);
接下来:既然有点击获取当前的obj模型模块,当然得获取下鼠标的位置吧
//鼠标的位置
this.mouse = new THREE.Vector2();
接下来:渲染器,干什么的?依次的设置是,alpha: true, antialias: true,加了好像清楚一点,然后就是渲染大小当然我的是800x800的全屏就用window.innerWidth, window.innerHeight,渲染显示比例,渲染背景颜色,最后是追加到dom节点
/** * @param { * 渲染器 * } */ this.renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true, }); // this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.setSize(800, 800); this.renderer.setPixelRatio(window.devicePixelRatio); this.renderer.setClearColor(0x87ceeb, 1.0); const container = document.getElementById("container"); container.appendChild(this.renderer.domElement);
接下来:控制器,不添加模型不能旋转,开始找了好多参数加里面,最后发现还是什么都不加效果控制的更舒坦一点
/**
* @param {
* 控制器
* }
*/
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
// this.controls.autoRotate=true;
// this.controls.target.set(0, 0, 0);
// this.controls.minDistance = 80;
// this.controls.maxDistance = 400;
// this.controls.maxPolarAngle = Math.PI / 3;
this.controls.update();
接下来:定义光线,就是有点光打在引入的3d模型上,让模型能正常显示颜色,要不然就是黑白的,参数乱设置的,也没搞懂
/**
* @param {
* 定义光线
* }
*/
this.directionalLight = new THREE.DirectionalLight(0xc1c1c1, 1);
// 光线照射的方向
this.directionalLight.position.set(0, 1000, 1000).normalize();
this.scene.add(this.directionalLight);
this.ambient = new THREE.AmbientLight(0xffffff, 1);
this.ambient.position.set(0, 0, 0);
this.scene.add(this.ambient);
${that.publicPath}models/zzs.mtl
,function(){})// var loader = THREE.Loader.Handlers.get( url );
var loader = manager.getHandler(url);
在methods中定义的loadObj(加载obj模型)函数代码
loadObj() { let that = this; let objLoader = new OBJLoader(); let mtlLoader = new MTLLoader(); let textureLoader = new THREE.TextureLoader(); // mtlLoader.load(`${that.publicPath}models/zzs.mtl`, function (materials) { mtlLoader.load( `${that.publicPath}models/zzs.mtl`, // `${that.publicPath}models/object.mtl`, function (materials) { objLoader.setMaterials(materials); objLoader.load( // `${that.publicPath}models/object.obj`, `${that.publicPath}models/zzs.obj`, function (obj) { obj.scale.multiplyScalar(1); obj.traverse(function (child) { console.log(child.name, "ddddd"); // 这里把匹配的模块单独提取出来,如果想让其中一个子模块动 // 到时候直接可以在函数外面methed中写一个方法设置,比方说 //就单独让m3这个子模块单独动了 // that.m3.position.x -= 0.1; // that.m3.position.y -= 0.1; // that.m3.position.z -= 0.1; if (child instanceof THREE.Mesh) { if (child.name == "Box001") { that.m1 = child; } else if (child.name == "Cylinder002") { that.m2 = child; } else if (child.name == "Cylinder003") { //圆柱 that.m3 = child; } else if (child.name == "Cylinder004") { //圆柱 that.m4 = child; } } }); // 这里是做关联,比方说我要移动m4模块,整个模块都会跟着动 // 我要移动m3模块,只有m3,m2,m1模块会动 that.cord4.add(that.m4); that.cord3.add(that.m3, that.cord4); that.cord2.add(that.m2, that.cord3); that.cord1.add(that.m1, that.cord2); that.scene.add(that.cord1); that.render(); return obj; /** * @param { * 将模型加入场景中 * } */ // that.scene.add(obj); /** * @param { * 将纹理加入模型中 * } */ // that.renderer.render(that.scene, that.camera); }, function (xhr) { console.log((xhr.loaded / xhr.total) * 100 + "% loaded"); }, function (error) { console.log("An error happened"); } ); } ); },
接下来:添加窗口监听事件,固定宽高的就不用了了吧,800x800监听window窗口大小不还是800x800展示么,不过像是用window.innerWidth做长window.innerHeight做宽全屏展示的还是要监听的
window.addEventListener("resize", this.onWindowResize, false);
然后这是this.onWindowResize的方法,卸载methods中就行
/**
* @param {
* 窗口监听函数
* }
*/
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
},
接下来:点击总得有个点击事件,
一、这个是800x800固定宽高的点击函数写法,因为不需要监听点击全屏啊,监听当前的dom容器就行了
this.$refs.container.addEventListener("click", this.clickTest, false);
二、这个是设置长window.innerWidth宽window.innerHeight的写法
window.addEventListener("click", this.clickTest, false);
三、点击函数方法:在methods中定义clickTest方法
开始只知道全屏的点击可以点击到模型本身并不知道800x800这种自定义宽高的怎么点击到当前obj模型,总是匹配不到,全网都没找到这个方法,可能全网就我是最笨的,
window.innerWidth x window.innerHeight全屏大小的写法
this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
800x800写法:因为监听的是当前的dom元素的点击区域,发现event对象中有layerX 和layerY 两个参数非常可疑,正是鼠标点击dom元素上在dom元素自身上的坐标,参考上面全屏写法所以就懂了。
this.raycaster.intersectObjects(this.objects);是判断当前点击的位置与照相机的位置相链接成一条直线,哪些模型会在这条线上,返回一个数组,这个数组如果有值,第[0]位就是最上面的,也就是所谓点击的这么模型中的子模块
this.mouse.x = (event.layerX / 800) * 2 - 1;
this.mouse.y = -(event.layerY / 800) * 2 + 1;
//点击事件 clickTest(event) { console.log(event) event.preventDefault(); this.objects = []; // this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1; this.mouse.x = (event.layerX / 800) * 2 - 1; this.mouse.y = -(event.layerY / 800) * 2 + 1; // this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; this.raycaster.setFromCamera(this.mouse, this.camera); this.scene.children.forEach((child) => { //根据需求判断哪些加入objects,也可以在生成object的时候push进objects if ( !(child instanceof THREE.GridHelper) && !(child instanceof THREE.DirectionalLight) && !(child instanceof THREE.AmbientLight) ) { child.children.forEach((childv) => { if (childv instanceof THREE.Mesh) { //根据需求判断哪些加入objects,也可以在生成object的时候push进objects this.objects.push(childv); } else if (childv instanceof THREE.Object3D) { this.meshDg(childv.children); } }); } }); console.log(this.objects, 6666666 + "obj"); var intersects = this.raycaster.intersectObjects(this.objects); console.log(intersects, 8888 + "intersects"); if ( intersects.length != 0 && intersects[0].object instanceof THREE.Mesh ) { this.selectObject = intersects[0].object; this.changeMaterial(this.selectObject); console.log(this.selectObject.name); // $("#alert_center").css({ // display: "block", // }); // $("#alert_font").html( // "当前点击的模块名字是:" + this.selectObject.name // ); this.render(); } },
点击事件,点击后改变当前对象的方法:在methods中定义,点击了给它变个颜色看看,纯属个人无聊
/**
* @param {
* 改变当前对象属性
* }
*/
changeMaterial(object) {
var material = new THREE.MeshLambertMaterial({
color: 0xffffff * Math.random(),
});
object.material = material;
},
动画:写完后面,就忘了前面,写着不报错无关痛痒,在methods中定义
/**
* @param {
* 动画
* }
*/
animate() {
requestAnimationFrame(this.animate);
this.render();
},
渲染:忘了具体干嘛的,应该少不了:在methos中定义
/**
* @param {
* 渲染
* }
*/
render() {
this.renderer.render(this.scene, this.camera);
},
<template> <div class="spring"> <!-- <div class="box"></div> --> <div id="container" ref="container"></div> </div> </template> <script> import * as THREE from "three"; import { OBJLoader, MTLLoader } from "three-obj-mtl-loader"; import { CSS2DRenderer, CSS2DObject } from "three-css2drender"; const OrbitControls = require("three-orbit-controls")(THREE); export default { name: "", components: {}, props: {}, data() { return { axes: "", group: "", scene: "", light: "", camera: "", controls: "", renderer: "", directionalLight: "", ambient: "", loader: "", //地板 glass_material: "", publicPath: process.env.BASE_URL, m1: "", m2: "", m3: "", mouse: "", objects: [], raycaster: "", getBoundingClientRect: {}, offsetWidth: "", offsetHeight: "", theta: 0, //相机旋转角度 cord1: {}, cord2: {}, cord3: {}, cord4: {}, selectObject: {}, //被点击的第一个元素 }; }, created() {}, mounted() { this.init(); this.loadObj(); this.animate(); }, methods: { /** * @param { * 初始化three.js相关内容} */ init() { /** * @param { * 场景 * } */ this.cord1 = new THREE.Object3D(); this.cord2 = new THREE.Object3D(); this.cord3 = new THREE.Object3D(); this.cord4 = new THREE.Object3D(); this.scene = new THREE.Scene(); this.raycaster = new THREE.Raycaster(); /** * @param { * 世界坐标系 * } */ this.scene.add(new THREE.AxesHelper(500)); // var helper = new THREE.GridHelper(3000, 50, 0xcd3700, 0x4a4a4a); // this.scene.add(helper); /** * @param { * 初始化相机 * } */ this.camera = new THREE.PerspectiveCamera( 1, // window.innerWidth / window.innerHeight, 1, 1, 10000 ); this.camera.position.set(0, 1000, 1000); this.camera.lookAt(new THREE.Vector3(0, 0, 0)); // this.camera.lookAt(0, 0, 0); this.scene.add(this.camera); //鼠标的位置 this.mouse = new THREE.Vector2(); /** * @param { * 渲染器 * } */ this.renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true, }); // this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.setSize(800, 800); this.renderer.setPixelRatio(window.devicePixelRatio); this.renderer.setClearColor(0x87ceeb, 1.0); const container = document.getElementById("container"); container.appendChild(this.renderer.domElement); /** * @param { * 获取当前dom元素坐标 * } */ this.getBoundingClientRect = this.$refs.container.getBoundingClientRect(); this.offsetWidth = this.$refs.container.offsetWidth; this.offsetHeight = this.$refs.container.offsetHeight; /** * @param { * 控制器 * } */ this.controls = new OrbitControls(this.camera, this.renderer.domElement); this.controls.update(); /** * @param { * 定义光线 * } */ this.directionalLight = new THREE.DirectionalLight(0xc1c1c1, 1); // 光线照射的方向 this.directionalLight.position.set(0, 1000, 1000).normalize(); this.scene.add(this.directionalLight); this.ambient = new THREE.AmbientLight(0xffffff, 1); this.ambient.position.set(0, 0, 0); this.scene.add(this.ambient); /** * @param { * 添加窗口监听事件(resize-onresize即窗口或框架被重新调整大小) * } */ this.$refs.container.addEventListener("click", this.clickTest, false); // window.addEventListener("resize", this.onWindowResize, false); }, /** * @param { * 外部模型加载函数 * } */ loadObj() { let that = this; let objLoader = new OBJLoader(); let mtlLoader = new MTLLoader(); let textureLoader = new THREE.TextureLoader(); // mtlLoader.load(`${that.publicPath}models/zzs.mtl`, function (materials) { mtlLoader.load( `${that.publicPath}models/zzs.mtl`, // `${that.publicPath}models/object.mtl`, function (materials) { objLoader.setMaterials(materials); objLoader.load( // `${that.publicPath}models/object.obj`, `${that.publicPath}models/zzs.obj`, function (obj) { obj.scale.multiplyScalar(1); obj.traverse(function (child) { console.log(child.name,'ddddd') if (child instanceof THREE.Mesh) { if (child.name == "Box001") { that.m1 = child; } else if (child.name == "Cylinder002") { that.m2 = child; } else if (child.name == "Cylinder003") { that.m3 = child; }else if (child.name == "Cylinder004") { that.m4 = child; } } }); that.cord4.add(that.m4); that.cord3.add(that.m3, that.cord4); // console.log(999); // that.cord3.add(that.m3); that.cord2.add(that.m2, that.cord3); that.cord1.add(that.m1, that.cord2); that.scene.add(that.cord1); that.render(); return obj; }, function (xhr) { console.log((xhr.loaded / xhr.total) * 100 + "% loaded"); }, function (error) { console.log("An error happened"); } ); } ); }, //递归 收集 THREE.Mesh meshDg(child) { child.forEach((childv) => { if (childv instanceof THREE.Mesh) { //根据需求判断哪些加入objects,也可以在生成object的时候push进objects this.objects.push(childv); } else if (childv instanceof THREE.Object3D) { this.meshDg(childv.children); } }); }, //点击事件 clickTest(event) { console.log(event) event.preventDefault(); this.objects = []; // this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1; this.mouse.x = (event.layerX / 800) * 2 - 1; this.mouse.y = -(event.layerY / 800) * 2 + 1; // this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; this.raycaster.setFromCamera(this.mouse, this.camera); this.scene.children.forEach((child) => { //根据需求判断哪些加入objects,也可以在生成object的时候push进objects if ( !(child instanceof THREE.GridHelper) && !(child instanceof THREE.DirectionalLight) && !(child instanceof THREE.AmbientLight) ) { child.children.forEach((childv) => { if (childv instanceof THREE.Mesh) { //根据需求判断哪些加入objects,也可以在生成object的时候push进objects this.objects.push(childv); } else if (childv instanceof THREE.Object3D) { this.meshDg(childv.children); } }); } }); console.log(this.objects, 6666666 + "obj"); var intersects = this.raycaster.intersectObjects(this.objects); console.log(intersects, 8888 + "intersects"); if ( intersects.length != 0 && intersects[0].object instanceof THREE.Mesh ) { this.selectObject = intersects[0].object; this.changeMaterial(this.selectObject); console.log(this.selectObject.name); //这里找的代码,用到了jq,用vue就更简单了 // $("#alert_center").css({ // display: "block", // }); // $("#alert_font").html( // "当前点击的模块名字是:" + this.selectObject.name // ); this.render(); } }, /** * @param { * 改变当前对象属性 * } */ changeMaterial(object) { var material = new THREE.MeshLambertMaterial({ color: 0xffffff * Math.random(), }); object.material = material; }, /** * @param { * 动画 * } */ animate() { requestAnimationFrame(this.animate); this.render(); }, /** * @param { * 渲染 * } */ render() { this.renderer.render(this.scene, this.camera); }, /** * @param { * 窗口监听函数 * } */ onWindowResize() { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); // this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.setSize(800, 800); }, }, computed: {}, }; </script> <style lang='scss' scoped> .spring { width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; } .box { width: 100%; height: 100px; } </style>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。