【问题标题】:Computing a projective transformation to texture an arbitrary quad计算投影变换以纹理任意四边形
【发布时间】:2014-01-10 04:53:09
【问题描述】:

我想计算一个投影变换以在 webgl 中对任意四边形进行纹理处理(如果可能/必要,使用三个.js 和着色器)。

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 描述了解决问题的解决方案,但我怀疑它是否是最简单和最常见的解决方案。现在我认为它可能是正确的!

----结论----

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

【问题讨论】:

    标签: webgl projective-geometry


    【解决方案1】:

    好的,我设法用 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

    【讨论】:

    • 这很棒。这正是我想要为我的项目做的事情。您是否有机会使用此代码(例如 jsfiddle)获得完整的工作示例?作为主角,它将非常有用。
    • 是的,我在另一台电脑上,有时间我会尝试推送到github上。
    • 你为什么对这个算法感兴趣?
    • 我是开源DropBot数字微流控自动化平台的共同创建者之一。我们的控制界面软件会扭曲网络摄像头视频馈送,以校正透视图以与叠加图案对齐。我们的当前版本的 GUI (Microdrop) 是一个独立的应用程序,可以在 PC 上运行,使用 GTK 和 GStreamer。但是,我们希望使用 WebGL 迁移到浏览器 UI,并且需要复制我们现有的视频透视注册。
    • 谢谢,如果你有机会的话,我会非常感激。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-04
    • 2012-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多