2011-04-10 116 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();