Main Menu

3D noob

Started by NateS, September 11, 2009, 22:23:28

Previous topic - Next topic

NateS

I started a post on JGO, but the LWJGL forums seem pretty dormant over there. It makes more sense to move my discussion here anyway. :)

I'm getting started with 3D by putting some 3D effects in my 2D games. For my first effect I want a spinning spiked ball. I created a simple spiked ball with Maya. I lifted some simple code to parse the OBJ text format and managed to get my model rendered with LWJGL. Sweet!

My render code looks like this:
FloatBuffer position = BufferUtils.createFloatBuffer(4);
position.put(new float[] {0.0f, 0.0f, 800.0f, 0.0f}).flip();
GL11.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, position);
GL11.glEnable(GL11.GL_LIGHT0);

FloatBuffer red = BufferUtils.createFloatBuffer(4);
red.put(new float[] {0.8f, 0.1f, 0.0f, 1.0f}).flip();
GL11.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT_AND_DIFFUSE, red);

GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glEnable(GL11.GL_LIGHTING);

GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glFrustum(-1, 1, -1, 1, 30f, 500.0f);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
GL11.glTranslatef(0.0f, 0.0f, -290.0f);

FloatBuffer projection = BufferUtils.createFloatBuffer(128);
GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, projection);

FloatBuffer modelview = BufferUtils.createFloatBuffer(128);
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, modelview);

IntBuffer viewport = BufferUtils.createIntBuffer(128);
GL11.glGetInteger(GL11.GL_VIEWPORT, viewport);

FloatBuffer winPos = BufferUtils.createFloatBuffer(3);
GLU.gluProject(800, 600, 1f, modelview, projection, viewport, winPos);
float xs = 800 / winPos.get(0);
float ys = 600 / winPos.get(1);
float zs = 1 / winPos.get(2);
GL11.glScalef(xs, ys, zs);

GL11.glRotatef(rotation, 0, 1, 1);

obj.draw();


I'm trying to use gluProject so (for example) I can model an object with a width of 60 and when I render it to the screen it will be 60 pixels wide. The above works, however I found the values for glFrustum mostly through trial and error. I found if I change zNear to 5f instead of 30f that my object is rendered 3 to 4 pixels too small. Can anyone help me understand why this occurs and also how I should know what values to plug in to glFrustum?

Ciardhubh

Quote from: NateS on September 11, 2009, 22:23:28
I started a post on JGO, but the LWJGL forums seem pretty dormant over there. It makes more sense to move my discussion here anyway. :)

I'm getting started with 3D by putting some 3D effects in my 2D games. For my first effect I want a spinning spiked ball. I created a simple spiked ball with Maya. I lifted some simple code to parse the OBJ text format and managed to get my model rendered with LWJGL. Sweet!

My render code looks like this:
FloatBuffer position = BufferUtils.createFloatBuffer(4);
position.put(new float[] {0.0f, 0.0f, 800.0f, 0.0f}).flip();
GL11.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, position);
GL11.glEnable(GL11.GL_LIGHT0);

FloatBuffer red = BufferUtils.createFloatBuffer(4);
red.put(new float[] {0.8f, 0.1f, 0.0f, 1.0f}).flip();
GL11.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT_AND_DIFFUSE, red);

GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glEnable(GL11.GL_LIGHTING);

GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glFrustum(-1, 1, -1, 1, 30f, 500.0f);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
GL11.glTranslatef(0.0f, 0.0f, -290.0f);

FloatBuffer projection = BufferUtils.createFloatBuffer(128);
GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, projection);

FloatBuffer modelview = BufferUtils.createFloatBuffer(128);
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, modelview);

IntBuffer viewport = BufferUtils.createIntBuffer(128);
GL11.glGetInteger(GL11.GL_VIEWPORT, viewport);

FloatBuffer winPos = BufferUtils.createFloatBuffer(3);
GLU.gluProject(800, 600, 1f, modelview, projection, viewport, winPos);
float xs = 800 / winPos.get(0);
float ys = 600 / winPos.get(1);
float zs = 1 / winPos.get(2);
GL11.glScalef(xs, ys, zs);

GL11.glRotatef(rotation, 0, 1, 1);

obj.draw();


I'm trying to use gluProject so (for example) I can model an object with a width of 60 and when I render it to the screen it will be 60 pixels wide. The above works, however I found the values for glFrustum mostly through trial and error. I found if I change zNear to 5f instead of 30f that my object is rendered 3 to 4 pixels too small. Can anyone help me understand why this occurs and also how I should know what values to plug in to glFrustum?

I'd start from the plane you want to draw on and calculate the distance of the clipping planes and the left/right/bottom/top coordinates from there. left, right, etc. are the bounds of the near clipping plane, so if you move the plane by adjusting znear, you change the frustum shape, which in turn affects the shape of your objects. gluPerspective+gluLookAt are usually easier to understand in the beginning.

This site has a nice tutorial on the frustum (they have a few other nice tutorials too):
http://www.lighthouse3d.com/opengl/viewfrustum/

The OpenGL API docs are also very good, e.g.:
http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/frustum.html
http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/glu/project.html

Also a book like the red book (The OpenGL programming guide) is a good start. Older version of the Redbook on viewing:
http://www.glprogramming.com/red/chapter03.html

However if you want a 3D object (in a 2D environment) to be the same size and shape you'd probably be better of with an orthographic projection instead of a perspective one:
http://www.opengl.org/sdk/docs/man/xhtml/glOrtho.xml

NateS

Great links, and I'll check out those GLU methods. Thanks! :)

Rene

If you're making a 2D game, you're probably better off using gluOrtho2D instead of glFrustum. To put it in a simple way, using orthogonal projection an object is always drawn the same size, regardless of its distance to the camera.

If you want one 'unit' to correspond to one pixel, call
gluOrtho2D(0, resolutionWidth, resolutionHeight, 0);

where  resolutionWidth is the horizontal resolution, and resolutionHeight is the vertical resolution.

In lwjgl, GLU is located in the org.lwjgl.util.glu.GLU class.
So your code will look something like this:
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GLU.gluOrtho2D(0, resolutionWidth, resolutionHeight, 0);



If you want even more control over your projection matrix, this site gives some good information about creating your own projection matrix:
http://www.songho.ca/opengl/gl_projectionmatrix.html

To pass you own martix to openGL, use the the following code:
FloatBuffer buf = BufferUtils.createFloatBuffer(16); //Create a FloatBuffer to hold 16 floats for the 4x4 matrix

//Fill the matrix with your data
buf.
put(2).put(0).put(0).put(0),
put(0).put(2).put(0).put(0),
put(2).put(0).put(1).put(0),
put(-1).put(-1).put(0).put(1);

//Set current matrix to Projection matrix
GL11.glMatrixMode(GL11.GL_PROJECTION);

//Pass your matrix to openGL
GL11.glLoadMatrix(buf);


The matrix I used in the code above is NOT the matrix you asked for, it is one that sets (0, 0) to be the lower left corner, and (1, 1) to be the upper right corner. If you want to make your own matrix, take a look at the site I just mentioned.

I didn't try any of the code so there may be some typo's in it, but I don't think so. And don't hesitate to ask is you have any more questions ;)
When I am king, they shall not have bread and shelter only, but also teachings out of books, for a full belly is little worth where the mind is starved - Mark Twain

Ciardhubh

Quote from: Rene on September 11, 2009, 23:33:36
The matrix I used in the code above is NOT the matrix you asked for, it is one that sets (0, 0) to be the lower left corner, and (1, 1) to be the upper right corner. If you want to make your own matrix, take a look at the site I just mentioned.

It's usually important to note that OpenGL's coordinate system has its origin in the bottom left corner (which is the mathematical standard). Some libraries like Slick (and I think Java's imaging classes) put the origin in the top left corner and invert the y coordinate. Not totally sure why, though. I always wondered what the rationale might be to invert the y axis. Maybe it's due to regional tradition. Anyway, gluOrtho2D(0, resolutionWidth, resolutionHeight, 0);  also sets up a top left origin. gluOrtho2D(0, resolutionWidth, 0, resolutionHeight) sets up a bottom left origin.

That's a source of confusion and takes some extra work to get code from one representation to the other. Just so you know, in case you wonder why objects or textures suddenly apear upside down.