Threejs开发经验记录

1、官网地址

参考地址

threejs官网
中文文档

2、安装threejs调试工具(扩展程序)Three.js Developer Tools

Three.js Developer Tools

没有用vue,调试工具能正常加载,但是项目saber中,不能正常加载,还在测试中
测试1、在html 中引入vue ,调试正常;
测试2、新建vue2项目测试正常
测试3、复制saber 项目,删除代码测试,后面删掉了一些html中又正常的,但是正常和不正常是不稳定的
在这里插入图片描述

3、辅助工具dat.GUI

源文档介绍
https://github.com/dataarts/dat.gui
主要用法介绍
three.js dat.GUI介绍

4、最好用组对象并命名

//创建两个网格模型mesh1、mesh2
var geometry = new THREE.BoxGeometry(20, 20, 20);
var material = new THREE.MeshLambertMaterial({color: 0x0000ff});
var group = new THREE.Group();
var mesh1 = new THREE.Mesh(geometry, material);
var mesh2 = new THREE.Mesh(geometry, material);
mesh2.translateX(25);
//把mesh1型插入到组group中,mesh1作为group的子对象
group.add(mesh1);
//把mesh2型插入到组group中,mesh2作为group的子对象
group.add(mesh2);
//把group插入到场景中作为场景子对象
scene.add(group);

5、递归遍历方法.traverse()

scene.traverse(function(obj) {
  if (obj.type === "Group") {
    console.log(obj.name);
  }
  if (obj.type === "Mesh") {
    console.log('  ' + obj.name);
    obj.material.color.set(0xffff00);
  }
  if (obj.name === "左眼" | obj.name === "右眼") {
    obj.material.color.set(0x000000)
  }
})

6、查找某个具体的模型

// 遍历查找scene中复合条件的子对象,并返回id对应的对象
var idNode = scene.getObjectById ( 4 );

// 遍历查找对象的子对象,返回name对应的对象(name是可以重名的,返回第一个)
var nameNode = scene.getObjectByName ( "左腿" );

7、three.js 颜色color的几种设置方式

three.js 颜色color的几种设置方式

var color = new THREE.Color();
var color = new THREE.Color( 0xff0000 );
var color = new THREE.Color("rgb(255, 0, 0)");
var color = new THREE.Color("rgb(100%, 0%, 0%)");
var color = new THREE.Color("hsl(0, 100%, 50%)");
var color = new THREE.Color( 1, 0, 0 );

8、 相机对象

相机对象

9、模型文件加载

模型文件加载

查看Threejs文档Geometry、Material、Light、Object3D等类,你可以发现这些类都提供了一个方法**.toJSON()**通过这个方法可以导出Threejs三维模型的各类数据,该方法的功能就是把Threejs的几何体、材质、光源等对象转化为JSON格式导出

10、WebGL渲染器

全屏渲染和局部渲染
WebGL渲染器

11、3D 项目常用功能

在这里插入图片描述
需求: 点击目标,出现目标的详细数据,可以在画布里面点击,也可以在画布外面点击按钮

源代码:

<template>
  <div>
    <div
      id="dContainer"
      ref="mainContent"
      :style="{width:width+'px',height:height+'px'}"
    ></div>
    <div id="messageTag">{{ label }}</div>
    <el-button type="primary" @click="selectMesh('vip3')">vip3</el-button>
    <el-button type="primary" @click="selectMesh('vip4')">vip4</el-button>
  </div>
</template>
<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import {
  CSS2DObject,
  CSS2DRenderer,
} from "three/examples/jsm/renderers/CSS2DRenderer";
export default {
  name: "three",
  data() {
    return {
      label: "",
      camera: {},
      renderer: {},
      scene: {},
      labelRenderer: {},
      wrapHtml: {},
      group: {},
      left:245,//如果是全屏 0
      top:110,//如果是全屏 0
      width:800, //如果是全屏 window.innerWidth
      height:800//如果是全屏 window.innerHeight
    };
  },
  mounted() {
    this.init3D();
    this.animate();
  },
  methods: {
    init3D() {
      //创建场景
      let scene = new THREE.Scene();
      //常用分组,方便对目标对象进行管理
      var group = new THREE.Group();
      this.group = group;
      this.scene = scene;
      this.scene.rotation.x = -0.6;
      scene.background = new THREE.Color(0xeeeeee);
      //创建灯光
      let light = new THREE.HemisphereLight(0xbbbbff, 0x444422, 1.5);
      light.position.set(0, 1000, 80);
      scene.add(light);

      var ambientLight = new THREE.AmbientLight(0xffffff);
      ambientLight.position.set(100, 500, 50);
      scene.add(ambientLight);
      //创建坐标辅助对象
      const axesHelper = new THREE.AxesHelper(1000);
      this.scene.add(axesHelper);

      const light2 = new THREE.DirectionalLight(0x000000);
      // const helper1 = new THREE.DirectionalLightHelper(light2, 10);
      // scene.add(helper1);

      const size = 1000;
      const divisions = 100;

      // const gridHelper = new THREE.GridHelper(size, divisions);
      // scene.add(gridHelper);
 
      //加载模型
      var loader = new GLTFLoader();
      loader.load(
        "/models/qiao.glb",
        function (gltf) {
          gltf.scene.rotation.x = 75;
          gltf.scene.traverse(function (child) {
            if (child.isMesh) {
              child.frustumCulled = false;
              child.castShadow = true;
              child.material.emissive = child.material.color;
              child.material.emissiveMap = child.material.map;
            }
          });
          scene.add(gltf.scene);
        },
        undefined,
        function (e) {
          console.error(e);
        }
      );

      //设置渲染器
      let renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true,
        logarithmicDepthBuffer: true,
        exposure: 0.6,
      });
      this.renderer = renderer;
      renderer.setPixelRatio(window.devicePixelRatio);

      renderer.setSize(this.width, this.height);
      renderer.gammaOutput = true;
      renderer.outputEncoding = THREE.sRGBEncoding;
      //渲染内容放在容器中
      let container = document.getElementById("dContainer");
      container.appendChild(renderer.domElement);
      //监听浏览器窗口变化
      window.addEventListener("resize", this.onWindowResize, false);
      //监听页面点击事件
      window.addEventListener("click", this.onmodelclick);

      //创建相机对象
      let camera = new THREE.PerspectiveCamera(405, this.width / this.height, 0.01, 1000);
      this.camera = camera;
      camera.position.set(0, 800, 0);
      camera.lookAt(scene.position);

      // const helper = new THREE.CameraHelper(camera);
      // this.scene.add(helper);

      //创建3D图形操控
      let controls = new OrbitControls(camera, renderer.domElement);
      controls.enablePan = false;
      controls.enableZoom = true;
      controls.target.set(0, 0, 0);
      controls.update();
      //加载图形
      this.loadImage("/img/bg/vip3.png", -165, 30, 20);
      this.loadImage("/img/bg/vip4.png", 165, 30, 20);
      //加载分组
      this.scene.add(this.group);
      //加载页面信息的html标签
      this.getDefaultCSS2DObject("messageTag");
      //渲染html标签
      this.CSS2DRendererFun();
    },
    //加载纹理图片
    loadImage(url, x, y, z) {
      new THREE.TextureLoader().load(
        url,
        (texture) => {
          const SIZE = 20;
          const img = texture.image;
          let height = (img && img.height) || SIZE;
          let width = (img && img.width) || SIZE;
          height = (SIZE / width) * height;
          width = SIZE;
          const mat = new THREE.MeshBasicMaterial({
            map: texture,
            side: THREE.DoubleSide,
            transparent: true,
          });

          const geom = new THREE.PlaneGeometry(width, height);
          const mesh = new THREE.Mesh(geom, mat);
          //设置围栏图片的位置
          mesh.position.x = x;
          mesh.position.z = z;
          mesh.position.y = y;
          mesh.name = url;
          this.group.add(mesh);
        },
        () => {},
        (error) => {
          console.log("error", error);
        }
      );
    },
    //窗口变化,重新渲染
    onWindowResize() {
      this.renderer.setSize(this.width, this.height);
      this.camera.aspect = this.width / this.height;
      this.camera.updateProjectionMatrix();
    },
    //加入html标签,此时没有指定标签内容和位置
    getDefaultCSS2DObject(domID) {
      let div = document.getElementById(domID);
      var label = new CSS2DObject(div);
      this.wrapHtml = label;
      this.scene.add(label);
    },
    //点击目标对象按钮
    selectMesh(name) {
      //清空所有目标对象的颜色和label值,当找到目标对象的时候重新给label赋值和移动label的位置
      this.group.children.forEach((element) => {
        element.material.color = new THREE.Color(0xffffff);
        this.label = "";
      });
      this.group.children.forEach((element) => {
        if (element.name.indexOf(name) != -1) {
          this.label = element.name;
          element.material.color = new THREE.Color(0xff0000);
          this.wrapHtml.position.copy(element.position);
          this.wrapHtml.position.y += 50;
        }
      });
    },
    //window点击事件只处理了canvas的点击事件
    onmodelclick(event) {
      //只处理了canvas的点击事件
      if (event.target.localName != "canvas") return;
      var mouse = new THREE.Vector2();
      var raycaster = new THREE.Raycaster();
      // mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
      // mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
      mouse.x = ((event.clientX - this.left) / this.width) * 2 - 1;
      mouse.y = -((event.clientY - this.top) / this.height) * 2 + 1;

      raycaster.setFromCamera(mouse, this.camera);
        //清空所有目标对象的颜色和label值,当找到目标对象的时候重新给label赋值和移动label的位置
      this.group.children.forEach((element) => {
        //两种赋值方式
        // element.material.color = new THREE.Color(0xffffff);
        element.material.color.set(0xffffff);
        this.label = "";
      });

      const intersects = raycaster.intersectObjects(this.group.children);
      if (intersects.length > 0) {
        let temp = intersects[0].object;
        this.label = temp.name;
        temp.material.color.set(0xff0000);
        this.wrapHtml.position.copy(temp.position);
        this.wrapHtml.position.y += 50;
      } else {
      }
    },
    CSS2DRendererFun() {
      var labelRenderer = new CSS2DRenderer();
      this.labelRenderer = labelRenderer;
      labelRenderer.setSize(this.width, this.height);
      labelRenderer.domElement.style.position = "absolute";
      labelRenderer.domElement.style.top = this.top+"px";
      labelRenderer.domElement.style.left = this.left+"px";
      labelRenderer.domElement.style.zIndex = 9999999;
      labelRenderer.domElement.style.pointerEvents = "none";
      document.body.appendChild(labelRenderer.domElement);
    },
    animate() {
      requestAnimationFrame(this.animate);
      this.labelRenderer.render(this.scene, this.camera); //渲染HTML标签对象
      this.renderer.render(this.scene, this.camera);
    },
  },
};
</script>

12、threejs 加载glb文件

1、3D软件下载
3dsMax
blender.

blender 快捷键

在线查看器

threejs 加载blender 导出的模型是,材质无法加载

Three.js学习笔记-Blender导出的gltf格式的材质无法显示问题处理

试过blender 导出 obj,fbx格式都无法加载,不知道是不是我记载错了

三维文件格式知多少

12、坐标

标签坐标问题—局部、世界坐标

关于模型标签问题中模型世界坐标的获取问题,前面几节课都没有详细展开讲解,本节课详细说明下。

如果你想给一个网格模型Mesh添加一个标签,无论是通过精灵模型Sprite方式,还是平面网格模型方式,还是HTML元素方式。你首先要做到的就是先准确获得需要添加标签网格模型在threejs三维空间中的世界坐标。

学习本节课及其后面几节课内容之前希望你先对课程中会提到的层级模型、局部位置、世界坐标、几何体顶点等概念有一定的了解,如果不是太熟悉的情况下,建议学习下threejs前面课程。

层级模型

复杂的项目,一个three.js场景往往包含包含多个模型对象,模型对象也会有一些父对象,这样就会形成一个层级模型,从数据结构的角度来看就是树结构。 一个模型相对世界坐标系原点的位置是世界坐标,相对父对象的位置是局部坐标。

局部坐标.position

.position属性是一个模型的局部位置坐标或者说本地坐标,相对父对象的位置,如果一个网格模型Mesh直接属于场景Scene,没有除了Scene以外的父对象,.position表示的局部位置坐标就是世界坐标。

世界坐标.getWorldPosition()

实际项目中一个模型对象可以有多个父对象,这个时候想获得该模型对象的世界坐标,就不能通过.position属性,应该通过.getWorldPosition()方法实现,该方法的使用参考基类Object3D。

mesh.position.set(60, 0, 0)

var group = new THREE.Group();
group.add(mesh);
//网格模型mesh的父对象也沿着x轴偏移
group.position.x+=60

//执行.updateMatrixWorld()方法更新世界矩阵
scene.updateMatrixWorld();
//.getWorldPosition(new THREE.Vector3())获得网格模型的世界坐标
sprite.position.copy(mesh.getWorldPosition())
sprite.position.y += 80
scene.add(sprite)

十三、在Three.js中对mesh使用”z-index” 不遮挡

在Three.js中对mesh使用”z-index” 不遮挡

mesh.renderOrder=99;
但根据我具体实现后,却发现根本没有作用,后来在网上查了,需要对mesh的material.depthTest属性设置为false
比如:
mesh.material.depthTest=false;

十四、轨道控制器(OrbitControls)

Orbit controls(轨道控制器)可以使得相机围绕目标进行轨道运动。

const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
const controls = new OrbitControls( camera, renderer.domElement );
//controls.update() must be called after any manual changes to the camera's transform
camera.position.set( 0, 20, 100 );
controls.update();
function animate() {
  requestAnimationFrame( animate );
  // required if controls.enableDamping or controls.autoRotate are set to true
  controls.update();
  renderer.render( scene, camera );
}

在这里插入图片描述
可以监听用户的操作,从而得到相机的最佳值,然后对相机设置最佳的参数

      this.controls.addEventListener('change',(e)=>{
            console.log('this.defaultCamera ', this.defaultCamera);

	 defaultCamera.position.x = 1116.9841018710713;
     defaultCamera.position.y = 879.4731685732406;
     defaultCamera.position.z = 983.9981898543468;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值