Подтвердить что ты не робот

Что случилось с моим нормальным отображением? Я думаю, это мои касательные

edit: вы можете начать с "Редактировать 3", потому что я решил много этого.

Вот скриншот моей обычной cubemap, примененной к исосфере:

enter image description here

Тангенс для моей кубовидной икосферы генерируется следующим кодом. 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 выглядит так: Кстати:

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

Я знаю, что текущие tcoords, position и normal являются точными (иначе вы не увидите скриншот выше).

Затем мой вершинный шейдер выглядит следующим образом:

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

Насколько я могу судить:

  • Мои касательные вычисляются правильно.
  • Моя нормальная карта выглядит как нормальная карта для меня.
  • Я изменяю свой свет и просматриваю направление в касательное пространство, чтобы соответствовать моей нормальной карте.

Результат ничего. То есть на экран ничего не рисуется. Не сплошной цвет. Так что, как и все позади, рисуется без окклюзии.

Если я отбрасываю поиск в свою нормальную карту и вместо этого просто использую касательную матрицу и вид, я получаю следующее:

enter image description here

Там есть вспышка объектива после обработки, создающая эти смешные биты и бобы. Я думаю, что это очень впечатляющий взгляд с поверхности, где нормали кажутся несколько точными.

Если я тогда просто трансформирую свет по касательной матрице, я получаю это:

enter image description here

Все это объединяется, чтобы сказать мне, что я понятия не имею, где я ошибаюсь.

У меня есть подозрение, что это мое касательное поколение, потому что другие части, похоже, следуют тому, что каждый учебник, который я прочитал, кажется, говорит. Тангенсы генерируются с учетом кубической икосферы. Таким образом, чтобы определить координаты <S,T> или <U,V> из обычных трехмерных координат кубама, I:

Вот выдержка из 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 2D texture image using the rules given in sections 3.8.5
 and 3.8.6." ...

ИЗМЕНИТЬ Я не знаю, почему я этого не делал раньше, но я выводил нормали, касательные и бинтенты в геометрическом шейдере, чтобы увидеть, как они выглядят. Я использовал этот учебник.

Здесь они

Желтые - это нормали лица, зелень - вершинные нормали. Я не уверен, почему нормали вершин кажутся неправильными, они не влияют на какое-либо другое освещение, поэтому, вероятно, это просто ошибка в моем геометрическом шейдере.

Тангенсы красные, бинормалы - синие. Они кажутся (это трудно сказать), как будто они являются перпендикулярными друг другу, что является правильным, но помимо этого они не указывают на равномерные направления. Это то, что давалось пестрым образцом, который у меня был раньше.

Я не знаю, как это исправить.

РЕДАКТИРОВАТЬ 2 Я выяснил проблему с отображением моих нормалей и т.д. Теперь это исправлено.

В результате я добавил несколько оттенков, чтобы сделать их более ясными, каждый цвет - это другая грань куба.

enter image description here

Что-то еще, что я изменил, это поиск в моей обычной карте. Я забыл отрегулировать диапазон обратно на -1 до 1 (от 0 до 1).

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

Это не устраняет мою проблему.

Запутанная часть состоит в том, что, когда я пытаюсь использовать нормали из моей текстуры, я не получаю ничего, что бы визуализировалось. Ничего не происходит в буфере глубины. Я проверил и дважды проверил, что текстура доступна из шейдера (отсюда исходный снимок экрана, показывающий текстуру, применяемую к сфере).

Потому что, хотя мои Тангенсы и Бинормалы указывают каждый путь; Я все еще ожидал, что что-то будет показано, даже если это неправильно. Но даже окружающий цвет не проходит. (это случается, даже если я оставлю только lightDir и viewdir.Если я просто игнорирую нормальную вершину и просматриваю текстуру, я теряю окружающий цвет)...

РЕДАКТИРОВАТЬ 3: Последняя проблема

Как это часто бывает, часть проблемы не имеет ничего общего с тем, где вы считаете это неправильным. Моя проблема заключалась в том, что я переписывал привязку своей обычной карты с другой текстурой.

Итак, с этой точки зрения, теперь я вижу, как меняют цвета. С моей красивой сексуальной картиной рельефа.

Тем не менее, теперь есть проблема в швах куб-карты. Я не уверен, если это из-за вычислений касательных или из-за того, как генерируется моя нормальная карта. Моя нормальная карта создается независимо от карты высоты для каждой грани.

Это объясняет, какой эффект влияет на шов. Я собираюсь изменить его, чтобы пробовать смежную грань на этих краях и видеть, что происходит.

Я все еще думаю, что возникающие касательные также будут иметь негативное влияние на эти швы. Мои мысли в том, что они будут указывать в противоположных направлениях на швы.

Снимок экрана: normal map seams

РЕДАКТИРОВАТЬ 4 Во время тестирования с EDIT1 я использовал очень маленькую поли-сетку для своей икосферы. Поэтому у меня были минимальные подразделения.

Я хотел посмотреть, как выглядела моя не совсем идеальная нормальная сфера с большим количеством полисов. Это мгновенно выявило эту проблему:

dang it...

В случае, если это не ясно, запуск слева для записи - это мой старый друг, шов, но ниже того, что, кажется, является краем треугольника.

Итак, после всего вышеизложенного, я думаю, что вернусь к своей первоначальной проблеме неправильных касательных.

Ищите какую-то помощь от тех, кто читает это.

РЕДАКТИРОВАТЬ 4 Ну, это было быстро. Этот сайт здесь http://www.geeks3d.com/20130122/normal-mapping-without-precomputed-tangent-space-vectors/ дал мне другой способ создания тангенсов. Хотя код кажется несколько похожим на то, что я делал на CPU, это не приводит к тем случайным ориентированным касательным, которые производят эти ребра из EDIT 3.

Сейчас я очень близко. У меня все еще есть швы, этот другой способ генерации тангенсов, похоже, увеличил их "шовность"

enter image description here

РЕДАКТИРОВАТЬ 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 является std::array of 6 std::deque of glm::vec4 s. Или, шесть сторон моего куба. Цвета в лицах имеют оттенки серого, я не использую поплавки по причинам, которые не имеют значения.

Теперь я изменил его на следующее, предупреждение, это уродливо и долго.

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

Итак, теперь я вроде как "истекаю кровью" в соседний кубик, чтобы пробовать высоту там, создавая нормальную карту. Это фактически увеличило внешний вид шва.

hmmmm

Но этот вопрос порождает собственные вопросы. Например... "Почему, черт возьми, увеличивается аффект?" Вы можете видеть, что теперь это своего рода эффект скоса.

Итак, я уверен, что правильно подобрал свои кубели, когда "истекал кровью" в следующую. Это возвращает меня к неверным касательным.

Даже если бы я полностью перепутал грани куба, это не дало бы эффекта скоса, это было бы что-то совершенно пятнистое. Например, даже на полностью плоском участке, то есть, кровотечение, генерирование нормальной карты в следующую грань будет иметь нулевой эффект, я все еще вижу массивный скос.

dang it more

Это заставляет меня думать, что если касательные были правильными раньше, нормальный вид карты "соответствовал" касательным направлениям? Я не знаю.

быстрое редактирование Я заметил, что во время моей первоначальной генерации карты я эффективно отбирал свои края. Если я удалю эту двойную выборку и просто использую 0 для дополнительного, я в конечном итоге вижу те же большие швы. Я не уверен, что это значит...

Еще одно быстрое редактирование На этом изображении показано что-то, что, по-моему, очень важно. enter image description here

Я вижу здесь, что две разные грани "указывают" в противоположных направлениях. Это связано с моим в очертании касательной генерации.

Итак, я вернулся к моим касательным, являющимся проблемой.

4b9b3361

Ответ 1

Нормальные карты лучше всего работают с нормальными векторными матрицами, изначально создавшими нормальную карту

Я считаю, что ваша проблема связана с неравномерным выравниванием ваших касательных по поверхности. Ультрафиолетовое отображение, как правило, является первым местом для поиска таких проблем. И отображение сферы с 2D-изображением не так просто (посмотрите на все топологии топологии земли, и вы поймете, что я имею в виду). В какой-то момент вы получите растяжение, ребра или сдвиг, и, скорее всего, какую-то комбинацию из всего вышеперечисленного. Обычно с UV-сопоставлением необходимо выбрать, где вы собираетесь скрыть эти эффекты на поверхности. Для этого часто выбираются полюса планет. Одно место, которое я бы посмотрел, состояло бы в том, чтобы перестроить ваши касательные и бинормали, чтобы все они разделяли общую глобальную ориентацию, т.е. tanget = север и бинормаль = восток, с нормальным лицом (высота). Неравномерность ваших касательных и бинормалов играет прямую роль в артефактах, которые иногда возникают при нормальных проблемах с отображением, потому что они могут скручивать эффект нормальной карты в этом месте, если обычная карта испекла с предположением, что все касательные и бинормали равномерно ориентированы.

В сущности, нормальная карта была запечена/создана с неявным пониманием ваших касательных и бинормалей. Если, когда нормальная карта снова применяется, касательные и бинормалы поверхности не согласуются с неявным пониманием, в котором первоначально была создана нормальная карта, тогда вы получите ошибки освещения и затенения.

Преимущество или ортогональная нормальная векторная матрица

Преимущество этого заключается в том, что касательные и бинормальные векторы часто используются для поиска координаты 2D-текстуры. Если ваша матрица не ортогональна, тогда вы рискуете потерями, поворотами или потерей точности при искаженных углах.


Определить равномерные, ортогональные нормальные векторные матрицы

Вы могли бы приблизиться к своим нормальным/касательным/бинормальным вычислениям в другом waut, что обеспечило бы два фактора:

  • равномерная ориентация объекта, т.е. все, указывающие в том же относительном направлении
  • ортогональные векторы, которые будут ограничивать сдвиг текстуры

Это будет работать путем преобразования предопределенной ортогональной векторной матрицы с помощью двух поворотов и одного перемещения. Для объяснения я не буду сворачивать эти три матричные операции в одну матрицу, но вам может потребоваться сделать это в вашем коде.

Сначала начните с уже определенной векторной матрицы

vec3 = [1, 0, 0, 0, 1, 0, 0, 0, 1]; введите описание изображения здесь

Во-вторых, выполните эти операции в пространстве объектов, а не в пространстве мира

В противном случае вам придется преобразовать этот объект обратно в мировой центр и повернуть его обратно в исходную ориентацию, затем применить нормальные преобразования, затем отправить объект обратно на рабочую позицию и ориентацию

В-третьих, создайте вектор из vtx [n] в центр объекта

Этот вектор скажет вам, сколько вращать нормальную векторную матрицу в двух направлениях:

  • Продольное вращение
  • Широтное вращение

введите описание изображения здесь

В-четвертых, поверните нормальную векторную матрицу для выравнивания

введите описание изображения здесь

Last, Переместите нормальную векторную матрицу на расстояние

введите описание изображения здесь

Промывка и повтор

введите описание изображения здесь


Если вам нужно сохранить неравномерные, неортогональные UVs

Вы можете создать нормальную карту, основанную на несогласованном УФ-макете, чтобы она вступила в силу и поэтому надлежащим образом применялась сама по себе без эффекта. Но ваша нормальная карта должна была бы быть создана из этой врожденной несоответствия, чтобы она элегантно применилась к этим УФ.


Пограничная пиксельная интерполяция?

В-третьих, глядя на то, как край нормальной складки карты следует форме куба, мне было интересно, как вы интерполируете крайние пиксели для вашей обычной карты.


Поиск текстуры GLSL для кубов?

Кроме того, и я, может быть, просто не нашел раздел вашего ответа, где вы обращаетесь к этому вопросу, но считаете ли вы, что используете функцию поиска в виде куба GLSL? gvec4 texture( gsamplerCube sampler, vec3 P, [float bias]);

Ответ 2

Здесь я - иллюстрация, которую я упомянул в комментарии:

Normal Illustration

Как вы можете видеть, красные линии являются сгенерированными нормалями, и каждая вершина в базе имеет два. Это вызывает проблемы с освещением, поскольку лицо каждого треугольника находится в другом направлении. Когда я впервые столкнулся с этим, мне пришлось взять среднее значение обоих нормалей на каждой вершине, представленной желтыми линиями, чтобы исправить вычисления освещения.

Что касается того, что вы получаете от вашей карты куба - bump, это может быть в том, как вы создаете свои вершины, чтобы определить свою сферу и как вы применяете свои координаты текстуры. Я не могу сказать напрямую, не видя вашего решения или проекта и не работая с ним. Проблема может даже не включать в себя ваши касательные, но может быть в пределах вашего отображения текстур, что вызывает эффект обертывания.

Это не прямой ответ на вашу проблему, но предложения, о которых нужно знать, поскольку существует множество способов реализации этих типов шейдеров и рендеринга.

Ответ 3

Мне потребовалось много времени, чтобы понять, как вычислить касательное пространство. Возможно, способ, которым я, наконец, получил это, может помочь.

У вас есть три вершины 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! для обоих случаев! И это s и t для нашего касательного и бинормального. Просто вставьте их обратно в уравнение положения, и у вас есть позиция, где uv = (0,1) и uv = (1,0). Вычитайте v0.pos, и у вас есть оси x и y! Также нормализуйте их.

И это ваше касательное пространство для v0. Матрица 3х3. Это не ортогонально. Но это нормально. Также вы вычисляете эту матрицу на вершину для каждого треугольника, используя эту вершину. Просто сравните их.

Интерполируйте их на вершинные матрицы при рендеринге и нормализуйте их на пиксель.

Хороший способ проверить - просто визуализировать столбец z - он должен быть нормальным.

Для освещения вычесть интерполированное положение из света и преобразовать его "касательной матрицей". Теперь ваш свет находится в касательном пространстве, где (0,0,1) находится к свету, а нормальные карты указывают вверх.