18

编辑:您可能想从“编辑 3”开始,因为我已经解决了很多问题

这是我应用于 icosphere 的普通立方体贴图的屏幕截图:

在此处输入图像描述

我的立方体贴图 icosphere 的切线是使用以下代码生成的。m_indices中的一个std::vector索引进入 中std::vector的顶点m_vertices

std::vector<glm::vec3> storedTan(m_vertices.size(),glm::vec3(0,0,0));

// tangents
for(int i = 0; i < m_indices.size(); i+=3)
{
    int i1 = m_indices[i];
    int i2 = m_indices[i+1];
    int i3 = m_indices[i+2];

    VertexData v1 = m_vertices[i1];
    VertexData v2 = m_vertices[i2];
    VertexData v3 = m_vertices[i3];

    glm::vec3 p1 = glm::vec3(v1.position[0],v1.position[1],v1.position[2]);
    glm::vec3 p2 = glm::vec3(v2.position[0],v2.position[1],v2.position[2]);
    glm::vec3 p3 = glm::vec3(v3.position[0],v3.position[1],v3.position[2]);

    glm::vec3 t1 = glm::vec3(v1.tcoords[0],v1.tcoords[1],v1.tcoords[2]);
    glm::vec3 t2 = glm::vec3(v2.tcoords[0],v2.tcoords[1],v2.tcoords[2]);
    glm::vec3 t3 = glm::vec3(v3.tcoords[0],v3.tcoords[1],v3.tcoords[2]);

    std::function<glm::vec2(glm::vec3)> get_uv = [=](glm::vec3 STR)
    {
        float sc, tc, ma;
        float x = std::abs(STR.x);
        float y = std::abs(STR.y);
        float z = std::abs(STR.z);
        if(x > y && x > z)
        {
            if(STR.x > 0)
            {
                sc = -STR.z;
                tc = -STR.y;
                ma = STR.x;
            }
            else
            {
                sc = STR.z;
                tc = -STR.t;
                ma = STR.x;
            }
        }
        else if(y > z)
        {
            if(STR.y > 0)
            {
                sc = STR.x;
                tc = STR.z;
                ma = STR.y;
            }
            else
            {
                sc = STR.x;
                tc = -STR.z;
                ma = STR.y;
            }
        }
        else
        {
            if(STR.z > 0)
            {
                sc = STR.x;
                tc = -STR.y;
                ma = STR.z;
            }
            else
            {
                sc = -STR.x;
                tc = -STR.y;
                ma = STR.z;
            }
        }
        return glm::vec2((sc/std::abs(ma) + 1.0) / 2.0,(tc/std::abs(ma) + 1.0) / 2.0);
    };

    glm::vec2 uv1 = get_uv(t1);
    glm::vec2 uv2 = get_uv(t2);
    glm::vec2 uv3 = get_uv(t3);

    glm::vec3 edge1 = p2 - p1;
    glm::vec3 edge2 = p3 - p1;

    glm::vec2 tedge1 = uv2 - uv1;
    glm::vec2 tedge2 = uv3 - uv1;

    float r = 1.0f / (tedge1.x * tedge2.y - tedge2.x - tedge1.y);

    glm::vec3 sdir((tedge2.y * edge1.x - tedge1.y * edge2.x) * r,
                   (tedge2.y * edge1.y - tedge1.y * edge2.y) * r,
                   (tedge2.y * edge1.z - tedge1.y * edge2.z) * r);

    glm::vec3 tdir((tedge1.x * edge2.x - tedge2.x * edge1.x) * r,
                   (tedge1.x * edge2.y - tedge2.x * edge1.y) * r,
                   (tedge1.x * edge2.z - tedge2.x * edge1.z) * r);

    m_vertices[i1].tangent[0] += sdir.x;
    m_vertices[i1].tangent[1] += sdir.y;
    m_vertices[i1].tangent[2] += sdir.z;

    m_vertices[i2].tangent[0] += sdir.x;
    m_vertices[i2].tangent[1] += sdir.y;
    m_vertices[i2].tangent[2] += sdir.z;

    m_vertices[i3].tangent[0] += sdir.x;
    m_vertices[i3].tangent[1] += sdir.y;
    m_vertices[i3].tangent[2] += sdir.z;

    storedTan[i1] += sdir;
    storedTan[i2] += sdir;
    storedTan[i3] += sdir;
}

for(int i = 0; i < m_vertices.size(); ++i)
{
    glm::vec3 n = glm::vec3(m_vertices[i].normal[0],m_vertices[i].normal[1],m_vertices[i].normal[2]);
    glm::vec3 t = glm::vec3(m_vertices[i].tangent[0],m_vertices[i].tangent[1],m_vertices[i].tangent[2]);

    glm::vec3 newT = glm::normalize(t - n * glm::dot(n,t));
    m_vertices[i].tangent[0] = newT.x;
    m_vertices[i].tangent[1] = newT.y;
    m_vertices[i].tangent[2] = newT.z;
    m_vertices[i].tangent[3] = (glm::dot(glm::cross(n,t), storedTan[i]) < 0.0f) ? -1.0f : 1.0f;
}

我的 VertexData 看起来像这样 BTW:

struct VertexData
{
    GLfloat position[4];
    GLfloat normal[3];
    GLfloat tcoords[3];
    GLfloat tangent[4];
};

我知道 currenttcoords和很好positionnormal否则你不会看到上面的屏幕截图)。

然后我的顶点着色器看起来像这样:

#version 400

layout (location = 0) in vec4 in_position;
layout (location = 1) in vec3 in_normal;
layout (location = 2) in vec3 in_UV;
layout (location = 3) in vec4 in_tangent;

struct PointLight
{
    bool active;

    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    float constant;
    float linear;
    float quadratic;
};

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 lightMVP;

uniform PointLight uLight;

smooth out vec3 ex_UV;
out vec3 ex_normal;
out vec3 ex_positionCameraSpace;
out vec3 ex_originalPosition;
out vec3 ex_positionWorldSpace;
out vec4 ex_positionLightSpace;
out vec3 ex_tangent;
out vec3 ex_binormal;

out PointLight ex_light;

void main()
{
    gl_Position = projection * view * model * in_position;

    ex_UV = in_UV;
    ex_normal = mat3(transpose(inverse(view * model))) * in_normal;
    ex_positionCameraSpace =  vec3(view * model * in_position);
    ex_originalPosition = vec3(in_position.xyz);
    ex_positionWorldSpace = vec3(model*in_position);
    ex_positionLightSpace = lightMVP * model * in_position;

    ex_tangent = mat3(transpose(inverse(view * model))) * in_tangent.xyz;
    ex_binormal = cross(ex_normal,ex_tangent);

    // provide the fragment shader with a light in view space rather than world space
    PointLight p = uLight;
    p.position = vec3(view * vec4(p.position,1.0));
    ex_light = p;
}

最后我的片段着色器看起来像这样:

#version 400

layout (location = 0) out vec4 color;

struct Material
{
    bool useMaps;
    samplerCube diffuse;
    samplerCube specular;
    samplerCube normal;
    float shininess;
    vec4 color1;
    vec4 color2;
};

struct PointLight
{
    bool active;

    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    float constant;
    float linear;
    float quadratic;
};

uniform Material uMaterial;

smooth in vec3 ex_UV;
in vec3 ex_normal;
in vec3 ex_positionCameraSpace;
in vec3 ex_originalPosition;
in vec3 ex_positionWorldSpace;
in vec4 ex_positionLightSpace;

in vec3 ex_tangent;
in vec3 ex_binormal;

in PointLight ex_light;

/* ******************
Provides a better lookup into a cubemap
******************* */
vec3 fix_cube_lookup(vec3 v, float cube_size)
{
    float M = max(max(abs(v.x), abs(v.y)), abs(v.z));
    float scale = (cube_size - 1) / cube_size;
    if (abs(v.x) != M)
        v.x *= scale;
    if (abs(v.y) != M)
        v.y *= scale;
    if (abs(v.z) != M)
        v.z *= scale;
    return v;
}

/* *********************
Calculates the color when using a point light. Uses shadow map
********************* */
vec3 CalcPointLight(PointLight light, Material mat, vec3 normal, vec3 fragPos, vec3 originalPos, vec3 viewDir)
{
    // replace the normal with lookup normal. This is now in tangent space
    vec3 textureLookup = fix_cube_lookup(normalize(ex_originalPosition),textureSize(mat.normal,0).x);
    normal = texture(mat.normal,textureLookup).rgb;

    // the direction the light is in in the light position - fragpos
    // light dir and view dir are now in tangent space
    vec3 lightDir = transpose(mat3(ex_tangent,ex_binormal,ex_normal)) * normalize(fragPos - light.position);
    viewDir = transpose(mat3(ex_tangent,ex_binormal,ex_normal)) * viewDir;

    // get the diffuse color
    textureLookup = fix_cube_lookup(normalize(ex_originalPosition),textureSize(mat.diffuse,0).x);
    vec3 diffuseMat = vec3(0.0);
    if(mat.useMaps)
        diffuseMat = texture(mat.diffuse,textureLookup).rgb;
    else
        diffuseMat = mat.color1.rgb;

    // get the specular color
    textureLookup = fix_cube_lookup(normalize(ex_originalPosition),textureSize(mat.specular,0).x);
    vec3 specularMat = vec3(0.0);
    if(mat.useMaps)
        specularMat = texture(mat.specular,textureLookup).rgb;
    else
        specularMat = mat.color2.rgb;

    // the ambient color is the amount of normal ambient light hitting the diffuse texture
    vec3 ambientColor = light.ambient * diffuseMat;

    // Diffuse shading
    float diffuseFactor = dot(normal, -lightDir);
    vec3 diffuseColor = vec3(0,0,0);
    vec3 specularColor = vec3(0,0,0);
    if(diffuseFactor > 0)
        diffuseColor = light.diffuse * diffuseFactor * diffuseMat;

    // Specular shading
    vec3 reflectDir = normalize(reflect(lightDir, normal));
    float specularFactor = pow(dot(viewDir,reflectDir), mat.shininess);
    if(specularFactor > 0 && diffuseFactor > 0)
        specularColor = light.specular * specularFactor * specularMat;

    float lightDistance = length(fragPos - light.position);
    float attenuation = light.constant + light.linear * lightDistance + light.quadratic * lightDistance * lightDistance;

    return ambientColor + (diffuseColor + specularColor) / attenuation;
}

void main(void)
{
    vec3 norm = normalize(ex_normal);
    vec3 viewDir = normalize(-ex_positionCameraSpace);

    vec3 result = CalcPointLight(ex_light,uMaterial,norm,ex_positionCameraSpace, ex_positionWorldSpace,viewDir);

    color = vec4(result,1.0);
}

据我所知:

  1. 我的切线计算正确。
  2. 我的法线贴图对我来说就像一张法线贴图。
  3. 我正在将光线和视图方向更改为切线空间以匹配我的法线贴图。

结果什么都没有。即没有任何东西被绘制到屏幕上。根本不是纯色。所以就像后面的一切都是没有遮挡的。

如果我将查找丢弃到我的法线贴图中,而只使用切线矩阵光并查看,我会得到以下信息:

在此处输入图像描述

这上面有一个后处理镜头光晕,它产生了那些有趣的点点滴滴。我认为重要的是来自法线似乎有些准确的表面的压倒性眩光。

如果我只是通过切线矩阵变换光线,我会得到:

在此处输入图像描述

所有这些结合起来告诉我我不知道我哪里出错了。

我有一种暗示,这是我的切线一代,因为其他作品似乎遵循我读过的每个教程似乎都在说的话。切线是在考虑立方体贴图的 icosphere 的情况下生成的。因此,要从立方体贴图通常的 3D 坐标中确定<S,T>2D<U,V>坐标,我:

  1. 用最大值确定我所在的脸
  2. 使用https://www.opengl.org/registry/specs/ARB/texture_cube_map.txt中的代码来确定 S、T 坐标

这是我正在谈论的https://www.opengl.org/registry/specs/ARB/texture_cube_map.txt的摘录。

  major axis
  direction     target                             sc     tc    ma
  ----------    -------------------------------    ---    ---   ---
   +rx          TEXTURE_CUBE_MAP_POSITIVE_X_ARB    -rz    -ry   rx
   -rx          TEXTURE_CUBE_MAP_NEGATIVE_X_ARB    +rz    -ry   rx
   +ry          TEXTURE_CUBE_MAP_POSITIVE_Y_ARB    +rx    +rz   ry
   -ry          TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB    +rx    -rz   ry
   +rz          TEXTURE_CUBE_MAP_POSITIVE_Z_ARB    +rx    -ry   rz
   -rz          TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB    -rx    -ry   rz

 Using the sc, tc, and ma determined by the major axis direction as
 specified in the table above, an updated (s,t) is calculated as
 follows

    s   =   ( sc/|ma| + 1 ) / 2
    t   =   ( tc/|ma| + 1 ) / 2

 This new (s,t) is used to find a texture value in the determined
 face's 2D texture image using the rules given in sections 3.8.5
 and 3.8.6." ...

编辑 我不知道为什么我以前没有,但我已经在几何着色器中输出了法线、切线和双切线,以查看它们面对的方式。我使用了本教程

他们来了

黄色是面法线,绿色是顶点法线。我不确定为什么顶点法线看起来是错误的,它们不会影响任何其他照明,所以它可能只是我的几何着色器中的一个错误。

切线是红色的,副法线是蓝色的。这些看起来(很难说)它们是相互垂直的,这是正确的,但除了它们没有指向统一的方向。这就是我以前的斑驳模式。

我不知道如何解决这个问题。

编辑 2 我已经解决了显示法线等问题。现在已修复。

结果,我添加了一些阴影使其更清晰,每种​​颜色都是不同的立方体面。

在此处输入图像描述

我改变的另一件事是查找我的法线贴图。我忘记将范围调整回 -1 到 1(从 0 到 1)。

normal = texture(mat.normal,textureLookup).rgb * 2.0 - 1.0;

这不能解决我的问题。

令人困惑的部分是,当我尝试使用纹理中的法线时,我没有得到任何渲染。深度缓冲区中没有任何内容。我已经检查并再次检查了纹理是否可以从着色器访问(因此原始屏幕截图显示了应用于球体的纹理)。

因为即使我的切线和副法线都指向各个方向;我仍然希望展示一些东西,即使它是错误的。但即使是环境颜色也没有通过。(即使我独自一人也会发生这种情况。如果我只是忽略顶点法线并查找纹理。我会失去环境颜色)lightDir......viewdir

编辑 3:最后一个问题

通常情况下,部分问题与您认为错误的地方无关。我的问题是我用不同的纹理覆盖了我的法线贴图的绑定。

所以,有了它,我现在可以看到我的颜色通过了。用我漂亮的性感凹凸贴图。

但是,现在立方体贴图的接缝处出现了问题。我不确定是因为计算了切线还是因为我的法线贴图的生成方式。我的法线贴图是从每个面的高度图独立生成的。

这可以解释我认为的一些接缝影响,我将对其进行修改以对这些边缘上的相邻面进行采样,看看会发生什么。

我仍然认为正在生成的切线也会对这些接缝产生不利影响。我的想法是,它们将在接缝处指向相反的方向。

截屏: 法线贴图接缝

编辑 4 从 EDIT1 开始测试时,我为我的 icosphere 使用了一个非常低的多边形网格。所以我有最小的细分。

我想看看我的不太完美的法线贴图球体看起来有很多多边形。这立即暴露了这个问题:

该死的...

如果不清楚,从左边跑来写的是我的老朋友,接缝,但在接缝下面,看起来像是三角形边缘。

所以在上述所有之后,我想我又回到了我原来的不正确切线问题。

仍在寻求任何正在阅读本文的人的帮助。

编辑 4 嗯,这很快。这个网站http://www.geeks3d.com/20130122/normal-mapping-without-precomputed-tangent-space-vectors/给了我另一种创建切线的方法。虽然代码看起来有点类似于我在 CPU 上所做的,但它并没有导致那些随机定向的切线从 EDIT 3 中产生这些边缘。

我现在非常接近。我仍然有接缝,这种生成切线的另一种方法似乎增加了它们的“接缝度”

在此处输入图像描述

编辑 5 我现在尝试修改我的法线贴图生成。之前的代码是这样的:

for(int i = 0; i < 6; ++i)
{   
    float scale = 15.0;
    std::deque<glm::vec4> normalMap(textureSize*textureSize);
    for(int x = 0; x < textureSize; ++x)
    {
        for(int y = 0; y < textureSize; ++y)
        {
            // center point
            int i11 = utils::math::get_1d_array_index_from_2d(x,y,textureSize);
            float v11 = cubeFacesHeight[i][i11].r;

            // to the left
            int i01 = utils::math::get_1d_array_index_from_2d(std::max(x-1,0),y,textureSize);
            float v01 = cubeFacesHeight[i][i01].r;

            // to the right
            int i21 = utils::math::get_1d_array_index_from_2d(std::min(x+1,textureSize-1),y,textureSize);
            float v21 = cubeFacesHeight[i][i21].r;

            // to the top
            int i10 = utils::math::get_1d_array_index_from_2d(x,std::max(y-1,0),textureSize);
            float v10 = cubeFacesHeight[i][i10].r;

            // and now the bottom
            int i12 = utils::math::get_1d_array_index_from_2d(x,std::min(y+1,textureSize-1),textureSize);
            float v12 = cubeFacesHeight[i][i12].r;

            glm::vec3 S = glm::vec3(1, 0, scale * v21 - scale * v01);
            glm::vec3 T = glm::vec3(0, 1, scale * v12 - scale * v10);

            glm::vec3 N = (glm::vec3(-S.z,-T.z,1) / std::sqrt(S.z*S.z + T.z*T.z + 1));

            N.x = (N.x+1.0)/2.0;
            N.y = (N.y+1.0)/2.0;
            N.z = (N.z+1.0)/2.0;
            normalMap[utils::math::get_1d_array_index_from_2d(x,y,textureSize)] = glm::vec4(N.x,N.y,N.z,v11);
        }
    }
    for(int x = 0; x < textureSize; ++x)
    {
        for(int y = 0; y < textureSize; ++y)
        {
            cubeFacesHeight[i][utils::math::get_1d_array_index_from_2d(x,y,textureSize)] = normalMap[utils::math::get_1d_array_index_from_2d(x,y,textureSize)];
        }
    }
}

cubeFacesHeight是s 中std::array的一个。或者,我的立方体贴图的六个面。脸上的颜色是灰度的,我不使用花车的原因并不重要。6 std::dequeglm::vec4

我现在将其更改为以下内容,警告,这既丑陋又长。

for(int i = 0; i < 6; ++i)
{
    // 0 is negative X
    // 1 is positive X
    // 2 is negative Y
    // 3 is positive Y
    // 4 is negative Z
    // 5 is positive Z

    // +X:  right -Z (left),    left +Z (right),    top -Y (right),     bottom +Y (right)
    // -X:  right +Z (left),    left -Z (right),    top -Y (left),      bottom +Y (left)
    // -Z:  right -X (left),    left +X (right),    top -Y (bottom),    bottom +Y (top)
    // +Z:  right +X (left),    left -X (right),    top -Y (top),       bottom +Y (bottom)
    // -Y:  right +X (top),     left -X (top),      top +Z (top),       bottom -Z (top)
    // +Y:  right +X (bottom),  left -X (bottom),   top -Z (bottom),    bottom +Z (bottom)

    //+Z is towards, -Z is distance
    const int NEGATIVE_X = 0;
    const int NEGATIVE_Y = 2;
    const int NEGATIVE_Z = 4;
    const int POSITIVE_X = 1;
    const int POSITIVE_Y = 3;
    const int POSITIVE_Z = 5;

    float scale = 15.0;
    std::deque<glm::vec4> normalMap(textureSize*textureSize);
    for(int x = 0; x < textureSize; ++x)
    {
        for(int y = 0; y < textureSize; ++y)
        {
            // center point
            int i11 = utils::math::get_1d_array_index_from_2d(x,y,textureSize);
            float v11 = cubeFacesHeight[i][i11].r;

            // to the left
            int i01 = utils::math::get_1d_array_index_from_2d(std::max(x-1,0),y,textureSize);
            float v01 = cubeFacesHeight[i][i01].r;
            if(x-1 < 0)
            {
                if(i == NEGATIVE_X)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,y,textureSize);
                    v01 = cubeFacesHeight[NEGATIVE_Z][i01].r;
                }
                else if(i == POSITIVE_X)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,y,textureSize);
                    v01 = cubeFacesHeight[POSITIVE_Z][i01].r;
                }
                else if(i == NEGATIVE_Z)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,y,textureSize);
                    v01 = cubeFacesHeight[POSITIVE_X][i01].r;
                }
                else if(i == POSITIVE_Z)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,y,textureSize);
                    v01 = cubeFacesHeight[NEGATIVE_X][i01].r;
                }
                else if(i == NEGATIVE_Y)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(y,0,textureSize);
                    v01 = cubeFacesHeight[NEGATIVE_X][i01].r;
                }
                else if(i == POSITIVE_Y)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(y,textureSize-1,textureSize);
                    v01 = cubeFacesHeight[NEGATIVE_X][i01].r;
                }
            }

            // to the right
            int i21 = utils::math::get_1d_array_index_from_2d(std::min(x+1,textureSize-1),y,textureSize);
            float v21 = cubeFacesHeight[i][i21].r;
            if(x+1 > textureSize-1)
            {
                if(i == NEGATIVE_X)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(0,y,textureSize);
                    v01 = cubeFacesHeight[POSITIVE_Z][i01].r;
                }
                else if(i == POSITIVE_X)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(0,y,textureSize);
                    v01 = cubeFacesHeight[NEGATIVE_Z][i01].r;
                }
                else if(i == NEGATIVE_Z)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(0,y,textureSize);
                    v01 = cubeFacesHeight[NEGATIVE_X][i01].r;
                }
                else if(i == POSITIVE_Z)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(0,y,textureSize);
                    v01 = cubeFacesHeight[POSITIVE_X][i01].r;
                }
                else if(i == NEGATIVE_Y)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(y,0,textureSize);
                    v01 = cubeFacesHeight[POSITIVE_X][i01].r;
                }
                else if(i == POSITIVE_Y)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(y,textureSize-1,textureSize);
                    v01 = cubeFacesHeight[POSITIVE_X][i01].r;
                }
            }

            // to the top
            int i10 = utils::math::get_1d_array_index_from_2d(x,std::max(y-1,0),textureSize);
            float v10 = cubeFacesHeight[i][i10].r;
            if(y-1 < 0)
            {
                if(i == NEGATIVE_X)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(0,x,textureSize);
                    v01 = cubeFacesHeight[NEGATIVE_Y][i01].r;
                }
                else if(i == POSITIVE_X)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,x,textureSize);
                    v01 = cubeFacesHeight[NEGATIVE_Y][i01].r;
                }
                else if(i == NEGATIVE_Z)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(x,textureSize-1,textureSize);
                    v01 = cubeFacesHeight[NEGATIVE_Y][i01].r;
                }
                else if(i == POSITIVE_Z)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(x,0,textureSize);
                    v01 = cubeFacesHeight[NEGATIVE_Y][i01].r;
                }
                else if(i == NEGATIVE_Y)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(x,0,textureSize);
                    v01 = cubeFacesHeight[POSITIVE_Z][i01].r;
                }
                else if(i == POSITIVE_Y)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(x,textureSize-1,textureSize);
                    v01 = cubeFacesHeight[NEGATIVE_Z][i01].r;
                }
            }

            // and now the bottom
            int i12 = utils::math::get_1d_array_index_from_2d(x,std::min(y+1,textureSize-1),textureSize);
            float v12 = cubeFacesHeight[i][i12].r;
            if(y+1 > textureSize-1)
            {
                if(i == NEGATIVE_X)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(0,x,textureSize);
                    v01 = cubeFacesHeight[POSITIVE_Y][i01].r;
                }
                else if(i == POSITIVE_X)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,x,textureSize);
                    v01 = cubeFacesHeight[POSITIVE_Y][i01].r;
                }
                else if(i == NEGATIVE_Z)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(x,0,textureSize);
                    v01 = cubeFacesHeight[POSITIVE_Y][i01].r;
                }
                else if(i == POSITIVE_Z)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(x,textureSize-1,textureSize);
                    v01 = cubeFacesHeight[POSITIVE_Y][i01].r;
                }
                else if(i == NEGATIVE_Y)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(x,0,textureSize);
                    v01 = cubeFacesHeight[NEGATIVE_Z][i01].r;
                }
                else if(i == POSITIVE_Y)
                {
                    i01 = utils::math::get_1d_array_index_from_2d(x,textureSize-1,textureSize);
                    v01 = cubeFacesHeight[POSITIVE_Z][i01].r;
                }
            }

            glm::vec3 S = glm::vec3(1, 0, scale * v21 - scale * v01);
            glm::vec3 T = glm::vec3(0, 1, scale * v12 - scale * v10);

            glm::vec3 N = (glm::vec3(-S.z,-T.z,1) / std::sqrt(S.z*S.z + T.z*T.z + 1));

            N.x = (N.x+1.0)/2.0;
            N.y = (N.y+1.0)/2.0;
            N.z = (N.z+1.0)/2.0;

            normalMap[utils::math::get_1d_array_index_from_2d(x,y,textureSize)] = glm::vec4(N.x,N.y,N.z,v11);
        }
    }
    for(int x = 0; x < textureSize; ++x)
    {
        for(int y = 0; y < textureSize; ++y)
        {
            cubeFacesHeight[i][utils::math::get_1d_array_index_from_2d(x,y,textureSize)] = normalMap[utils::math::get_1d_array_index_from_2d(x,y,textureSize)];
        }
    }
}

所以我现在有点“流血”到相邻的立方体表面,以在生成法线贴图的同时对那里的高度进行采样。这实际上增加了接缝的外观。

嗯嗯

但这种方式提出了它自己的问题。例如……“到底为什么影响会增加?” 你可以看到它现在是一种斜角效果。

所以,我很确定当“流血”到下一个时,我已经正确地匹配了我的立方体面。这让我回到了不正确的切线。

即使我完全混合了立方体面,它也不会产生斜角效果,它会是完全参差不齐的东西。例如,即使在完全平坦的部分上,即将法线贴图生成渗入下一张面,效果为零,我仍然看到一个巨大的斜面。

更该死

这让我觉得如果切线之前是正确的,那么法线贴图会“匹配”切线方向吗?我不知道。

快速编辑 我注意到我在原始地图生成期间有效地对我的面部边缘进行了两次采样。如果我删除这个双重采样并且只使用 0 作为附加值,我最终会看到同样的大接缝。我不确定这意味着什么...

另一个快速编辑 这张图片显示了我认为很有说服力的东西。 在此处输入图像描述

我可以在这里看到两个不同的面孔“指向”相反的方向。这是我的片段切线生成。

所以我回到我的切线是一个问题。

4

3 回答 3

1

法线贴图最适用于最初创建法线贴图的法线向量矩阵

我相信你的问题与你的切线在表面上的非均匀对齐有关。UV 贴图通常是解决此类问题的首选。而且,用 2D 图像映射球体并不是那么容易(看看所有各种地球投影拓扑,你就会明白我的意思)。在某些时候,你会得到拉伸、边缘或剪切,并且很可能是上述所有情况的某种组合。通常使用 UV 贴图,重点是选择要在表面上隐藏这些效果的位置。通常为此选择行星的两极。我会寻找的一个地方是重新调整你的切线和副法线,以便它们都共享一个共同的全局方向,即。tanget = 北,binormal = 东,法线朝外(高度)。

本质上,法线贴图是在对切线和副法线的隐含理解下烘焙/创建的。如果在重新应用法线贴图时,表面的切线和副法线与最初创建法线贴图的隐含理解不一致,那么您将得到光照和着色错误。

收益或正交法向量矩阵

这样做的好处是切线和副法线向量通常用于查找 2D 纹理坐标。如果您的矩阵是非正交的,那么您将面临在倾斜角度处发生剪切、旋转或精度损失的风险。


定义统一的正交法向量矩阵

您可以在不同的 waut 中处理您的法线/切线/副法线计算,这将确保两个因素:

  1. 统一的对象方向,即……都指向相同的相对方向
  2. 正交向量,这将限制纹理查找剪切

这将通过两次旋转和一次移动来转换预定义的正交向量矩阵来实现。为了解释起见,我不会将这三个矩阵运算合并为一个矩阵,但您可能应该在代码中这样做。

首先,从一个已经定义好的向量矩阵开始

vec3 = [1, 0, 0, 0, 1, 0, 0, 0, 1]; 在此处输入图像描述

其次,在对象空间而不是世界空间中执行这些操作

否则,您必须将该对象转换回世界中心,并将其旋转回其原点方向,然后应用法线转换,然后将对象发送回其工作位置和方向

三、创建一个从vtx[n]到物体中心的向量

这个向量会告诉你在两个方向上旋转你的法线向量矩阵多少:

  • 纵向旋转
  • 横向旋转

在此处输入图像描述

四、旋转你的法线向量矩阵来对齐

在此处输入图像描述

最后,将您的法线向量矩阵移动距离

在此处输入图像描述

冲洗并重复

在此处输入图像描述


如果您需要保持非均匀、非正交的 UV

您可以基于不协调的 UV 布局创建法线贴图,这样它就会使该布局生效,从而适当地应用自身而没有效果。但是您的法线贴图必须从这种与生俱来的不协调中创建,以便它可以优雅地应用于这些 UV。


边缘像素插值?

第三,查看法线贴图折痕的边缘如何跟随立方体贴图的形状,我想知道您如何为法线贴图插入边缘像素。


立方体贴图的 GLSL 纹理查找?

另外,我可能只是没有找到您解决此问题的答案部分,但是您是否考虑过使用 GLSL 立方体贴图查找功能? gvec4 texture( gsamplerCube sampler, vec3 P, [float bias]);

于 2016-12-30T23:49:57.907 回答
0

这是我在评论中提到的插图:

正常插图

如您所见,红线是生成的法线,底部的每个顶点都有两个。这会导致照明问题,因为每个三角形的面都在不同的方向。当我第一次遇到这个问题时,我必须在黄线表示的每个顶点上取两个法线的平均值来修复光照计算。

至于您从立方体中获得的外观 - 凹凸贴图,这可能是您如何生成顶点以定义球体以及如何应用纹理坐标。如果没有看到您的整个解决方案或项目并使用它,我无法直接说出。问题甚至可能不涉及您的切线,但可能在您的纹理映射中导致环绕效果。

这不是您问题的直接答案,而是需要注意的建议,因为有许多不同的方法可以实现这些类型的着色器和渲染。

于 2015-06-04T01:21:40.777 回答
0

那天我花了很长时间才了解如何计算切线空间。也许我最终得到它的方式可以提供帮助。

您有三个顶点 v0、v1、v2。每个都有一个位置,法线和紫外线。让我们计算 v0 的切线空间。z 轴将为 v0.normal。我们需要计算 x 和 y 轴。

三角形上的任意一点都可以表示为 v0.pos + (v1.pos-v0.pos)*t + (v2.pos-v0.pos)*s。任何纹理坐标都可以表示为 v0.uv + (v1.uv - v0.uv)*t + (v2.uv - v0.uv)*s。

在切线空间中,我们需要 v1.uv - v0.uv = (1,0) 和 v2.uv-v0.uv = (0,1)。我们可以解决这个问题!对于这两种情况!这就是我们的切线和副法线的 s 和 t。只需将它们重新代入位置方程,就可以得到 uv=(0,1) 和 uv=(1,0) 的位置。减去 v0.pos,你就有了 x 和 y 轴!也使它们正常化。

这就是 v0 的切线空间。一个 3x3 矩阵。它不一定是正交的。但这没关系。您还可以使用该顶点为每个三角形计算每个顶点的矩阵。只是平均他们。

在渲染时对每个顶点矩阵进行插值,并按像素对它们进行归一化。

一个很好的测试方法是只渲染 z 列 - 它应该是正常的。

对于照明,从光中减去插值位置并通过“切线矩阵”对其进行变换。现在您的灯光在切线空间中,其中 (0,0,1) 朝向灯光,法线贴图指向上方。

于 2016-07-22T08:43:02.640 回答