在上一篇文章中,我们对Three.js进行了初步的了解,发现尽管它最终渲染在二维的画布上,但通过计算和渲染技术,它能够展现出三维世界的视觉效果。本文将深入探讨Three.js的核心组成部分:场景、物体、相机和控制器。我们将通过具体的代码示例来逐一解析这些组件的作用和如何使用它们来构建一个基本的3D应用。
场景(Scenc)
在Three.js中,场景是所有物体和光源的容器,你可以把它想象成一个舞台,所有的3D对象都在这个舞台上展示。创建一个场景非常简单:
const scene = new THREE.Scene();
这行代码就创建了一个新的场景。在这个场景中,你可以添加各种物体,比如几何体、灯光、相机等。
辅助线
为了更好地理解和调试场景中物体的位置,添加辅助辅助线。这个有三个轴,红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴。传入辅助线得长度
const axesHelper = new THREE.AxesHelper( 5 );
scene.add( axesHelper )
物体(Objects)
Three.js提供了多种类型的物体,如立方体、球体、平面等,这些都是通过几何体(Geometry)和材质(Material)来定义的。几何体定义了物体的形状,而材质定义了表面的外观,如颜色、纹理等。
创建一个立方体
创建一个立方体需要一个几何体和一个材质。代码如下:
基础网格物体
// 创建一个立方体几何体,指定宽、高和深度
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建一个材质,这里使用简单的MeshBasicMaterial,并设置颜色为蓝色
const material = new THREE.MeshBasicMaterial({ color: 0x0000ff });
// 将几何体和材质组合成一个网格(Mesh)物体
const cube = new THREE.Mesh(geometry, material);
// 将立方体添加到场景中
scene.add(cube);
基础线物体
当然线物体还包含线段 环线 就不挨个写了
// 创建一个立方体几何体,指定宽、高和深度
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建一个材质,这里使用简单的MeshBasicMaterial,并设置颜色为蓝色
const material = new THREE.MeshBasicMaterial({ color: 0x0000ff });
// 将几何体和材质组合成一个线(Line)物体
const cube = new THREE.Line(geometry, material);
// 将立方体添加到场景中
scene.add(cube);
基础点物体
// 创建一个立方体几何体,指定宽、高和深度
const geometry = new THREE.SphereGeometry(3, 32, 16);
// 创建一个材质,这里使用简单的PointsMaterial,并设置颜色为蓝色
const material = new THREE.PointsMaterial({ color: 0x0000ff,size:0.1 });
// 将几何体和材质组合成一个点(Points)物体
const cube = new THREE.Points(geometry, material);
// 将立方体添加到场景中
scene.add(cube);
在场景中添加了立方体后,你可以通过修改其位置、旋转和缩放等属性来控制它的显示。
相机(Camera)
在Three.js中,相机决定了场景中哪些部分将被渲染到画布上。最常用的相机是透视相机(Perspective Camera),它模仿人眼的视角,使得远处的物体看起来比近处的小。
设置透视相机
//创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
//设置相机位置,起始是0.0.0
camera.position.z = 5;
在这段代码中,创建了一个透视相机,并设置了视野角度、宽高比以及可视距离的最近和最远范围。将相机的位置向后移动,以便能看到前面的立方体。
材质 (Materials)
在Three.js中,材质决定了物体表面的视觉属性,例如其颜色、是否光滑、是否反光等。Three.js提供了多种材质,常见的有:
MeshBasicMaterial:不受光照影响的简单材质,只显示对象的纯色或纹理。MeshLambertMaterial:一种考虑光照的材质,用于创建非光滑表面的物体,适合大部分非金属表面。MeshPhongMaterial:提供光泽表面的材质,可以创建更逼真的金属和塑料等效果。MeshStandardMaterial:基于物理的材质,能够创建更复杂和逼真的视觉效果,支持更广泛的光照和反射特性。
每种材质都有多个属性可以配置,如颜色(color)、透明度(opacity)和反光性(reflectivity)等。
new THREE.MeshPhongMaterial({
color: 0x555555, // 灰色
specular: 0xffffff, // 设置高光颜色
shininess: 30 // 控制高光的亮度
});
添加灯光和灯光辅助
//环境光,不会进行反射光
const light = new THREE.AmbientLight( 0x404040 ); // 柔和的白光
scene.add( light );
//平行光
const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
directionalLight.position.set( 10, 10, 10 );
scene.add( directionalLight );
//平行光赋值对象
const helper = new THREE.DirectionalLightHelper( directionalLight, 5 );
scene.add( helper );
贴图(Textures)
贴图是指在物体表面覆盖的图像,用于增加材质的细节和现实感。Three.js使用TextureLoader类来加载贴图。
贴图的加载和应用
首先,需要创建一个贴图加载器,然后使用它加载贴图文件,最后将这个贴图赋给材质的map属性。
const loader = new THREE.TextureLoader(); // 加载一个贴图
const texture = loader.load('textures/wood.jpg', (texture)=> {
// 贴图加载完成后的回调函数
texture.wrapS = texture.wrapT = THREE.RepeatWrapping; //设置UV
texture.repeat.set(2, 2); // 设置重复次数
});
// 应用贴图到材质
const material = new THREE.MeshPhongMaterial({ map: texture });
这是普通贴图,就是把我们得一张静态图片贴到物体上,我们加载了一个木头纹理,并将其设置为重复显示。
高级材质和贴图技术
- 法线贴图(Normal Maps) :法线贴图用于模拟物体表面的小凹凸,增强视觉上的细节和深度,而不需要增加更多的几何细节。
- 置换贴图(Displacement Maps) :置换贴图通过改变物体表面的几何形状来增加细节,这种方式比法线贴图更真实,但也更消耗资源。
- 环境贴图(Environment Maps) :环境贴图用于创建反射和折射效果,使物体能够反射或透视其周围环境的图像。
- 光照贴图(Light Maps) :光照贴图是预计算的贴图,用于增加场景中的静态光照效果,如阴影和光照强度
- 金属贴图(Light Maps) :金属贴图用于定义物体表面哪些部分是金属的,哪些部分是非金属的,通常与粗糙度贴图配合使用以达到更真实的效果。金属部分在金属贴图中显示为白色,非金属部分显示为黑色。
- 凹凸贴图(Bump Maps):凹凸贴图是用来模拟物体表面凹凸不平的细节,它可以增加物体表面的视觉深度感而不需要增加额外的几何体。凹凸贴图通过影响光照的散射来实现这一效果
- 粗糙度贴图(Roughness Maps) :粗糙度贴图决定了物体表面的粗糙程度,影响材质反光的强度和范围。粗糙表面散射更多光线,反射较弱;平滑表面则反射更多光线,形成明显的高光
const normalMap = loader.load('textures/normal_map.jpg');
const material = new THREE.MeshStandardMaterial({ color: 0x885522, normalMap: normalMap });
通过高级贴图技术,可以大幅提升你的Three.js项目中物体的真实感和视觉效果
BufferGeometry
在 Three.js 中,BufferGeometry 是一种更高效的几何体表示方式,它直接在 GPU 上处理顶点数据,这使得渲染过程更快。BufferAttribute 用于存储这些数据,如顶点位置、法线、颜色、UV坐标等。可以做到自定义图形,颜色等
要创建一个 BufferAttribute,你需要一个类型化数组(如 Float32Array),该数组包含你想要传递给 GPU 的数据
// 假设我们创建一个包含两个三角形的简单正方形平面
// 顶点位置数组
const vertices = new Float32Array([
-1.0, -1.0, 0.0, // 第一个三角形的顶点
1.0, -1.0, 0.0,
-1.0, 1.0, 0.0,
1.0, -1.0, 0.0, // 第二个三角形的顶点
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0
]);
// 创建 BufferAttribute
const positionAttribute = new THREE.BufferAttribute(vertices, 3); // 每个顶点由3个值组成
// 创建一个 BufferGeometry 并添加该属性
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', positionAttribute);
这里定义了一个顶点数组,其中包含了组成两个三角形的顶点坐标。每个顶点由三个数值表示(X, Y, Z)。然后,我们使用这个数组创建了一个 BufferAttribute 对象,并指定每个顶点由三个值组成。最后,我们将这个属性添加到 BufferGeometry 对象中
BufferAttribute 还支持动态更新数据,这对于实现动态效果(如粒子系统或动态形变)非常有用。你可以通过修改类型化数组中的数据然后更新属性来实现这一点
// 假设某个时间点需要更新顶点位置
vertices[0] = newX;
vertices[1] = newY;
vertices[2] = newZ;
// 更新 GPU 上的数据
positionAttribute.needsUpdate = true;
BufferAttribute星空
// 设置场景
var scene = new THREE.Scene();
// 设置相机
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 创建渲染器
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建星星的材质
var starMaterial = new THREE.PointsMaterial({
color: 0xffffff,
size: 0.1
});
// 创建星星的几何体
var starGeometry = new THREE.BufferGeometry();
var starCount = 5000; // 星星数量
var positions = new Float32Array(starCount * 3); // 每个星星需要3个值(x, y, z)
for (let i = 0; i < starCount * 3; i++) {
positions[i] = (Math.random() - 0.5) * 100; // 创建一个范围在-50到50的随机位置
}
// 创建 BufferAttribute 并附加到星星的几何体
starGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
// 使用几何体和材质创建星星
var stars = new THREE.Points(starGeometry, starMaterial);
scene.add(stars);
// 添加动画
function animate() {
requestAnimationFrame(animate);
// 旋转星星
stars.rotation.x += 0.0005;
stars.rotation.y += 0.001;
renderer.render(scene, camera);
}
animate();
控制器(Controls)
为了更好地观察场景中的对象,Three.js提供了多种控制器,允许用户通过鼠标和键盘来交互。OrbitControls是其中一种常用的控制器,允许用户围绕目标旋转、缩放和平移视角
//这个需要单独进行引入
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
结合所有元素
要使这一切运行,我们还需要一个渲染器来将场景渲染到浏览器的画布上。下面是将场景、相机和控制器结合起来的完整代码:
import './style.css'
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const scene = new THREE.Scene();
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x0000ff });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
function animate() {
requestAnimationFrame(animate);
// 在这里可以添加物体的动画效果或更新控制器等
controls.update()
renderer.render(scene, camera);
}
animate();
这些就是Three.js得一些基本东西,也是核心得要素。