绘制物体三要素
约 2219 字大约 7 分钟
2025-02-22
几何体
定义
几何体是物体在三维空间中的形状,它决定了物体的外观和结构。在 Three.js 中,几何体是通过THREE.Geometry
类来创建的。几何体包含了顶点、面和法线等属性,用于描述物体的形状和结构。
常见类型
- 规则几何体: 立方体、球体、圆柱体、圆锥体、圆环等。
- 复杂几何体: TubeGeometry(管道几何体)可用于创建管状结构,常用于模拟管道、绳索等;LatheGeometry(旋转几何体)通过绕轴旋转一条曲线来创建几何体,可用于制作花瓶、碗等具有旋转对称性质的物体。自定义的几何体,可以通过顶点坐标和面索引来定义。
const geometry = new THREE.BoxGeometry(1, 1, 1); // 创建一个立方体几何体
const geometry = new THREE.CapsuleGeometry(1, 1, 4, 8); // 创建一个胶囊体几何体
const geometry = new THREE.CircleGeometry(5, 32); // 创建一个圆形几何体
const geometry = new THREE.ConeGeometry(5, 20, 32); // 创建一个圆锥体几何体
const geometry = new THREE.CylinderGeometry(5, 5, 20, 32); // 创建一个圆柱体几何体
const geometry = new THREE.PlaneGeometry(1, 1); // 创建一个平面几何体
const geometry = new THREE.SphereGeometry(15, 32, 16); // 创建一个球体几何体
const geometry = new THREE.TorusGeometry(10, 3, 16, 100); // 创建一个圆环几何体
具体的几何体类型和参数可以参考 Three.js 的官方文档。
知识点: 创建几何体其中有个参数叫分段数:几何体由多少个分段组成,分段数越多,几何体越精细,但计算量也越大。所有的图形它的基础图形都是三角形,分段数越多,三角形数量越多,图形越精细。例如球形体,分段数越多,球体越接近于一个完美的球体。
以上几何体的构造函数想使用哪个去官网找一下就行了。
BufferGeometry
这里要介绍一下 BufferGeometry,它是 Three.js 中一种更高效的几何体类型,它使用缓冲区来存储顶点数据,而不是使用数组。BufferGeometry 可以减少内存占用和提高渲染性能,因此在创建复杂几何体时,推荐使用 BufferGeometry。
const geometry = new THREE.BufferGeometry();
//创建顶点
const vertices = new Float32Array([
-1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0,
]);
//创建颜色
const colors = new Float32Array([
1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0,
]);
// itemSize = 3 因为每个顶点都是一个三元组。
// 将缓冲区数据添加到几何体中
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
// 将颜色缓冲区数据添加到几何体中
geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
//wireframe:是否以线框模式绘制几何体
//vertexColors:是否使用顶点颜色
const material = new THREE.MeshBasicMaterial({ wireframe: true, vertexColors: true });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
Float32Array: 是 JavaScript 中的一种数据类型,用于存储 32 位浮点数。在 Three.js 中,顶点数据通常使用 Float32Array 来存储,因为浮点数可以精确地表示顶点的位置、法线、颜色等属性。
BufferAttribute: 这个类用于存储与 BufferGeometry 相关联的 attribute(例如顶点位置向量,面片索引,法向量,颜色值,UV 坐标以及任何自定义 attribute )。 利用 BufferAttribute,可以更高效的向 GPU 传递数据。
材质
定义
材质是物体在三维空间中的外观,它决定了物体的颜色、纹理和光照效果。在 Three.js 中,材质是通过THREE.Material
类来创建的。材质包含了颜色、纹理、光照效果等属性,用于描述物体的外观。
常见类型
- 基础网格材质(MeshBasicMaterial): 用于创建简单的物体,不考虑光照效果。
- 物理网格材质(MeshPhysicalMaterial): 用于创建具有物理属性的物体,考虑光照效果。
- 标准网格材质(MeshStandardMaterial): 用于创建具有标准光照效果的物体。
- 点精灵材质(SpriteMaterial): 用于创建点精灵,即一个简单的二维图像,可以用于表示物体的位置。
- ...
有些材料不受光照影响,有些材料受光照影响。所以,在创建物体时,需要根据需要选择适合的材质类型。
光照效果
- 漫反射: 光线会向各个方向反射,所以物体表面看起来是均匀的。颜色取决于入射光线的颜色和物体表面的颜色。
- 镜面反射: 光线会向特定方向反射,所以物体表面看起来会有高光效果。颜色取决于入射光线的颜色和物体表面的颜色。
- 环境光: 是一种全局光照,它从各个方向均匀地照射到物体表面。颜色取决于环境光线的颜色和物体表面的颜色。
- 自发光: 是一种特殊的反射,它使物体表面看起来像是在发光。颜色取决于物体表面的颜色。
- 高光: 是一种特殊的反射,它使物体表面看起来有光泽。颜色取决于物体表面的颜色和高光强度。
- 哑光: 是一种特殊的反射,它使物体表面看起来有粗糙感。颜色取决于物体表面的颜色和哑光强度。
- ...
基础网格材质
// 创建一个基础网格材质,颜色为绿色,不受光照影响
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
map 颜色贴图: 用于定义物体的表面颜色,可以通过加载纹理图像来实现。
在上一章节的 3d 看房示例中就使用了 map 颜色贴图,通过加载 6 张纹理图,然后分别赋值给 6 个面,从而实现一个立方体的纹理贴图。
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load("path/to/texture.jpg");
const material = new THREE.MeshBasicMaterial({ map: texture });
标准网格材质
// 创建一个标准网格材质,颜色为绿色,考虑光照效果
var material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
roughness: 粗糙度,取值范围为 0 到 1,数值越大,表面越粗糙。
metalness: 金属度,取值范围为 0 到 1,数值越大,表面越像金属。
emissive: 自发光,材质的放射(光)颜色,基本上是不受其他光照影响的固有颜色。默认为黑色。
emissiveIntensity: 自发光强度,默认值为 1。
envMap: 环境贴图,用于模拟环境光效果。
标准网格材质由于受光照影响,所以需要添加光源,如果不添加光源,那么物体将不会显示出来,但是可以通过设置自发光(emissive)来让物体显示出来。
const material = new THREE.MeshStandardMaterial({
color: 0x00ff00, // 颜色
roughness: 0.5, // 粗糙度
metalness: 0.5, // 金属度
emissive: 0x00ff00, // 自发光
emissiveIntensity: 1, // 自发光强度
envMap: textureLoader.load("path/to/envMap.jpg"), // 环境贴图
});
网格
定义
网格是 Three.js 中的一种基本对象,它由几何体和材质组成。几何体定义了物体的形状,材质定义了物体的外观。网格对象可以用于创建各种三维物体,例如立方体、球体、圆柱体等。
创建与使用
通过将几何体和材质作为参数传递给 THREE.Mesh 构造函数来创建网格对象,然后可以对网格进行位置、旋转、缩放等变换操作,还可以为其添加事件监听器,实现与用户的交互。例如:
// 创建一个立方体几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建一个基础网格材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// 创建一个网格对象
const cube = new THREE.Mesh(geometry, material);
// 将网格对象添加到场景中
scene.add(cube);
示例
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 10;
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 添加环境光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
// 添加点光源
const pointLight = new THREE.PointLight(0xffffff, 0.8);
pointLight.position.set(5, 5, 5);
scene.add(pointLight);
// 定义球体数量
const numSpheres = 10;
const spheres = [];
// 创建多个球体
for (let i = 0; i < numSpheres; i++) {
// 创建球体几何体
const radius = Math.random() + 0.2; // 随机半径
const geometry = new THREE.SphereGeometry(radius, 32, 32);
// 创建随机颜色的材质
const color = new THREE.Color(Math.random(), Math.random(), Math.random());
const material = new THREE.MeshPhongMaterial({ color: color });
// 创建网格对象
const sphere = new THREE.Mesh(geometry, material);
// 设置随机位置
sphere.position.x = (Math.random() - 0.5) * 10;
sphere.position.y = (Math.random() - 0.5) * 10;
sphere.position.z = (Math.random() - 0.5) * 10;
// 添加到场景和数组中
scene.add(sphere);
spheres.push(sphere);
}
//创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 启用阻尼效果
controls.enableDamping = true;
function animate() {
requestAnimationFrame(animate);
// 让球体随机移动
spheres.forEach((sphere) => {
sphere.position.x += (Math.random() - 0.5) * 0.01;
sphere.position.y += (Math.random() - 0.5) * 0.01;
sphere.position.z += (Math.random() - 0.5) * 0.01;
});
renderer.render(scene, camera);
}
animate();
//监听窗口大小变化
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});