3D 世界中的每个对象都有一个法线,它可以帮助 OpenGL 确定一个对象需要反射多少光。您可能忘记指定曲面的法线。如果不指定它们,OpenGL 将以相同的方式照亮您世界中的所有对象。
为了获得 3D 曲面的法线,您至少需要三个顶点,这意味着它至少是一个三角形。
样品:
为了计算表面的法线,您需要两个向量。由于您在 3D 空间中有三个顶点,这意味着这些样本点可能包含一个三角形:
// Top triangle, three points in 3D space.
vertices = new float[] {
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
0.0f, 1.0f, -1.0f,
}
鉴于这三点,您现在可以通过以下方式定义两个向量:
// Simple vector class, created by you.
Vector3f vector1 = new Vector3f();
Vector3f vector2 = new Vector3f();
vector1.x = vertices[0] - vertices[3];
vector1.y = vertices[1] - vertices[4];
vector1.z = vertices[2] - vertices[5];
vector2.x = vertices[3] - vertices[6];
vector2.y = vertices[4] - vertices[7];
vector2.z = vertices[5] - vertices[8];
现在,当您有两个向量时,您终于可以通过使用叉积来获得曲面的法线。简而言之,叉积是一种产生一个新向量的操作,该向量包含一个垂直于输入向量的角度。这是我们需要的常态。
要在代码中获得叉积,您必须编写自己的计算方法。理论上,您可以根据以下公式计算叉积:
A X B = (Ay * Bz - Az * By, Az * Bx - Ax * Bz, Ax * By - Ay * Bx)
在代码中(通过使用上面的向量):
public Vector3f crossProduct(Vector3f vector1, Vector3f vector2) {
Vector3f normalVector = new Vector3f();
// Cross product. The normalVector contains the normal for the
// surface, which is perpendicular both to vector1 and vector2.
normalVector.x = vector1.y * vector2.z - vector1.z * vector2.y;
normalVector.y = vector1.z * vector2.x - vector1.x * vector2.z;
normalVector.z = vector1.x * vector2.y - vector1.y * vector2.x;
return normalVector;
}
在任何进一步的评论之前;您可以在数组中指定您的法线,并在需要时将它们放入 OpenGL,但是如果您深入研究它,您对这个主题的理解会更好,并且您的代码将更加灵活。
所以现在我们有一个法线,您可以循环,将向量值分配给您的法线数组(如 NeHe 的端口,但动态)并设置 OpenGL 以GL_NORMAL_ARRAY
使 OpenGL 正确反射对象上的光:
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
// I'm assuming you know how to put it into a FloatBuffer.
gl.glNormalPointer(GL10.GL_FLOAT, 0, mNormalsBuffer);
// Draw your surface...
最后一条评论;如果您正在使用其他顶点值(如 5.0f、10.0f 或更大),您可能想要规范化从该crossProduct()
方法返回的向量以获得一些性能。否则 OpenGL 必须计算新向量以获得单位向量,这可能是一个性能问题。
另外,您的new float[] {-4f, 0.9f, 6f, 1f}
forGL_POSITION
也不完全正确。当第四个值设置为时,无论前三个值是什么,都1.0f
表示光的位置是。0, 0, 0
为了为您的灯光位置指定一个矢量,请将第四个值更改为0.0f
。