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

时间:2018-11-07 10:44:28

标签: java opengl lwjgl normals wavefront

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

enter image description here

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

VertexShader:

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

FragmentShader:

#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来计算法线。如前所述,在向程序中添加灯光时,我无法渲染模型。我在计算中做错了吗?

1 个答案:

答案 0 :(得分: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,而dot(a,b)=length(a)*length(b)*cos(alpha)是vec4纹理坐标。通过这种方式,对象始终具有一定的光线
          • 或着色器代码中的一些错误(因此一眼无法发现问题,但谁知道?)嗯,我为此使用了与光方向垂直的模型点积,在您的代码中似乎存在交叉产品。
      • 未使用/不接受/不直接将纹理分配给模型,或者矢量仅返回一个全黑的像素位置
    • 着色器代码是否存在错误?编译错误记录为异常?

      • 修复它;)

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

编辑对点积的另一条评论:点积是找出强度的必要条件。 alpha的几何定义,其中max(dot(a,b),0)是a和b之间的角度。

  • 如果模型法线与光方向相同,则需要完整强度。

  • 如果模型法线在垂直方向(90度)作为光方向,则您希望强度为0(或环境强度)

  • 如果模型法线在光方向上为60度,则需要一半强度(或环境强度)

编辑2  -因为点积现在可以产生负数,所以max(dot(a,b),0.3)会将其截掉(方向相反)。为了获得快速的氛围,您可以将其更改为 public static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler() { AllowAutoRedirect = false, MaxConnectionsPerServer = int.MaxValue, UseCookies = false, ServerCertificateCustomValidationCallback = ValidateLocalhostCertificate }); private static bool ValidateLocalhostCertificate(HttpRequestMessage arg1, X509Certificate2 arg2, X509Chain arg3, SslPolicyErrors arg4) { if (arg1.RequestUri.Host == "127.0.0.1") { return true; } else { // default validation } }

摘要:

  • 使用点积计算强度。

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