【基础】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格式文件