2011-04-10 86 views
4

我需要在WebGL中实现相当于“Google Maps”风格的缩放效果。具体来说,我有一个简单的2维场景,总是垂直于相机。当用户点击场景时,相机应缩放到超出咔嗒声的位置,但更接近2维场景。点击放大WebGL

例如看到的jsfiddle,实现了现场,但没有缩放:在渲染

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

如果你有一个启用了WebGL的浏览器,你会看到一个三角形和正方形(2维) -7在Z轴上。我已经把一个占位符handleMouseUp()事件处理程序记录任何点击事件,但我有点失去了如何将点击事件给出的坐标转换为相机的新位置(或者我相当于一个新的查看矩阵)。 (jsfiddle基于learningwebgl.com的教程1,并使用glMatrix http://code.google.com/p/glmatrix/库进行矩阵操作。请记住,这是WebGL,它类似于OpenGL ES,并且无法访问glu *函数。 )

+0

+1将learningwebgl场景变为jsfiddle。我没有想到你可以把着色器放到HTML部分。 – brainjam 2011-04-10 23:45:13

回答

9

我写了一些东西在这个jsfiddle应该帮助你。

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

只需点击WebGL的窗口放大到鼠标指向哪里。

的基本思想是,在WebGL的窗口的一个点,通过使用投影矩阵pMatrix和视图矩阵从3维空间投影它得到(视图矩阵取决于相机在哪里和在哪个方向是看着)。这些矩阵的组成在代码中被命名为pvMatrix

如果你想从窗口反向变换到三个空间,你必须使用pvMatrix的倒数,将剪辑空间坐标(x,y,z)和'unproject'取回到3D。在剪辑空间中,坐标在[-1,1]范围内,并且z坐标是深度。

在OpenGL的世界里,这些变换在gluProject()gluUnproject()(你可以谷歌的更多信息和源代码)来实现。

在jsfiddle示例中,我们计算剪辑空间中的(x,y)坐标,然后对z的两个不同值计算(x,y,z)。由此我们得到了三维空间中映射到(x,y)上的两个点,并且我们可以推导出一个方向矢量。然后,我们可以在该方向移动相机以获得变焦效果。

在代码中,相机位置在eye矢量的否定处。

本示例说明如何在您点击的方向上移动相机。如果你想实际移动到场景中的特定对象,你必须实现诸如对象选择之类的东西,这是一种不同的鱼类。我给出的例子并不知道场景中的物体。

+0

谢谢,这正是我正在寻找的帮助。 – laslowh 2011-04-11 13:20:58

+0

@brain。我想知道你的解决方案是否可以用来检测被点击的对象?我正在考虑做一个向量(从点击点转换成世界坐标,在检查碰撞对象时通过世界点燃)。 – AlvinfromDiaspar 2014-06-09 21:37:16

+0

@Alvin,是的,你可以这样做。不过也有其他方法可以使用,例如'颜色编码'(见http://www.khronos.org/message_boards/showthread.php/7017-Picking-tutorial) – brainjam 2014-06-10 14:21:50

3

这实际上是brainjam的答案的一部分,但为了防止jsfiddle离开,我想确保代码已存档。这里是主要位:

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) 
    } 

而且整个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();