Нормальное отображение пошло ужасно неправильно
Я пытался реализовать нормальное отображение в своем приложении opengl, но не могу заставить его работать.
Это диффузная карта (к которой я добавляю коричневый цвет), и это карта нормалей.
Чтобы получить тангенциальные и битангентные (в других местах, называемых бинормалями?) Векторы, я запускаю эту функцию для каждого треугольника в моей сетке:
void getTangent(const glm::vec3 &v0, const glm::vec3 &v1, const glm::vec3 &v2,
const glm::vec2 &uv0, const glm::vec2 &uv1, const glm::vec2 &uv2,
std::vector<glm::vec3> &vTangents, std::vector<glm::vec3> &vBiangents)
{
// Edges of the triangle : postion delta
glm::vec3 deltaPos1 = v1-v0;
glm::vec3 deltaPos2 = v2-v0;
// UV delta
glm::vec2 deltaUV1 = uv1-uv0;
glm::vec2 deltaUV2 = uv2-uv0;
float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
glm::vec3 tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y)*r;
glm::vec3 bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x)*r;
for(int i = 0; i < 3; i++) {
vTangents.push_back(tangent);
vBiangents.push_back(bitangent);
}
}
После этого я вызываю glBufferData для загрузки вершин, нормалей, uvs, касательных и битангентов в GPU. Вершинный шейдер:
#version 430
uniform mat4 ProjectionMatrix;
uniform mat4 CameraMatrix;
uniform mat4 ModelMatrix;
in vec3 vertex;
in vec3 normal;
in vec2 uv;
in vec3 tangent;
in vec3 bitangent;
out vec2 fsCoords;
out vec3 fsVertex;
out mat3 TBNMatrix;
void main()
{
gl_Position = ProjectionMatrix * CameraMatrix * ModelMatrix * vec4(vertex, 1.0);
fsCoords = uv;
fsVertex = vertex;
TBNMatrix = mat3(tangent, bitangent, normal);
}
Фрагмент шейдера:
#version 430
uniform sampler2D diffuseMap;
uniform sampler2D normalMap;
uniform mat4 ModelMatrix;
uniform vec3 CameraPosition;
uniform struct Light {
float ambient;
vec3 position;
} light;
uniform float shininess;
in vec2 fsCoords;
in vec3 fsVertex;
in mat3 TBNMatrix;
out vec4 color;
void main()
{
//base color
const vec3 brownColor = vec3(153.0 / 255.0, 102.0 / 255.0, 51.0 / 255.0);
color = vec4(brownColor * (texture(diffuseMap, fsCoords).rgb + 0.25), 1.0);//add a fixed base color (0.25), because its dark as hell
//general vars
vec3 normal = texture(normalMap, fsCoords).rgb * 2.0 - 1.0;
vec3 surfacePos = vec3(ModelMatrix * vec4(fsVertex, 1.0));
vec3 surfaceToLight = normalize(TBNMatrix * (light.position - surfacePos)); //unit vector
vec3 eyePos = TBNMatrix * CameraPosition;
//diffuse
float diffuse = max(0.0, dot(normal, surfaceToLight));
//specular
float specular;
vec3 incidentVector = -surfaceToLight; //unit
vec3 reflectionVector = reflect(incidentVector, normal); //unit vector
vec3 surfaceToCamera = normalize(eyePos - surfacePos); //unit vector
float cosAngle = max(0.0, dot(surfaceToCamera, reflectionVector));
if(diffuse > 0.0)
specular = pow(cosAngle, shininess);
//add lighting to the fragment color (no attenuation for now)
color.rgb *= light.ambient;
color.rgb += diffuse + specular;
}
Изображение, которое я получаю, совершенно неверно. (свет установлен на камеру)
Что я здесь не так делаю?
2 ответа
Моя ставка на настройку цвета / смешивание во фрагментном шейдере...
Вы устанавливаете выходной цвет более одного раза
Если я правильно помню, на некоторых драйверах GFX, которые делают большие проблемы, например, все после линии
color = vec4(brownColor * (texture(diffuseMap, fsCoords).rgb + 0.25), 1.0);//add a fixed base color (0.25), because its dark as hell
может быть удален драйвером...
вы добавляете
color
а такжеintensities
вместоcolor*intensity
но я мог не заметить чего-то.
попробуй просто нормальный / рельефный сначала
Не обращайте внимания на эмбиент, рефлекс, зеркальность... и затем, если это сработает, добавьте остальные по одному Всегда проверяйте журналы компиляции шейдера
Лень для дальнейшего анализа вашего кода, вот как я это делаю:
Размер слева - это объект космического корабля (похожий на ZXS Elite's Viper) с фиксированной функцией. Правая сторона такая же (немного другое вращение объекта) с GLSL-шейдерами на месте и этой картой нормалей / ударов
[Вершина]
//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
// texture units:
// 0 - texture0 map 2D rgba
// 1 - texture1 map 2D rgba
// 2 - normal map 2D xyz
// 3 - specular map 2D i
// 4 - light map 2D rgb rgb
// 5 - enviroment/skybox cube map 3D rgb
uniform mat4x4 tm_l2g;
uniform mat4x4 tm_l2g_dir;
uniform mat4x4 tm_g2s;
uniform mat4x4 tm_l2s_per;
uniform mat4x4 tm_per;
layout(location=0) in vec3 pos;
layout(location=1) in vec4 col;
layout(location=2) in vec2 txr;
layout(location=3) in vec3 tan;
layout(location=4) in vec3 bin;
layout(location=5) in vec3 nor;
out smooth vec3 pixel_pos;
out smooth vec4 pixel_col;
out smooth vec2 pixel_txr;
//out flat mat3 pixel_TBN;
out smooth mat3 pixel_TBN;
//------------------------------------------------------------------
void main(void)
{
vec4 p;
p.xyz=pos;
p.w=1.0;
p=tm_l2g*p;
pixel_pos=p.xyz;
p=tm_g2s*p;
gl_Position=p;
pixel_col=col;
pixel_txr=txr;
p.xyz=tan.xyz; p.w=1.0; pixel_TBN[0]=normalize((tm_l2g_dir*p).xyz);
p.xyz=bin.xyz; p.w=1.0; pixel_TBN[1]=normalize((tm_l2g_dir*p).xyz);
p.xyz=nor.xyz; p.w=1.0; pixel_TBN[2]=normalize((tm_l2g_dir*p).xyz);
}
//------------------------------------------------------------------
[Фрагмент]
//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
in smooth vec3 pixel_pos;
in smooth vec4 pixel_col;
in smooth vec2 pixel_txr;
//in flat mat3 pixel_TBN;
in smooth mat3 pixel_TBN;
uniform sampler2D txr_texture0;
uniform sampler2D txr_texture1;
uniform sampler2D txr_normal;
uniform sampler2D txr_specular;
uniform sampler2D txr_light;
uniform samplerCube txr_skybox;
const int _lights=3;
uniform vec3 light_col0=vec3(0.1,0.1,0.1);
uniform vec3 light_dir[_lights]= // direction to local star in ellipsoid space
{
vec3(0.0,0.0,+1.0),
vec3(0.0,0.0,+1.0),
vec3(0.0,0.0,+1.0),
};
uniform vec3 light_col[_lights]= // local star color * visual intensity
{
vec3(1.0,0.0,0.0),
vec3(0.0,1.0,0.0),
vec3(0.0,0.0,1.0),
};
out layout(location=0) vec4 frag_col;
const vec4 v05=vec4(0.5,0.5,0.5,0.5);
const bool _blend=false;
const bool _reflect=true;
//------------------------------------------------------------------
void main(void)
{
float a=0.0,b,li;
vec4 col,blend0,blend1,specul,skybox;
vec3 normal;
col=(texture2D(txr_normal,pixel_txr.st)-v05)*2.0; // normal/bump maping
// normal=pixel_TBN*col.xyz;
normal=pixel_TBN[0];
blend0=texture(txr_texture0,pixel_txr.st);
blend1=texture(txr_texture1,pixel_txr.st);
specul=texture(txr_specular,pixel_txr.st);
skybox=texture(txr_skybox,normal);
if (_blend)
{
a=blend1.a;
blend0*=1.0-a;
blend1*=a;
blend0+=blend1;
blend0.a=a;
}
col.xyz=light_col0; col.a=0.0; li=0.0; // normal shading (aj s bump mapingom)
for (int i=0;i<_lights;i++)
{
b=dot(light_dir[i],normal.xyz);
if (b<0.0) b=0.0;
// b*=specul.r;
li+=b;
col.xyz+=light_col[i]*b;
}
col*=blend0;
if (li<=0.1)
{
blend0=texture2D(txr_light,pixel_txr.st);
blend0*=1.0-a;
blend0.a=a;
col+=blend0;
}
if (_reflect) col+=skybox*specul.r;
col*=pixel_col;
if (col.r<0.0) col.r=0.0;
if (col.g<0.0) col.g=0.0;
if (col.b<0.0) col.b=0.0;
a=0.0;
if (a<col.r) a=col.r;
if (a<col.g) a=col.g;
if (a<col.b) a=col.b;
if (a>1.0)
{
a=1.0/a;
col.r*=a;
col.g*=a;
col.b*=a;
}
frag_col=col;
}
//------------------------------------------------------------------
Эти исходные коды немного устарели и представляют собой смесь разных вещей для конкретного приложения.
Так что извлекайте из него только то, что вам нужно. Если вы перепутали с именами переменных, прокомментируйте меня...
tm_
обозначает матрицу преобразованияl2g
обозначает местную систему координат в преобразование глобальной системы координатdir
означает, что трансформация меняет только направление (смещение 0,0,0)g2s
обозначает глобальный экран...per
это перспективное преобразование...
Журнал компиляции GLSL
Вы должны получить его содержимое программно после компиляции вашего шейдера (не приложения!!!). Я делаю это с вызовом функции glGetShaderInfoLog
для каждого шейдера программа, которую я использую...
[Заметки]
Некоторые драйверы оптимизируют "неиспользуемые" переменные. Как вы можете видеть на изображении txr_texture1
не найден, даже если фрагментный шейдер содержит его в коде, но смешивание не используется в этом приложении, поэтому драйвер удалил его самостоятельно...
Журналы шейдеров могут многое показать (ошибки синтаксиса, предупреждения...)
Есть несколько GLSL IDE для упрощения работы с шейдерами, но я предпочитаю свою собственную, потому что я могу напрямую использовать в ней код целевого приложения. Моя выглядит так:
каждое текстовое окно является источником шейдера (вершина, фрагмент,...), правая нижняя часть - буфер обмена, левая верхняя часть - журнал шейдера после последней компиляции, а левая нижняя часть - предварительный просмотр. Мне удалось закодировать его как IDE в стиле Borland (с ключами и подсветкой синтаксиса), другие IDE, которые я видел, выглядят похожими (разные цвета грубой:)) в любом случае, если вы хотите поиграть с загрузкой шейдера в такое приложение или сделать это самостоятельно очень поможет...
Также может быть проблема с созданием TBN
Вам следует визуально проверить, соответствуют ли векторы TBN (тангенциальные, бинормальные, нормальные) поверхности объекта, рисуя цветные линии в каждой позиции вершины. Просто чтобы быть уверенным... как-то так:
Я постараюсь, чтобы ваш код работал. Вы пробовали это с движущейся камерой?
Я нигде не вижу, чтобы вы преобразовали TBNMatrix
с матрицами преобразования, просмотра и моделирования. Вы пробовали с vec3 normal = TBNMatrix[2];
оригинальные нормали? (Фрагмент шейдера)
Следующее может помочь. В шейдере Vertex у вас есть:
uniform mat4 ProjectionMatrix;
uniform mat4 CameraMatrix;
uniform mat4 ModelMatrix;
Однако здесь должны использоваться только эти 3 матрицы:
uniform mat4 PCM;
uniform mat4 MIT; //could be mat3
uniform mat4 ModelMatrix; //could be mat3
Более эффективно вычислять произведение этих матриц на CPU (и дает то же самое, потому что умножение матриц является ассоциативным). Затем этот продукт PCM можно использовать как для вычисления новой позиции с одним умножением на вершину:
gl_Position = PCM * vec4(vertex, 1.0);
MIT
это обратная транспонирование ModelMatrix
, вы должны рассчитать его на процессоре. Это можно использовать для преобразования нормалей:
vec4 tang = ModelMatrix*vec4(tangent,0);
vec4 bita= ModelMatrix*vec4(bitangent,0);
vec4 norm= PCMIT*vec4(tangent,0);
TBNMatrix = mat3(normalize(tang.xyz), normalize(bita.xyz), normalize(normal.xyz));
Я не уверен, что происходит с тангенсом и битангенсом, но таким образом нормаль будет оставаться перпендикулярной им. Это легко доказать. Здесь я использую ° b как скалярное произведение векторов a и b. Итак, пусть n - некоторая нормаль, а a - некоторый вектор на поверхности (например, касательная {bi}, ребро треугольника), и пусть A - любое преобразование. Затем:
0 = an = A ^ (- 1) A a ° n = A a ° A ^ (- T) n = 0
Где я использовал равенство A x ° y = x ° A^T y. Следовательно, если a перпендикулярно n, то A a перпендикулярно A^(-T) n, поэтому мы должны преобразовать его с помощью обратной транспонирования матрицы. Однако нормаль должна иметь длину 1, поэтому после преобразований ее следует нормализовать.
Вы также можете получить нормаль перпендикуляра, выполнив это:
vec3 normal = normalize(cross(tangent, bitangent));
Где крест (a,b) является функцией, которая вычисляет перекрестное произведение a и b, ведьма всегда перпендикулярна как a, так и b.
Извините за мой английский:)