计算模型光照的法线导致模型不再渲染

Calculating normals for model lighting results in the model no longer rendering

我正在 java 中开发一个简单的渲染引擎,它可以将 OBJ 文件渲染到屏幕上。我目前正在研究用于照亮屏幕上模型的照明系统。在介绍照明系统之前,我能够轻松地将模型加载到屏幕上:

但是,当我将灯光添加到屏幕时,模型不再显示。我正在使用以下着色器渲染光线:

顶点着色器:

#version 150

in vec3 position;
in vec2 textureCoordinates;
in vec3 normals;

out vec2 passTextureCoordinates;
out vec3 surfaceNormal;
out vec3 toLightVector;

uniform mat4 transformationMatrixTextured;
uniform mat4 projectionMatrixTextured;
uniform mat4 viewMatrixTextured;
uniform vec3 lightLocation;

void main(void){
    vec4 worldPosition = transformationMatrixTextured * vec4(position,1.0);
    gl_Position = projectionMatrixTextured * viewMatrixTextured * worldPosition;
    passTextureCoordinates = textureCoordinates;

    surfaceNormal =  (transformationMatrixTextured * vec4(normals,0.0)).xyz;
    toLightVector =  lightLocation - worldPosition.xyz;
}

片段着色器:

#version 150

in vec2 passTextureCoordinates;
in vec3 surfaceNormal;
in vec3 toLightVector;

out vec4 out_Color;

uniform sampler2D textureSampler;
uniform vec3 lightColor;

void main(void){

    vec3 unitNormal = normalize(surfaceNormal);
    vec3 unitLightVector = normalize(toLightVector);

    float nDot1 = dot(unitNormal, unitLightVector);
    float brightness = max(nDot1, 0.0);
    vec3 diffuse = brightness * lightColor;

    out_Color = vec4(diffuse, 1.0) * texture(textureSampler,passTextureCoordinates);

}

我一直在使用 tutorial series by ThinMatrix 来帮助我创建这个程序。然而,一个很大的区别是我还希望能够加载以编程方式创建的模型,而不是仅使用由 OBJLoader 加载的模型。因此,我必须创建一种方法来计算给定顶点数组和索引数组的法线。

我解决这个问题的方法是:

/**
 * Sum.
 *
 * @param arg1 the arg 1
 * @param arg2 the arg 2
 * @return the vector 3 f
 */
public static Vector3f sum(Vector3f arg1, Vector3f arg2) {
    return new Vector3f(arg1.x + arg2.x, arg1.y + arg2.y, arg1.z + arg2.z);
}

/**
 * Subtract.
 *
 * @param arg1 the arg 1
 * @param arg2 the arg 2
 * @return the vector 3 f
 */
public static Vector3f subtract(Vector3f arg1, Vector3f arg2) {
    return new Vector3f(arg1.x - arg2.x, arg1.y - arg2.y, arg1.z - arg2.z);
}

/**
 * Cross product.
 *
 * @param arg1 the arg 1
 * @param arg2 the arg 2
 * @return the vector 3 f
 */
public static Vector3f crossProduct(Vector3f arg1, Vector3f arg2) {
    return new Vector3f(arg1.y * arg2.z - arg2.y * arg1.z, arg2.x * arg1.z - arg1.x * arg2.z, arg1.x * arg2.y - arg2.x * arg1.y);
}

/**
 * Gets the normals.
 *
 * @param vertices the vertices
 * @param indexes the indexes
 * @return the normals
 */
public static float[] getNormals(float[] vertices, int[] indexes) {
    vertices = convertToIndexless(vertices, indexes);
    Vector3f tmp;
    float[] tmpArray = new float[vertices.length / 3];
    int tmpArrayCounter = 0;
    for(int i = 0; i < vertices.length; i+=9) {
        Vector3f edge1 = subtract(new Vector3f(vertices[i], vertices[i + 1], vertices[i + 2]) , new Vector3f(vertices[i + 3], vertices[i + 4], vertices[i + 5]));
        Vector3f edge2 = subtract(new Vector3f(vertices[i], vertices[i + 1], vertices[i + 2]) , new Vector3f(vertices[i + 6], vertices[i + 7], vertices[i + 8]));

        tmp = crossProduct(edge1, edge2);
        tmpArray[tmpArrayCounter++] = tmp.getX();
        tmpArray[tmpArrayCounter++] = tmp.getY();
        tmpArray[tmpArrayCounter++] = tmp.getZ();
    }
    return tmpArray;
}

/**
 * Convert to indexless.
 *
 * @param vertices the vertices
 * @param indexes the indexes
 * @return the float[]
 */
private static float[] convertToIndexless(float[] vertices, int[] indexes) {
    float[] tmpArray = new float[indexes.length * 3];
    for(int i = 0; i < indexes.length; i++) {
        tmpArray[i * 3]     = vertices[indexes[i] * 3];
        tmpArray[i * 3 + 1] = vertices[indexes[i] * 3 + 1];
        tmpArray[i * 3 + 2] = vertices[indexes[i] * 3 + 2];
    }
    return tmpArray;
}

我将此方法基于 this question 关于计算法线的内容。正如我之前所说,在向程序添加灯光时我无法渲染模型。我在计算中做错了什么吗?

找出问题根源的详细描述

老实说我不明白the model does no longer show up是什么意思。所以我将 post 一些提示,告诉你如何找出正在发生的事情。

  • 渲染出来的模型是全黑的吗?

    • 可能有几个必须检查的问题来源:

      • 打开一些背景颜色,比如蓝色,看看结果如何。
        • 模型是看不见的,所以都是蓝色的吗?模型的法线是错误的。可以开启渲染两边,关键字face culling一探究竟。如果它使用背面渲染进行渲染,那么模型的法线就是一个问题
        • 模型是否可见,但模型呈现黑色,背景为蓝色?
          • 光线方向不对(很有可能)
          • 我建议更改着色器,以便始终有一些最小量的光,即所谓的环境光。然后每个对象得到一个最小的闪电,即使相对于光源的角度不好,比如顶点着色器中的 intensity = max(dot(n, l), ambience);ambience 作为参数,n 的归一化法线对象和 l 归一化的光方向。在片段着色器中,我使用了 gl_FragColor = vec4(intensity*tc.r,intensity*tc.g,intensity*tc.b,tc.a);,其中 tc 是一个 vec4 纹理坐标。这样物体总有一些光
          • 或者着色器代码中的一些错误(第一眼看不出那里有问题,但谁知道呢?)好吧,我为此使用了垂直于光方向的模型的点积,在你的代码中似乎有叉积。
      • 纹理未被使用/接受/分配给模型或矢量仅返回一个全黑的像素位置
    • 着色器代码有错误吗?记录为异常的编译错误?

      • 修复它 ;)

我猜问题是混合使用了叉积和错误的光线方向(我一开始在我的模型中遇到了同样的问题)

编辑 对点积的更多评论:点积是找出强度所需要的。它的几何定义 dot(a,b)=length(a)*length(b)*cos(alpha) 其中 alpha 是 a 和 b 之间的角度。

  • 如果模型法线与光线方向相同,那么您需要全强度。

  • 如果模型法线与光方向正交(90度),那么你需要0强度(或环境强度)

  • 如果模型法线与光方向成 60 度,那么你需要一半强度(或环境强度)

等等

编辑 2 - 因为点积现在可能会产生负面结果 max(dot(a,b),0) 会将其剪掉(相反方向)。为了快速营造氛围,您可以将其更改为 max(dot(a,b),0.3)

总结:

  • 使用点积计算强度。

  • 使用环境光来保持一些光线,即使相对于光源的角度不好。