[three.js]搭建场景

背景

虽然一直在从事three.js方面的开发工作,但是都是在同事搭建好的场景下工作的。最近接手的任务让我可以从0到1搭建一个场景,顺便复习一下

搭建三维场景

<script lang="ts" setup>
import { ref, onMounted } from 'vue';                                         
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
// 导入 DRACOLoader
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';

onMounted(() => {
  createScene();
});

let scene: any, camera: any, renderer: any, controls: any;                                               
let css3dRenderer: any;

function createScene() {
  //场景
  scene = new THREE.Scene();

  //辅助观察的坐标系
  const axesHelper = new THREE.AxesHelper(100);
  scene.add(axesHelper);

  //光源设置
  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
  directionalLight.position.set(100, 60, 50);
  scene.add(directionalLight);
  const ambient = new THREE.AmbientLight(0xffffff, 0.4);
  scene.add(ambient);

  //渲染器和相机
  let renderCanvas = document.getElementById('renderCanvas');
  const width = renderCanvas.offsetWidth;
  const height = renderCanvas.offsetHeight; 
  camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000); 
  // camera.position.set(292, 223, 185);
  camera.position.set(200, 250, 410); //根据渲染范围尺寸数量级设置相机位置
  // camera.lookAt(0, -23, 41);

  renderer = new THREE.WebGLRenderer();
  renderer.setSize(width, height);
  renderCanvas.appendChild(renderer.domElement);

  // 设置相机控件轨道控制器OrbitControls
  controls = new OrbitControls(camera, renderer.domElement);
  controls.target.set(0, -23, 41);

  // 加载模型
  const loader = new GLTFLoader();

  loader
    .setDRACOLoader(new DRACOLoader().setDecoderPath('/js/draco/')) 
    .setKTX2Loader(new KTX2Loader().setTranscoderPath('/js/basis/').detectSupport(renderer as THREE.WebGLRenderer)); 

  loader.load('./三维图.glb', (gltf: any) => { 
    scene.add(gltf.scene);
  });

  // 加入文字标签
  const textLabel = tag3D('1组');
  css3dRenderer = labelRenderer(renderCanvas);

  scene.add(textLabel);

  render();
}

// 渲染循环
function render() {
  renderer.render(scene, camera); //执行渲染操作
  css3dRenderer.render(scene, camera);
  controls.update();
  // console.log('camera.position: ', camera.position); 
  // console.log('controls.target: ', controls.target); 
  // mesh.rotateY(0.01); //每次绕y轴旋转0.01弧度
  requestAnimationFrame(render); //请求再次执行渲染函数render,渲染下一帧
}

// 创建一个HTML标签
function tag3D(name) {
  // 创建div元素(作为标签)
  let div = document.createElement('div');
  div.innerHTML = name;
  div.classList.add('tag');
  //div元素包装为CSS3模型对象CSS3DObject
  var label = new CSS3DObject(div);
  div.style.pointerEvents = 'none'; //避免HTML标签遮挡三维场景的鼠标事件
  // 设置HTML元素标签在three.js世界坐标中位置
  label.position.set(0, 0, 50);
  //缩放CSS3DObject模型对象
  label.scale.set(0.04, 0.04, 0.04); //根据相机渲染范围控制HTML 3D标签尺寸
  // label.rotateY(Math.PI / 2); //控制HTML标签CSS3对象姿态角度
  label.rotateX(-Math.PI / 2);
  return label; //返回CSS3模型标签
}

// 创建一个CSS2渲染器CSS2DRenderer
function labelRenderer(container) {
  var labelRenderer = new CSS3DRenderer();
  labelRenderer.setSize(container.offsetWidth, container.offsetHeight);
  labelRenderer.domElement.style.position = 'absolute';
  // 相对标签原位置位置偏移大小
  labelRenderer.domElement.style.top = '0px';
  labelRenderer.domElement.style.left = '0px';
  // //设置.pointerEvents=none,以免模型标签HTML元素遮挡鼠标选择场景模型
  labelRenderer.domElement.style.pointerEvents = 'none';
  container.appendChild(labelRenderer.domElement);
  return labelRenderer;
}
</script>
<template>
  <div id="renderCanvas" class="renderCanvas"></div>
</template>

注意点

一定要有renderer.render(scene, camera);否则屏幕上啥也看不见;加了CSS3DRenderer,也要有相应的css3dRenderer.render(scene, camera);

如果有OrbitControls,相机就不用设置lookAt,直接设置OrbitControls的target就行了

如果使用了CSS3DRenderer/CSS2DRenderer渲染了html元素,记得加上下面的语句,否则OrbitControls无法控制相机了

  // //设置.pointerEvents=none,以免模型标签HTML元素遮挡鼠标选择场景模型
  labelRenderer.domElement.style.pointerEvents = 'none';

GLTFLoader也不是上来就能load的,需要加上下面的东西才能load,这是因为我的模型是经过DRACO压缩的,纹理经过KTX2压缩。(参考

loader
    .setDRACOLoader(new DRACOLoader().setDecoderPath('/js/draco/'))   
    .setKTX2Loader(new KTX2Loader().setTranscoderPath('/js/basis/').detectSupport(renderer as THREE.WebGLRenderer));

后记

刚学three.js的时候,按照教程也没遇到这么多问题,好久不写,问题一堆,感觉基础摇摇欲坠,所以特地记录一下

参考

参考1
参考2

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值