我的法线贴图有什么问题?我认为这是我的切线

时间:2022-09-10 21:23:47

edit: you might want to start at "Edit 3" because I've solved a lot of this

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

Here's a screenshot of my normal cubemap applied to an icosphere:

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

我的法线贴图有什么问题?我认为这是我的切线

The tangents for my cubemapped icosphere are generated with the following code. m_indices in an std::vector of indices into the std::vector of vertices in m_vertices.

我的立方体贴图icosphere的切线是使用以下代码生成的。 m_indices在std :: index中的向量到m_vertices中的顶点的std :: vector中。

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;
}

My VertexData looks like this BTW:

我的VertexData看起来像BTW:

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

I know that the current tcoords, position and normal are fine (otherwise you wouldn't see the screenshot above).

我知道当前的tcoords,位置和法线都很好(否则你不会看到上面的截图)。

Then my vertex shader looks like this:

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

#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;
}

And finally my fragment shader looks like this:

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

#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);
}

As far as I can tell:

据我所知:

  1. My tangents are being calculated correctly.
  2. 我的切线正确计算。

  3. My normal map looks like a normal map to me.
  4. 我的法线贴图对我来说就像一张普通的贴图。

  5. I'm changing my light and view direction to tangent space to match my normal map.
  6. 我正在改变我的光线并查看切线空间的方向以匹配我的法线贴图。

The result is nothing. I.e. nothing is drawn to the screen. Not a solid color at all. So like everything behind is drawn with no occlusion.

结果一无所获。即没有任何东西被画到屏幕上。根本不是纯色。所以背后的一切都是在没有遮挡的情况下绘制的。

If I discard the lookup into my normal map, and instead just use the tangent matrix light and view I get the following:

如果我将查找丢弃到我的法线贴图中,而只是使用切线矩阵光和视图,我得到以下内容:

我的法线贴图有什么问题?我认为这是我的切线

There's a post-processing lens flare on this that's producing those funny bits and bobs. What's important I think is the overwhelming glare from the surface where the normals seems somewhat accurate.

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

If I then just transform the light by the tangent matrix I get this:

如果我然后通过切线矩阵转换光,我得到:

我的法线贴图有什么问题?我认为这是我的切线

All of this combines to tell me I have no idea where I'm going wrong.

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

I have an inkling that it's my tangent generation because the other pieces seem to follow what every tutorial I've read appear to say. The tangents are generated with a cubemapped icosphere in mind. So to determine the <S,T> or <U,V> 2D coordinates from a cubemaps usual 3D coordinates, I:

我有一个暗示,这是我的切线一代,因为其他部分似乎遵循我读过的每个教程似乎都会说。切线是在考虑立方体图像的情况下生成的。因此,要从立方体贴图通常的3D坐标确定 2D坐标,I: ,v> ,t>

  1. Use the largest value to determine the face I'm in
  2. 使用最大值来确定我所在的面孔

  3. Use the code from https://www.opengl.org/registry/specs/ARB/texture_cube_map.txt to determine the S,T coordinates
  4. 使用https://www.opengl.org/registry/specs/ARB/texture_cube_map.txt中的代码确定S,T坐标

Here's an excerpt from https://www.opengl.org/registry/specs/ARB/texture_cube_map.txt that I'm talking about.

以下是我正在谈论的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." ...

EDIT I don't know why I didn't before, but I've output the normals, tangents and bitangents in a geometry shader to see the way they're facing. I used this tutorial.

编辑我不知道为什么我之前没有,但我在几何着色器中输出法线,切线和比特数,以查看它们面对的方式。我用过这个教程。

Here they are

他们来了

The yellows are the face normals, the greens are the vertex normals. I'm not sure why the vertex normals seem wrong, they don't affect any other lighting so it's probably just an error in my geometry shader.

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

Tangents are red, Binormals are blue. These seem (it's hard to tell) like they are prependicular to each-other, which is correct, but other than that they are not pointing in uniform directions. This is what's given the mottled sort of pattern I had before.

切线是红色的,Binormals是蓝色的。这些似乎(很难说)就像它们彼此垂直,这是正确的,但除此之外,它们并没有指向统一的方向。这就是我之前的斑驳模式。

I have no idea how to fix this.

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

EDIT 2 I've figured out the problem with displaying my normals etc. This is fixed now.

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

The result, I added some shading to make it clearer, each color is a different cube face.

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

我的法线贴图有什么问题?我认为这是我的切线

Something else I've changed is the lookup into my normal map. I forgot to adjust the range back into -1 to 1 (from 0 to 1).

我改变的其他东西是查找我的法线贴图。我忘了将范围调整回-1到1(从0到1)。

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

This doesn't fix my problem.

这不能解决我的问题。

The confusing part is that when I try using the normals from my texture, I don't get anything rendered. Nothing at all into the depth buffer. I've checked and double checked that the texture is accessible from the shader (hence the original screenshot showing the texture applied to the sphere).

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

Because even though my Tangents and Binormals are pointing every which way; I'd still expect something to be shown, even if it's wrong. But not even the ambient color is coming through. (this happens even if I leave my lightDir and viewdir alone. If I just ignore the vertex normal and lookup the texture. I lose ambient color)...

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

EDIT 3: One last problem

编辑3:最后一个问题

As is often the case, part of the problem had nothing to do with where you think it's wrong. My problem was that I was overwriting the binding of my normal map with a different texture.

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

So, with that out of the way, I can now see my colours come through. With my nice sexy bump mapping.

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

However, there's now a problem at the seams of the cubemap. I'm not sure if it's because of the tangents being calculated or because of the way my normal map is generated. My normal map is generated from a height map for each face, independently.

但是,立方体贴图的接缝处现在存在问题。我不确定这是因为正在计算切线还是因为生成法线贴图的方式。我的法线贴图是从每张脸的高度贴图中独立生成的。

This would explain some seam affect I think, I'm going to modify it to sample the adjacent face on those edges and see what happens.

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

I still think that the tangents being generated are also going to have an adverse affect on these seams as well. My thoughts is that they are going to be pointing in opposite directions at the seams.

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

Screenshot: 我的法线贴图有什么问题?我认为这是我的切线

EDIT 4 While testing from EDIT1 down I was using a very very low poly mesh for my icosphere. So I had minimal sub-divisions.

编辑4在从EDIT1测试时,我使用非常低的多边形网格作为我的icosphere。所以我的细分很少。

I wanted to see how my not quite perfect normal mapped sphere looked with lots of polys. This instantly revealed this problem:

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

我的法线贴图有什么问题?我认为这是我的切线

In case it's not clear, running from left to write is my old friend, the seam, but below that are, what appears to be, triangle edges.

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

So after all of the above, I think I'm back to my original problem of incorrect tangents.

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

Still looking for some help from anyone that's reading this.

还在寻找任何正在阅读此内容的人的帮助。

EDIT 4 Well, that was quick. This site here http://www.geeks3d.com/20130122/normal-mapping-without-precomputed-tangent-space-vectors/ gave me another way to create tangents. Whilst the code seems somewhat similar to what I was doing on the CPU, it's not resulting in those randomly oriented tangents that was producing those edges from EDIT 3.

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

I am very close now. I still have the seams, this other method of generating the tangents seems to have increased their "seaminess"

我现在非常接近。我仍然有接缝,这种产生切线的其他方法似乎增加了它们的“缝隙”

我的法线贴图有什么问题?我认为这是我的切线

EDIT 5 I've now tried modifying my normal map generation. The previous code went like this:

编辑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 is an std::array of 6 std::deques of glm::vec4s. Or, the six sides of my cubemap. The colors in the faces are greyscale, I'm not using floats for reasons that don't matter.

cubeFacesHeight是一个std :: array,包含6 std :: deques of glm :: vec4s。或者,我的立方体贴图的六个方面。面部的颜色是灰度,我没有使用浮动,原因无关紧要。

I've now changed it to the following, warning, this is ugly and long.

我现在把它改成了以下,警告,这是丑陋和漫长的。

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)];
        }
    }
}

So I'm now sort of "bleeding" into the adjacent cubeface to sample the height there while generating the normalmap. This has actually increased the seam appearance.

因此,我现在在相邻的立方体面上“流血”,以便在生成法线贴图时对其进行采样。这实际上增加了接缝外观。

我的法线贴图有什么问题?我认为这是我的切线

But this sort of raises it's own questions. For instance... "why in the hell is the affect increased?" You can see that it's now a sort of bevel effect.

但这种提出了它自己的问题。例如......“为什么地狱的影响会增加?”你可以看到它现在是一种斜面效应。

So, I'm fairly sure I've matched up my cubefaces correctly when "bleeding" into the next one. This brings me back to the tangents being incorrect.

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

Even if I completely mixed up the cube faces, it wouldn't give a bevel effect, it would be something completely spotty. For example, even on a completely flat section, i.e., bleeding the normal map generation into the next face would have zero effect, I still see a massive bevel.

即使我完全混淆了立方体面,它也不会产生斜面效果,它会完全不稳定。例如,即使在完全平坦的部分上,即将正常地图生成放入下一个面部也没有效果,我仍然看到一个巨大的斜面。

我的法线贴图有什么问题?我认为这是我的切线

This makes me think that if the tangents were correct before, the normal map sort of "matched" the tangent directions? I don't know.

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

quick edit I noticed that I was effectively sampling my face edges twice during my original map generation. If I remove this double sampling and just use 0 for the additional, I end up seeing those same big seams. I'm not sure what this means...

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

Another quick edit This image shows something that I think is very telling. 我的法线贴图有什么问题?我认为这是我的切线

另一个快速编辑此图像显示了我认为非常有用的内容。

I can see here that two different faces are "pointing" in opposite directions. This is with my in fragment tangent generation.

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

So I'm back to my tangents being a problem.

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

3 个解决方案

#1


1  

Normal Maps work best with the normal vector matrices that originally created the normal map

I believe your problem has to do with non uniform alignment of your tangents across the surface. UV mapping is generally the first place to look with issues like this. And, mapping a sphere with a 2D image ain't so easy (look at all the various earth projection topologies and you'll see what I mean). At some point, you're going to get stretching, edges, or shearing, and most likely some combination of all of the above. Usually with UV mapping, the point is to choose where you're going to hide these effects on the surface. The poles of planets are often chosen for this. One place I would look would be to realign your tangents and binormals so that they're all sharing a common, global orientation, ie. tanget = north, and binormal = east, with the normal facing out (altitude). The non-uniformity of your tangents and binormals plays a direct role in the artifacts that sometimes arise in normal mapping issues, because they can twist the effect of the normal map, at that location, if the normal map was baked with the assumption that all tangents and binormals are uniformly oriented.

我相信你的问题与你的切线在表面上的非均匀对齐有关。 UV映射通常是第一个看到这样的问题的地方。并且,使用2D图像映射球体并不是那么容易(查看所有各种地球投影拓扑结构,您将看到我的意思)。在某些时候,你会得到拉伸,边缘或剪切,并且很可能是上述所有的组合。通常使用UV贴图,重点是选择在表面上隐藏这些效果的位置。通常选择行星的极点。我想看的一个地方是重新调整你的切线和副法线,这样它们就可以分享一个共同的全局方向,即。 tanget = north,binormal = east,法线朝外(海拔高度)。你的切线和副法线的不均匀性在正常映射问题中有时出现的伪像中起直接作用,因为它们可以扭曲法线贴图在该位置的效果,如果法线贴图是在假设所有切线和副法线均匀定向。

In essence, the normal map was baked/created with an implicit understanding of your tangents and binormals. If, when the normal map is reapplied, the tangents and binormals of the surface do not align with the implicit understanding in which the normal map was originally created, then you will get lighting and shading errors.

从本质上讲,通过对切线和副法线的隐式理解来烘焙/创建法线贴图。如果在重新应用法线贴图时,曲面的切线和副法线与最初创建法线贴图的隐式理解不对齐,那么您将获得光照和着色错误。

The benefit or orthogonal normal vector matrix

The benefit of this is that the tangent and binormal vectors are often used to lookup the 2D texture coordinate. If your matrix is non-orthogonal, then you run the risk ofshearing, rotations, or loss of precision at skewed angles.

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


Define Uniform, Orthogonal Normal Vector Matrices

You could approach your normal/tangent/binormal calculations in a different waut that would assure two factors:

您可以在不同的波形中接近正常/切线/正常计算,以确保两个因素:

  1. uniform object orientation, ie... all pointing in the same relative direction
  2. 统一的物体取向,即......都指向相同的相对方向

  3. orthogonal vectors, which will limit texture lookup shearing
  4. 正交向量,将限制纹理查找剪切

This will work by transforming a predefined, orthogonal vector matrix through two rotations and one move. For the sake of explanation, I won't collapse those three matrix operations into a single matrix, but it might behoove you to do so in your code.

这将通过两个旋转和一个移动来变换预定义的正交矢量矩阵。为了便于解释,我不会将这三个矩阵运算折叠成单个矩阵,但您可能需要在代码中执行此操作。

First, start with an already defined vector matrix

vec3 = [1, 0, 0, 0, 1, 0, 0, 0, 1]; 我的法线贴图有什么问题?我认为这是我的切线

vec3 = [1,0,0,0,1,0,0,0,1];

Second, perform these operations in object space, not world space

Otherwise you'll have to transform that object back to world center, and rotate it back to it's origin orientation, then apply the normal transformations, then send the object back to it's work position and orientation

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

Third, create a vector from vtx[n] to the object center

This vector will tell you how much to rotate your normal vector matrix in two directions:

此向量将告诉您在两个方向上旋转法向量矩阵的程度:

  • Longitudinal rotation
  • Latitudinal rotation

我的法线贴图有什么问题?我认为这是我的切线

Fourth, Rotate your normal vector matrix to align

我的法线贴图有什么问题?我认为这是我的切线

Last, Move your normal vector matrix by the distance

我的法线贴图有什么问题?我认为这是我的切线

Rinse and Repeat

我的法线贴图有什么问题?我认为这是我的切线


If you needed to keep non-uniform, non-orthogonal UVs

You can create a normal map based on incongruous UV layout such that it would take that layout into effect and therefore appropriately apply itself without effect. But your normal map would have to be created from this innate incongruity so that it would elegantly apply itself to those UVs.

您可以基于不协调的UV布局创建法线贴图,以便使布局生效,因此适当地应用自身而不会产生影响。但是你的法线贴图必须是从这种天生的不协调性中创造出来的,这样它才能优雅地应用于那些紫外线。


Edge Pixel Interpolation?

Third, looking at how the edge of the normal map crease follows the shape of the cubemap, I was wondering how you're interpolating edge pixels for your normal map.

第三,看看法线贴图的边缘如何遵循立方体贴图的形状,我想知道你是如何插入法线贴图的边缘像素的。


GLSL texture lookup for cubemaps?

Also, and I maybe I just didn't find the section of your answer where you address this, but have you considered using the GLSL cubemap lookup function? gvec4 texture( gsamplerCube sampler, vec3 P, [float bias]);

另外,我可能只是在你解决这个问题时找不到你的答案部分,但是你考虑过使用GLSL立方体图查找功能吗? gvec4 texture(gsamplerCube sampler,vec3 P,[float bias]);

#2


0  

Here I is the illustration that I mentioned in the comment:

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

我的法线贴图有什么问题?我认为这是我的切线

As you can see the red lines are a generated normal and each vertex at the base has two. This causes lighting problems because the face of each triangle is in a different direction. When I first came across this I had to take an average of both normals on each vertex represented by the yellow lines to fix lighting calculations.

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

As for the seem you are getting from your cube - bump map this could be in how you are generating your vertices to define your sphere and how you are applying your texture coordinates. I can not tell directly without seeing your whole solution or project and working with it. The problem may not even involve your tangents but could be within your texture mapping causing a wrapping effect.

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

This is not a direct answer to your problem but suggestions to be aware of since there are many different ways to implement these types of shaders and renderings.

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

#3


0  

It took me a long time back in the day to understand how to compute tangent space. Maybe the way I finally got it can help.

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

You have three vertices v0, v1, v2. Each has a position, normal and uv. Let's compute the tangent space for v0. The z axis will be v0.normal. We need to compute the x and y axis.

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

Any point on the triangle can be expressed as v0.pos + (v1.pos-v0.pos)*t + (v2.pos-v0.pos)*s. Any texture coordinate can be expressed as v0.uv + (v1.uv - v0.uv)*t + (v2.uv - v0.uv)*s.

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

In tangent space we need to have v1.uv - v0.uv = (1,0) and v2.uv-v0.uv = (0,1). We can solve for that s,t! for both cases! And that is the s and t for our tangent and binormal. Just plug them back into the position equation and you have the position where uv=(0,1) and uv=(1,0). Subtract v0.pos and you have your x and y axis! Also normalize them.

在切线空间中,我们需要v1.uv - v0.uv =(1,0)和v2.uv-v0.uv =(0,1)。我们可以解决那个问题!对于这两种情况!这就是我们的切线和副法线的s和t。只需将它们插回到位置方程式中,您就可以获得uv =(0,1)和uv =(1,0)的位置。减去v0.pos,你有x和y轴!也标准化他们。

And that is you tangent space for v0. A 3x3 matrix. It's not orthogonal necessarily. But that is ok. Also you compute this matrix per vertex for every triangle using that vertex. Just average them.

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

Interpolate those per vertex matrices when rendering, and normalize them per pixel.

在渲染时插值每个顶点矩阵,并按像素标准化它们。

A good way to test is to just render the z column - it should be the normal.

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

For lighting subtract the interpolated position from the light and transform it by the "tangent matrix". Now your light is in tangent space, where (0,0,1) is towards the light, and normal maps point straight up.

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

#1


1  

Normal Maps work best with the normal vector matrices that originally created the normal map

I believe your problem has to do with non uniform alignment of your tangents across the surface. UV mapping is generally the first place to look with issues like this. And, mapping a sphere with a 2D image ain't so easy (look at all the various earth projection topologies and you'll see what I mean). At some point, you're going to get stretching, edges, or shearing, and most likely some combination of all of the above. Usually with UV mapping, the point is to choose where you're going to hide these effects on the surface. The poles of planets are often chosen for this. One place I would look would be to realign your tangents and binormals so that they're all sharing a common, global orientation, ie. tanget = north, and binormal = east, with the normal facing out (altitude). The non-uniformity of your tangents and binormals plays a direct role in the artifacts that sometimes arise in normal mapping issues, because they can twist the effect of the normal map, at that location, if the normal map was baked with the assumption that all tangents and binormals are uniformly oriented.

我相信你的问题与你的切线在表面上的非均匀对齐有关。 UV映射通常是第一个看到这样的问题的地方。并且,使用2D图像映射球体并不是那么容易(查看所有各种地球投影拓扑结构,您将看到我的意思)。在某些时候,你会得到拉伸,边缘或剪切,并且很可能是上述所有的组合。通常使用UV贴图,重点是选择在表面上隐藏这些效果的位置。通常选择行星的极点。我想看的一个地方是重新调整你的切线和副法线,这样它们就可以分享一个共同的全局方向,即。 tanget = north,binormal = east,法线朝外(海拔高度)。你的切线和副法线的不均匀性在正常映射问题中有时出现的伪像中起直接作用,因为它们可以扭曲法线贴图在该位置的效果,如果法线贴图是在假设所有切线和副法线均匀定向。

In essence, the normal map was baked/created with an implicit understanding of your tangents and binormals. If, when the normal map is reapplied, the tangents and binormals of the surface do not align with the implicit understanding in which the normal map was originally created, then you will get lighting and shading errors.

从本质上讲,通过对切线和副法线的隐式理解来烘焙/创建法线贴图。如果在重新应用法线贴图时,曲面的切线和副法线与最初创建法线贴图的隐式理解不对齐,那么您将获得光照和着色错误。

The benefit or orthogonal normal vector matrix

The benefit of this is that the tangent and binormal vectors are often used to lookup the 2D texture coordinate. If your matrix is non-orthogonal, then you run the risk ofshearing, rotations, or loss of precision at skewed angles.

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


Define Uniform, Orthogonal Normal Vector Matrices

You could approach your normal/tangent/binormal calculations in a different waut that would assure two factors:

您可以在不同的波形中接近正常/切线/正常计算,以确保两个因素:

  1. uniform object orientation, ie... all pointing in the same relative direction
  2. 统一的物体取向,即......都指向相同的相对方向

  3. orthogonal vectors, which will limit texture lookup shearing
  4. 正交向量,将限制纹理查找剪切

This will work by transforming a predefined, orthogonal vector matrix through two rotations and one move. For the sake of explanation, I won't collapse those three matrix operations into a single matrix, but it might behoove you to do so in your code.

这将通过两个旋转和一个移动来变换预定义的正交矢量矩阵。为了便于解释,我不会将这三个矩阵运算折叠成单个矩阵,但您可能需要在代码中执行此操作。

First, start with an already defined vector matrix

vec3 = [1, 0, 0, 0, 1, 0, 0, 0, 1]; 我的法线贴图有什么问题?我认为这是我的切线

vec3 = [1,0,0,0,1,0,0,0,1];

Second, perform these operations in object space, not world space

Otherwise you'll have to transform that object back to world center, and rotate it back to it's origin orientation, then apply the normal transformations, then send the object back to it's work position and orientation

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

Third, create a vector from vtx[n] to the object center

This vector will tell you how much to rotate your normal vector matrix in two directions:

此向量将告诉您在两个方向上旋转法向量矩阵的程度:

  • Longitudinal rotation
  • Latitudinal rotation

我的法线贴图有什么问题?我认为这是我的切线

Fourth, Rotate your normal vector matrix to align

我的法线贴图有什么问题?我认为这是我的切线

Last, Move your normal vector matrix by the distance

我的法线贴图有什么问题?我认为这是我的切线

Rinse and Repeat

我的法线贴图有什么问题?我认为这是我的切线


If you needed to keep non-uniform, non-orthogonal UVs

You can create a normal map based on incongruous UV layout such that it would take that layout into effect and therefore appropriately apply itself without effect. But your normal map would have to be created from this innate incongruity so that it would elegantly apply itself to those UVs.

您可以基于不协调的UV布局创建法线贴图,以便使布局生效,因此适当地应用自身而不会产生影响。但是你的法线贴图必须是从这种天生的不协调性中创造出来的,这样它才能优雅地应用于那些紫外线。


Edge Pixel Interpolation?

Third, looking at how the edge of the normal map crease follows the shape of the cubemap, I was wondering how you're interpolating edge pixels for your normal map.

第三,看看法线贴图的边缘如何遵循立方体贴图的形状,我想知道你是如何插入法线贴图的边缘像素的。


GLSL texture lookup for cubemaps?

Also, and I maybe I just didn't find the section of your answer where you address this, but have you considered using the GLSL cubemap lookup function? gvec4 texture( gsamplerCube sampler, vec3 P, [float bias]);

另外,我可能只是在你解决这个问题时找不到你的答案部分,但是你考虑过使用GLSL立方体图查找功能吗? gvec4 texture(gsamplerCube sampler,vec3 P,[float bias]);

#2


0  

Here I is the illustration that I mentioned in the comment:

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

我的法线贴图有什么问题?我认为这是我的切线

As you can see the red lines are a generated normal and each vertex at the base has two. This causes lighting problems because the face of each triangle is in a different direction. When I first came across this I had to take an average of both normals on each vertex represented by the yellow lines to fix lighting calculations.

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

As for the seem you are getting from your cube - bump map this could be in how you are generating your vertices to define your sphere and how you are applying your texture coordinates. I can not tell directly without seeing your whole solution or project and working with it. The problem may not even involve your tangents but could be within your texture mapping causing a wrapping effect.

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

This is not a direct answer to your problem but suggestions to be aware of since there are many different ways to implement these types of shaders and renderings.

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

#3


0  

It took me a long time back in the day to understand how to compute tangent space. Maybe the way I finally got it can help.

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

You have three vertices v0, v1, v2. Each has a position, normal and uv. Let's compute the tangent space for v0. The z axis will be v0.normal. We need to compute the x and y axis.

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

Any point on the triangle can be expressed as v0.pos + (v1.pos-v0.pos)*t + (v2.pos-v0.pos)*s. Any texture coordinate can be expressed as v0.uv + (v1.uv - v0.uv)*t + (v2.uv - v0.uv)*s.

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

In tangent space we need to have v1.uv - v0.uv = (1,0) and v2.uv-v0.uv = (0,1). We can solve for that s,t! for both cases! And that is the s and t for our tangent and binormal. Just plug them back into the position equation and you have the position where uv=(0,1) and uv=(1,0). Subtract v0.pos and you have your x and y axis! Also normalize them.

在切线空间中,我们需要v1.uv - v0.uv =(1,0)和v2.uv-v0.uv =(0,1)。我们可以解决那个问题!对于这两种情况!这就是我们的切线和副法线的s和t。只需将它们插回到位置方程式中,您就可以获得uv =(0,1)和uv =(1,0)的位置。减去v0.pos,你有x和y轴!也标准化他们。

And that is you tangent space for v0. A 3x3 matrix. It's not orthogonal necessarily. But that is ok. Also you compute this matrix per vertex for every triangle using that vertex. Just average them.

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

Interpolate those per vertex matrices when rendering, and normalize them per pixel.

在渲染时插值每个顶点矩阵,并按像素标准化它们。

A good way to test is to just render the z column - it should be the normal.

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

For lighting subtract the interpolated position from the light and transform it by the "tangent matrix". Now your light is in tangent space, where (0,0,1) is towards the light, and normal maps point straight up.

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