【问题标题】:Calculating normals for model lighting results in the model no longer rendering计算模型照明的法线导致模型不再渲染
【发布时间】:2018-11-07 10:44:27
【问题描述】:

我正在开发一个简单的 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 计算法线的这种方法。正如我之前所说,在程序中添加灯光时,我无法渲染模型。我在计算中做错了吗?

【问题讨论】:

  • 您的法线向量应该类似于vec4 unitNormal = normalize( vec4(surfaceNormal, 0.0) ) 和片段着色器中的光向量。
  • @Victor 不,那部分代码很好。为什么要 4D 法线?
  • 你能展示你对计算出的法线做了什么吗?您正在计算每个三角形一个法线,因此您不能直接将它们作为顶点属性上传。
  • 叉积 不可交换,改变顺序会改变结果向量的符号。这将通过更改其符号来影响dot(normal, lightdirection),从而影响max(nDot1, 0.0)。您应该选择顶点的顺序,使法线始终朝外。
  • 如果您激活了face culling,您可以测试使用max(-nDot1, 0.0)(注意“-”号)是否会改变事情。

标签: java opengl lwjgl normals wavefront


【解决方案1】:

详细描述找出根本问题

老实说,我不明白the model does no longer show up 是什么意思。因此,我将发布一些提示,让您了解发生了什么。

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

    • 可能有几个问题来源需要检查:

      • 打开一些背景颜色,如蓝色,看看结果如何。
        • 模型是不可见的,所以一切都是蓝色的吗?模型的法线是错误的。可以开启两边渲染,关键词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)

总结:

  • 使用点积计算强度。

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

【讨论】:

    猜你喜欢
    • 2021-06-15
    • 2013-08-19
    • 1970-01-01
    • 1970-01-01
    • 2010-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多