我正在制作一个 webgl 应用程序,我想知道是否有任何方法可以使 Canvas 内的对象(使用 blender 制作的 3D 模型)可点击。这样,当我单击它们时,会弹出一个包含数据的窗口。
最佳答案
我知道(并且已经使用过)两种主要方法。
第一个是分配一个单独的帧缓冲区并用不同的颜色渲染交互对象。然后,在发生鼠标事件时,您读取与鼠标位置相对应的像素,并找到与刚刚读取的颜色相对应的对象。例如,它可能看起来有点像这样。
纹理和阴影场景:
渲染用于 HitTest :
这种方法很有趣,因为它很简单。但它存在一些性能挑战,其中主要挑战是渲染场景两次并读回像素数据(缓慢且同步)。第一个很简单:只需为帧缓冲区保留一个脏标志,并仅在发生事件且仅在设置了该标志时重绘它(当然,然后重置它)。我通过从帧缓冲区读取和缓存大块像素来解决第二个问题:
getPixel: function (x, y) {
var screenSize = this._screen.getCssSize();
x = x * HIT_TEST_BUFFER_SIZE[0] / screenSize[0] | 0;
y = y * HIT_TEST_BUFFER_SIZE[1] / screenSize[1] | 0;
var rx = x >> PIXEL_CACHE_BUCKET_IDX_SHIFT,
ry = y >> PIXEL_CACHE_BUCKET_IDX_SHIFT,
pixelCache = this._pixelCache,
bucket = pixelCache[[rx, ry]];
if (!bucket) {
this._framebuffer.bind();
bucket = pixelCache[[rx, ry]] = new Uint8Array(
4 * PIXEL_CACHE_BUCKET_SIZE[0] * PIXEL_CACHE_BUCKET_SIZE[1]
);
var gl = this._gl;
gl.readPixels(
rx << PIXEL_CACHE_BUCKET_IDX_SHIFT,
ry << PIXEL_CACHE_BUCKET_IDX_SHIFT,
PIXEL_CACHE_BUCKET_SIZE[0],
PIXEL_CACHE_BUCKET_SIZE[1],
gl.RGBA,
gl.UNSIGNED_BYTE,
bucket
);
this._framebuffer.unbind();
}
var bucketOffset = 4 * (
(y - ry * PIXEL_CACHE_BUCKET_SIZE[1]) * PIXEL_CACHE_BUCKET_SIZE[0] +
x - rx * PIXEL_CACHE_BUCKET_SIZE[0]
);
return bucket.subarray(bucketOffset, bucketOffset + 3);
}
第二种主要方法是向场景转换光线。您获取鼠标位置,用它构造一条射线并将其从摄像机位置转换到场景中以查找它与哪个对象相交。该对象将是鼠标光标指向的一个。实际上,该方法有一个不错的实现 in Three.js ,你可以使用它或者将其作为引用来实现你自己的算法。该方法的主要挑战是搜索射线相交对象的算法复杂性。它可以通过基于您的场景构建的空间索引来解决。
关于javascript - 如何使 Canvas 内的对象可点击弹出窗口?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37827449/