// PickFace.js (c) 2012 matsuda and kanda // Vertex shader program var VSHADER_SOURCE = 'attribute vec4 a_Position; ' + 'attribute vec4 a_Color; ' + 'attribute float a_Face; ' + // 表面的编号(1-6),不能使用int类型(当前顶点属于哪一个表面)【float类型】 'uniform mat4 u_MvpMatrix; ' + 'uniform int u_PickedFace; ' + // 被选中的表面编号【int类型】 'varying vec4 v_Color; ' + 'void main() { ' + ' gl_Position = u_MvpMatrix * a_Position; ' + //对顶点的位置进行模型视图投影变换 ' int face = int(a_Face); ' + // 转换为int类型(鼠标一点击就会把他编码成颜色值得a分量) ' vec3 color = (face == u_PickedFace) ? vec3(1.0) : a_Color.rgb; ' + //vec3(1.0)所有的分量都为1,此时为白色;否则顶点颜色还是以前的颜色 ' if(u_PickedFace == 0) { ' + // 前三个分量RGB, 通过第四个分量来判断是点击的是哪一个顶面 ' v_Color = vec4(color, a_Face/255.0); ' + // 颜色的第4个分量使得图形的透明度不一样 ' } else { ' + ' v_Color = vec4(color, a_Color.a); ' + ' } ' + '} '; // Fragment shader program var FSHADER_SOURCE = '#ifdef GL_ES ' + 'precision mediump float; ' + '#endif ' + 'varying vec4 v_Color; ' + 'void main() { ' + ' gl_FragColor = v_Color; ' + '} '; //旋转的速度 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; }