【问题标题】:Parralax mapping does not work correctly using OpenGL and GLSL使用 OpenGL 和 GLSL 时视差映射无法正常工作
【发布时间】:2014-11-20 17:38:15
【问题描述】:

我尝试使用 OpenGL 和 GLSL API 在我的 3D 引擎中实现视差映射,但显示不正确。为了学习和应用这种技术的复杂性,我受到以下 PDF 教程(第 16、17 和 18 页)的启发:

https://www.opengl.org/sdk/docs/tutorials/TyphoonLabs/Chapter_4.pdf

要产生一个非常基本的视差效果(没有任何照明效果),我需要使用 2 个纹理:

- 1 diffuse (color) texture (BPP: 24 -> RGB - format: JPEG)

- 1 displacement (height/grayscale) texture (BPP: 24 -> RGB - format: JPEG)

我使用著名且非常有用的软件“CrazyBump”来生成我的置换贴图。此外,该软件可以显示视差映射在像我这样的外部 3D 应用程序中的样子的 3D 视图。

第一次,这里是'CrazyBump'的显示(CrazyBump使用灯光效果,但这里不重要):

如您所见,视差效果已正确渲染。

现在这是我的场景中的渲染(使用由“CrazyBump”生成的相同置换纹理,没有亮度。我只想看到上面的表面的假变形)。

如您所见,显示不一样,当然也不正确。

为了尝试产生相同的效果,我将课程应用到我在帖子开头谈到的 PDF 文件中。

关于信息,我之前已经为我的引擎实现了“法线映射”技术(所以切线和双切线向量是正确的!)。

要执行我的着色器程序,我需要相机在世界空间和矩阵(ModelViewProj、ModelMatrix 和 NormalMatrix)中的位置。

这是我使用的客户端 C++ 代码:

glm::mat4 modelViewMatrix = pRenderBatch->GetModelViewMatrix();

glm::mat3 normalMatrix = glm::mat3(glm::vec3(modelViewMatrix[0]),
    glm::vec3(modelViewMatrix[1]), glm::vec3(modelViewMatrix[2]));

this->SetUniform("ModelViewProjMatrix", pRenderBatch->GetModelViewProjMatrix());
this->SetUniform("ModelViewMatrix", modelViewMatrix);
this->SetUniform("NormalMatrix", normalMatrix);

//Bound on channel 0
glActiveTexture(GL_TEXTURE0);
this->m_pTextureManager.PushAndBindTexture(
    pMaterial->GetDiffuseTexture());
{
    this->SetUniform("DiffuseSampler", 0);
}
//Bound on channel 1
glActiveTexture(GL_TEXTURE1);
    this->m_pTextureManager.PushAndBindTexture(
        pMaterial->GetDisplacementTexture());
{
    this->SetUniform("HeightSampler", 1);
}

顶点着色器:

#version 440

/*
** Vertex attributes.
*/
layout (location = 0) in vec4 VertexPosition;
layout (location = 1) in vec2 VertexTexture;
layout (location = 2) in vec3 VertexNormal;
layout (location = 3) in vec3 VertexTangent;
layout (location = 4) in vec3 VertexBitangent;

/*
** Uniform matrices.
*/
uniform mat4 ModelViewProjMatrix;
uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;

//Outputs
out vec2 TexCoords;
out vec3 viewDir_TS;

/*
** Vertex shader entry point.
*/
void main(void)
{
    //Texture coordinates
    TexCoords = VertexTexture;

    //Vertex position in world space
    vec3 Position_CS = vec3(ModelViewMatrix * VertexPosition);
    //Vertex normal in world space
    vec3 Normal_CS = NormalMatrix * VertexNormal;
    //Vertex tangent in world space
    vec3 Tangent_CS = NormalMatrix * VertexTangent;
    //Vertex bitangent in world space
    vec3 Bitangent_CS = NormalMatrix * VertexBitangent;

    //View vector in world space
    vec3 viewDir_CS = -Position_CS;

    //TBN matrix
    mat3 TBN =  mat3(
        Tangent_CS.x, Bitangent_CS.x, Normal_CS.x,
        Tangent_CS.y, Bitangent_CS.y, Normal_CS.y,
        Tangent_CS.z, Bitangent_CS.z, Normal_CS.z);

    //2 others ways to compute view vector in tangent space

        //mat3 TBN = transpose(mat3(Tangent_CS, Bitangent_CS, Normal_CS));

        /*viewDir_TS = vec3(
            dot(viewDir_CS, Tangent_CS),
            dot(viewDir_CS, Bitangent_CS),
            dot(viewDir_CS, Normal_CS)
        );*/

    //View vector converted in tangent space (not normalized)
    viewDir_TS = TBN * viewDir_CS;

    gl_Position = ModelViewProjMatrix * VertexPosition;
}

最后是片段着色器:

#version 440

layout (location = 0) out vec4 FragColor;

//Texture coordinates
in vec2 TexCoords;

//View (camera) vector in tangent space
in vec3 viewDir_TS;

//Diffuse texture sampler
uniform sampler2D DiffuseSampler;
//Displacement texture sampler
//(height map/grayscale map)
uniform sampler2D HeightSampler;

/*
** Fragment shader entry point
*/
void main(void)
{
    //Parralax intensity {scale(s), bias(b)}
    vec2 ScaleBias = vec2(0.04f, 0.02f);

    //Height(h) range [0;1] (float) recovered from height map (HeightSampler)
    float Height = texture2D(HeightSampler, TexCoords.st).r; 

    //Height scaled and biased according to the formula: hsb = h · s + b
    float HSB = Height * ScaleBias.x + ScaleBias.y;

    //View vector in tangent space normalized
    vec3 viewDirNorm_TS = normalize(viewDir_TS);

    //Computes texture offset according to the formula: Tn = To + (hsb · V{x, y}) 
    vec2 textOffset = TexCoords + (viewDirNorm_TS.xy * HSB);

    //Computes final diffuse texture color using parralax offset
    FragColor = texture2D(DiffuseSampler, textOffset);
}

我尝试修改比例和偏差值但没有成功:显示仍然不正确。

我认为我的置换纹理没有正确加载,但事实并非如此(有关信息,我使用了 NVIDIA NSight degugger)。

如果我以以下方式加载置换贴图 (GL_LUMINANCE):

glTexImage2D(this->m_Target, 0, GL_LUMINANCE,
    this->m_PixelData.GetWidth(), this->m_PixelData.GetHeight(),
            0, GL_BGR, GL_UNSIGNED_BYTE, OFFSET_BUFFER(0));

像素缓冲区开始于:

如果我以以下方式加载置换贴图 (GL_RGB):

glTexImage2D(this->m_Target, 0, GL_RGB, this->m_PixelData.GetWidth(), this->m_PixelData.GetHeight(), 0, GL_BGR, GL_UNSIGNED_BYTE, OFFSET_BUFFER(0));

像素缓冲区开始于:

在这两种情况下,我们都有灰度像素。

所以我的问题似乎不是来自内存中加载的纹理。也许矩阵有问题或空间有问题。我真的迷路了。

有人可以帮帮我吗?

非常感谢您的帮助!

【问题讨论】:

  • 单一的textOffset 方法是一种早期方法,它进行了非常大的近似:偏移处的深度将是相同的。为了获得更好的效果,跟踪(或“步进”)高度图,直到找到交叉点。使用二分搜索来改进它。然后使用交叉点的坐标作为颜色。
  • 你想说公式 Tn = To + (hsb · V{x, y}) 不正确。所以行 vec2 textOffset = TexCoords + (viewDirNorm_TS.xy * HSB);是不正确的 ?这是我看到的所有教程中使用的方法,结果似乎是正确的。我不明白你的观点。
  • 我不确定该方法的细节,但它必须是近似的。我以为这就是你的问题。现在我仔细观察,看起来viewDir_TS 可能只需要取反,或者您的切线空间不起作用。 FragColor = vec4(viewDir_TS, 1); 会很有趣。
  • 我之前应该注意到的,但是法线矩阵应该是glm::inverseTranspose(glm::mat3(modelViewMatrix)),虽然不会再次与正交变换产生差异。我认为在 TBN 之前可能由于column-major construction 而被转置。

标签: opengl glsl opengl-es-2.0 opengl-4 glsles


【解决方案1】:

问题刚刚出线:

float HSB = Height * ScaleBias.x + ScaleBias.y;

不是加法而是减法:

float HSB = Height * ScaleBias.x - ScaleBias.y;

屏幕截图 1:

截图 2:

当然,我已经为亮度添加了法线贴图。

希望这篇文章对你有用!

【讨论】:

    【解决方案2】:

    单一的textOffset 方法是一种早期的视差技术,它进行了非常大的近似:偏移处的深度将是相同的。这就是为什么效果看起来有点奇怪的原因。我认为在您的情况下,其中一个切向量正面临错误的方向。作为测试,尝试否定textOffset.xtextOffset.y。我更习惯于在跳转到切线空间之前看到着色器使用眼睛空间,但看不到您的代码有任何直接问题。

    为了获得更好的效果,跟踪(或“步进”)高度图,直到找到交叉点。使用二分法或割线搜索来改进它。然后使用交叉点的坐标作为颜色。这有时称为浮雕贴图、陡峭视差贴图和视差遮挡贴图。

    这是未经测试的,但希望能给出这个想法:

    const int steps = 20;
    const float scale = 0.1;
    
    vec3 pos = vec3(TexCoords, 1.0); //1.0 as ray starts at surface/max height
    vec3 dir = -viewDir_TS / viewDir_TS.z; //I assume viewDir_TS.z is negative
    dir.xy *= scale;
    dir /= steps;
    
    //linear steps
    float height;
    for (int i = 0; i < steps; ++i)
    {
        pos += dir;
        height = texture2D(HeightSampler, pos.xy).r
        if (pos.z < height)
            break;
    }
    
    //binary search
    for (int i = 0; i < 4; ++i)
    {
        dir *= 0.5;
        if (pos.z < height)
            pos -= dir;
        else
            pos += dir;
        height = texture2D(HeightSampler, pos.xy).r
    }
    
    //write output
    FragColor = texture2D(DiffuseSampler, pos.xy);
    

    一些较小的事情:我一直看到法线和切线向量乘以法线矩阵。切线实际上应该乘以模型视图矩阵,尽管如果变换是正交的,这并不重要,这几乎总是如此。不需要计算和存储双切线,因为它可以通过叉积动态计算,避免了绘图时的一点内存带宽。

    【讨论】:

    • 我已经更新了关于 C++ 客户端(统一变量)和使用眼睛空间而不是世界空间的顶点着色器代码的帖子,但结果是相同的。现在切向量是正确的,因为我使用相同的法线映射技术。我对这项技术使用相同的切线空间。
    • 我将按照您的建议尝试陡峭视差映射技术。我会让你跟上。如果您想看到关于此的好文章,请查看:sunandblackcat.com/tipFullView.php?topicid=28 谢谢您的回复。
    猜你喜欢
    • 2013-12-27
    • 1970-01-01
    • 2014-01-11
    • 1970-01-01
    • 2018-09-09
    • 1970-01-01
    • 2014-12-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多