three MeshSurfaceSampler 网格表面采样器

143 阅读3分钟

以下是一个使用 MeshSurfaceSampler 在网格表面上生成粒子效果的完整示例。此示例会在 Three.js 场景中创建一个网格(例如一个球体),并在其表面采样点,将这些点作为粒子展示。

MeshSurfaceSampler 有三个方法

    构造函数
        MeshSurfaceSampler( mesh : Mesh )
        mesh — ​​进行采样的表面网格。
        创建一个新的 MeshSurfaceSampler。如果输入的几何体是索引的,将创建一个非索引的副本。构造后,采样器无法返回样本,直到调用 build 方法。
    
    // 创建一个球体网格
    const geometry = new THREE.SphereGeometry(1, 32, 32);
    const material = new THREE.MeshBasicMaterial({ color: 0x0077ff, wireframe: true });
    const mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);
    // 创建用于存放粒子的几何体
    const particleGeometry = new THREE.BufferGeometry();
    const particlesMaterial = new THREE.PointsMaterial({ color: 0xff8800, size: 0.05 });
    const particleCount = 1000;
    const positions = new Float32Array(particleCount * 3); // 每个点需要 x, y, z 三个坐标
    // 创建 MeshSurfaceSampler 实例
    const sampler = new MeshSurfaceSampler(mesh).build();
    // 使用 MeshSurfaceSampler 采样粒子位置
    const position = new THREE.Vector3();
    for (let i = 0; i < particleCount; i++) {
        sampler.sample(position);
        positions[i * 3] = position.x;
     ** **  positions[i * 3 + 1] = position.y;
        positions[i * 3 + 2] = position.z;
    }
    // 将采样的粒子位置添加到几何体中
    particleGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
    // 创建粒子并添加到场景中
    const particles = new THREE.Points(particleGeometry, particlesMaterial);
    scene.add(particles);
  • setWeightAttribute ( name : String ) : this setWeightAttribute(name: String): thisMeshSurfaceSampler 类中的一个方法,用于设置网格中某个顶点属性的权重。这样可以控制不同区域的采样概率,例如让某些区域的采样点更密集或稀疏。
    name:字符串类型,指定顶点属性的名称,通常可以是网格的顶点属性,比如 "uv""color" 等。
  • build () : this build(): thisMeshSurfaceSampler 中的一个方法,用于初始化和预处理采样器,以便后续能够在网格表面上有效地进行点的采样。必须在调用 sample() 之前使用 build(),否则采样器将无法正常工作。
  • sample ( targetPosition : Vector3, targetNormal : Vector3, targetColor : Color, targetUV : Vector2 ) : this sample(targetPosition: Vector3, targetNormal: Vector3, targetColor: Color, targetUV: Vector2): thisMeshSurfaceSampler 类中的方法,用于从网格表面随机采样一个点。该方法可以返回该点的位置信息、法线、颜色和 UV 坐标等。
参数说明
    targetPosition: Vector3,采样点的坐标位置,必填。
    targetNormal: Vector3,采样点的法线方向,用于指定采样点的表面法线,可选。
    targetColor: Color,采样点的颜色,用于获取采样点的顶点颜色信息,可选。
    targetUV: Vector2,采样点的 UV 坐标,用于获取采样点的 UV 坐标信息,可选。

功能扩展 使用随机采样器生成一个字母星球


    // 创建一个透明球体作为采样表面
    const sphereGeometry = new THREE.SphereGeometry(1, 64, 64);
    const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x000000, transparent: true, opacity: 0 });
    const sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);
    scene.add(sphereMesh);
    // 使用 MeshSurfaceSampler 从球体上采样点
    const sampler = new MeshSurfaceSampler(sphereMesh).build();
    // 随机生成的文字数组
    const textArray = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]; // 替换为需要显示的文字
    // 创建一个组来存放所有的文字平面
    const planeGroup = new THREE.Group();
    const particleCount = 1000; // 设置生成的文字数量
    for (let i = 0; i < particleCount; i++) {
        const position = new THREE.Vector3();
        sampler.sample(position); // 从球体表面上随机采样一个点
        // 随机选择一个文字
        const text = textArray[Math.floor(Math.random() * textArray.length)];
        // 创建文字纹理
        const canvas = document.createElement('canvas');
        canvas.width = 128;
        canvas.height = 128;
        const context = canvas.getContext('2d');
        context.fillStyle = 'white';
        context.font = 'bold 60px Arial';
        context.textAlign = 'center';
        context.textBaseline = 'middle';
        context.fillText(text, canvas.width / 2, canvas.height / 2);
        const texture = new THREE.CanvasTexture(canvas);
        const material = new THREE.MeshBasicMaterial({ map: texture, transparent: true });
        // 创建平面几何体并应用文字纹理
        const planeGeometry = new THREE.PlaneGeometry(0.2, 0.2); // 设置平面的大小
        const planeMesh = new THREE.Mesh(planeGeometry, material);
        planeMesh.position.copy(position); // 将平面放置在采样点位置
        planeMesh.lookAt(new THREE.Vector3(0, 0, 0)); // 确保平面朝向球心(调整文字方向)
        planeGroup.add(planeMesh); // 将平面加入到组中
    }
    // 添加文字平面组到场景
    scene.add(planeGroup);