ThreeJS掘金最通俗入门教程05-ThreeJS调试方法

809 阅读6分钟

献给读者,我起这个标题绝对不是想着靠标题给大家吸引进来给我引流加阅读量什么的,我总结看了一遍掘金所有ThreeJS的入门文章,发现大家很多都是写的给自己看的文章,偏向于让所有人都能看懂的部分还是少了。如果说这篇文章确实对您有帮助您可以点赞支持下我产出更多优秀内容。如果说确实存在需要改进的地方您可以在评论区留下宝贵的意见。我一定认真学习,认真改进

相机控件OrbitControls.js

通过Three.js的相机控件OrbitControls.js可以对Threejs的三维场景进行缩放、平移、旋转操作,本质上 改变的并不是场景,而是相机的参数,通过前面的学习应该知道,相机的位置角度不同,同一个场景的 渲染效果是不一样,比如一个相机绕着一个场景旋转,就像场景旋转一样。

如果你想深入了解相机控制器OrbitControls的每一个功能,OrbitControls是如何对Three.js正投影相机 和透视投影相机对象进行封装的,可以阅读Three.js\examples\js\controls目录下的OrbitControls.js文 件。

调用OrbitControls的时候需要引入OrbitControls.js文件。

旋转缩放平移

如果你想实现一个场景旋转缩放平移的效果,直接把相机对象camera作为OrbitControls构造函数的参 数就可以。

鼠标操作:通过拖动鼠标左键可以720旋转展示三维场景,通过拖动鼠标右键可以平移三维场景,通过上下滚动鼠标中键可以缩放三维场景。 【创建控件对象 相机对象camera作为参数 , 控件可以监听鼠标的变化,改变相机对象的属性 var controls =new THREE.OrbitControls(camera); 禁止旋转平移缩放(.enablePan属性】

比如一个展示一个三维场景,比如一辆轿车产品,你不希望鼠标右键拖动会产生一个平移效果。可以通 过设置相机空间对象OrbitControls的.enablePan属性

controls.enablePan =false;【禁止右键拖拽 通过.enableZoom属性可以控制是否允许鼠标中键缩放场景,.enableZoom属性默认值true。 controls.enableZoom =false;禁止缩放 通过.enableRotate属性可以控制是否允许鼠标左键旋转场景,.enableRotate属性默认值true。 controls.enableRotate =false;禁止旋转】

设置缩放范围

在实际应用中,你想控制一个产品或一个房间户型的缩放范围,可以通过相机空间OrbitControls 的.minZoom和.maxZoom属性实现 【缩放范围 controls.minZoom =0.5; controls.maxZoom =2;】

设置旋转范围

展示一个三维场景,你想控制360度旋转范围,比如一辆轿车,你不希望用户看到轿车的底盘,你 可以通过设置相机的旋转范围属性来实现。 通过.minPolarAngle和.maxPolarAngle属性控制上下360度的旋转范围,通过.minAzimuthAngle 和.maxAzimuthAngle属性控制左右360度的旋转范围,上下左右两个360度旋转也就是常说的720 旋转展示。 上下旋转范围controls.minPolarAngle =0; controls.maxPolarAngle =Math.PI; 左右旋转范围controls.minAzimuthAngle = -Math.PI * (100/180); controls.maxAzimuthAngle =Math.PI * (100/180)

属性

名称功能
autoRotateBoolean默认为false,当设定为true时,相机自动围绕目标旋转但必须在animation中循环调用update()
autoRotateSpeedFloat当前者为true时默认是2.0,代表每轮60fps用时30s,值越小转动越慢
rotateSpeedFloat旋转速度(ORBIT的旋转速度,鼠标左键),默认1
enableDampingBoolean默认为false,设置为true则启用阻尼(惯性,用来控制相机的一个重量),必须在你的animation循环中调用update
dampingFactorFloat前者为true时使用阻尼惯性,可以理解为就只向一个方向移动
enabledBoolean是否启用控件,默认为true
enableKeysBoolean能否用键盘来控制,默认true使用上下左右键来控制物体的移动
keysObject控制相机平移的4个键,默认四个箭头键{Left:37,UP:38,RIGHT:39,DOWN:40}
enablePanBoolean相机平移,默认为true
panSpeedFloat默认的移动速度,默认为1
keyPanSpeedFloat相机平移的速度,默认每按一次控制方向键移动7.0像素
enableRotateBoolean水平垂直旋转相机,默认为true。只想着控制单轴,通过PloarAngle/AzimuthAngle的最小值和最大值设置为相同的值,这将导致垂直或水平旋转固定在该值
enableZoomBoolean相机的缩放
maxAzimuthAngleFloat水平旋转,范围-Math.PI_Math.PI或者Infinity,默认是Infinity
objectCamera正在控制的相机
zoomSpeedFloat Zoom(变焦的速度),默认为1
disposenull移除所有的事件监听
reset通过最近一次调用saveState来保存或者初始化来重置为当前的状态
saveState保存当前控制的状态,可以稍后通过Reset来恢复
update更新控件,在手动改变了摄像机的转换后必须调用。在设置了autoRotate或enableDamping时也要在循环中调用

代码示例

下面的代码看着没有什么出奇的,但是问题的关键在于你要去实际根据上面的API去操作下相机,然后感受下相机在API的控制下是怎么运作的。通过这些基础理论之后,我们就可以用相机控制器在往后实现很多神奇的效果。

<template>
  <section id="container"></section>
</template>
<script>
import { defineComponent, onMounted, reactive, toRefs } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
let scene, point;
export default defineComponent({
  setup() {
    const state = reactive({
      renderer: "",
      controls: "",
      camera: "",
      requestID: "",
    });
    // 加载场景
    const loadScene = () => {
      scene = new THREE.Scene();
    };
    // 加载相机
    const loadCamera = () => {
      state.camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      state.camera.position.set(200, 300, 200); //设置相机位置
      state.camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
    };
    // 加载渲染器
    const loadRenderer = () => {
      state.renderer = new THREE.WebGLRenderer();
      state.renderer.setSize(window.innerWidth, window.innerHeight);
      let container = document.getElementById("container");
      state.renderer.setClearColor(0xb9d3ff, 1);
      container.appendChild(state.renderer.domElement);
      state.renderer.render(scene, state.camera);
    };
    // 加载控制器
    const loadController = () => {
      state.controls = new OrbitControls(
        state.camera,
        state.renderer.domElement
      );
      state.controls.enablePan = false;
      state.controls.maxAzimuthAngle = Math.PI * 0.5;
      state.controls.minAzimuthAngle = Math.PI * 0.25;
      state.controls.maxPolarAngle = Math.PI * 0.5;
      state.controls.minPolarAngle = Math.PI * 0.25;
      state.controls.addEventListener("change", render);
    };
    // 加载渲染实体
    const loadGeometry = () => {
        let boxGeometry=new THREE.BoxBufferGeometry(20,20,20);
        let material=new THREE.MeshLambertMaterial({color:0x00ff00});
        let mesh=new THREE.Mesh(boxGeometry,material);
        scene.add(mesh);
    };
    // 实际要执行的渲染
    const render = () => {
    //   point.rotation.x += 0.01;
    //   point.rotation.y += 0.01;
      state.renderer.render(scene, state.camera);
    };
    const animation = () => {
      state.requestID = requestAnimationFrame(animation);
      render();
    };
    const loadLight=()=>{
      var point = new THREE.PointLight(0xffffff);
      point.position.set(400, 200, 300); //点光源位置
      scene.add(point); //点光源添加到场景中
      //环境光
      var ambient = new THREE.AmbientLight(0x444444);
      scene.add(ambient);
    }
    const init = () => {
      loadScene();
      loadCamera();
      loadRenderer();
      loadController();
      loadGeometry();
      loadLight()
      animation();
    };
    onMounted(() => {
      init();
    });
    return {
      ...toRefs(state),
    };
  },
});
</script>

实现效果

image.png

然后这里的话,由于我的Gitee项目工程用的是vue3+vite,那么阅读STATS这个官方提供的性能监控工具的源码就能够发现,他并不是能够直接模块化导入的,他用的是nodeJS的那一套module.exports导出。那么我们吧代码下载下来之后也需要进行一些对应的修改,让他适应我们的项目:

image.png

然后这样我们就可以在Vue文件中直接使用Stats来对我们的页面做性能监控了。

ThreeJS性能控件

所谓性能控件,在页面上的显示效果就是能够及时显示ThreeJS性能的控件。算是某种程度上的插件,并且这个插件由于某些原因,不能直接通过npm直接install。所以我们需要在vite暴露出去的index.html中导入stats这个工具。

直接在html文件中引入ThreeJS然后再引入这个性能监控工具的话,他的实现效果是这样的:

image.png

代码实现

<template>
  <section id="container"></section>
</template>
<script>
import { defineComponent, onMounted, reactive, toRefs } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import Stats from '../../../utils/stats';
let scene, point;
export default defineComponent({
  setup() {
    const state = reactive({
      renderer: "",
      controls: "",
      camera: "",
      requestID: "",
      stats: ""
    });
    // 加载场景
    const loadScene = () => {
      scene = new THREE.Scene();
    };
    // 加载相机
    const loadCamera = () => {
      state.camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      state.camera.position.set(200, 300, 200); //设置相机位置
      state.camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
    };
    // 加载渲染器
    const loadRenderer = () => {
      state.renderer = new THREE.WebGLRenderer();
      state.renderer.setSize(window.innerWidth, window.innerHeight);
      let container = document.getElementById("container");
      state.renderer.setClearColor(0xb9d3ff, 1);
      container.appendChild(state.renderer.domElement);
      state.renderer.render(scene, state.camera);
    };
    // 加载控制器
    const loadController = () => {
      state.controls = new OrbitControls(
        state.camera,
        state.renderer.domElement
      );
      state.controls.enablePan = false;
      state.controls.maxAzimuthAngle = Math.PI * 0.5;
      state.controls.minAzimuthAngle = Math.PI * 0.25;
      state.controls.maxPolarAngle = Math.PI * 0.5;
      state.controls.minPolarAngle = Math.PI * 0.25;
      state.controls.addEventListener("change", render);
    };
    // 加载渲染实体
    const loadGeometry = () => {
        let boxGeometry=new THREE.BoxBufferGeometry(20,20,20);
        let material=new THREE.MeshLambertMaterial({color:0x00ff00});
        let mesh=new THREE.Mesh(boxGeometry,material);
        scene.add(mesh);
    };
    // 实际要执行的渲染
    const render = () => {
    //   point.rotation.x += 0.01;
    //   point.rotation.y += 0.01;
      state.renderer.render(scene, state.camera);
    };
    const animation = () => {
      state.requestID = requestAnimationFrame(animation);
      render();
      
    //   state.stats.update();
    //   state.orbitControl.update();
    };
    const loadLight=()=>{
      var point = new THREE.PointLight(0xffffff);
      point.position.set(400, 200, 300); //点光源位置
      scene.add(point); //点光源添加到场景中
      //环境光
      var ambient = new THREE.AmbientLight(0x444444);
      scene.add(ambient);
    }
    const loadStats=()=>{
        let container = document.getElementById("container");
        state.stats=new Stats();
        container.appendChild(state.stats.dom);
    }
    const init = () => {
      loadScene();
      loadCamera();
      loadRenderer();
      loadController();
      loadGeometry();
      loadLight()
      animation();
      loadStats()
    };
    onMounted(() => {
      init();
    });
    return {
      ...toRefs(state),
    };
  },
});
</script>

实现效果

image.png

坐标系控件

在场景中添加如下代码

var axes = new THREE.AxisHelper(30);
      scene.add(axes);

实现效果

image.png

海克斯科技传送门

参考文献