Android OpenGL ES - I can't make gluLookAt/gluPerspective work
the following text is a bit l开发者_C百科enghty as I wanted to make sure that people with similar problems that e.g. google this page can easily follow the exact problem and its solutions easily. But now on to my issue:
I recently started programming OpenGL ES on an Android smartphone and ran into problems understanding how gluPerspective and gluLookAt are used. I think I understand what they do, however I cannot see the triangle I draw. I run the following code in the onDrawFrame-method of my activity. Said activity implements GLSurfaceView.Renderer with the onDrawFrame marked with @Override. My code is:
gl.glViewport(0, 0, screen_width, screen_height);
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); //set background to black
gl.glClearDepthf(1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_PROJECTION); // Change PROJECTION matrix
gl.glLoadIdentity();
float aspect_ratio = (float) screen_width / screen_height;
GLU.gluPerspective(gl, 67, aspect_ratio, 1, 100);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glMatrixMode(GL10.GL_MODELVIEW); // Change MODELVIEW matrix
gl.glLoadIdentity();
GLU.gluLookAt(gl, 0, 0, 20, 0, 0, 0, 0, 1, 0);
// make triangle with vertices (-2,0,0), (2,0,0), and (0,.5,0)
float[] vertices = { -2f, 0, 0, 2f, 0, 0, 0, .5f, 0 };
ByteBuffer buffer = ByteBuffer.allocateDirect(vertices.length * 4);
buffer.order(ByteOrder.nativeOrder());
FloatBuffer vertex_buffer = buffer.asFloatBuffer();
for (int i = 0; i < 9; i++) {
vertex_buffer.put(vertices[i]);
}
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertex_buffer);
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisable(GL10.GL_DEPTH_TEST);
The next points are short notes on what I putatively know about camera settings in the above code. PLEASE correct me if I got something wrong in the following explanations, as that might already solve all of my issues!
- glMatrixMode(GL10.GL_PROJECTION) switches to changing the camera matrix (for which I load an identity matrix directly afterwards)
- GLU.gluPerspective(gl, 67, aspect_ratio, 1, 100) sets a perspective projection with a view frustrum of 67 degree view angle in the y-dimension. Using "aspect_ration=(float)width/height" OpenGL computes the view angle in the x-dimension. Further the "1" is the near clipping plane, while "100" is the value of the far clipping plane in this case.
- As far as I know it does not matter, where in the code gluLookAt is called, as long as it is done before the actual drawing (I'm unsure about this though). In this example I change to the MODELVIEW matrix first, load an identity matrix, and then gluLookAt is called. 3.
- "GLU.gluLookAt(gl, 0, 0, 20, 0, 0, 0, 0, 1, 0)" actually consists of three 3-element vectors. The initial one ((0,0,20) in this example) sets the position of the camera in world coordinates (i.e. 20 units on the positive Z-axis, which means it is "sticking out" of the front of the phone's screen). The second vector determines the point in the world coordinate system the camera looks at (in this example the origin of the coordinate system, i.e. the camera looks along the negative z-axis). The third vector is the up-vector that defines the direction of the y-Axis of the cameras coordinate system (and therefore also the x-axis via cross product with the vector from camera position to the point it looks at).
The triangle drawing code was copied from a tutorial (but I found similar code also in other tutorials, so I assume this is not the problem).
As far as I get it, the camera is now positioned on (0,0,20) and looks towards the origin of the coordinate system. Its drawing frustrum starts at z-value 19 and goes as far as -80 (the camera is at z-axis point 20 and looks along the negative z-axis; its viewing frustrum has near and far plane at 1 and 100 (both looking along the negative z-axis in this case), respectively). Therefore, the triangle should be visible, as its vertices are positioned around the origin on the z-axis=0 plane (i.e. the xy-plane), which is clearly within the viewing frustrum. Further the drawn vertices are arranged in counter-clockwise order; the default in OpenGL.
So after this long introduction into the problem my simple question is: What did I mess up so that this code does not do what I expect it to?
I've only ever used OpenGL on PC, Mac and iOS, but from what I can tell, the code looks fine. Then again, mine always does, too, but I still get the empty screen for a while, pretty much any time I start on anything 3D from scratch (regardless of API)... so here are some things to try, pretty much in the order in which to try them. Hopefully at least one of them will be something you haven't tried yet!
- Add in a
glFlush
at the end, because you're not actually drawing all that much. You'd think the driver would do this for you when you do the buffer swap, but it seems that there's no guarantees of that -- Mac OS X doesn't always seem to, for example. - Set background to non-black. If something is drawing in black, you want to see it.
- Disable
GL_LIGHTING
. If this is enabled (which I think it is by default?) but you're not supplying normals, everything will come out black. (See above.) - Disable
GL_CULL_FACE
. It doesn't matter how carefully you think about the winding order, there's still a possibility you might have got it wrong. But ifGL_CULL_FACE
is off, that becomes impossible. - Disable
GL_DEPTH_TEST
. This has saved me a couple of times, when I've typedglClearDepth(0)
by accident. Looks like you're OK in that respect, but it's still worth a go. - Check
glGetError
religiously, after EVERY API call, just in case. (assert(glGetError()==GL_NO_ERROR
) will do for starters, but you really want to have a little loop that also prints the result ofgluErrorString
so that it's easier to see at a glance what might be wrong.) - Make sure you do a
glBindBuffer
to set yourARRAY_BUFFER
andARRAY_ELEMENT_BUFFER
bindings to 0. To initialize a VBO, you have to bind it, so if you've got something somewhere that sets one up it might just leave it bound. This will stuff up yourglVertexPointer
. - Along the lines of the last few - set up more state explicitly. In this case, for example, you might want to
glDisableClientState
the other array types, just in case. GL starts up with vertex arrays disabled, according to the docs, but it's a possibility worth eliminating.
I found the answer after looking at further tutorials (and then also noticed it in the ones I used before)! Thank all of you for your answers, they were very helpful in diagnosing OpenGL problems in general and will for sure help me in the future!
The solution to my problem is... that the problem is not glulookat or gluperspective, but in my case the internal position of the FloatBuffer. I did not set the position of variable vertex_buffer to 0 after the for(0 < i < 9) loop. Setting the buffer's position to 0 can be accomplished with at least two commands:
- vertex_buffer.rewind()
- vertex_buffer.position(0)
To my knowledge they do the same thing. I quickly tested where in the code the position reset belongs and in my example code I could call it after gl.glEnableClientState(GL10.GL_VERTEX_ARRAY), however I had to call it before gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertex_buffer), otherwise the initial problem returned.
I hope this helps other people with similar issues or maybe is just a short of glulookat and gluperspective ;)
Thanks again for your suggestions!
For OpenGL ES Users
In my case I had to write
gl.glMatrixMode(gl.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl,45,1,1.5f,50);
gl.glMatrixMode(gl.GL_MODELVIEW);
gl.glLoadIdentity();
Instead of just
GLU.gluPerspective(gl,45,1,1.5f,50);
In onSurfaceChanged
method.
精彩评论