我正在OpenGL上创建模型加载程序。我照顾了光线和镜面反射,但仍停留在法线贴图上。我认为我在法线贴图计算中犯了一个错误。
正常图像为:
当我应用法线贴图效果时,它就是它的外观,这是屏幕截图:
我的顶点着色器:
#version 430 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 uvw;
layout(location = 3) in vec3 tangent;
layout(location = 4) in vec3 biTangent;
uniform mat4 M;
uniform mat4 MVP;
uniform mat3 N;
out block
{
vec4 position;
vec3 normal;
vec2 uvw;
vec3 tangent;
vec3 biTangent;
mat3 TBN;
} Out;
void main()
{
Out.position = M * vec4(position, 1.0);
Out.normal = normalize(N * normal);
Out.uvw = uvw;
Out.tangent = (M * vec4(tangent, 0.0)).xyz;
Out.TBN = mat3(tangent, biTangent, normal);
gl_Position = MVP * vec4(position, 1.0);
}
片段着色器是:
#version 430 core
#define M_PI 3.14159265358979323846
layout(binding = 0) uniform sampler2D dts;
layout(binding = 1) uniform sampler2D sts;
layout(binding = 2) uniform sampler2D nts;
struct Light {
vec3 position;
vec3 filterColor;
float multiplier;
};
struct Material {
vec3 baseColor;
float baseColorMultiplier;
float roughness;
float ior;
};
uniform Light light;
uniform Material material;
in block
{
vec4 position;
vec3 normal;
vec2 uvw;
vec3 tangent;
vec3 biTangent;
mat3 TBN;
} In;
out vec4 color;
vec3 Le(Light light, vec4 position, vec3 wi) {
vec3 Le;
float dist = length(wi);
Le = light.filterColor / (dist * dist);
return Le;
}
vec3 Fresnel(vec3 spec, vec3 normal, vec3 wi)
{
return spec + (1 - spec) * pow((1 - max(0.0, dot(wi, normal))), 5);
}
vec3 Normal()
{
vec4 norm = texture2D(nts, In.uvw);
return vec3(norm);
}
vec3 Diffuse(Material material, vec2 uvw) {
vec4 diff = texture2D(dts, uvw);
return vec3(diff) / M_PI;
}
vec3 Reflection(Material material, vec3 wi, vec3 normal, vec2 uvw)
{
vec3 f;
float cosTheta = dot(normal, wi);
vec4 spec = texture2D(sts, uvw);
f += vec3(1.0) * vec3(spec) * pow(max(0.0, abs(cosTheta)), material.roughness);
f += Fresnel(vec3(spec), normal, wi);
return f;
}
vec3 BRDF(Light light, Material material, vec3 wo, vec3 wi, vec4 position, vec3 normal, vec2 uvw) {
vec3 L;
// Evaluate emitted light
vec3 Li = Le(light, position, wi);
// Diffuse
vec3 f = Diffuse(material, uvw);
// Reflection
float cosThetaI = max(0.0, dot(wi, normal));
if(cosThetaI > 0.0) {
f += Reflection(material, wi, normal, uvw);
}
// BRDF function
L += f * Li * max(0.0, dot(wi, normal));
return L;
}
void main(void)
{
vec3 L;
vec3 wi, wo;
// Evaluate incoming and outgoing light direction
wi = normalize(light.position - vec3(In.position));
wo = reflect(-wi, In.normal);
// Evaluate normal map
vec4 normal = texture2D(nts, In.uvw);
normal = normalize(normal * 2.0 -1.0);
normal = vec4(normalize(In.TBN * normal.xyz), 0.0);
L += BRDF(light, material, wo, wi, In.position, normal.xyz, In.uvw);
color = vec4(L, 1.0);
}
我的切线和切线计算代码是:
void CGLPrimitive::CalculateTangentBiTangent()
{
for (unsigned int i = 0; i < positions.size(); i+=3) {
CVector3<float> p1 = positions[i];
CVector3<float> p2 = positions[i + 1];
CVector3<float> p3 = positions[i + 2];
CVector2<float> uv1 = uv[i];
CVector2<float> uv2 = uv[i+1];
CVector2<float> uv3 = uv[i+2];
CVector3<float> e1 = p2 - p1;
CVector3<float> e2 = p3 - p1;
CVector2<float> deltaUV1 = uv2 - uv1;
CVector2<float> deltaUV2 = uv3 - uv1;
float f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y);
CVector3<float> t;
t.x = f * (deltaUV2.y * e1.x - deltaUV1.y * e2.x);
t.y = f * (deltaUV2.y * e1.y - deltaUV1.y * e2.y);
t.z = f * (deltaUV2.y * e1.z - deltaUV1.y * e2.z);
t = Normalize(t);
tangents.push_back(t);
tangents.push_back(t);
tangents.push_back(t);
CVector3<float> b;
b.x = f * (-deltaUV2.x * e1.x + deltaUV1.x * e2.x);
b.y = f * (-deltaUV2.x * e1.y + deltaUV1.x * e2.y);
b.z = f * (-deltaUV2.x * e1.z + deltaUV1.x * e2.z);
b = Normalize(b);
biTangents.push_back(b);
biTangents.push_back(b);
biTangents.push_back(b);
}
}
我想知道我在哪里犯错。谢谢。
答案 0 :(得分:1)
tangent
和biTangent
必须像使用normal
向量那样通过常规矩阵进行转换:
Out.tangent = normalize(N * tangent);
Out.biTangent = normalize(N * biTangent);
Out.normal = normalize(N * normal);
在片段着色器中,计算是在世界空间中完成的。为此,您必须将法线贴图的法线向量转换为世界空间。
mat3(tangent, biTangent, normal)
是从纹理空间转换为模型空间的矩阵。
从纹理空间转换为世界空间的矩阵是:
Out.TBN = N * mat3(tangent, biTangent, normal);
或
Out.TBN = mat3(Out.tangent, Out.biTangent, Out.normal);
使用以上切线空间矩阵的计算来解决您的问题。
答案 1 :(得分:1)
几件事:
1)正如Rabbid76所说,您必须将切线和二进制切线相乘。您可以使用modelview矩阵,因为这些向量不会因比例尺不均匀而遭受任何失真。
2)我们不知道GPU中纹理的格式。如果它的Alpha通道设置为1.0,然后将整个矢量归一化:
|V|
归一化的矢量可能会在4D中进行归一化,但可能不会在3D中进行标准化。只需从纹理中提取rgb即可使用。
vec4 normal = texture2D(nts, In.uvw);
normal = normalize(normal * 2.0 -1.0);
3)这是做什么用的? vec3 normal = texture2D(nts, In.uvw).rgb;
我相信这是您问题的根源,因为您以错误的方式平滑法线。示例:
normal = normal * 0.5 + 0.5;
将成为normal (1,0,0)
但是
(1,0.5,0.5)
将成为normal (-1,0,0)
我还建议您通过绘制法线,切线和双切线(例如,使用几何着色器)来调试它们,以确保它们正确,从而调试它们。