WebGL入门教程(十八)点选立方体

发表于2017-12-13
评论0 3.3k浏览
一、点选立方体原理

这里用了一个比较巧妙(山寨)的方法判断是否点击到了立方体:

  1. 获取鼠标点击的位置;
  2. 将立方体绘制成红色(也可以是其他颜色);
  3. 判断鼠标点击位置的颜色
  4. 恢复立方体的颜色
二、点选立方体示例

由以上原理,当鼠标点击位置的颜色跟立方体重绘之后的颜色一致为红色时,则点击的位置就在立方体上。(如果背景色也为红色,就尴尬了)。

function main() {
    var gl = getGL();
    var vsFile = "./res/shader/click.vert.glsl";
    var fsFile = "./res/shader/click.frag.glsl";
    initShaderProgram(gl, vsFile, fsFile, function (shaderProgram) {
        var n = initVertexBuffers(gl, shaderProgram);
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.enable(gl.DEPTH_TEST);
        var u_Clicked = gl.getUniformLocation(shaderProgram, 'u_Clicked');
        gl.uniform1i(u_Clicked, 0);
        var viewMat = lookAt(3.0, 3.0, 7.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
        var projMat = getPerspectiveProjection(30, 16 / 9, 1, 100);
        var vpMat = multiMatrix44(projMat, viewMat);
        var u_MvpMatrix = gl.getUniformLocation(shaderProgram, 'u_MvpMatrix');
        gl.uniformMatrix4fv(u_MvpMatrix, false, vpMat);
        var tick = function() {
            draw(gl, n);
            requestAnimationFrame(tick);
        };
        tick();
        var canvas = document.getElementById("container");
        canvas.onmousedown = function(ev) {
            var x = ev.clientX, y = ev.clientY;
            var rect = ev.target.getBoundingClientRect();
            if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) {
                var x_in_canvas = x - rect.left;
                var y_in_canvas = rect.bottom - y;
                var click = check(gl, shaderProgram, n, x_in_canvas, y_in_canvas);
                if (click){
                    console.log('The cube was selected!');
                }
            }
        }
    })
}
function check(gl, shaderProgram, n, x, y) {
    var click = false;
    var u_Clicked = gl.getUniformLocation(shaderProgram, 'u_Clicked');
    gl.uniform1i(u_Clicked, 1);
    draw(gl, n);
    var pixels = new Uint8Array(4);
    gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    if (pixels[0] === 255){
        click = true;
    }
    gl.uniform1i(u_Clicked, 0);
    draw(gl, n);
    return click;
}
function draw(gl, n) {
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}
function initVertexBuffers(gl, shaderProgram) {
    var vertices = new Float32Array([
        1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0,
        1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0,
        1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0,
        -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0,
        -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,
        1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0
    ]);
    var colors = new Float32Array([
        0.2, 0.58, 0.82, 0.2, 0.58, 0.82, 0.2, 0.58, 0.82, 0.2, 0.58, 0.82,
        0.5, 0.41, 0.69, 0.5, 0.41, 0.69, 0.5, 0.41, 0.69, 0.5, 0.41, 0.69,
        0.0, 0.32, 0.61, 0.0, 0.32, 0.61, 0.0, 0.32, 0.61, 0.0, 0.32, 0.61,
        0.78, 0.69, 0.84, 0.78, 0.69, 0.84, 0.78, 0.69, 0.84, 0.78, 0.69, 0.84,
        0.32, 0.18, 0.56, 0.32, 0.18, 0.56, 0.32, 0.18, 0.56, 0.32, 0.18, 0.56,
        0.73, 0.82, 0.93, 0.73, 0.82, 0.93, 0.73, 0.82, 0.93, 0.73, 0.82, 0.93,
    ]);
    var indices = new Uint8Array([
        0, 1, 2, 0, 2, 3,
        4, 5, 6, 4, 6, 7,
        8, 9, 10, 8, 10, 11,
        12, 13, 14, 12, 14, 15,
        16, 17, 18, 16, 18, 19,
        20, 21, 22, 20, 22, 23
    ]);
    initArrayBuffer(gl, shaderProgram, vertices, gl.FLOAT, 3, 'a_Position');
    initArrayBuffer(gl, shaderProgram, colors, gl.FLOAT, 3, 'a_Color');
    var indexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
    return indices.length;
}

如图,当点击的位置在立方体上时,会在控制台输出”The cube was selected!”。

三、点选立方体某一面原理

原理:

  1. 记录每个顶点所在面的编号
  2. 鼠标点击后,将顶点所在面的编号写入颜色的Alpha中
  3. 读取颜色的Alpha值,即为选中的选中的面
  4. 重绘立方体,将选中的图设置为白色
四、点选立方体某一面示例

首先需要指定每个顶点所在的面的编号,然后在点击的时候将其设置在颜色中,通过颜色的Alpha值将选中的面传回js,然后在js中将选中的面绘制成白色。

function main() {
    var gl = getGL();
    var vsFile = "./res/shader/click.vert.glsl";
    var fsFile = "./res/shader/click.frag.glsl";
    initShaderProgram(gl, vsFile, fsFile, function (shaderProgram) {
        var n = initVertexBuffers(gl, shaderProgram);
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.enable(gl.DEPTH_TEST);
        var u_ClickedFace = gl.getUniformLocation(shaderProgram, 'u_ClickedFace');
        gl.uniform1i(u_ClickedFace, -1);
        var viewMat = lookAt(3.0, 3.0, 7.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
        var projMat = getPerspectiveProjection(30, 16 / 9, 1, 100);
        var vpMat = multiMatrix44(projMat, viewMat);
        var u_MvpMatrix = gl.getUniformLocation(shaderProgram, 'u_MvpMatrix');
        gl.uniformMatrix4fv(u_MvpMatrix, false, vpMat);
        var tick = function() {
            draw(gl, n);
            requestAnimationFrame(tick);
        };
        tick();
        var canvas = document.getElementById("container");
        canvas.onmousedown = function(ev) {
            var x = ev.clientX, y = ev.clientY;
            var rect = ev.target.getBoundingClientRect();
            if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) {
                var x_in_canvas = x - rect.left;
                var y_in_canvas = rect.bottom - y;
                var face = checkFace(gl, shaderProgram, n, x_in_canvas, y_in_canvas);
                gl.uniform1i(u_ClickedFace, face);
            }
        }
    })
}
function checkFace(gl, shaderProgram, n, x, y) {
    var u_ClickedFace = gl.getUniformLocation(shaderProgram, 'u_ClickedFace');
    gl.uniform1i(u_ClickedFace, 0);
    draw(gl, n);
    var pixels = new Uint8Array(4);
    gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    return pixels[3];// A中保存的是平面编号
}
function draw(gl, n) {
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}
function initVertexBuffers(gl, shaderProgram) {
    var vertices = new Float32Array([
        1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0,
        1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0,
        1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0,
        -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0,
        -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,
        1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0
    ]);
    var colors = new Float32Array([
        0.2, 0.58, 0.82, 0.2, 0.58, 0.82, 0.2, 0.58, 0.82, 0.2, 0.58, 0.82,
        0.5, 0.41, 0.69, 0.5, 0.41, 0.69, 0.5, 0.41, 0.69, 0.5, 0.41, 0.69,
        0.0, 0.32, 0.61, 0.0, 0.32, 0.61, 0.0, 0.32, 0.61, 0.0, 0.32, 0.61,
        0.78, 0.69, 0.84, 0.78, 0.69, 0.84, 0.78, 0.69, 0.84, 0.78, 0.69, 0.84,
        0.32, 0.18, 0.56, 0.32, 0.18, 0.56, 0.32, 0.18, 0.56, 0.32, 0.18, 0.56,
        0.73, 0.82, 0.93, 0.73, 0.82, 0.93, 0.73, 0.82, 0.93, 0.73, 0.82, 0.93,
    ]);
    var indices = new Uint8Array([
        0, 1, 2, 0, 2, 3,
        4, 5, 6, 4, 6, 7,
        8, 9, 10, 8, 10, 11,
        12, 13, 14, 12, 14, 15,
        16, 17, 18, 16, 18, 19,
        20, 21, 22, 20, 22, 23
    ]);
    var faces = new Uint8Array([
        1, 1, 1, 1,
        2, 2, 2, 2,
        3, 3, 3, 3,
        4, 4, 4, 4,
        5, 5, 5, 5,
        6, 6, 6, 6,
    ]);
    initArrayBuffer(gl, shaderProgram, vertices, gl.FLOAT, 3, 'a_Position');
    initArrayBuffer(gl, shaderProgram, colors, gl.FLOAT, 3, 'a_Color');
    initArrayBuffer(gl, shaderProgram, faces, gl.UNSIGNED_BYTE, 1, 'a_Face');
    var indexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
    return indices.length;
}

如图,选中的面会被绘制成白色。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引