【问题标题】:OpenGL Projective Texture Mapping via Shaders通过着色器进行 OpenGL 投影纹理映射
【发布时间】:2014-05-09 02:07:45
【问题描述】:

我正在尝试通过在 OpenGL 3+ 中使用着色器来实现一种简单的投影纹理映射方法。虽然网络上有一些示例,但我在使用着色器创建工作示例时遇到了麻烦。

我实际上计划使用两个着色器,一个用于绘制普通场景,另一个用于投影纹理映射。我有一个绘制场景void ProjTextureMappingScene::renderScene(GLFWwindow *window) 的函数,我正在使用 glUseProgram() 在着色器之间切换。正常绘图工作正常。但是,我不清楚我应该如何在已经纹理化的立方体上渲染投影纹理。我是否必须以某种方式使用模板缓冲区或帧缓冲区对象(场景的其余部分应该不受影响)?

我也不认为我的投影纹理映射着色器是正确的,因为我第二次渲染立方体时它显示为黑色。此外,我尝试使用颜色进行调试,只有着色器的 t 组件似乎非零(因此立方体显示为绿色)。我在下面的片段着色器中覆盖了texColor,只是为了调试。

顶点着色器

#version 330

uniform mat4 TexGenMat;
uniform mat4 InvViewMat;

uniform mat4 P;
uniform mat4 MV;
uniform mat4 N;

layout (location = 0) in vec3 inPosition;
//layout (location = 1) in vec2 inCoord;
layout (location = 2) in vec3 inNormal;

out vec3 vNormal, eyeVec;
out vec2 texCoord;
out vec4 projCoords;

void main()
{
    vNormal = (N * vec4(inNormal, 0.0)).xyz;

    vec4 posEye    = MV * vec4(inPosition, 1.0);
    vec4 posWorld  = InvViewMat * posEye;
    projCoords     = TexGenMat * posWorld;

    // only needed for specular component
    // currently not used
    eyeVec = -posEye.xyz;

    gl_Position = P * MV * vec4(inPosition, 1.0);
}

片段着色器

#version 330

uniform sampler2D projMap;
uniform sampler2D gSampler;
uniform vec4 vColor;

in vec3 vNormal, lightDir, eyeVec;
//in vec2 texCoord;
in vec4 projCoords;

out vec4 outputColor;

struct DirectionalLight
{
    vec3 vColor;
    vec3 vDirection;
    float fAmbientIntensity;
};

uniform DirectionalLight sunLight;

void main (void)
{
    // supress the reverse projection
    if (projCoords.q > 0.0)
    {
        vec2 finalCoords = projCoords.st / projCoords.q;
        vec4 vTexColor = texture(gSampler, finalCoords);
        // only t has non-zero values..why?
        vTexColor = vec4(finalCoords.s, finalCoords.t, finalCoords.r, 1.0);
        //vTexColor = vec4(projCoords.s, projCoords.t, projCoords.r, 1.0);
        float fDiffuseIntensity = max(0.0, dot(normalize(vNormal), -sunLight.vDirection));
        outputColor = vTexColor*vColor*vec4(sunLight.vColor * (sunLight.fAmbientIntensity + fDiffuseIntensity), 1.0);
    }
}

TexGen 矩阵的创建

biasMatrix = glm::mat4(0.5f, 0, 0, 0.5f,
                  0, 0.5f, 0, 0.5f,
                  0, 0, 0.5f, 0.5f,
                  0, 0, 0, 1);

    // 4:3 perspective with 45 fov
    projectorP = glm::perspective(45.0f * zoomFactor, 4.0f / 3.0f, 0.1f, 1000.0f);
    projectorOrigin = glm::vec3(-3.0f, 3.0f, 0.0f);
    projectorTarget = glm::vec3(0.0f, 0.0f, 0.0f);
    projectorV = glm::lookAt(projectorOrigin, // projector origin
                                    projectorTarget,     // project on object at origin 
                                    glm::vec3(0.0f, 1.0f, 0.0f)   // Y axis is up
                                    );
    mModel = glm::mat4(1.0f);
...
texGenMatrix = biasMatrix * projectorP * projectorV * mModel;
invViewMatrix = glm::inverse(mModel*mModelView);

再次渲染立方体

我也不清楚立方体的模型视图应该是什么?它应该使用幻灯机的视图矩阵(就像现在一样)还是普通的视图投影仪?目前,立方体在场景视图的中间呈现为黑色(如果调试则为绿色),就像它从幻灯机中显示的那样(我制作了一个切换热键,以便我可以看到幻灯机“看到”的内容)。立方体也随着视图移动。如何将投影投射到立方体本身?

mModel = glm::translate(projectorV, projectorOrigin);
// bind projective texture
tTextures[2].bindTexture();
// set all uniforms
...
// bind VBO data and draw
glBindVertexArray(uiVAOSceneObjects);
glDrawArrays(GL_TRIANGLES, 6, 36);

在主场景相机和幻灯机相机之间切换

if (useMainCam)
{
    mCurrent   = glm::mat4(1.0f);
    mModelView = mModelView*mCurrent;
    mProjection = *pipeline->getProjectionMatrix();
}
else
{
    mModelView  = projectorV;
    mProjection = projectorP;
}

【问题讨论】:

    标签: c++ opengl mapping textures glsl


    【解决方案1】:

    我已经解决了这个问题。我遇到的一个问题是我混淆了两个相机系统(世界和投影纹理相机)中的矩阵。现在,当我为投影纹理映射部分设置制服时,我为 MVP 值使用正确的矩阵 - 与我用于世界场景的矩阵相同。

    glUniformMatrix4fv(iPTMProjectionLoc, 1, GL_FALSE, glm::value_ptr(*pipeline->getProjectionMatrix()));
    glUniformMatrix4fv(iPTMNormalLoc, 1, GL_FALSE, glm::value_ptr(glm::transpose(glm::inverse(mCurrent))));
    glUniformMatrix4fv(iPTMModelViewLoc, 1, GL_FALSE, glm::value_ptr(mCurrent));
    glUniformMatrix4fv(iTexGenMatLoc, 1, GL_FALSE, glm::value_ptr(texGenMatrix));
    glUniformMatrix4fv(iInvViewMatrix, 1, GL_FALSE, glm::value_ptr(invViewMatrix));
    

    此外,invViewMatrix 只是视图矩阵的逆矩阵,而不是模型视图(这并没有改变我的行为,因为模型是恒等的,但它是错误的)。对于我的项目,我只想选择性地渲染一些具有投影纹理的对象。为此,对于每个对象,我必须确保当前着色器程序是使用glUseProgram(projectiveTextureMappingProgramID) 的投影纹理程序。接下来,我计算这个对象所需的矩阵:

    texGenMatrix = biasMatrix * projectorP * projectorV * mModel;
    invViewMatrix = glm::inverse(mView);
    

    回到着色器,顶点着色器是正确的,只是我为当前对象重新添加了 UV 纹理坐标 (inCoord) 并将它们存储在 texCoord 中。

    对于片段着色器,我更改了主要功能以钳制投影纹理,使其不会重复(我无法让它与客户端一起工作GL_CLAMP_TO_EDGE),并且我还使用默认对象纹理和 UV 坐标,以防投影仪没有覆盖整个对象(我还从投影纹理中删除了照明,因为在我的情况下不需要它):

    void main (void)
    {
        vec2 finalCoords    = projCoords.st / projCoords.q;
        vec4 vTexColor      = texture(gSampler, texCoord);
        vec4 vProjTexColor  = texture(projMap, finalCoords);
        //vec4 vProjTexColor  = textureProj(projMap, projCoords);
        float fDiffuseIntensity = max(0.0, dot(normalize(vNormal), -sunLight.vDirection));
    
        // supress the reverse projection
        if (projCoords.q > 0.0)
        {
            // CLAMP PROJECTIVE TEXTURE (for some reason gl_clamp did not work...)
            if(projCoords.s > 0 && projCoords.t > 0 && finalCoords.s < 1 && finalCoords.t < 1)
                //outputColor = vProjTexColor*vColor*vec4(sunLight.vColor * (sunLight.fAmbientIntensity + fDiffuseIntensity), 1.0);
                outputColor = vProjTexColor*vColor;
            else
                outputColor = vTexColor*vColor*vec4(sunLight.vColor * (sunLight.fAmbientIntensity + fDiffuseIntensity), 1.0);
        }
        else
        {
            outputColor = vTexColor*vColor*vec4(sunLight.vColor * (sunLight.fAmbientIntensity + fDiffuseIntensity), 1.0);
        }
    }
    

    如果你被卡住并且由于某种原因无法让着色器工作,你可以查看“OpenGL 4.0 Shading Language Cookbook”(纹理章节)中的一个示例 - 我实际上错过了这个,直到我自己让它工作.

    除了上述所有内容之外,如果算法正常工作,调试的一个很大帮助是为投影相机绘制平截头体(作为线框)。我使用着色器绘制截锥体。片段着色器只是分配纯色,而顶点着色器在下面列出并解释:

    #version 330
    
    // input vertex data
    layout(location = 0) in vec3 vp;
    
    uniform mat4 P;
    uniform mat4 MV;
    uniform mat4 invP;
    uniform mat4 invMV;
    void main()
    {
        /*The transformed clip space position c of a
        world space vertex v is obtained by transforming 
        v with the product of the projection matrix P 
        and the modelview matrix MV
    
        c = P MV v
    
        So, if we could solve for v, then we could 
        genrerate vertex positions by plugging in clip 
        space positions. For your frustum, one line 
        would be between the clip space positions 
    
        (-1,-1,near) and (-1,-1,far), 
    
        the lower left edge of the frustum, for example.
    
        NB: If you would like to mix normalized device 
        coords (x,y) and eye space coords (near,far), 
        you need an additional step here. Modify your 
        clip position as follows
    
        c' = (c.x * c.z, c.y * c.z, c.z, c.z)
    
        otherwise you would need to supply both the z 
        and w for c, which might be inconvenient. Simply 
        use c' instead of c below.
    
    
        To solve for v, multiply both sides of the equation above with 
    
              -1       
        (P MV) 
    
        This gives
    
              -1      
        (P MV)   c = v
    
        This is equivalent to
    
          -1  -1      
        MV   P   c = v
    
         -1
        P   is given by
    
        |(r-l)/(2n)     0         0      (r+l)/(2n) |
        |     0    (t-b)/(2n)     0      (t+b)/(2n) |
        |     0         0         0         -1      |
        |     0         0   -(f-n)/(2fn) (f+n)/(2fn)|
    
        where l, r, t, b, n, and f are the parameters in the glFrustum() call.
    
        If you don't want to fool with inverting the 
        model matrix, the info you already have can be 
        used instead: the forward, right, and up 
        vectors, in addition to the eye position.
    
        First, go from clip space to eye space
    
             -1   
        e = P   c
    
        Next go from eye space to world space
    
        v = eyePos - forward*e.z + right*e.x + up*e.y
    
        assuming x = right, y = up, and -z = forward.
        */
        vec4 fVp = invMV * invP * vec4(vp, 1.0);
        gl_Position = P * MV * fVp;
    }
    

    制服是这样使用的(确保使用正确的矩阵):

    // projector matrices
    glUniformMatrix4fv(iFrustumInvProjectionLoc, 1, GL_FALSE, glm::value_ptr(glm::inverse(projectorP)));
    glUniformMatrix4fv(iFrustumInvMVLoc, 1, GL_FALSE, glm::value_ptr(glm::inverse(projectorV)));
    // world camera
    glUniformMatrix4fv(iFrustumProjectionLoc, 1, GL_FALSE, glm::value_ptr(*pipeline->getProjectionMatrix()));
    glUniformMatrix4fv(iFrustumModelViewLoc, 1, GL_FALSE, glm::value_ptr(mModelView));
    

    要获取截锥体的顶点着色器所需的输入顶点,您可以执行以下操作来获取坐标(然后只需将它们添加到您的顶点数组中):

    glm::vec3 ftl = glm::vec3(-1, +1, pFar); //far top left
    glm::vec3 fbr = glm::vec3(+1, -1, pFar); //far bottom right
    glm::vec3 fbl = glm::vec3(-1, -1, pFar); //far bottom left
    glm::vec3 ftr = glm::vec3(+1, +1, pFar); //far top right
    glm::vec3 ntl = glm::vec3(-1, +1, pNear); //near top left
    glm::vec3 nbr = glm::vec3(+1, -1, pNear); //near bottom right
    glm::vec3 nbl = glm::vec3(-1, -1, pNear); //near bottom left
    glm::vec3 ntr = glm::vec3(+1, +1, pNear); //near top right
    
    glm::vec3   frustum_coords[36] = {
        // near
        ntl, nbl, ntr, // 1 triangle
        ntr, nbl, nbr,
        // right
        nbr, ftr, ntr,
        ftr, nbr, fbr,
        // left
        nbl, ftl, ntl,
        ftl, nbl, fbl,
        // far
        ftl, fbl, fbr,
        fbr, ftr, ftl,
        //bottom
        nbl, fbr, fbl,
        fbr, nbl, nbr,
        //top
        ntl, ftr, ftl,
        ftr, ntl, ntr
    };
    

    说到底,很高兴看看它的样子:

    如您所见,我应用了两个投影纹理,一个是 Blender 的 Suzanne 猴子头上的生物危害图像,另一个是地板和一个小立方体上的笑脸纹理。您还可以看到立方体部分被投影纹理覆盖,而其余部分则以其默认纹理显示。最后,您可以看到投影仪相机的绿色平截头体线框 - 一切看起来都正确。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-05
      • 1970-01-01
      • 1970-01-01
      • 2013-08-23
      • 1970-01-01
      • 1970-01-01
      • 2010-09-19
      相关资源
      最近更新 更多