gpt4 book ai didi

OpenGL 法线贴图

转载 作者:行者123 更新时间:2023-12-04 08:38:29 27 4
gpt4 key购买 nike

我正在尝试使用我创建的简单立方体来实现法线贴图。我跟着这个教程https://learnopengl.com/Advanced-Lighting/Normal-Mapping但是我真的不知道在绘制 3d 对象时应该如何完成法线贴图,因为本教程使用的是 2d 对象。
特别是,我的立方体似乎几乎正确点亮,但我认为它不应该如何工作。我正在使用几何着色器来输出绿色矢量法线和红色矢量切线,以帮助我解决问题。在这里,我发布了我工作的三个屏幕截图。
直接照明
directly lighted
侧灯
side lighted
在这里,我实际上尝试以不同的方式计算法线和切线。 (大错特错)
another trial
在第一张图片中,我一次计算了我的立方体法线和切线。这似乎适用于面部,但如果我旋转立方体,我认为相邻面上的照明是错误的。正如您在第二张图片中看到的,它并非完全不存在。
在第三张图片中,我尝试对每个顶点的所有法线和切线求和,因为我认为应该这样做,但结果似乎非常错误,因为光照太少。
最后,我的问题是我应该如何计算法线和切线。
我应该考虑每个相对面的每个面计算或每个顶点的总向量,还是其他?
编辑 -
我将法线和切线传递给顶点着色器并设置我的 TBN 矩阵。但是正如您在第一张图片中看到的那样,我的立方体一个面一个面地绘制,似乎与我直接看到的那个面相邻的那些面(光线充足)没有正确点亮,我不知道为什么。我认为我没有正确计算我的“每面”法线和切线。我认为计算一些通常计算对象的法线和切线可能是正确的方法。
如果计算第二张图像(绿色法线,红色切线)中可见的法线和切线来设置 TBN 矩阵是正确的,为什么右脸似乎没有很好地照明?
编辑 2——
顶点着色器:

void main(){

texture_coordinates = textcoord;
fragment_position = vec3(model * vec4(position,1.0));

mat3 normalMatrix = transpose(inverse(mat3(model)));
vec3 T = normalize(normalMatrix * tangent);
vec3 N = normalize(normalMatrix * normal);
T = normalize(T - dot(T, N) * N);
vec3 B = cross(N, T);
mat3 TBN = transpose(mat3(T,B,N));
view_position = TBN * viewPos; // camera position
light_position = TBN * lightPos; // light position
fragment_position = TBN * fragment_position;

gl_Position = projection * view * model * vec4(position,1.0);
}
在 VS 中,我设置了 TBN 矩阵,并将所有光、片段和 View 向量转换为切线空间;这样做我将不必在片段着色器中进行任何其他计算。
片段着色器:
 void main() {
vec3 Normal = texture(TextSamplerNormals,texture_coordinates).rgb; // extract normal
Normal = normalize(Normal * 2.0 - 1.0); // correct range
material_color = texture2D(TextSampler,texture_coordinates.st); // diffuse map

vec3 I_amb = AmbientLight.color * AmbientLight.intensity;
vec3 lightDir = normalize(light_position - fragment_position);

vec3 I_dif = vec3(0,0,0);
float DiffusiveFactor = max(dot(lightDir,Normal),0.0);
vec3 I_spe = vec3(0,0,0);
float SpecularFactor = 0.0;

if (DiffusiveFactor>0.0) {
I_dif = DiffusiveLight.color * DiffusiveLight.intensity * DiffusiveFactor;

vec3 vertex_to_eye = normalize(view_position - fragment_position);
vec3 light_reflect = reflect(-lightDir,Normal);
light_reflect = normalize(light_reflect);

SpecularFactor = pow(max(dot(vertex_to_eye,light_reflect),0.0),SpecularLight.power);
if (SpecularFactor>0.0) {
I_spe = DiffusiveLight.color * SpecularLight.intensity * SpecularFactor;
}
}

color = vec4(material_color.rgb * (I_amb + I_dif + I_spe),material_color.a);

}
enter image description here

最佳答案

处理不连续性与连续性
你正在以错误的方式思考这个问题。
根据用例,您的法线贴图可能是连续的或不连续的。例如,在您的立方体中,想象如果每个面都有不同的表面类型,那么法线将根据您当前所在的面而有所不同。
您使用的法线由纹理本身决定,而不是由片段中的任何混合决定。
实际算法是

  • 加载正常的 rgb 值
  • 转换为 -1 到 1 范围
  • 按模型矩阵旋转
  • 在着色计算中使用新值

  • 如果您想要连续的法线,那么您需要确保您使用的纹理空间中的图表符合纹理坐标的限制。
    从数学上讲,这意味着如果 U 和 V 是映射到形状的法向场 N 的 R^2 区域,那么如果映射的函数是 f ,则应该是:
    如果 lim S(x_1, x_2) = lim S(y_1, y_2) 其中 {x1,x2}\subset U 和 {y_1, y_2}\subset V 那么 lim f(x_1, x_2) = lim f(y_1, y_2) .
    简单来说,如果图表中的坐标映射到形状接近的位置,那么它们映射到的法线也应该在法线空间中接近。
    TL; DR 不在片段中。这应该由法线贴图本身在烘焙时完成,而不是在渲染时由您完成。
    处理切线空间
    您有 2 个选择。选项 n1,您将切线 T 和法线 N 传递给着色器。在这种情况下,副法线 B 是 T X N 并且基础 {T, N, B} 为您提供了需要表达法线的真实空间。
    假设在切线空间中,x 是边,y 是向前 z 是向上。您转换后的法线变为 (xB, yT, zN)。
    如果不通过切线,则必须先创建一个与法线正交的随机向量,然后将其用作切线。
    (注 N 为模型法线,其中 (x,y,z) 为法线贴图法线)

    关于OpenGL 法线贴图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64682383/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com