three.js基础

记录下three.js的学习历程。

Three.js是基于 WebGL 技术,用于浏览器中开发 3D 交互场景的 JS 引擎。
Three.js擅长 WebGL 场景渲染,Three.js不擅长物理碰撞,因此不适合开发 3D 游戏
three.js基本概念包括画布Canvas、渲染器Renderer、场景Scene、相机Camera、光源Light、几何体Geometry、材质Material、颜色纹理、点模型、线模型、网格模型、阴影、外部三维模型、动画等 

基础

<canvas id="basic"></canvas>
<button type="button" name="button" onclick="saveCanvasImage()">下载</button>

<script type="importmap">
  {
    "imports": {
      "three": "./js/build/three.module.js",
      "three/addons/": "./js/jsm/"
    }
  }
</script>
<script type="module">
  import * as THREE from "three";
  // Three.js是基于 WebGL 技术,用于浏览器中开发 3D 交互场景的 JS 引擎。
  // Three.js擅长 WebGL 场景渲染,Three.js不擅长物理碰撞,因此不适合开发 3D 游戏

  // three.js基本概念包括画布Canvas、渲染器Renderer、场景Scene、相机Camera、光源Light、几何体Geometry、材质Material、
  // 颜色纹理、点模型物体、线模型物体、网格模型物体、阴影、外部三维模型、动画等

  console.log(THREE.REVISION); //157

  //渲染器
  const domId = document.getElementById("basic");
  const width = 500;
  const height = 400;
  const renderer = new THREE.WebGLRenderer({
    canvas: domId,
    antialias: true, //抗锯齿,平滑
    preserveDrawingBuffer: true, //渲染器渲染的内容保存到本地
  });
  renderer.setSize(width, height); //设置渲染区域的尺寸(像素px)
  renderer.setClearColor(0x000000, 1); //设置背景颜色,参数2为透明度值
  renderer.setClearAlpha(0.8); //改变背景透明度值
  renderer.setPixelRatio(window.devicePixelRatio); //设置设备像素比,以免渲染模糊问题

  //场景
  const scene = new THREE.Scene();
  // 背景颜色
  // scene.background = new THREE.Color("#ffffff");
  // 指数雾,参数:雾的颜色、雾的密度
  // scene.fog = new THREE.FogExp2(0xffffff, 0.015);
  // 线性雾,参数:雾的颜色、开始应用雾的最小距离、应用雾的最大距离
  // scene.fog = new THREE.Fog(0xffffff, 10, 100);
  // 强制覆写材质
  // scene.overrideMaterial = new THREE.MeshLambertMaterial({
  //   color: 0xffffff,
  // });

  // 透视相机PerspectiveCamera 使用perspective projection(透视投影)来进行投影。
  // 这一投影模式被用来模拟人眼所看到的景象,它是3D场景的渲染中使用得最普遍的投影模式。
  // PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
  // fov — 摄像机视锥体垂直视野角度
  // aspect — 摄像机视锥体长宽比
  // near — 摄像机视锥体近端面
  // far — 摄像机视锥体远端面
  const camera = new THREE.PerspectiveCamera(30, width / height, 0.1, 1000); //透视投影相机参数设置
  camera.position.set(50, 50, 50); //设置相机位置
  camera.lookAt(0, 0, 0);
  scene.add(camera);

  // 坐标轴辅助 用于简单模拟3个坐标轴的对象.红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
  // AxesHelper( size : Number ) size -- (可选的) 表示代表轴的线段长度。默认为 1。
  const axesHelper = new THREE.AxesHelper(150);
  scene.add(axesHelper);

  // 立方缓冲几何体BoxGeometry
  // BoxGeometry(width : Float, height : Float, depth : Float, widthSegments : Integer, heightSegments : Integer, depthSegments : Integer)
  // width — X 轴上面的宽度,默认值为 1。
  // height — Y 轴上面的高度,默认值为 1。
  // depth — Z 轴上面的深度,默认值为 1。
  // widthSegments — (可选)宽度的分段数,默认值是 1。
  // heightSegments — (可选)高度的分段数,默认值是 1。
  // depthSegments — (可选)深度的分段数,默认值是 1。
  const geometry = new THREE.BoxGeometry(3, 2, 2);
  console.log(geometry.name); //'' 几何体名字
  console.log(geometry.type); //BoxGeometry 几何体类型
  console.log(geometry.parameters); //几何体定义参数
  console.log(geometry.userData); //{} 自定义数据
  console.log(geometry.getAttribute("position")); // BufferAttribute
  console.log(geometry.getAttribute("normal")); // BufferAttribute
  console.log(geometry.getAttribute("uv")); // BufferAttribute 顶点uv坐标,二维顶点坐标,作用:纹理贴图上提取像素映射到网格模型Mesh的几何体表面上。
  console.log(geometry.computeBoundingBox()); // undefined
  console.log(geometry.computeBoundingSphere()); // undefined

  // 点材质
  const pointMaterial = new THREE.PointsMaterial({
    color: 0xff0000,
    size: 1, //点对象像素尺寸
  });
  //点模型物体
  const points = new THREE.Points(geometry, pointMaterial);
  points.position.set(-5, 0, -5);
  scene.add(points);
  console.log(points.name, points.type, points.userData); //'' Points {}
  console.log(points.isObject3D, points.isPoints, points.visible); //true true true
  console.log(points.castShadow, points.receiveShadow); //false false 是否投射阴影 是否接收阴影
  console.log(points.matrix, points.matrixWorld); //Matrix4 Matrix4 物体的局部变换矩阵 物体的世界变换矩阵
  console.log(points.position); //Vector3 {x: -5, y: 1, z: 0} 表示对象局部位置的Vector3
  console.log(points.rotation); //Euler 物体的局部旋转,以弧度来表示
  console.log(points.scale); //Vector3 {x: 1, y: 1, z: 1} 物体的局部缩放
  console.log(points.up); //Vector3 {x: 0, y: 1, z: 0} 这个属性由lookAt方法所使用,例如来决定结果的朝向。
  console.log(points.animations); //[] 三维物体所属的动画剪辑数组
  console.log(points.children); //[] 含有对象的子级的数组

  // 线材质
  const lineMaterial = new THREE.LineBasicMaterial({
    color: 0x00ff00, //线条颜色
  });
  // 创建线模型物体
  const line = new THREE.LineLoop(geometry, lineMaterial);
  line.position.set(-5, 0, 0);
  scene.add(line);
  console.log(line.isObject3D, line.isLine, line.isLineLoop, line.type); //true true true 'LineLoop'

  //网格材质:默认正面可见,反面不可见。
  const meshMaterial = new THREE.MeshBasicMaterial({
    color: 0x0000ff,
    // side: THREE.FrontSide, //默认只有正面可见
    side: THREE.DoubleSide, //两面可见
    // side: THREE.BackSide,  //设置只有反面可见
  });
  //网格模型:由多个三角形的面拼接构成。正反面区分:依相机观察方向的三个点顺序逆时针为正面,顺时针为反面
  const mesh = new THREE.Mesh(geometry, meshMaterial);
  mesh.position.set(-5, 0, 5);
  scene.add(mesh);
  console.log(mesh);
  console.log(mesh.isObject3D, mesh.isMesh, mesh.type); //true true 'Mesh'
  mesh.rotateX(Math.PI); // 物体绕着x轴旋转
  mesh.translateY(-1); //物体沿着y轴负方向平移
  mesh.scale.set(1.5, 1, 0.8); //物体缩放

  const meshMaterial2 = new THREE.MeshBasicMaterial({
    color: 0xffffff,
    side: THREE.DoubleSide,
    wireframe: true, //线条模式渲染
  });
  const geometry2 = new THREE.BoxGeometry(3, 3, 3);
  const mesh2 = new THREE.Mesh(geometry, meshMaterial2);
  mesh2.position.set(-5, 0, 10);
  scene.add(mesh2);
  geometry2.scale(2, 2, 2); // 几何体缩放
  geometry2.translate(3, 3, 0); // 几何体平移
  geometry2.rotateX(Math.PI); // 几何体绕着x轴旋转

  //物体克隆
  const mesh3 = mesh2.clone();
  //材质克隆
  mesh3.material = mesh2.material.clone();
  mesh3.material.color.set(0xffff00);
  mesh3.position.set(5, 0, -5);

  const mesh4 = mesh2.clone();
  mesh4.material = mesh2.material.clone();
  mesh4.material.color.set(new THREE.Color(0xff00ff));
  mesh4.position.set(5, 0, 0);
  const axis = new THREE.Vector3(1, 0, 0); //向量axis
  mesh4.translateOnAxis(axis, 2); //沿着axis轴表示方向平移
  mesh4.rotateOnAxis(axis, Math.PI / 2); //绕axis轴旋转
  mesh4.scale.set(1.5, 1, 0.8);

  //组
  const group = new THREE.Group();
  group.add(mesh3, mesh4);
  group.position.set(10, 0, 0);
  group.rotateY(Math.PI);
  // 递归遍历group包含所有的对象
  group.traverse(function (obj) {
    if (obj.isMesh) {
      // obj.material.color.set(0xffff00);
      obj.material.visible = true;
      obj.visible = true;
    }
  });
  console.log("group的子对象", group.children);
  scene.add(group);

  // 对象移除
  // group.remove(mesh3);
  // scene.remove(mesh);

  console.log("scene的子对象", scene.children);

  mesh4.name = "test";
  // 根据name查找对象节点
  const nameNode = scene.getObjectByName("test");
  nameNode.material.color.set(0xff0000);

  const worldPosition = new THREE.Vector3();
  nameNode.getWorldPosition(worldPosition);
  //世界坐标-子对象的坐标叠加所有父对象的坐标
  console.log("世界坐标", worldPosition);
  //本地坐标-子对象相对于父对象的坐标
  console.log("本地坐标", nameNode.position);
  //可视化mesh的局部坐标系
  const meshAxesHelper = new THREE.AxesHelper(50);
  nameNode.add(meshAxesHelper);

  const cbox = new THREE.Box3().setFromObject(nameNode);
  const size = cbox.getSize(new THREE.Vector3());
  console.log(size); //Vector3 {x: 77.25, y: 40.8, z: 51.000000000000014}

  const box3 = new THREE.Box3();
  box3.expandByObject(nameNode);
  console.log("查看包围盒", box3);
  const scale = new THREE.Vector3();
  box3.getSize(scale);
  console.log("模型包围盒尺寸", scale);
  const center = new THREE.Vector3();
  box3.getCenter(center);
  console.log("模型中心坐标", center);

  renderer.render(scene, camera);

  function onResize() {
    const width = 500; //若需全屏使用window.innerWidth
    const height = 500; //若需全屏使用window.innerHeight
    //画布宽高比变化后更新透视投影相机PerspectiveCamera的aspect属性
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
    // 重置渲染器输出画布canvas尺寸
    renderer.setSize(width, height);
    // 更新内部的渲染缓冲区
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.render(scene, camera);
  }
  window.addEventListener("resize", onResize, false);

  window.saveCanvasImage = () => {
    const link = document.createElement("a");
    link.href = renderer.domElement.toDataURL("image/png");
    link.download = "pic.png";
    link.click();
  };
</script>

 

posted @ 2024-04-05 14:35  carol2014  阅读(19)  评论(0编辑  收藏  举报