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

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

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

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

Касательные для моей кубической икосферы генерируются с помощью следующего кода. 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);
}

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

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

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

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

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

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

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

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

Все это в совокупности говорит мне, что я понятия не имею, где я иду не так.

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

  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 и ниже я использовал очень и очень низкополигональную сетку для своей икосферы. Так что у меня были минимальные подразделения.

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

черт возьми ...

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

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

Все еще ищу помощь от любого, кто читает это.

РЕДАКТИРОВАТЬ 4
Ну, это было быстро. Этот сайт здесь http://www.geeks3d.com/20130122/normal-mapping-without-precomputed-tangent-space-vectors/ дал мне еще один способ создания касательных. Хотя код кажется несколько похожим на то, что я делал на процессоре, он не приводит к тем случайно ориентированным касательным, которые создавали эти ребра из 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 является std::array из 6 std::dequeс glm::vec4s. Или шесть сторон моей кубической карты. Цвета на лицах серые, я не использую поплавки по причинам, которые не имеют значения.

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

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 для дополнительного, я получу те же самые большие швы. Я не уверен, что это значит …

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

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

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

18

Решение

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

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

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

Выгода или ортогональная нормальная векторная матрица

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

Наконец, переместите вашу нормальную векторную матрицу на расстояние

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

Промыть и повторить

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


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

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


Крайняя пиксельная интерполяция?

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


GLSL текстурный поиск для кубических карт?

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

1

Другие решения

Вот иллюстрация, которую я упомянул в комментарии:

Нормальная иллюстрация

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

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

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

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. Матрица 3х3. Это не обязательно ортогонально. Но это нормально. Также вы вычисляете эту матрицу для каждой вершины для каждого треугольника, использующего эту вершину. Просто усредните их.

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

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

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

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector