计算模型光照的法线导致模型不再渲染
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)
总结:
使用点积计算强度。
使用环境光来保持一些光线,即使相对于光源的角度不好。
我正在 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)
总结:
使用点积计算强度。
使用环境光来保持一些光线,即使相对于光源的角度不好。