webgl - 计算投影变换以纹理任意四边形

标签 webgl projective-geometry

我想计算一个投影变换来纹理 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 中的一个解决方案,但意识到它是一个执行简单透视校正插值的解决方案,幸运的是默认情况下启用该解决方案。

我在梯形上发现了很多东西,这是我想要解决的更普遍问题的简单版本:1 , 23 。我首先认为这些会有帮助,但相反,它们导致我大量阅读和误解。

最后,这个page描述了解决该问题的解决方案,但我怀疑它是否是最简单且最常见的解决方案。现在我认为这可能是正确的!

----结论----

我一直在寻找解决方案,不是因为这是一个特别复杂的问题,而是因为我正在寻找一个简单且典型/通用的解决方案。我认为在很多情况下(每个视频映射应用程序)这是一个很容易解决的问题,而且答案也很简单。

最佳答案

好吧,我设法用 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(mouseX-@sceneX-@sceneWidth/2, -(mouseY-@sceneY)+@sceneHeight/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

关于webgl - 计算投影变换以纹理任意四边形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20718663/

相关文章:

webgl - 如何快速学习 WebGL?

javascript - 在世界地图上绘制的条形图

optimization - WebGL 绘图调用真的非常慢吗?

image-processing - 使用单应性描述两个图像之间的非线性变换

glsl - 如何使用 Open GL ES 2.0 或 WebGL 创建雾?

textures - WebGL 如何用于通用计算(GPGPU)?

image-processing - 需要一些帮助来理解公式

computer-vision - 计算单应性给定两个相机之间的旋转和平移

matlab - 如何实现 3d 重建算法