2014-11-20 63 views
3

我尝试使用OpenGL和GLSL API在我的3D引擎中实现parralax映射,但显示不正确。要了解并应用这种技术,我用以下的PDF教程(16页,17和18)的启发的复杂性:Parralax映射不能正确使用OpenGL和GLSL

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

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

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

enter image description here

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

enter image description here

我用着名的非常有用的软件'CrazyBump'来生成我的置换贴图。此外,该软件还可以显示在我的外部3D应用程序中Parralax映射的外观。

在第一时间,下面是“CrazyBump”显示(CrazyBump利用灯光效果,但它并不重要):

enter image description here

正如你可以看到parralax效果正确呈现。现在这里是我场景中的渲染(使用'CrazyBump'生成的同样的位移纹理,没有亮度,我想看到的是像上面那样的假表面变形)。

enter image description here

正如你所看到的,显示是不一样的,当然不正确。

要尝试产生相同的效果,我在我的帖子开头讨论的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的信息)。

enter image description here

如果我加载我位移映射以下方式(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)); 

像素缓冲器开始于:

enter image description here

如果我加载我位移映射如下(GL_RGB):

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

像素缓冲区开始的:

enter image description here

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

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

有人可以帮助我吗?

非常感谢您的帮助!

+0

单个'textOffset'方法是一个很早的方法,它可以做出非常大的近似值:偏移处的深度将是相同的。为了获得更好的效果,请在高度图中追踪(或“步骤”),直到找到交点。使用二进制搜索来改进它。然后使用交点的坐标来表示颜色。 – jozxyqk 2014-11-20 17:44:10

+0

你想说公式Tn = To +(hsb·V {x,y})是不正确的。所以行vec2 textOffset = TexCoords +(viewDirNorm_TS.xy * HSB);是不正确的 ?这是我看到的所有教程中使用的方法,结果似乎是正确的。我不明白你的观点。 – user1364743 2014-11-20 17:51:55

+0

我不确定该方法的具体细节,但必须是近似的。我认为这是你的问题所在。现在我看起来像'viewDir_TS'看起来可能只需要否定或者切线空间不起作用。 'FragColor = vec4(viewDir_TS,1);'会很有趣。 – jozxyqk 2014-11-20 18:07:53

回答

1

单一的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); 

一对夫妇的小事情:我看到正常的和切线矢量通过正常的矩阵相乘,所有的时间。该切线应该真的被模型视图矩阵相乘,尽管如果变换是正常的,这几乎总是如此,这并不重要。计算和存储bitangents并不是必须的,因为它可以通过交叉产品动态计算,避免了绘图时的一点存储器带宽。

+0

我已经更新了关于C++客户端(统一变量)和顶点着色器代码的帖子,使用眼图空间而不是世界空间,但结果是一样的。现在切向量是正确的,因为我使用相同的法线贴图技术。我为这种技术使用相同的切线空间。 – user1364743 2014-11-20 21:48:16

+0

我会尝试像你建议我一样陡峭的parralax映射技术。我会及时通知你。如果您想查看关于此的好文章,请参阅:http://sunandblackcat.com/tipFullView.php?topicid=28感谢您的回复。 – user1364743 2014-11-20 21:58:46

3

问题只是从线传来:

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

这不是加法而是减法:

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

截图1:

enter image description here

截图2:

enter image description here

当然,我已经添加了光度的法线贴图。

我希望这篇文章会有用!