通过 GUI 采样拾取瓦片(Sample Pick Tiles Through GUI)


一、原帖内容概述

原帖内容非常精简,作者 meded90 在 three.js 官方论坛的 Showcase 板块发布了该演示,并在 2026-08-15 与 2026-08-17 两次更新。两次更新均以视频演示为主,正文没有附加大段文字说明,也没有贴出可直接复制运行的代码段。

从标题、标签以及视频缩略内容来看,该演示的核心是:

在 three.js 场景中,通过 GUI(图形用户界面) 来采样/拾取(pick)瓦片(tiles),并实时切换或应用不同的 纹理(textures) 与 材质(materials)。

原帖 HTML 中的视频占位符对应以下视频资源(仅作存档,无需翻译):

  • 帖子 1 视频 1:.../7778d046413f47a68cc162ef484fe7adc45010b2.mp4
  • 帖子 1 视频 2:.../5efe4e564e471f3b3a0c6c9fe40df861dfcb35c4.mp4
  • 帖子 2 视频 1:.../43dfb42b8bb4c5923b8685ed1c888e57d9fc525b.mp4
  • 帖子 2 视频 2:.../9c9eeef27b62516933994890b650f7cd6f1dfc18.mp4

二、核心代码说明

原文正文未包含任何可直接提取的核心代码片段,两次帖子均只有视频占位与 “Updated” 文本。因此,本次深度解读无法“保留原帖中的核心代码”,因为原帖并未以文本形式提供代码。

不过,从标题、标签和相关主题讨论可以推断,该实现通常涉及以下技术点:

  • 瓦片/图块(tiles)的纹理管理与切换
  • GUI 控件驱动材质参数或 UV 偏移
  • 鼠标拾取(picking)选择特定瓦片
  • 纹理图集(texture atlas)或纹理数组(texture array)优化

下面以常见的 three.js / WebGL 实现思路为例,给出补充性伪代码,不属于原帖代码,仅供理解实现方向。


三、3 个核心优化要点

1. 使用纹理图集 / 纹理数组,减少纹理绑定切换

如果每个瓦片都单独上传一张纹理,会频繁切换 GPU 纹理绑定,导致性能下降。更优做法是:

  • 将所有瓦片合并到一张 纹理图集(Texture Atlas) 中
  • 或使用 纹理数组(Texture Array) / 纹理矩阵
  • 在着色器中通过 UV 偏移与缩放来选择不同瓦片

补充 GLSL 示例(仅示意,非原帖代码):

uniform sampler2D uAtlas;
uniform vec2 uTileScale;   // 例如 1/8, 1/8
uniform vec2 uTileOffset;  // 例如第 3 列第 2 行

varying vec2 vUv;

void main() {
  vec2 tileUV = fract(vUv * uTileScale + uTileOffset);
  vec4 color = texture2D(uAtlas, tileUV);
  gl_FragColor = color;
}

术语提示:在 GLSL 3.0 及以上版本中,texture2D 应使用 texture;在 WebGL2 中建议用 texture(uAtlas, tileUV)。


2. 使用 GPU 拾取(颜色编码 / ID 渲染)代替 CPU 遍历

当瓦片数量较多时,用 CPU 端射线投射逐瓦片检测拾取效率较低。常见优化是:

  • 创建一个离屏渲染目标(WebGLRenderTarget)
  • 为每个瓦片分配唯一颜色 ID
  • 渲染到离屏目标后,用 readPixels 读取鼠标位置的颜色,反查瓦片 ID

补充 JS 示例(仅示意,非原帖代码):

const pickingRT = new THREE.WebGLRenderTarget(width, height);

// 1. 使用 ID 材质渲染
scene.overrideMaterial = pickingMaterial;
renderer.setRenderTarget(pickingRT);
renderer.render(scene, camera);

// 2. 读取鼠标位置像素
const pixel = new Uint8Array(4);
renderer.readRenderTargetPixels(pickingRT, mouseX, mouseY, 1, 1, pixel);

// 3. 由颜色反推瓦片 ID
const tileID = (pixel[0] << 16) | (pixel[1] << 8) | pixel[2];

// 4. 恢复默认渲染
scene.overrideMaterial = null;
renderer.setRenderTarget(null);

这种 GPU 拾取 的优点是无需复杂几何遍历,拾取速度与瓦片数量几乎无关;代价是需要额外的离屏渲染与分析通道。


3. 复用几何体与材质实例,避免频繁创建/销毁

如果 GUI 只是在不同瓦片之间切换纹理或颜色,而不是修改网格拓扑,建议:

  • 多个瓦片共享同一个 BufferGeometry 或 PlaneGeometry
  • 使用 InstancedMesh(实例化网格)减少 draw call
  • 材质参数变更时,只更新 uniforms 或纹理 offset/repeat,不要反复 new THREE.MeshStandardMaterial()

补充 JS 示例(仅示意,非原帖代码):

const geometry = new THREE.PlaneGeometry(1, 1);
const material = new THREE.MeshStandardMaterial({
  map: atlasTexture,
});

const mesh = new THREE.InstancedMesh(geometry, material, tileCount);

// GUI 只更新 uniform 或 UV offset
material.map.offset.set(uOff, vOff);
material.map.repeat.set(uScale, vScale);
material.map.updateMatrix();

通过缓存纹理单元、复用材质实例,可显著降低 CPU 与 GPU 端的分配/释放开销。


四、GLSL / WebGL 专业术语检查

原文中直接出现的技术标签为:

  • textures — 纹理
  • materials — 材质

在相关主题与推断实现中,涉及术语应统一为:

英文术语推荐中文译名备注
tiles瓦片 / 图块在地形、纹理图集、3D Tiles 语境下不宜译为“瓷砖”
picking拾取不要译为“采摘”
GUI图形用户界面三.js 社区常直接用 “GUI”
texture atlas纹理图集也称“纹理图册”
texture array纹理数组WebGL2 支持的数组纹理
UV coordinatesUV 坐标UV 不翻译是通用做法
raycasting射线投射 / 光线投射three.js 中 Raycaster 常保留英文
render target渲染目标WebGLRenderTarget 是核心类
readPixels读取像素对应 readRenderTargetPixels
instanced mesh实例化网格THREE.InstancedMesh
draw call绘制调用不译为宜
shader着色器顶点/片元着色器
spritesheet精灵表 / 雪碧图与 texture atlas 类似
cube map立方体贴图环境映射常用
transparency透明度材质属性
shadow material阴影材质用于阴影映射

原帖并未直接出现 GLSL 着色器代码,因此没有可校正的 GLSL 语法问题。以上检查主要针对相关讨论中可能出现的术语翻译一致性。


五、延伸阅读

原帖页面还列出了若干相关主题,可作为同类技术参考:

  • Procedure generating terrain tile / only background at this moment
    《程序化生成地形瓦片 / 目前仅有背景》
  • Change floor image in canvas 3D
    《在 Canvas 3D 中更换地面图片》
  • High-Resolution Multiscale Panoramic from Tile Zoom Cameras
    《来自瓦片变焦相机的高分辨率多尺度全景》
  • 3d-Tiles-Renderer: A 3D Tiles implementation for three.js from NASA JPL
    《3d-Tiles-Renderer:来自 NASA JPL 的 three.js 3D Tiles 实现》
  • How to use spritesheet in this special case?
    《如何在这种特殊情况下使用精灵表?》

这些相关主题可以帮助理解该演示在纹理、材质、瓦片拾取和性能优化方面的技术脉络。


六、结语

meded90 的原帖以视频为主,并未提供可直接复制的核心代码,因此本文没有“保留核心代码”的内容。不过,通过标题、标签和相关讨论,可以合理推断其实现方向为:GUI 驱动 + 瓦片拾取 + 纹理/材质切换,并由此提炼出三条通用性能优化路径。

如果你需要落地类似功能,建议优先考虑:

  1. 纹理图集 / 纹理数组
  2. GPU 拾取通道
  3. 几何体与材质实例复用

这三点能够在保证交互体验的同时,有效控制 WebGL 渲染压力。