Java – unexpected action of light source in the room
I've written several Android apps, but this is my first time using 3D programming
I created a room (4 walls, ceiling and floor) with several objects that can move the camera like walking I texture all surfaces with various images and everything works as expected
According to the specific situation, the room is 14 units wide, 16 units deep (centered on the origin) and 3 units high (1 origin and 2 below) There are two objects in the middle of the room, a cube and an inverted pyramid
Then I add a light source to shade the cube and pyramid I read and followed several Nehe ports, so I used what I worked on in the course for lighting and applied it to my new code
gl.glEnable(GL10.GL_LIGHTING); gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_AMBIENT,new float[] { 0.1f,0.1f,1f },0); gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_DIFFUSE,new float[] { 1f,1f,GL10.GL_POSITION,new float[] { -4f,0.9f,6f,0); gl.glEnable(GL10.GL_LIGHT0);
The result is that cubes and pyramids have no shadows They look the same on both sides opposite the light as they face it When the camera points directly away from the light source, the room looks like it was before I added the lighting code When I rotate the camera to face the light source, the whole room (including objects) will darken until the camera directly faces the light source
What happened here? I read a lot about lighting and how it works, but I don't see any sign why it doesn't illuminate all the sides of the room. Cubes and pyramids are colored according to the light position Is there some expected light behavior because it is "inside" the room? Did I miss something simple just because I was a novice?
Solution
Every object in the 3D world has a normal, which can help OpenGL determine how much light the object needs to reflect You may forget to specify the normal of the surface If you do not specify them, OpenGL will illuminate all objects in your world in the same way
In order to get surface normals in 3D, you need at least three vertices, which means that it is at least one triangle
Sample:
To calculate the normal of the surface, you need two vectors Since there are three vertices in 3D space, this means that these sample points may contain triangles:
// Top triangle,three points in 3D space. vertices = new float[] { -1.0f,1.0f,-1.0f,0.0f,}
Given these three points, you can now define two vectors in the following way:
// 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];
Now, when you have two vectors, you can finally use cross product to get the normal of the surface Soon, the cross product was an operation that produced a new vector containing an angle perpendicular to the input vector This is the normal situation we need
To get a cross product in your code, you must write your own method to calculate it Theoretically, you can calculate the cross product according to this formula:
In code (by using the vector above):
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; }
Before further comments; You can specify normals in an array and put them in OpenGL when needed, but you will understand this topic better if you delve into it and your code will be more flexible
So now we have a normal that can be cycled, assign the vector value to an ordinary array (such as Nehe's port, but dynamic), and set OpenGL to use GL_ NORMAL_ Array so that OpenGL correctly reflects the light on the object:
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); // I'm assuming you kNow how to put it into a FloatBuffer. gl.glNormalPointer(GL10.GL_FLOAT,mNormalsBuffer); // Draw your surface...
Another final comment; If you are using other vertex values (such as 5.0f, 10.0f or greater), you may want to normalize the vector returned from the crossproduct () method for some performance Otherwise, OpenGL must calculate the new vector to obtain the unit vector, which may be a performance problem
In addition, GL_ The new floating point [] {- 4f, 1F} of position is incorrect When the fourth value is set to 1.0F, it means that the lamp position is 0, regardless of the first three values To specify a vector for the light position, change the fourth value to 0.0F