2013-12-21 105 views
2

我想計算一個投影變換來紋理webgl中的任意四邊形(如果可能/需要,使用three.js和着色器)。計算投影變換以紋理任意四邊形

Quadrilateral Interpolation

This是我想要的獲得,從this答案服用。

一切都很好地在帖子中描述,所以我想通過一些工作我可以解決問題。這裏是解決方案的僞代碼:

precompute A matrix (should be trivial since texture coordinates are in [0,1] interval) 
compute B matrix according to the vertex positions (not possible in the vertex shader since we need the four coordinates of the points) 
use B in the fragment shader to compute the correct texture coordinate at each pixel 

但是我想知道是否有更簡單的方法來做到這一點在webgl。

----相關主題的鏈接----

還有就是要解決數學描述here的問題類似的方式,但由於它的解決方案來計算多對多點映,似乎一矯枉過正給我。

我認爲this是OpenGL的解決方案,但意識到它是一個解決方案,執行一個簡單的透視正確的插值,這是幸運地默認啓用。

我在梯形上發現了很多東西,這是我想解決的更一般問題的一個簡單版本:123。我首先認爲那些會有所幫助,但是他們卻讓我引起了很多閱讀和誤解。

最後,這個page描述了一個解決方案來解決這個問題,但我懷疑它是最簡單和最常見的解決方案。現在我想這可能是正確的!

---- ----結論

我一直在尋找很多關於解決方案,而不是因爲它是一個特別複雜的問題,而是因爲我一直在尋找一個簡單的和典型的/通用的解決方案。我認爲在很多情況下(每個視頻地圖應用)它都是一個簡單的問題,並且會有微不足道的答案。

回答

2

好吧,我設法與three.js所和CoffeeScript的做到這一點(我不得不實施失蹤Matrix3功能):

class Quad 
constructor: (width, height, canvasKeyboard, scene) -> 
    @sceneWidth = scene.width 
    @sceneHeight = scene.height 

    # --- QuadGeometry --- # 

    @geometry = new THREE.Geometry() 

    normal = new THREE.Vector3(0, 0, 1) 

    @positions = [] 
    @positions.push(x: -width/2, y: height/2) 
    @positions.push(x: width/2, y: height/2) 
    @positions.push(x: -width/2, y: -height/2) 
    @positions.push(x: width/2, y: -height/2) 

    for position in @positions 
     @geometry.vertices.push(new THREE.Vector3(position.x, position.y, 0)) 

    uv0 = new THREE.Vector4(0,1,0,1) 
    uv1 = new THREE.Vector4(1,1,0,1) 
    uv2 = new THREE.Vector4(0,0,0,1) 
    uv3 = new THREE.Vector4(1,0,0,1) 

    face = new THREE.Face3(0, 2, 1) 
    face.normal.copy(normal) 
    face.vertexNormals.push(normal.clone(), normal.clone(), normal.clone()) 

    @geometry.faces.push(face) 
    @geometry.faceVertexUvs[ 0 ].push([ uv0.clone(), uv2.clone(), uv1.clone() ]) 

    face = new THREE.Face3(1, 2, 3) 
    face.normal.copy(normal) 
    face.vertexNormals.push(normal.clone(), normal.clone(), normal.clone()) 

    @geometry.faces.push(face) 
    @geometry.faceVertexUvs[ 0 ].push([ uv1.clone(), uv2.clone(), uv3.clone() ]) 

    @geometry.computeCentroids() 

    # --- Mesh --- # 

    @texture = new THREE.Texture(canvasKeyboard[0]) 
    @texture.needsUpdate = true 

    C = new THREE.Matrix4() 

    @uniforms = { "texture": { type: "t", value: @texture }, "resolution": { type: "v2", value: new THREE.Vector2(@sceneWidth, @sceneHeight) }, "matC": { type: "m4", value: C } } 

    shaderMaterial = new THREE.ShaderMaterial(
     uniforms:  @uniforms, 
     vertexShader: $('#vertexshader').text(), 
     fragmentShader: $('#fragmentshader').text() 
    ) 

    @mesh = new THREE.Mesh(@geometry, shaderMaterial) 

    @mesh.position.set(0,0,1) 

    scene.add(@mesh) 

    # --- Sprites --- # 

    @sprites = [] 

    for i in [0..3] 
     position = @positions[i] 
     m = new THREE.SpriteMaterial({color: new THREE.Color('green') ,useScreenCoordinates: true }) 
     s = new THREE.Sprite(m) 
     s.scale.set(32, 32, 1.0) 
     s.position.set(position.x,position.y,1) 
     scene.add(s) 
     @sprites.push(s) 

    # --- Mouse handlers --- # 
    # those functions enable to drag the four sprites used to control the corners 

    scene.$container.mousedown(@mouseDown) 
    scene.$container.mousemove(@mouseMove) 
    scene.$container.mouseup(@mouseUp) 

screenToWorld: (mouseX, mouseY) -> 
    return new THREE.Vector3([email protected]@sceneWidth/2, -([email protected])[email protected]/2, 1) 

worldToScreen: (pos) -> 
    return new THREE.Vector2((pos.x/@sceneWidth)+0.5, (pos.y/@sceneHeight)+0.5) 

computeTextureProjection:()=> 
    pos1 = @worldToScreen(@sprites[0].position) 
    pos2 = @worldToScreen(@sprites[1].position) 
    pos3 = @worldToScreen(@sprites[2].position) 
    pos4 = @worldToScreen(@sprites[3].position) 

    srcMat = new THREE.Matrix3(pos1.x, pos2.x, pos3.x, pos1.y, pos2.y, pos3.y, 1, 1, 1) 
    srcMatInv = @inverseMatrix(srcMat) 
    srcVars = @multiplyMatrixVector(srcMatInv, new THREE.Vector3(pos4.x, pos4.y, 1)) 
    A = new THREE.Matrix3(pos1.x*srcVars.x, pos2.x*srcVars.y, pos3.x*srcVars.z, pos1.y*srcVars.x, pos2.y*srcVars.y, pos3.y*srcVars.z, srcVars.x, srcVars.y, srcVars.z) 

    dstMat = new THREE.Matrix3(0, 1, 0, 1, 1, 0, 1, 1, 1) 
    dstMatInv = @inverseMatrix(dstMat) 
    dstVars = @multiplyMatrixVector(dstMatInv, new THREE.Vector3(1, 0, 1)) 
    B = new THREE.Matrix3(0, dstVars.y, 0, dstVars.x, dstVars.y, 0, dstVars.x, dstVars.y, dstVars.z) 

    Ainv = @inverseMatrix(A) 

    C = @multiplyMatrices(B,Ainv) 

    ce = C.elements 

      # I used a Matrix4 since I don't think Matrix3 works in Three.js shaders 

    @uniforms.matC.value = new THREE.Matrix4(ce[0], ce[3], ce[6], 0, ce[1], ce[4], ce[7], 0, ce[2], ce[5], ce[8], 0, 0, 0, 0, 0) 

,這裏是片段着色器:

#ifdef GL_ES 
    precision highp float; 
    #endif 

    uniform sampler2D texture; 
    uniform vec2 resolution; 

    uniform mat4 matC; 

    void main() { 
     vec4 fragCoordH = vec4(gl_FragCoord.xy/resolution, 1, 0); 
     vec4 uvw_t = matC*fragCoordH; 
     vec2 uv_t = vec2(uvw_t.x/uvw_t.z, uvw_t.y/uvw_t.z); 
     gl_FragColor = texture2D(texture, uv_t); 
    } 

附加註意

Maptastic是一個Javascript/CSS投影映射工具。 https://github.com/glowbox/maptasticjs

+0

這太好了。這正是我想爲我的項目做的。任何機會你有一個完整的工作示例使用此代碼(例如,jsfiddle)?作爲主角,這將是非常有用的。 – naitsirhc

+0

是的,我已經在另一臺電腦上使用過了,當我有空時,我會嘗試在github上推它。 –

+1

你爲什麼對這個算法感興趣? –