如何使画布内的对象可以弹出窗口?

时间:2016-06-15 06:16:15

标签: javascript webgl

我正在制作一个webgl应用程序,我想知道是否有任何方法可以在画布内点击制作对象(使用搅拌器制作的3D模型)。因此,当我点击它们时,弹出窗口包含数据。

2 个答案:

答案 0 :(得分:1)

我知道(并且已经使用过)两种主要方法。

第一个是分配一个单独的帧缓冲区并使用不同的颜色渲染交互式对象。然后,在鼠标事件时,您读取与鼠标位置对应的像素,并找到与刚刚读取的颜色对应的对象。对于exapmle,它看起来可能有点像这样。

纹理和阴影场景:

Textured and shaded scene

针对热门测试呈现:

Rendered for hit testing

由于它的简单性,这种方法很有趣。但它有一些性能挑战,其中主要的是渲染场景两次并回读像素数据(慢速和同步)。第一个很容易:只为帧缓冲区保留一个脏标志,并仅在事件时重绘它,并且只有在设置了标志时(当然重置它)。第二个我通过从帧缓冲区大块像素中读取和缓存来解决这个问题:

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实际上是一个不错的实现,您可以使用它或将其作为实现您自己的算法的参考。该方法的主要挑战是搜索光线与之相交的对象的算法复杂性。它可以通过建立在你场景上的空间索引来解决。

答案 1 :(得分:0)

Canvas是一个简单的图形API。它绘制的像素非常好,仅此而已。有办法通过鼠标位置“伪造”事件处理程序,但这需要更多的工作。基本上,您将注册鼠标单击的位置,而不是将其与3D模型的位置相匹配,以查看您是否匹配。您将无法将事件处理程序直接附加到画布中的3d blender对象。在可伸缩矢量图形(SVG)中可以正常工作。只是不在画布上。

function handleMouseDown(e) {
    // tell the browser we'll handle this event
    e.preventDefault();
    e.stopPropagation();

    // save the mouse position
    // in case this becomes a drag operation
    lastX = parseInt(e.clientX - offsetX);
    lastY = parseInt(e.clientY - offsetY);

    // hit all existing FrameControlPt of your blender objects
    var hit = -1;
    for (var i = 0; i < FrameControlPt.length; i++) {
        var location = FrameControlPt[i];
        var dx = lastX - location.x;
        var dy = lastY - location.y;
        if (dx * dx + dy * dy < stdRadius * stdRadius) {
            hit = i;
        }
    }

    // hit all existing buttons in the canvas
    for (var i = 0; i < btn.length; i++) {
        if ((lastX < (btn[i].x + btn[i].width)) &&
            (lastX > btn[i].x) &&
            (lastY < (btn[i].y + btn[i].height)) &&
            (lastY > btn[i].y)) {
            console.log("Button #" + (i + 1) + " has been clicked!!");
            // execute button function
            btn[i].action(); // execute a custom function
        }
    }

    // if hit then set the isDown flag to start a drag
    if (hit < 0) {
        drawAll();
    } else {
        draggingCircle = FrameControlPt[hit];
        isDown = true;
    }
}

显然你必须处理MouseUp(event)..在这个例子中,我允许用户在画布中拖放元素。您必须调整您的活动以符合您的预期用途。

代码extract from this sample.