I am trying to understand the basic concepts around the co-ordinates system in OpenGL so I have been making a test application from guides online.
Presently I have drawn a simple Square to the screen, using simple Co-ordinates of:
-1.0f, 1.0f, 0.0f, // 0, Top Left
-1.0f, -1.0f, 0.0f, // 1, Bottom Left
1.0f, -1.0f, 0.0f, // 2, Bottom Right
1.0f, 1.0f, 0.0f, // 3, Top Right
In my application I run the following code:
GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f, 100.0f);
My basic understanding here is that the code is setting the viewing port angle to 45 degrees and the width to height ratio of the window size.
Another thing I am doing is setting the viewing position as -4 units on the Z axis:gl.glTranslatef(0, 0, -4);
This is what the result looks like in Landscape...
And in Portrait...
My questions are:
How does the co-ordinate system work, how many Pixels does one unit represent? How does changing the orientation and width to height ratio effect the equation?
If I wanted to draw a square the size of the screen, with a View Port of 45 degrees and a Viewing position of z-4... how does one figure out the required width and height in units?
I'll try to answer the best I can.
There wouldn't be any reason to change the width to height ratio or 45 degree angle. Doing it the way you have it keeps the things from being stretched horizontally or vertically in an unusual way. Because you are using a perspective view, you have 3D space with depth as apposed to an Orthographic view where there is no depth. In doing glTranslatef(0,0,-4) what you've actually done is changed the MODELVIEW Matix, moving it 4 in the negative z direction, presumably before actually drawing the square. By default, the "camera" is sitting at 0,0,0 with Y (up) as the upward direction.
You may be able to translate 3D space to pixels, but with a Perspective view type, I'm not at all sure you'd really want or need to. A 2D Orthographic view would be a different story, though, as many people use OpenGL for 2D games as well. Wanting a square exactly the size of the screen, Orthographic is probably the way to go, and you should be able to with a few Google searches be able to figure out your pixel density to 2d space comparison.
Related
I have a question regarding transformations in OpenGL ES 2. I'm currently drawing a rectangle using triangle fans as depicted in the image below. The origin is located in its center, while its width and height are 0.6 and 2 respectively. I assume that these sizes are related to the model space. However, in order to maintain the ratio of height and width on a tablet or phone one has to do a projection that considers the proportion of the device lengths (again width and height). This is why I call orthoM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);and the aspectRatio is given by float aspectRatio = (float) width / (float) height. This finally leads to the rectangle shown in the image below. Now, I would like to move the rectangle along the x-axis to the border of the screen. However, I was not able to come up with the correct calculation to do so, either I moved it too little or too much. So how would the calculation look like? Furtermore, I'm a little bit confused about the sizes given in the model space. What are the max and min values that can be achieved there?
Thanks a lot!
Vertex position of the rectangle are in world space. A way to do this it could be get the screen coordinates you want to move to and then transform them into world space.
For example:
If the screen is 300 x 200 and you are in the center 0,0 in world space (or 150, 100) in screen space). You want to translate to 300.
So the transformation should be screen_position to normalized device coordiantes and then multiply by inverseOf(projection matrix * view matrix) and divided by the w component.
Here it is explained for mouse that it is finally the same, just that you know the z because it is the one you used for your rectangle already (if it is on the plane x,y): OpenGL Math - Projecting Screen space to World space coords.
I'm trying to make 2D graphics for my Android app that consists of six thin rectangles that each take up about 1/6th of the screen in width and equal the screen's height. I'm not sure the right way to determine the bounds of the x and y OpenGL coordinate plane on screen. Eventually I will need to write logic that tests which of the 6 rectangles a touch event occurs in, so I have been trying to solve this problem by remapping OpenGL's coordinate plane into the device's screen coordinate plane (where the origin (0,0) is at the top left of the screen instead of the middle.
I declare one of my six rectangles like so:
private float vertices1[] = {
2.0f, 10.0f, 0.0f, // 0, Top Left
2.0f, -1.0f, 0.0f, // 1, Bottom Left
4.0f, -1.0f, 0.0f, // 2, Bottom Right
4.0f, 10.0f, 0.0f, // 3, Top Right
};
but since i'm not sure what the visible limits are on the x and y planes (in the OpenGL coordinate system) I have no concrete way of knowing what vertices my rectangle needs to be instantiated with to occupy 1/6th of the display. Whats the ideal way to do this?
I've tried approaches such as using glOrthoof() to remap OpenGL's coordinates into easy to work with device screen coordinates:
gl.glViewport(0, 0, width, height);
// Select the projection matrix
gl.glMatrixMode(GL10.GL_PROJECTION);
// Reset the projection matrix
gl.glLoadIdentity();
// Calculate the aspect ratio of the window
GLU.gluPerspective(gl, 45.0f,(float) width / (float) height,0.1f, 100.0f);
gl.glOrthof(0.0f,width,height, 0.0f, -1.0f, 5.0f);
// Select the modelview matrix
gl.glMatrixMode(GL10.GL_MODELVIEW);
// Reset the modelview matrix
gl.glLoadIdentity();
but when I do my rectangle dissapears completely.
You certainly don't want to use a perspective projection for 2D graphics. That just doesn't make much sense. A perspective projection is for... well, creating a perspective projection, which is only useful if your objects are actually placed in 3D space.
Even worse, you have two calls to set up a perspective matrix:
GLU.gluPerspective(gl, 45.0f,(float) width / (float) height,0.1f, 100.0f);
gl.glOrthof(0.0f,width,height, 0.0f, -1.0f, 5.0f);
While that's legal, it rarely makes sense. What essentially happens if you do this is that both projections are applied in succession. So the first thing to do is get rid of the gluPerspective() call.
To place your 6 rectangles, you have a few options. Almost the easiest one is to not apply any transformations at all. This means that you will specify your input coordinates in normalized device coordinates (aka NDC), which is a range of [-1.0, 1.0] in both the x- and y-direction. So for 6 rectangles rendered side by side, you would use a y-range of [-1.0, 1.0] for all the rectangles, and an x-range of [-1.0, -2.0/3.0] for the first, [-2.0/3.0, -1.0/3.0] for the second, etc.
Another option is that you use an orthographic projection that makes specifying the rectangles even more convenient. For example, a range of [0.0, 6.0] for x and [0.0, 1.0] for y would make it particularly easy:
gl.glOrthof(0.0f, 6.0f, 0.0f, 1.0f, -1.0f, 1.0f);
Then all rectangles have a y-range of [0.0, 1.0], the first rectangle has a x-range of [0.0, 1.0], the second rectangle [1.0, 2.0], etc.
BTW, if you're just starting with OpenGL, I would pass on ES 1.x, and directly learn ES 2.0. ES 1.x is a legacy API at this point, and I wouldn't use it for any new development.
Learning OpenGL ES 2.0, using java (for Android).
Currently, I'm fooling around with the following to set up ViewPort, ViewMatrix, and Frustum and to do translation:
GLES20.glViewport(0, 0, width, height) // max, full screen
Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY,
lookZ, upX, upY, upZ);
Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
Matrix.translateM(mModelMatrix, 0, x, y, z);
Here's what I want to do:
In short, I want to display objects as realistically possible, in terms of their positions and shapes when they are projected on the device screen. (At this stage, I'm not concerned about texture, lighting, etc.)
Questions:
Suppose that I want to display a cube (each edge being 4 inches long) as if it's floating 20 inches behind the display screen of a 10" tablet that I'm holding directly in front of my eyes, 16 inches away. The line of sight is on (along) the z-axis running through the center of the display screen of the tablet perpendicularly, and the center of the cube is on the z-axis.
What are the correct values of the parameters I should pass to the above two functions to set up ViewMatrix and Frustum to simulate the above situation?
And what would be the value (length) of the edges of the cube to be defined in the model space, centered at (0, 0, 0) if NO SCALING will be used?
And finally, what would be the value of z I should pass to the above translate function, so that the cube appears to be 20 inches behind the display screen?
Do I need to set up something else?
Let's go through this step by step. Firstly, it makes sense to use inch as the world space unit. This way, you don't have to convert between units.
Let's start with the projection. If you only want objects behind the tablet to be visible, then you can just set znear to 16. zfar can be chosen arbitrarily (depending on the scene).
Next, we need the vertical field of view. If the tablet's screen is h inches high (this could be calculated from the aspect ratio and diagonal length. If you need this calculation, leave a comment), the fovy can be calculated as follows:
float fovy = 2 * atan(h / 2 / 16); //screen is 16 inches away
//needs to be converted to degrees
Matrix.perspectiveM(mProjectionMatrix, 0, fovy * 180.0f / PI, aspect, znear, zfar);
That's already been the harder part.
Let's go on to the view matrix. The view matrix is used if your camera is not aligned with the world coordinate system. Now it depends on how you want to set up the world coordinate system. If you want the eye to be the origin, you don't need a view matrix at all. We could also specify the display as the origin like so:
//looking from 16 inches in front of the tablet to the origin
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 16, 0, 0, 0, 0, 1, 0);
Positioning the cube is equally easy. If you want it to have an edge length of 4 inches, then make a cube with edge length 4. If you want its center to be positioned 20 inches behind the screen, translate it by this amount (assuming the view matrix above):
Matrix.translateM(mModelMatrix, 0, 0, 0, -20);
I'm trying to draw rectangles with a certain distance in x-axis to each other.
The distance on each rectangle should be
(screen width - rectangle width)
so that only one rectangle can be fully displayed on the screen at a time. And if I drag the first rectangle to left, the portion of that rectangle not displayed (or cut) on the screen would be the amount displayed on second rectangle .
The problem is that I could not figure out how to calculate the ratio of my rectangle's width to screen width.
My rectangle vertices are
final float[] rectangleVerticesData = {
// X, Y, Z
-1.0f, 2.0f, 0.0f,
-1.0f, -2.0f, 0.0f,
1.0f, 2.0f, 0.0f,
-1.0f, -2.0f, 0.0f,
1.0f, -2.0f, 0.0f,
1.0f, 2.0f, 0.0f
};
Based above, my rectangle width is 2. I am using a tablet with 1280 pixels width.
Using the formula above, I got
1280 - 2 = 1278
When I ran the program, the expected output is not as what I am trying to achieve. When I drag the first rectangle, the next rectangle is not displayed even if the first rectangle is fully dragged to left off the screen.
When I tried hard-coding distance to 10, the rectangles are displayed with that distance. I guess 1278 is so long to be the distance between them. How can I calculate the correct distance between the triangles based on screen width so that only one rectangle can be fully displayed at a time on the screen?
Any help would be greatly appreciated. Thank you.
In OpenGL, the screen is considered as a quad of normalised size ie, it extends from vertex (-1,-1) to vertex (+1,+1). The vertex input given to OpenGL for drawing does not range according to the screen pixel size, but is converted internally for drawing to actual screen size. Since there are many devices with varying screen sizes, to make the GL code run across all devices without hardcoding screen sizes, this is required.
So you will have to scale your calculations accordingly.
I'm trying to draw a square in OpenGL ES (Android), 2D and covering the whole screen.
At the moment I'm just using trial and error but am sure there has got to be a better way to get the size of the screen. Below is how I'm currently initializing square:
float[] square = new float[] { -0.1f, -0.1f, 0.0f,
0.1f, -0.1f, 0.0f,
-0.1f, 0.1f, 0.0f,
0.1f, 0.1f, 0.0f };
Ideally the 0.1f in the x axis would be be the width and 0.1 in y the height of the window. Any help would be greatly appreciated.
Cheers
I think the size of the screen depends on your projection. For 2D graphics, most people use glOrtho to define the parallel projection. It's up to you to specify the size here.
Also, you can specify a larger size and have multiple 2D textures positioned within the clipping bounds, or you can have a single texture with the vertices mapped to the corners of your specified projection. This single texture would contain your entire display contents.
The following site explains this a little better.
http://www.scottlu.com/2008/04/fast-2d-graphics-wopengl-es.html
. . .
WindowManager w = getWindowManager();
Display d = w.getDefaultDisplay();
int width = d.getWidth();
int height = d.getHeight();
. . .
see http://groups.google.com/group/android-developers/browse_thread/thread/229c677ef0c5ae97