Нормальное отображение и освещение не работает, неправильно отображается

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

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

Освещение не работает при нормальном отображении

У меня такое ощущение, что это как-то связано с положением света (в настоящее время изменяющимся со времени от х = -10 до х = 10), которое не было должным образом включено в расчеты, пока я начинаю менять матрицу модели (с помощью переводов / масштабирование). Я публикую весь соответствующий код и надеюсь, что вы, ребята, сможете как-то увидеть то, что мне не хватает, так как я смотрел на свой код в течение нескольких дней.

Вершинный шейдер

#version 330

layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec3 tangent;
layout(location = 3) in vec3 color;
layout(location = 4) in vec2 texCoord;

// fragment pass through
out vec3 Position;
out vec3 Normal;
out vec3 Tangent;
out vec3 Color;
out vec2 TexCoord;

out vec3 TangentSurface2Light;
out vec3 TangentSurface2View;

uniform vec3 lightPos;

// vertex transformation
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
mat3 normalMatrix = transpose(mat3(inverse(view * model)));
Position = vec3((view * model) * vec4(position, 1.0));
Normal = normalMatrix * normal;
Tangent = tangent;
Color = color;
TexCoord = texCoord;

gl_Position = projection * view * model * vec4(position, 1.0);

vec3 light = vec3(view * vec4(lightPos, 1.0));
vec3 n = normalize(normalMatrix * normal);
vec3 t = normalize(normalMatrix * tangent);
vec3 b = cross(n, t);
mat3 mat = mat3(t.x, b.x ,n.x, t.y, b.y ,n.y, t.z, b.z ,n.z);
vec3 vector = normalize(light - Position);
TangentSurface2Light = mat * vector;
vector = normalize(-Position);
TangentSurface2View = mat * vector;
}

Фрагмент шейдера

#version 330

in vec3 Position;
in vec3 Normal;
in vec3 Tangent;
in vec3 Color;
in vec2 TexCoord;

in vec3 TangentSurface2Light;
in vec3 TangentSurface2View;

out vec4 outColor;

uniform vec3 lightPos;
uniform mat4 view;
uniform sampler2D texture0;
uniform sampler2D texture_normal; // normal

uniform float repeatFactor = 1;

void main()
{
vec4 texColor = texture(texture0, TexCoord * repeatFactor);
vec3 light = vec3(view * vec4(lightPos, 1.0));
float dist = length(light - Position);
float att = 1.0 / (1.0 + 0.01 * dist + 0.001 * dist * dist);
// Ambient
vec4 ambient = vec4(0.2);
// Diffuse
vec3 surface2light = normalize(TangentSurface2Light);
vec3 norm = normalize(texture(texture_normal, TexCoord * repeatFactor).xyz * 2.0 - 1.0);
float contribution = max(dot(norm, surface2light), 0.0);
vec4 diffuse = contribution * vec4(0.8);
// Specular
vec3 surf2view = normalize(TangentSurface2View);
vec3 reflection = reflect(-surface2light, norm); // reflection vector
float specContribution = pow(max(dot(surf2view, reflection), 0.0), 32);
vec4 specular = vec4(0.6) * specContribution;

outColor = (ambient + (diffuse * att)+ (specular * pow(att, 3))) * texColor;
}

OpenGL чертежный код

void Render()
{
...

glm::mat4 view, projection; // Model will be done via MatrixStack
view = glm::lookAt(position, position + direction, up); // cam pos, look at (eye pos), up vec
projection = glm::perspective(45.0f, (float)width/(float)height, 0.1f, 1000.0f);
glUniformMatrix4fv(glGetUniformLocation(basicShader.shaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(basicShader.shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection));

// Lighting
lightPos.x = 0.0 + sin(time / 125) * 10;

glUniform3f(glGetUniformLocation(basicShader.shaderProgram, "lightPos"), lightPos.x, lightPos.y, lightPos.z);

// Objects  (use bump mapping on this cube)
bumpShader.Use();
glUniformMatrix4fv(glGetUniformLocation(bumpShader.shaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(bumpShader.shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glUniform3f(glGetUniformLocation(bumpShader.shaderProgram, "lightPos"), lightPos.x, lightPos.y, lightPos.z);
MatrixStack::LoadIdentity();
MatrixStack::Scale(2);
MatrixStack::ToShader(glGetUniformLocation(bumpShader.shaderProgram, "model"));

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, resources.GetTexture("container"));
glUniform1i(glGetUniformLocation(bumpShader.shaderProgram, "img"), 0);
glActiveTexture(GL_TEXTURE1); // Normal map
glBindTexture(GL_TEXTURE_2D, resources.GetTexture("container_normal"));
glUniform1i(glGetUniformLocation(bumpShader.shaderProgram, "normalMap"), 1);

glUniform1f(glGetUniformLocation(bumpShader.shaderProgram, "repeatFactor"), 1);
cubeNormal.Draw();

MatrixStack::LoadIdentity();
MatrixStack::Translate(glm::vec3(0.0f, -22.0f, 0.0f));
MatrixStack::Scale(glm::vec3(200.0f, 20.0f, 200.0f));
MatrixStack::ToShader(glGetUniformLocation(bumpShader.shaderProgram, "model"));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, resources.GetTexture("floor"));
glActiveTexture(GL_TEXTURE1); // Normal map
glBindTexture(GL_TEXTURE_2D, resources.GetTexture("floor_normal"));
glUniform1f(glGetUniformLocation(bumpShader.shaderProgram, "repeatFactor"), 100);
cubeNormal.Draw();

MatrixStack::LoadIdentity();
glActiveTexture(GL_TEXTURE0);

...
}

РЕДАКТИРОВАТЬ
Теперь я загрузил свои объекты, используя библиотеку ASSIMP с включенным флагом ‘aiProcess_CalcTangentSpace’, и соответственно изменил мои шейдеры, чтобы приспособиться к новым изменениям. Так как ASSIMP теперь автоматически вычисляет правильные векторы касательных, у меня должны быть действительные векторы касательных, и моя проблема должна быть решена (как отметил Никол Болас), но у меня все еще есть та же проблема с зеркальным освещением, действующим странно, и рассеянное освещение действительно не появляется , Я думаю, что есть еще что-то, что не работает правильно. Я пометил ваш ответ как правильный ответ Никол Болас (на данный момент) и соответственно обновил свой код, поскольку я все еще что-то упускаю.

Наверное, это как-то связано с переводом. Как только я добавляю перевод (-22.0f в направлении y) к матрице модели, он реагирует со странным освещением. Пока пол (который на самом деле является кубом) не имеет перевода, освещение выглядит хорошо.

3

Решение

вычисление касательных векторов в вершинном шейдере

Ну, вот твоя проблема. Это невозможно для произвольной поверхности.

Тангенс и битангенс не являются произвольными векторами, перпендикулярными друг другу. Это векторы направления пространства модели, которые указывают в направлении координат текстуры. Касательные указывают в направлении координаты текстуры S, а касательные указывают в направлении координаты текстуры T (или U и V для координат tex, если вы предпочитаете).

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

Помните: касательное пространство — это пространство, перпендикулярное поверхности. Но вам нужно знать, как эта поверхность отображается на объекте, чтобы знать, например, где находится «вверх». Возьмите квадратную поверхность. Вы можете отобразить текстуру так, чтобы часть квадрата + Y была ориентирована вдоль направления + T текстуры. Или это может быть вдоль + X квадрата. Вы даже можете отобразить его так, чтобы текстура была искажена или повернута на произвольный угол.

Касательные и битангентные векторы предназначены для коррекции этого отображения. Они указывают в направлениях S и T в модельном пространстве. Таким образом, в сочетании с нормой они образуют матрицу преобразования для преобразования из касательного пространства в любое пространство, в котором находятся 3 вектора (обычно вы преобразуете NBT в пространство камеры или любое другое пространство, которое вы используете для освещения перед их использованием).

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

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

Вершинные шейдеры не могут получить доступ к более чем одной вершине. Геометрические шейдеры не могут (как правило) получить доступ к достаточному количеству вершин, чтобы сделать это. Вычислить тангенс / битангенс в автономном режиме на процессоре.

8

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

 mat3 mat = mat3(t.x, b.x ,n.x, t.y, b.y ,n.y, t.z, b.z ,n.z);

Неправильно. Чтобы правильно использовать матрицу tbn, вы должны транспонировать ее следующим образом:

 mat3 mat = transpose(mat3(t.x, b.x ,n.x, t.y, b.y ,n.y, t.z, b.z ,n.z));

затем используйте его для преобразования вашего света и просмотра векторов в касательном пространстве. В качестве альтернативы (и менее эффективно), передайте нетранспонированную матрицу tbn фрагментному шейдеру и используйте ее для преобразования сэмплированной нормали в пространство просмотра. Это легко пропустить, но очень важно. Увидеть http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/ для получения дополнительной информации.

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

0

По вопросам рекламы [email protected]