简体   繁体   English

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

[英]Calculating normals for model lighting results in the model no longer rendering

I'm working on a simple render engine in java which can render OBJ files to the screen.我正在使用 Java 开发一个简单的渲染引擎,它可以将 OBJ 文件渲染到屏幕上。 I'm currently working on the lighting system which is used to light up the models which are present on the screen.我目前正在研究用于照亮屏幕上模型的照明系统。 Before introducing the lighting system I was able to load models to the screan easely:在介绍照明系统之前,我能够轻松地将模型加载到屏幕上:

在此处输入图片说明

however, when I add the light to the screen the model does no longer show up.但是,当我将灯光添加到屏幕时,模型不再显示。 I'm using the following shader to render the light:我正在使用以下着色器来渲染灯光:

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);

}

I've been using the tutorial series by ThinMatrix to help me with creating this program.我一直在使用ThinMatrix教程系列来帮助我创建这个程序。 However, the one big difference is that I also want to be able to load programmaticly created models, as uppose to only using models loaded by the OBJLoader.但是,一个很大的区别是我还希望能够加载以编程方式创建的模型,因为假设只使用由 OBJLoader 加载的模型。 Because of this I had to create a way to calculate normals given a vertices array and an index array.因此,我必须创建一种方法来计算给定顶点数组和索引数组的法线。

My approach to this problem was this:我解决这个问题的方法是这样的:

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

I've based this approach on this question about calculating normals.我基于这个关于计算法线的问题采用了这种方法。 As I said before I'm not able to render the model when adding a light to the program.正如我之前所说,在向程序添加灯光时我无法渲染模型。 Am I doing something wrong in my calculations?我在计算中做错了什么吗?

Long description to find out root problem找出根本问题的详细描述

Honestly I do not get what means the model does no longer show up .老实说,我不明白the model does no longer show up是什么意思。 So I am going to post a few hints how you can find out what is going on.因此,我将发布一些提示,以便您了解正在发生的事情。

  • Is the rendered model all black?渲染的模型是全黑的吗?

    • There could be several sources of problems which have to be checked:可能有几个必须检查的问题来源:

      • Turn on some background color like blue to see what is the result.打开一些背景颜色,如蓝色,看看结果是什么。
        • Is the model invisible, so all is blue?模型是隐形的,所以都是蓝色的? Normals of the model are wrong.模型的法线是错误的。 You can turn on to render both sides, keyword face culling to find out.可以开启两边渲染,关键字face culling来查找。 If it renders with back-face rendering then the normals of the model are an issue如果它使用背面渲染进行渲染,则模型的法线是一个问题
        • Is the model visible, but the model is rendered black and the background is blue?模型是否可见,但模型呈现黑色且背景呈现蓝色?
          • either direction of the light is wrong (very likely)光的方向是错误的(很可能)
          • I suggest to change the shader so there is always some minimum amount of light, so called ambient light.我建议更改着色器,以便总有一些最小的光量,即所谓的环境光。 Then each object gets a minimum lightning, even though there is a bad angle with respect to the light source like intensity = max(dot(n, l), ambience);然后每个物体都会得到最小的闪电,即使相对于光源的角度不好,比如intensity = max(dot(n, l), ambience); in the vertex shader with ambience as a parameter and n the normalized normal of the object and l the normalized light direction.在顶点着色器中,以ambience为参数, n对象的归一化法线和l归一化的光方向。 In the fragment shader I used gl_FragColor = vec4(intensity*tc.r,intensity*tc.g,intensity*tc.b,tc.a);在片段着色器中,我使用了gl_FragColor = vec4(intensity*tc.r,intensity*tc.g,intensity*tc.b,tc.a); with tc being a vec4 texture coordinate. tc是 vec4 纹理坐标。 In this way the object always has some light这样物体总是有一些光
          • or some bug in the shader code (could not spot a problem there at first sight so, but who knows?) Well I used dot product of model normal to light direction for this, in your code there seems to be the cross product.或者着色器代码中的一些错误(乍一看没有发现问题,但谁知道?)好吧,我为此使用了模型垂直于光方向的点积,在您的代码中似乎有叉积。
      • The texture is not used / accepted / assigned right to the model or the vector is returning only one pixel location which is all black纹理未被使用/接受/分配给模型或向量仅返回一个全黑的像素位置
    • Is there an error in the shader code?着色器代码中是否有错误? Compilation error which is logged as an exception?编译错误记录为异常?

      • fix it ;)修理它 ;)

I guess the problem is a mixture of using the cross product and wrong light direction (I had the same problem in my model at the beginning)我猜这个问题是使用叉积和错误的光方向的混合(一开始我在我的模型中遇到了同样的问题)

Edit One more comment to dot product: the dot product is what you need for finding out the intensity.编辑对点积的一条评论:点积是找出强度所需要的。 The geometric definition of it dot(a,b)=length(a)*length(b)*cos(alpha) where alpha is the angle between a and b.它的几何定义dot(a,b)=length(a)*length(b)*cos(alpha)其中alpha是 a 和 b 之间的角度。

  • If the model normal is the same direction as the light direction then you want full intensity.如果模型法线的方向与光的方向相同,那么您需要全强度。

  • If the model normal is in orthogonal direction (90 degrees) as the light direction then you want 0 intensity (or ambience intensity)如果模型法线在正交方向(90 度)作为光方向,那么您需要 0 强度(或环境强度)

  • If the model normal is 60 degrees in the direction of the light direction then you want half intensity (or ambience intensity)如果模型法线在光方向的方向上是 60 度,那么您需要一半强度(或环境强度)

etc等等

Edit 2 - because the dot product can have negative results now the max(dot(a,b),0) would clip this off (for opposite direction).编辑 2 - 因为点积现在可能会产生负面结果,所以max(dot(a,b),0)会将其剪掉(对于相反的方向)。 For quick ambience you can change this to max(dot(a,b),0.3)为了快速营造氛围,您可以将其更改为max(dot(a,b),0.3)

Summary:概括:

  • Use dot product for calculating intensity.使用点积计算强度。

  • Use ambient light for keeping some light, even if the angle with respect to the light source is bad.使用环境光保持一些光线,即使相对于光源的角度不好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM