当前位置: 首页 > article >正文

【基础】Three.js 自定义几何体和复制几何体

通过自定义顶点数据,可以创建任意的几何体。像threejs的长方体BoxGeometry、球体SphereGeometry等几何体都是基于BufferGeometry类构建的,它表示一个没有任何形状的空几何体。
请添加图片描述

1. 自定义点模型

通过javascript 类型化数组 Float32Array创建一组xyz坐标数据用来表示几何体的顶点坐标。每3个一组为一个坐标点。
在这里插入图片描述

point.ts文件:

import * as THREE from "three";

//创建一个空的几何体对象
const geometry = new THREE.BufferGeometry();

//类型化数组创建顶点数据
const vertices = new Float32Array([
  0,
  0,
  0, //顶点1坐标
  20,
  0,
  0, //顶点2坐标
  20,
  20,
  0, //顶点3坐标
  0,
  20,
  0, //顶点4坐标
  10,
  30,
  30, //顶点5坐标
  10,
  40,
  15, //顶点6坐标
]);

// 创建属性缓冲区对象: 3个为一组,表示一个顶点的xyz坐标
const attribute = new THREE.BufferAttribute(vertices, 3);
geometry.attributes.position = attribute; // 设置几何体attributes属性的位置属性

// 点材质
const material = new THREE.PointsMaterial({
  color: 0xffff00,
  size: 2.0, //点对象像素尺寸
});
const points = new THREE.Points(geometry, material); // 点对象
export {points}

主文件:

<template>
  <div class="wrapper">
    <div ref="threeRef"></div>
  </div>
</template>
<script setup lang="ts">
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { points} from "./components/points";
import { onMounted, ref } from "vue";
const threeRef = ref();

onMounted(() => {
  // 场景
  const scene = new THREE.Scene();
  // 添加点进入场景
  scene.add(points);

  // 相机
  const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  );
  camera.position.set(50, 50, 50); // 相机位置
  
  // 渲染器
  const renderer = new THREE.WebGLRenderer({
    antialias: true, // 设置锯齿属性,为了获得更好的图像质量
  });
  // 定义threejs输出画布的尺寸(单位:像素px)
  renderer.setSize(window.innerWidth, window.innerHeight);
  // 为了适应不同的硬件设备屏幕,设置设备像素比
  renderer.setPixelRatio(window.devicePixelRatio);
  // 插入到任意HTML元素中
  threeRef.value.append(renderer.domElement);
  renderer.render(scene, camera);

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

  // 可拖拽查看模型
  const controls = new OrbitControls(camera, renderer.domElement);
  controls.addEventListener("change", function () {
    renderer.render(scene, camera); //执行渲染操作
  });

  window.onresize = function () {
    // 更新相机纵横比
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    // 更新渲染器的大小
    renderer.setSize(window.innerWidth, window.innerHeight);
  };
});
</script>
<style scoped></style>

2. 自定义线模型

new THREE.Line( geometry, material ); 创建一条连续的线
在这里插入图片描述

new THREE.LineLoop( geometry, material ); 创建环线
在这里插入图片描述

new THREE.LineSegments( geometry, material ); 创建线段
在这里插入图片描述
(为了观察由哪些点生成的线,保留了点模型)

//创建一个空的几何体对象
const geometry = new THREE.BufferGeometry();

//类型化数组创建顶点数据
const vertices = new Float32Array([
  0,0,0, //顶点1坐标
  20,0,0, //顶点2坐标
  20,20,0, //顶点3坐标
  0,20,0, //顶点4坐标
  10,30,30, //顶点5坐标
  10,40,15, //顶点6坐标
]);

// 创建属性缓冲区对象: 3个为一组,表示一个顶点的xyz坐标
const attribute = new THREE.BufferAttribute(vertices, 3);
geometry.attributes.position = attribute; // 设置几何体attributes属性的位置属性

// 点材质
const material = new THREE.PointsMaterial({
  color: 0xffff00,
  size: 2.0, //点对象像素尺寸
});
// 线材质
const material1 = new THREE.LineBasicMaterial({
  color: 0xff0000, //线条颜色
});
const points = new THREE.Points(geometry, material); // 点对象
const lines = new THREE.Line(geometry, material1); // 线对象
export {points,lines } // 在主文件中引入,并添加到场景中即可

4. 网格模型(三角形概念)

网格模型Mesh其实就一个一个三角形(面)拼接构成。使用网格模型Mesh渲染几何体geometry,就是几何体所有顶点坐标三个为一组,构成一个三角形,多组顶点构成多个三角形,就可以用来模拟表示物体的表面。

可见性:
三个顶点的顺序是【逆时针】方向,该面视为【正面】
三个顶点的顺序是【顺时针】方向,该面视为【反面】,默认不可见
在这里插入图片描述

import * as THREE from "three";

//创建一个空的几何体对象
const geometry = new THREE.BufferGeometry();
//顶点数据
const vertices = new Float32Array([
  0, 0, 0, //顶点1坐标
  50, 0, 0, //顶点2坐标
  50,50, 0, //顶点3坐标
]);
// 创建属性缓冲区对象: 3个为一组,表示一个顶点的xyz坐标
const attribute = new THREE.BufferAttribute(vertices, 3);
geometry.attributes.position = attribute; // 设置几何体attributes属性的位置属性

// 网格模型:
const material = new THREE.MeshBasicMaterial({
  color: 0xffff00, //材质颜色
  //   side: THREE.BackSide,// 仅有反面可见
  side: THREE.DoubleSide, //两面可见
  transparent: true, // 开启透明
  opacity: 0.5, //透明度
});
const flat = new THREE.Mesh(geometry, material); // 网格对象
export { flat };

4. 自定义矩形平面

一个矩形平面,可以至少通过两个三角形拼接而成,而且两个三角形是紧密贴合的,所以会存在两个相同的坐标点。
注意:为了让两个三角形的正反面显示一致,每一个三角形顶点的的顺序应该保持一致!!
在这里插入图片描述
在这里插入图片描述

import * as THREE from "three";

//创建一个空的几何体对象
const geometry = new THREE.BufferGeometry();
//顶点数据
const vertices = new Float32Array([
    0, 0, 0, //顶点1坐标
    80, 0, 0, //顶点2坐标
    80, 80, 0, //顶点3坐标

    0, 0, 0, //顶点4坐标   和顶点1位置相同
    80, 80, 0, //顶点5坐标  和顶点3位置相同
    0, 80, 0, //顶点6坐标
]);

// 创建属性缓冲区对象: 3个为一组,表示一个顶点的xyz坐标
const attribute = new THREE.BufferAttribute(vertices, 3);
geometry.attributes.position = attribute; // 设置几何体attributes属性的位置属性

// 网格模型:
const material = new THREE.MeshBasicMaterial({
  color: 0xffff00, //材质颜色
  //   side: THREE.BackSide,// 仅有反面可见
  side: THREE.DoubleSide, //两面可见
  transparent: true, // 开启透明
  opacity: 0.5, //透明度
});
const flat = new THREE.Mesh(geometry, material); // 网格对象
export { flat };

也可以使用几何体顶点索引数据geometry.index来实现
在这里插入图片描述

import * as THREE from "three";

//创建一个空的几何体对象
const geometry = new THREE.BufferGeometry();
//顶点数据
const vertices = new Float32Array([
  0, 0, 0, //顶点1坐标
  80, 0, 0, //顶点2坐标
  80, 80, 0, //顶点3坐标
  0, 80, 0, //顶点6坐标
]);

// Uint16Array类型数组创建顶点索引数据
const indexes = new Uint16Array([
  // 下面索引值对应顶点位置数据中的顶点坐标
  0, 1, 2, 0, 2, 3,
]);
// 索引数据赋值给几何体的index属性
geometry.index = new THREE.BufferAttribute(indexes, 1); //1个为一组

// 创建属性缓冲区对象: 3个为一组,表示一个顶点的xyz坐标
const attribute = new THREE.BufferAttribute(vertices, 3);
geometry.attributes.position = attribute; // 设置几何体attributes属性的位置属性

// 网格模型
const material = new THREE.MeshBasicMaterial({
  color: 0xffff00, //材质颜色
  //   side: THREE.BackSide,// 仅有反面可见
  side: THREE.DoubleSide, //两面可见
  transparent: true, // 开启透明
  opacity: 0.5, //透明度
});
const flat = new THREE.Mesh(geometry, material); // 网格对象
export { flat };

如果将材质更换成其他受光照影响的材质,比如MeshLambertMaterial,则无法正常显示平面,因为受光照影响的材质,需要给几何体设置顶点法线

// 每个顶点的法线数据和顶点位置数据一一对应
const normals = new Float32Array([
  0,
  0,
  1, //顶点1法线( 法向量 )
  0,
  0,
  1, //顶点2法线
  0,
  0,
  1, //顶点3法线
  0,
  0,
  1, //顶点4法线
]);
// 设置几何体的顶点法线属性.attributes.normal
geometry.attributes.normal = new THREE.BufferAttribute(normals, 3);
// 网格模型:
const material2 = new THREE.MeshLambertMaterial({
  color: 0xffff00, //材质颜色
  //   side: THREE.BackSide,// 仅有反面可见
  side: THREE.DoubleSide, //两面可见
  transparent: true, // 开启透明
  opacity: 0.5, //透明度
});

在场景中添加光源:

  //环境光
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
  scene.add(ambientLight);
  // 平行光
  const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
  // 设置光源的方向:通过光源position属性和目标指向对象的position属性计算
  directionalLight.position.set(10, 15, 25);
  // 方向光指向对象网格模型
  directionalLight.target = flat;

5. 内置原始几何体

请添加图片描述

import * as THREE from "three";
// 立方体
const geometry1 = new THREE.BoxGeometry(8, 8, 8);
// 球体
const geometry2 = new THREE.SphereGeometry(10);

const material1 = new THREE.MeshLambertMaterial({
  color: 0x00ffff,
  wireframe: true, //线条模式渲染mesh对应的三角形数据
});
const material2 = new THREE.MeshLambertMaterial({
  color: 0xffffff,
  wireframe: true, //线条模式渲染mesh对应的三角形数据
});
const box = new THREE.Mesh(geometry1, material1);
const sphere = new THREE.Mesh(geometry2, material2);
// 位置平移
sphere.translateX(-15);
box.translateX(-15);

// 克隆一个box对象
const box2 = box.clone();
// 克隆几何体材质,并重新设置的材质和几何体属性
box2.material = box.material.clone();
box2.material.color.set(0xff0000);
box2.translateY(15);
// 缩小尺寸
box2.scale.set(0.5, 0.5, 0.5);
export {  sphere, box, box2 };

https://threejs.org/docs/index.html?q=geo#api/zh/geometries/BoxGeometry

添加循环动画:

  // 渲染循环
  function render() {
    box.rotateY(0.01); // 旋转动画
    sphere.rotateY(-0.01);
    // 同步mesh2和mesh的姿态角度一样,不管mesh姿态角度怎么变化,mesh2始终保持同步
    box2.rotation.copy(box.rotation);
    renderer.render(scene, camera);
    requestAnimationFrame(render);
  }
  render();

🔍【基础】Three.js的零基础入门篇(附案例代码)
🔍【基础】Three.js中添加操作面板,GUI可视化调试(附案例代码)
🔍【基础】Three.js加载纹理贴图、加载外部gltf格式文件


http://www.kler.cn/news/293514.html

相关文章:

  • 研1日记5
  • IP学习——twoday
  • 43. 1 ~ n 整数中 1 出现的次数【难】
  • 路由器的固定ip地址是啥意思?固定ip地址有什么好处
  • 算法练习小技巧之有序集合--套路详细解析带例题(leetcode)
  • 使用 Nginx 部署前端 Vue.js 项目
  • 吐血整理 ChatGPT 3.5/4.0 新手使用手册~ 【2024.09.04 更新】
  • 数据时域循环移位,频域会怎么样
  • 混合模式属性background-blend-mode
  • 【基础算法总结】双指针
  • 【Hadoop|HDFS篇】DataNode
  • 梯度弥散问题及解决方法
  • C++ Dll 库 的创建与使用方法记录
  • 打造安心宠物乐园:EasyCVR平台赋能猫咖/宠物店的智能视频监控解决方案
  • Linux——进程概念
  • 数据结构(邓俊辉)学习笔记】排序 2——快速排序:性能分析
  • springboot项目引入Sentinel熔断
  • SpringBoot和Mybatis框架怎么防止SQL注入
  • 轴承知识大全,详细介绍(附3D图纸免费下载)
  • Codeforces Round 970 (Div. 3)(ABCDEF)
  • LLVM IR指令VM混淆分析
  • 【LeetCode面试150】——205同构字符串
  • 解决解压缩时的错误提示 “无法成功完成操作, 因为文件包含病毒或者潜在垃圾文件“
  • 论文解读 | KDD2024 演化图上的森林矩阵快速计算
  • 【OpenCV1】虚拟环境的使用、opencv的使用、图像和视频的创建和显示
  • 政府招商引资管理数字化平台:渠道、意向客户、项目管理、招商载体、绩效一体化管理平台
  • Spring MVC思想 实践开发 核心组件 流程分析
  • 【go-zero】win启动rpc服务报错 panic: context deadline exceeded
  • 设计模式学习-命令模式
  • HTTP 方法