threejs(三)通过绘制简易星系场景介绍点线面体

254 阅读5分钟

本篇文章八青妹从绘制点、绘制线条、绘制平面、再到绘制立体图像,搭建一个简易的场景。从本文中,学习几何体和材质的使用。

注:本文的案例中,暂不介绍光照的影响。后面写完动画后,再来写光照。

1. 满天繁星(绘制点)

1.1 基础概念

threejs在场景中放置一个物体,需要先设置形状、材质,然后用一个实例包裹住,最后渲染到场景当中去。那即使是一个点,也是如此。这里使用的物体模式是BufferGeometry,点材质是PointsMaterial

  • BufferGeometry(): 这是 Three.js 中更加高效的几何体类型。它使用 TypedArray(如 Float32Array)来存储几何数据,并直接与 WebGL 的 Buffer 对象进行交互。相比于 GeometryBufferGeometry 在创建和修改时可能更加复杂,但它的性能优势非常明显,尤其是在处理大量顶点数据的场景中。

  • PointsMaterial():Three.js 中用于渲染点云效果的材质类型。PointsMaterial 的主要特点包括:

    1. 点的渲染方式: PointsMaterial 使用 WebGL 的 gl.POINTS 渲染模式,即将几何体的每个顶点渲染为一个点。这使得它能够快速高效地渲染大量的点云数据。
    2. 点的大小: PointsMaterial 可以通过 size 属性来设置点的大小,以像素为单位。这使得我们能够控制点的视觉效果。
    3. 点的颜色: PointsMaterial 可以通过 color 属性来设置点的颜色。颜色可以是一个单一的值,也可以是一个纹理贴图。
    4. 透明度: PointsMaterial 支持透明度设置,通过 transparent 和 opacity 属性来控制。这使得我们能够创造出一些特殊的视觉效果,如烟雾、粒子等。

星空背景的思路为:创建BufferGeometry,点的材质为大小0.1的白色,随机生成5000个。

const geometry = new THREE.BufferGeometry();
// 创建点材质
const material = new THREE.PointsMaterial({ color: 0xffffff,size: 0.1,transparent: true });
// 创建点对象
const points = new THREE.Points(geometry,material);
scene.add(points);

1.2 代码实现

在官方文档中,使用了数学函数(MathUtils.randFloatSpread)来生成随机数。参考文档THREE.MathUtils.randFloatSpread 定义坐标点和设置位置的地方可以改为下述代码,效果类似。

const vertices = [];
for(let i = 0; i < 10000; i++) {
    const x = THREE.MathUtils.randFloatSpread(2000);
    const y = THREE.MathUtils.randFloatSpread(2000);
    const z = THREE.MathUtils.randFloatSpread(2000);
    vertices.push(x,y,z);

}
geometry.setAttribute('position',new THREE.Float32BufferAttribute(vertices,3));

2. 行星轨道(绘制线条)

2.1 基本概念

对于线条来说,可使用的材质有LineBasicMaterialLineDashedMaterial,两种材质的区别显而易见,后者是虚线段。

LineBasicMaterial是用于渲染线条的基础材质类型,它是一种非光照材质,只根据设置的颜色和其他属性来渲染线条。 以下是 THREE.LineBasicMaterial 的主要属性:

  1. color: 设置线条的颜色。可以是一个 hex 颜色值、一个 CSS 颜色名或一个 RGB 数组。默认值为 0xffffff
  2. linewidth: 设置线条的宽度。默认值为 1。注意, WebGL 规范不要求浏览器支持可变线宽,所以实际渲染的线宽可能与设置的不一致。
  3. transparent: 设置为 true 时,材质将具有透明度。默认值为 false

2.2 代码实现圆圈的绘制

首先,我们先看下直线绘制是怎么编写的。

const geometry = new THREE.BufferGeometry();
//线条材质
const material = new THREE.LineBasicMaterial( { color: 0x0000ff,transparent: true,opacity:0.9 } );
//定义三个点
const vertices=[
    -2,0,0,
    0,2,0,
    2,0,0
]
//每三个数字确定一个位置信息
geometry.setAttribute('position',new THREE.BufferAttribute(new Float32Array(vertices),3));

//将顶点位置和材质组合在一起
const line = new THREE.Line( geometry, material );
scene.add( line );

效果如下所示:

line.png

可以看到的是,线是画在每一对连续的顶点之间,线条不是闭合的。现在,八青妹想要的是一个圆形的线条,该如何实现呢?由上图可以联想到用线条可以画三角形、四边形、五边形……N边形,等等,如果多边形的每条边到中心点的距离都一样,且每条边的长度也一样,当边越来越多的时候,是不是就接近于圆呢?

将上述代码中vertices的赋值改为如下方式:

//半径
const radius = 2;
//线段的个数,值越大圆越光滑
const segments = 36;
const vertices = [];
 for(let i = 0; i <= segments; i++) {
    const theta = (i / segments) * Math.PI * 2;
    vertices.push(Math.cos(theta) * radius,Math.sin(theta) * radius,0);
}
circle.png

3. 发光的太阳(绘制平面)

3.1 基础概念

MeshBasicMaterial是一个以简单着色(平面或线框)方式来绘制几何体的材质,该材质不受光照的影响,只根据设置的颜色和纹理来渲染物体。

 THREE.MeshBasicMaterial 的常用属性:

  1. color: 设置材质的颜色。可以是一个 hex 颜色值、一个 CSS 颜色名或一个 RGB 数组。默认值为 0xffffff
  2. map: 设置纹理贴图。如果设置了纹理贴图,它会覆盖在材质的颜色上。
  3. wireframe: 设置为 true 时,物体将以线框模式渲染。默认值为 false
  4. transparent: 设置为 true 时,材质将具有透明度。默认值为 false

PlaneGeometry是一个用于生成平面几何体的类。PlaneGeometry(width : Float, height : Float, widthSegments : Integer, heightSegments : Integer)

  1. width — 平面沿着 X 轴的宽度。默认值是 1
  2. height — 平面沿着 Y 轴的高度。默认值是 1
  3. widthSegments — (可选)平面的宽度分段数,默认值是 1
  4. heightSegments — (可选)平面的高度分段数,默认值是 1

3.2 代码实现

// 创建太阳平面几何体
const sunGeometry = new THREE.PlaneGeometry(1.5,1.5);

// 创建太阳发光材质
const sunMaterial = new THREE.MeshBasicMaterial({
    color: 0xffff00
});

// 创建太阳mesh
const sun = new THREE.Mesh(sunGeometry,sunMaterial);
scene.add(sun);

效果如下所示:

sun.png

4. 行星 (立体图像)

4.1 基础概念

需要在轨道上绘制一个球体代表行星。SphereGeometry是threejs中创建球体几何体的类,可以用来创建各种球体形状。

THREE.SphereGeometry 的常用属性:

  1. radius: 球体的半径。默认值为 1
  2. widthSegments: 球体围绕 X 轴划分的水平分段数量。默认值为 32。值越大,球体越平滑。
  3. heightSegments: 球体围绕 Y 轴划分的垂直分段数量。默认值为 16。值越大,球体越平滑。

4.2 代码实现

在放置这个球体的时候要注意,这个球体中心距离中心原点的距离为轨道的半径。

const geometry_sphere = new THREE.SphereGeometry(0.5,32,32);
const material_sphere = new THREE.MeshBasicMaterial({ color: 0xffffff });
const sphere = new THREE.Mesh(geometry_sphere,material_sphere);
scene.add(sphere);

//旋转角度
let rotateAngle=0
sphere.position.set(radius * Math.cos(rotateAngle),radius * Math.sin(rotateAngle),0)