WEBGL学习【十三】鼠标点击立方体改变颜色的原理与实现

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。更多学习资料请访问我爱科技论坛:www.52tech.tech https://blog.csdn.net/m0_37981569/article/details/79035437 // PickFace.
版权声明:本文为博主原创文章,未经博主允许不得转载。更多学习资料请访问我爱科技论坛:www.52tech.tech https://blog.csdn.net/m0_37981569/article/details/79035437
// PickFace.js (c) 2012 matsuda and kanda
// Vertex shader program
var VSHADER_SOURCE =
    'attribute vec4 a_Position;\n' +
    'attribute vec4 a_Color;\n' +
    'attribute float a_Face;\n' +   // 表面的编号(1-6),不能使用int类型(当前顶点属于哪一个表面)【float类型】
    'uniform mat4 u_MvpMatrix;\n' +
    'uniform int u_PickedFace;\n' + // 被选中的表面编号【int类型】
    'varying vec4 v_Color;\n' +
    'void main() {\n' +
    '  gl_Position = u_MvpMatrix * a_Position;\n' + //对顶点的位置进行模型视图投影变换
    '  int face = int(a_Face);\n' + // 转换为int类型(鼠标一点击就会把他编码成颜色值得a分量)
    '  vec3 color = (face == u_PickedFace) ? vec3(1.0) : a_Color.rgb;\n' +   //vec3(1.0)所有的分量都为1,此时为白色;否则顶点颜色还是以前的颜色
    '  if(u_PickedFace == 0) {\n' + // 前三个分量RGB, 通过第四个分量来判断是点击的是哪一个顶面
    '    v_Color = vec4(color, a_Face/255.0);\n' +  // 颜色的第4个分量使得图形的透明度不一样
    '  } else {\n' +
    '    v_Color = vec4(color, a_Color.a);\n' +
    '  }\n' +
    '}\n';

// Fragment shader program
var FSHADER_SOURCE =
    '#ifdef GL_ES\n' +
    'precision mediump float;\n' +
    '#endif\n' +
    'varying vec4 v_Color;\n' +
    'void main() {\n' +
    '  gl_FragColor = v_Color;\n' +
    '}\n';

//旋转的速度
var ANGLE_STEP = 20.0; // Rotation angle (degrees/second)

function main() {
   // 获取canvas
    var canvas = document.getElementById('webgl');

    // 获取上下文信息
    var gl = getWebGLContext(canvas);
    if (!gl) {
        console.log('Failed to get the rendering context for WebGL');
        return;
    }

    // 初始化着色器
    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
        console.log('Failed to intialize shaders.');
        return;
    }

    // 设置顶点的信息
    var n = initVertexBuffers(gl);
    if (n < 0) {
        console.log('Failed to set the vertex information');
        return;
    }

    // 用白色清空背景色,启动深度测试
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.enable(gl.DEPTH_TEST);

    // 获取模型视图投影矩阵的存储地址,获取点击面的位置
    var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
    var u_PickedFace = gl.getUniformLocation(gl.program, 'u_PickedFace');
    if (!u_MvpMatrix || !u_PickedFace) {
        console.log('Failed to get the storage location of uniform variable');
        return;
    }

    // 计算模型视图投影矩阵
    var viewProjMatrix = new Matrix4();
    viewProjMatrix.setPerspective(30.0, canvas.width / canvas.height, 1.0, 100.0);
    viewProjMatrix.lookAt(0.0, 0.0, 7.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);


    // 初始化被选中的表面(刚开始没有那个面被选中,还是以前的颜色)
    gl.uniform1i(u_PickedFace, -1);

    var currentAngle = 0.0; // Current rotation angle
    // Register the event handler
    canvas.onmousedown = function (ev) {   // Mouse is pressed
        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) {
            // 获取鼠标当前点击的位置,转换为canvas元素中的坐标
            var x_in_canvas = x - rect.left, y_in_canvas = rect.bottom - y;

            //调用这个函数区检测里方法体的哪一个面被点击了(face这个分量此时存储的是第四个a分量, read(pixes[3]) ;
            var face = checkFace(gl, n, x_in_canvas, y_in_canvas, currentAngle, u_PickedFace, viewProjMatrix, u_MvpMatrix);
            //console.log("The current face is"+face.toString());
            var num = parseFloat(face)/255.0;       //这里除255等价于 <===> a/255, 得到的其实是一个唯一编号,就是把一个在缓冲区中改变了颜色的那个面的第四个分量
            console.log("当前点击的面为"+num.toString()+" "+face.toString());


            //把当前选中的一面传给顶点着色器()
            gl.uniform1i(u_PickedFace, face); // Pass the surface number to u_PickedFace

            //用选中的编号重新绘制立方体
            draw(gl, n, currentAngle, viewProjMatrix, u_MvpMatrix);
        }
    }

    var tick = function () {   // Start drawing
        currentAngle = animate(currentAngle);
        draw(gl, n, currentAngle, viewProjMatrix, u_MvpMatrix);
        requestAnimationFrame(tick, canvas);
    };
    tick();
}

function initVertexBuffers(gl) {
    // Create a cube
    //    v6----- v5
    //   /|      /|
    //  v1------v0|
    //  | |     | |
    //  | |v7---|-|v4
    //  |/      |/
    //  v2------v3

    var vertices = new Float32Array([   // Vertex coordinates
        1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0,    // v0-v1-v2-v3 front
        1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0,    // v0-v3-v4-v5 right
        1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0,    // v0-v5-v6-v1 up
        -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0,    // v1-v6-v7-v2 left
        -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,    // v7-v4-v3-v2 down
        1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0     // v4-v7-v6-v5 back
    ]);

    var colors = new Float32Array([   // Colors
        0.32, 0.18, 0.56, 0.32, 0.18, 0.56, 0.32, 0.18, 0.56, 0.32, 0.18, 0.56, // v0-v1-v2-v3 front
        0.5, 0.41, 0.69, 0.5, 0.41, 0.69, 0.5, 0.41, 0.69, 0.5, 0.41, 0.69,  // v0-v3-v4-v5 right
        0.78, 0.69, 0.84, 0.78, 0.69, 0.84, 0.78, 0.69, 0.84, 0.78, 0.69, 0.84, // v0-v5-v6-v1 up
        0.0, 0.32, 0.61, 0.0, 0.32, 0.61, 0.0, 0.32, 0.61, 0.0, 0.32, 0.61,  // v1-v6-v7-v2 left
        0.27, 0.58, 0.82, 0.27, 0.58, 0.82, 0.27, 0.58, 0.82, 0.27, 0.58, 0.82, // v7-v4-v3-v2 down
        0.73, 0.82, 0.93, 0.73, 0.82, 0.93, 0.73, 0.82, 0.93, 0.73, 0.82, 0.93, // v4-v7-v6-v5 back
    ]);

    //把我的表面编号传进去
    var faces = new Uint8Array([   // Faces
        1, 1, 1, 1,     // v0-v1-v2-v3 front
        2, 2, 2, 2,     // v0-v3-v4-v5 right
        3, 3, 3, 3,     // v0-v5-v6-v1 up
        4, 4, 4, 4,     // v1-v6-v7-v2 left
        5, 5, 5, 5,     // v7-v4-v3-v2 down
        6, 6, 6, 6,     // v4-v7-v6-v5 back
    ]);

    var indices = new Uint8Array([   // Indices of the vertices
        0, 1, 2, 0, 2, 3,    // front
        4, 5, 6, 4, 6, 7,    // right
        8, 9, 10, 8, 10, 11,    // up
        12, 13, 14, 12, 14, 15,    // left
        16, 17, 18, 16, 18, 19,    // down
        20, 21, 22, 20, 22, 23     // back
    ]);

    // Create a buffer object
    var indexBuffer = gl.createBuffer();
    if (!indexBuffer) {
        return -1;
    }

    // Write vertex information to buffer object
    if (!initArrayBuffer(gl, vertices, gl.FLOAT, 3, 'a_Position')) return -1; // Coordinates Information
    if (!initArrayBuffer(gl, colors, gl.FLOAT, 3, 'a_Color')) return -1;      // Color Information
    if (!initArrayBuffer(gl, faces, gl.UNSIGNED_BYTE, 1, 'a_Face')) return -1;// Surface Information

    // Unbind the buffer object
    gl.bindBuffer(gl.ARRAY_BUFFER, null);

    // Write the indices to the buffer object
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

    return indices.length;
}

//检测哪一个面被选中了(【根据点中的位置返回表面编号】)
function checkFace(gl, n, x, y, currentAngle, u_PickedFace, viewProjMatrix, u_MvpMatrix) {
    var pixels = new Uint8Array(4); // Array for storing the pixel value

    //将表面编号写入到a分量(如果选中了)
    //鼠标一旦点击,就把u_PickedFace变量从-1变为0
    gl.uniform1i(u_PickedFace, 0);  // Draw by writing surface number into alpha value

    //此时每个表面的a值就取决于表面的编号(这一步绘制工作会在颜色缓冲区中执行,不会显示在屏幕上)
    draw(gl, n, currentAngle, viewProjMatrix, u_MvpMatrix);

    // 读取(x y)处的像素颜色. pixels[3] 存储了表面编号
    gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

    //var num = pixels[3].toString();
    //console.log("pixels[3] is "+num/255);
    for (var i=0; i<pixels.length; i++){
        console.log(pixels[i]);
    }
    //a分量返回出去(pixels[3]此时存储了表面的编号)
    return pixels[3];
}

var g_MvpMatrix = new Matrix4(); // Model view projection matrix
function draw(gl, n, currentAngle, viewProjMatrix, u_MvpMatrix) {
    // Caliculate The model view projection matrix and pass it to u_MvpMatrix
    g_MvpMatrix.set(viewProjMatrix);
    g_MvpMatrix.rotate(currentAngle, 1.0, 0.0, 0.0); // Rotate appropriately
    g_MvpMatrix.rotate(currentAngle, 0.0, 1.0, 0.0);
    g_MvpMatrix.rotate(currentAngle, 0.0, 0.0, 1.0);
    gl.uniformMatrix4fv(u_MvpMatrix, false, g_MvpMatrix.elements);

    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);     // Clear buffers
    gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);   // Draw
}

var last = Date.now();  // Last time that this function was called
function animate(angle) {
    var now = Date.now(); // Calculate the elapsed time
    var elapsed = now - last;
    last = now;
    // Update the current rotation angle (adjusted by the elapsed time)
    var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
    return newAngle % 360;
}

function initArrayBuffer(gl, data, type, num, attribute) {
    // Create a buffer object
    var buffer = gl.createBuffer();
    if (!buffer) {
        console.log('Failed to create the buffer object');
        return false;
    }
    // Write date into the buffer object
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
    // Assign the buffer object to the attribute variable
    var a_attribute = gl.getAttribLocation(gl.program, attribute);
    if (a_attribute < 0) {
        console.log('Failed to get the storage location of ' + attribute);
        return false;
    }
    gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
    // Enable the assignment to a_attribute variable
    gl.enableVertexAttribArray(a_attribute);

    return true;
}
相关文章
Qml实用技巧:在可视元素之前半透明覆盖一个可视元素,阻止鼠标透(界面)传(防止点击到被遮挡的按钮)
Qml实用技巧:在可视元素之前半透明覆盖一个可视元素,阻止鼠标透(界面)传(防止点击到被遮挡的按钮)
Qml实用技巧:在可视元素之前半透明覆盖一个可视元素,阻止鼠标透(界面)传(防止点击到被遮挡的按钮)
|
1月前
|
监控
ThreeJs限制模型拖动的范围
这篇文章讲解了在Three.js中如何限制模型拖动的范围,确保模型在特定边界内移动,提供了实现拖动限制的代码示例和技术细节。
50 1
ThreeJs限制模型拖动的范围
|
1月前
ThreeJs的场景实现鼠标拖动旋转控制
这篇文章介绍了如何在Three.js中实现通过鼠标拖动来旋转场景中的模型,并提供了实现这一功能的代码示例。
76 0
|
6月前
|
定位技术
Pyglet综合应用|推箱子游戏地图编辑器之图片跟随鼠标
Pyglet综合应用|推箱子游戏地图编辑器之图片跟随鼠标
61 0
|
图形学
Echarts实战案例代码(29):来找茬,柱状图在F11全屏下显示三角形的异形情况案例
Echarts实战案例代码(29):来找茬,柱状图在F11全屏下显示三角形的异形情况案例
116 0
|
Web App开发 JavaScript 前端开发
Magnifier.js - 支持鼠标滚轮缩放的图片放大镜效果
  Magnifier.js 是一个 JavaScript 库,能够帮助你在图像上实现放大镜效果,支持使用鼠标滚轮放大/缩小功能。放大的图像可以显示在镜头本身或它的外部容器中。Magnifier.js 使用Event.js 作为跨浏览器的事件处理器。
1664 0
C#(三十五)之在滚动窗口中绘图
窗体中的三个属性: Size:窗体大小(包括标题栏和边框) ClientSize:工作区大小(不包括标题栏和边框) AutoScrollMinSize:出现滚动条的最小尺寸
215 0
C#(三十五)之在滚动窗口中绘图
|
vr&ar 图形学
【Unity3D 灵巧小知识点】☀️ | Unity通过 射线 获取 鼠标的世界坐标 和 鼠标点击的物体信息
Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 。 包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者,借助 Unity 将创意变成现实。 Unity 平台提供一整套完善的软件解决方案,可用于创作、运营和变现任何实时互动的2D和3D内容,支持平台包括手机、平板电脑、PC、游戏主机、增强现实和虚拟现实设备。
【Unity3D 灵巧小知识点】☀️ | Unity通过 射线 获取 鼠标的世界坐标 和 鼠标点击的物体信息