法线贴图严重错误

2021-12-19 00:00:00 opengl c++ lighting glsl glm-math

我尝试在我的 opengl 应用程序中实现法线贴图,但我无法让它工作.

I tried to implement normal mapping in my opengl application but I can't get it to work.

这个是漫反射贴图(我添加了棕色)和这个是法线贴图.

This is the diffuse map (which I add a brown color to) and this is the normal map.

为了获得切线和双切线(在其他地方称为副法线?)向量,我对网格中的每个三角形运行此函数:

In order to get the tangent and bitangent (in other places called binormals?) vectors, I run this function for every triangle in my mesh:

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.顶点着色器:

After that, I call glBufferData to upload the vertices, normals, uvs, tangents and bitangents to the GPU. The vertex shader:

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

我得到的图像完全不正确.(灯位于相机上)

The image I get is completely incorrect. (light positioned on camera)

我在这里做错了什么?

推荐答案

我的赌注是在片段着色器中的颜色设置/混合...

My bet is on the color setting/mixing in fragment shader...

  1. 您正在多次设置输出颜色

如果我没记错的话,一些 gfx 驱动程序会出现大问题,例如行后的所有内容

If I remember correctly on some gfx drivers that do a big problems for example everything after the line

color = vec4(brownColor * (texture(diffuseMap, fsCoords).rgb + 0.25), 1.0);//add a fixed base color (0.25), because its dark as hell

可以被驱动删除...

您正在添加 colorintensities 而不是 color*intensity

you are adding color and intensities instead of color*intensity

但我可以忽略一些东西.

but I could overlook someting.

首先尝试普通/凹凸阴影

忽略环境、反射、镜面反射...然后如果它有效,则将其余的一一添加.始终检查着色器的编译日志

Ignore ambient,reflect,specular... and then if it works add the rest one by one. Always check the shader's compilation logs

懒得进一步分析你的代码,所以我是这样做的:

左侧尺寸为固定功能渲染的太空船物体(类似于ZXS Elite的Viper).右侧相同(对象的旋转略有不同),带有 GLSL 着色器 和此法线/凹凸贴图

Left size is space ship object (similar to ZXS Elite's Viper) rendered with fixed function. Right side the same (a bit different rotation of object) with GLSL shader's in place and this normal/bump map

[顶点]

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

这些源代码有点旧,并且针对特定应用混合了不同的东西

These source codes are bit old and mix of different things for specific application

所以只提取你需要的东西.如果您对变量名称感到困惑,请评论我...

So extract only what you need from it. If you are confused with the variable names then comment me...

  • tm_ 代表变换矩阵
  • l2g 代表局部坐标系到全局坐标系的变换
  • dir 表示变换只是改变方向(偏移量为 0,0,0)
  • g2s 代表 global to screen ...
  • per 是透视变换...
  • tm_ stands for transform matrix
  • l2g stands for local coordinate system to global coordinate system transform
  • dir means that transformation changes just direction (offset is 0,0,0)
  • g2s stands for global to screen ...
  • per is perspective transform ...

GLSL 编译日志

您必须在编译着色器(不是应用程序!!!)后以编程方式获取其内容.我通过为每个着色器调用函数 glGetShaderInfoLog 来做到这一点,我使用的程序......

You have to obtain its content programaticaly after compilation of your shader's (not application!!!). I do it with calling the function glGetShaderInfoLog for every shader,program I use ...

[注释]

某些驱动程序优化了未使用"的变量.正如您在图像中看到的那样,即使片段着色器在代码中具有 txr_texture1 也未找到,但此应用程序中未使用混合,因此驱动程序自行删除了它...

Some drivers optimize "unused" variables. As you can see at the image txr_texture1 is not found even if the fragment shader has it in code but the blending is not used in this App so driver deleted it on its own...

着色器日志可以显示很多信息(语法错误、警告...)

Shader logs can show you much (syntax errors, warnings...)

很少有 GLSL IDE 可以简化着色器,但我更喜欢我自己的,因为我可以直接在其中使用目标应用程序代码.我的看起来像这样:

there are few GLSL IDEs for making shader's easy but I prefer my own because I can use in it the target app code directly. Mine looks like this:

每个txt窗口都是一个着色器源(顶点,片段,...)右下角是剪贴板,左上角是上次编译后的着色器日志,左下角是预览.我设法像 Borland 风格的 IDE 一样编码它(还有键和语法高亮)我看到的其他 IDE 看起来很相似(粗的不同颜色:))无论如何如果你想玩着色器的下载这样的应用程序或者自己做它会有很大帮助...

each txt window is a shader source (vertex,fragment,...) the right bottom is clipboard, left top is shader's log after last compilation and left bottom is the preview. I managed to code it like Borland style IDE (with the keys also and syntax highlight) the other IDEs I saw look similar (different colors of coarse:)) anyway if you want to play with shader's download such App or do it your self it will help a lot...

TBN 创建也可能存在问题

您应该通过在每个顶点位置绘制彩色线来目视检查 TBN 向量(切线、副法线、法线)是否对应于对象表面.只是为了确定......像这样:

You should visually check if the TBN vectors (tangent,binormal,normal) correspond to object surface by drawing colored lines at each vertex position. Just to be sure... something like this:

相关文章