Каждый объект в вашем трехмерном мире имеет нормальный , где он помогает OpenGL определить, сколько света должен отразить объект. Вы, вероятно, забыли указать нормали для ваших поверхностей. Не указывая их, OpenGL будет освещать все объекты в вашем мире одинаково.
Чтобы получить нормаль поверхности в 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];
Теперь, когда у вас есть два вектора, вы, наконец, можете получить нормаль поверхности, используя Cross Product . Вкратце, перекрестное произведение представляет собой операцию, в результате которой создается новый вектор, содержащий угол, перпендикулярный входным векторам. Это нормальный , который нам нужен.
Чтобы получить кросс-произведение в вашем коде, вы должны написать свой собственный метод для его расчета. Теоретически вы вычисляете перекрестное произведение по следующей формуле:
A X B = (A.y * B.z - A.z * B.y, A.z * B.x - A.x * B.z, A.x * B.y - A.y * B.x)
В коде (используя векторы выше):
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}
для GL_POSITION
не совсем корректен. Когда четвертое значение установлено на 1.0f
, это означает, что позиция освещения равна 0, 0, 0
, независимо от того, какие первые три значения. Чтобы указать вектор для вашей позиции освещения, измените четвертое значение на 0.0f
.