Click to Zoom WebGL

I need to implement what constitutes the effect of scaling the Google Maps style in WebGL. In particular, I have a simple two-dimensional scene that is always perpendicular to the camera. When the user clicks on the scene, the camera should scale to a location located above the click, but closer to the 2-dimensional scene.

For example, see this jsfiddle, which implements the scene, but without scaling:

http://jsfiddle.net/JqBs8/4/

If you have a browser with WebGL support, you should see a triangle and a square (two-dimensional) displayed on -7 along the Z axis. I put the handleMouseUp () event handler in the placeholder, which logs any click events, but I lost a little about how to translate the coordinates given by the click event to a new location for the camera (or, I think, this is equivalent to the new viewing matrix).

(jsfiddle is based on tutorial 1 from the web site training website webgl.com and uses the glMatrix library http://code.google.com/p/glmatrix/ for matrix operations. Keep in mind that this is a WebGL that is similar to OpenGL ES and does not have access to glu * functions.)

+4
source share
2 answers

I wrote something in this jsfiddle that should help you.

http://jsfiddle.net/zttnZ/2/

Just click on the WebGL window to zoom in on the mouse.

The main idea is that the point in the WebGL window is obtained by projecting it from 3-space using the projection matrix pMatrix and the view matrix (the view matrix depends on where the camera is and the direction in which it is looking). The composition of these matrices in the code is called pvMatrix .

If you want the opposite conversion from the window back to three spaces, you must take the clip space coordinate (x, y, z) and "unproject" back into 3D using the inverse of pvMatrix . In the clip space, the coordinates are in the range [-1,1], and the z coordinate is the depth.

In the OpenGL world, these transformations are implemented in gluProject() and gluUnproject() (which you can get from Google for more information and source code).

In the jsfiddle example, we calculate the coordinates (x, y) in the clip space, and then unproject (x, y, z) for two different z values. From here we get two points in three-dimensional space that map to (x, y), and we can derive the direction vector. Then we can move the camera in this direction to get a zoom effect.

In the code, the camera position is in the negation of the eye vector.

This example shows how to move the camera in the direction you click. If you want to actually go to specific objects in the scene, you need to implement something like selecting an object, which is another fish maker. In the above example, I do not know the objects in the scene.

+9
source

This is really part of the brainjam answer, but just in case the jsfiddle had to leave, I wanted to make sure the code was archived. Here is the main bit:

  function handleMouseUp(event) { var world1 = [0,0,0,0] ; var world2 = [0,0,0,0] ; var dir = [0,0,0] ; var w = event.srcElement.clientWidth ; var h = event.srcElement.clientHeight ; // calculate x,y clip space coordinates var x = (event.offsetX-w/2)/(w/2) ; var y = -(event.offsetY-h/2)/(h/2) ; mat4.inverse(pvMatrix, pvMatrixInverse) ; // convert clip space coordinates into world space mat4.multiplyVec4(pvMatrixInverse, [x,y,-1,1], world1) ; vec3.scale(world1,1/world1[3]) ; mat4.multiplyVec4(pvMatrixInverse, [x,y,0,1], world2) ; vec3.scale(world2,1/world2[3]) ; // calculate world space view vector vec3.subtract(world2,world1,dir) ; vec3.normalize(dir) ; vec3.scale(dir,0.3) ; // move eye in direction of world space view vector vec3.subtract(eye,dir) ; drawScene(); console.log(event) } 

And all the js ...

  var gl; function initGL(canvas) { try { gl = canvas.getContext("experimental-webgl"); gl.viewportWidth = canvas.width; gl.viewportHeight = canvas.height; } catch (e) { } if (!gl) { alert("Could not initialise WebGL, sorry :-("); } } function getShader(gl, id) { var shaderScript = document.getElementById(id); if (!shaderScript) { return null; } var str = ""; var k = shaderScript.firstChild; while (k) { if (k.nodeType == 3) { str += k.textContent; } k = k.nextSibling; } var shader; if (shaderScript.type == "x-shader/x-fragment") { shader = gl.createShader(gl.FRAGMENT_SHADER); } else if (shaderScript.type == "x-shader/x-vertex") { shader = gl.createShader(gl.VERTEX_SHADER); } else { return null; } gl.shaderSource(shader, str); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert(gl.getShaderInfoLog(shader)); return null; } return shader; } var shaderProgram; function initShaders() { var fragmentShader = getShader(gl, "shader-fs"); var vertexShader = getShader(gl, "shader-vs"); shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Could not initialise shaders"); } gl.useProgram(shaderProgram); shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); } var mvMatrix = mat4.create(); var pMatrix = mat4.create(); function setMatrixUniforms() { gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix); gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix); } var triangleVertexPositionBuffer; var squareVertexPositionBuffer; function initBuffers() { triangleVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); var vertices = [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); triangleVertexPositionBuffer.itemSize = 3; triangleVertexPositionBuffer.numItems = 3; squareVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); vertices = [ 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0 ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); squareVertexPositionBuffer.itemSize = 3; squareVertexPositionBuffer.numItems = 4; } var eye = vec3.create([0,0,0]) ; // negation of actual eye position var pvMatrix = mat4.create(); var pvMatrixInverse = mat4.create(); function drawScene() { gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix); mat4.identity(mvMatrix); // calculate the view transform mvMatrix, and the projection-view matrix pvMatrix mat4.translate(mvMatrix, eye); mat4.multiply(pMatrix,mvMatrix,pvMatrix) ; mat4.translate(mvMatrix, [-1.5, 0.0, -7.0]); gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); setMatrixUniforms(); gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems); mat4.translate(mvMatrix, [3.0, 0.0, 0.0]); gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); setMatrixUniforms(); gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems); } function handleMouseUp(event) { var world1 = [0,0,0,0] ; var world2 = [0,0,0,0] ; var dir = [0,0,0] ; var w = event.srcElement.clientWidth ; var h = event.srcElement.clientHeight ; // calculate x,y clip space coordinates var x = (event.offsetX-w/2)/(w/2) ; var y = -(event.offsetY-h/2)/(h/2) ; mat4.inverse(pvMatrix, pvMatrixInverse) ; // convert clip space coordinates into world space mat4.multiplyVec4(pvMatrixInverse, [x,y,-1,1], world1) ; vec3.scale(world1,1/world1[3]) ; mat4.multiplyVec4(pvMatrixInverse, [x,y,0,1], world2) ; vec3.scale(world2,1/world2[3]) ; // calculate world space view vector vec3.subtract(world2,world1,dir) ; vec3.normalize(dir) ; vec3.scale(dir,0.3) ; // move eye in direction of world space view vector vec3.subtract(eye,dir) ; drawScene(); console.log(event) } function webGLStart() { var canvas = document.getElementById("lesson01-canvas"); initGL(canvas); initShaders(); initBuffers(); gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.enable(gl.DEPTH_TEST); canvas.onmouseup = handleMouseUp; drawScene(); } webGLStart(); 
+3
source

Source: https://habr.com/ru/post/1347446/


All Articles