how can I make it so I can plug in pixel coordinates into gl.glTranslatef() ?
Currently, I do this to get the texture appear at the bottom of the screen:
gl.glPushMatrix();
gl.glTranslatef(1.45f, -2.76f, 0);
gl.glScalef(1 / scaleX, 1 / scaleY, 0);
Square sq = getTexture(resourceId);
sq.draw(gl);
gl.glPopMatrix();
Without having to plug in the "1.45f, -2.76f, 0" values, the texture appears at the centre of the screen. How can I position my textures using pixel coordinates? Most of my texture's dimensions are 32x32, and a few 16x16.
Before I have used ((GL11Ext) gl).glDrawTexfOES() to render my textures, however I was unable to perform any transformations to the textures, for example I couldn't rotate them, etc.
I don't know how you set up your projection, but what you should be doing is use glOrtho and glViewport to set up your scene. given a window of size (width, height):
// init opengl at some previous point
gl.glViewport(0, 0, width, height);
// choose bottom-left corner, e.g. (0,0)
// use your own values for near and far planes
float left = 0, bottom = 0, near = 0.1f, far = 1000.0f;
gl.glMatrixMode(GL_PROJECTION);
gl.glOrtho(left, left + width, bottom, bottom + height, near, far);
gl.glMatrixMode(GL_MODELVIEW);
gl.glLoadIdentity();
and you will get pixel-coords (bottom-left at (left,bottom)) for your application
Related
I am drawing an image based texture using opengl in android and trying to rotate it about its center.
But the result is not as expected and it appears skewed.
First screen grab is the texture drawn without rotation and the second one is the one drawn with 10 degree rotation.
Code snippet is as below:
mViewWidth = viewWidth;//View port width
mViewHeight = viewHeight;//View port height
float ratio = (float) viewWidth / viewHeight;
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
.....
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 5, 0f, 0f, 0f, 0.0f, 1.0f, 0.0f);
Matrix.setRotateM(mRotationMatrix, 0, 10, 0, 0, 1.0f);
Matrix.multiplyMM(temp, 0, mProjectionMatrix, 0, mViewMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, temp, 0, mRotationMatrix, 0);
GLES20.glUniformMatrix4fv(mRotationMatrixHandle , 1, false, mRotationMatrix, 0);
And in shader:
....
" gl_Position = uMVPMatrix*a_position;\n"
....
The black area in the first screen grab is the area of GLSurfaceView and the grey area is the area where I am trying to draw the image.
The image is already at origin and I think there is no need to translate before rotating it.
The basic problem is that you're scaling your geometry to adjust for the screen aspect ratio before you apply the rotation.
It might not be obvious that you're actually scaling the geometry. But by calculating the coordinates you use for drawing to adjust for the aspect ratio, you are effectively applying a non-uniform scaling transformation to the geometry. And if you then rotate the result, it will get distorted.
What you need to do is apply the rotation before you scale. This will require some reorganization of your current code. Since you apply the scaling before you pass the coordinates to OpenGL, and then do the rotation in the shader, you can't easily change the order. You either have to:
Apply both transformations, in the proper order, to the input coordinates before you pass them to OpenGL, and remove the rotation from the shader code.
Apply both transformations, in the proper order, in the shader code. To do this, you would not modify the input coordinates to adjust to the aspect ratio, and pass a scaling factor into the shader instead.
For the first option, applying a 2D rotation in your own code is easy enough, and it looks like you only have 4 vertices, so there is no efficiency concern. Still the second options is certainly more elegant. So instead of scaling the coordinates in your client code, pass a scaling factor as a uniform into the shader. Then, in the GLSL code, apply the rotation first, and scale the resulting coordinates.
Another option is that you build the complete transformation matrix (again based on applying the individual transformations in the correct order), and pass that matrix into the shader.
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 know that normalised coordinates should be -1 (Left) and +1 (Right) and -1 (Bottom) and +1 (Top)
like this:
But after applying this:
From my onSurfaceChanged method
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
Matrix.orthoM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
//The above line can be replaced with:
//Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
//But I get the same results with either frustumM or otrhoM
And this in my onDrawFrame method
Matrix.setLookAtM(mVMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
(I then pass mMVPMatrix into my custom class's draw method where it is rotated and translated).
But, my co-ordinates seem to change - this is roughly what happens:
As you can see, the x co-ordinates are altered somewhat, -1 and +1 are no longer the edges of the screen (on the device I'm using at the moment, the outer edges become -1.7 and +1.7)
Y coordinates remain unchanged.
Would appreciate if someone could point out where I'm going wrong? I need it to be -1 through +1 like it should be.
Thanks
To my eyes it appears correct. If your screen is not a square, then are you sure you want your x axis to be stretched so it behaves like a square screen? Because if you do that, then if you tell OpenGL to draw a square, it will appear as a rectangle on the screen instead if you don't have your x axis edges be larger than your y axis edges when your screen width is wider than your screen height, as suggested by the image. That's why you pass your ratio to the projection, so it knows how to draw things properly.
What is happening when you draw a regular square on the screen? Is it appearing as a square or a rectangle?
Thanks to stack overflow I am very close to figuring out lots of problems I've been having. I got one more for this weekend.
I have an object called GameObject2D that is basically a square with some textures. Everything works when projecting this object in 3D. I use a method called prepare3Ddrawing to do this. I then want to do 2D drawing so I have a I am using a method to prep the projection matrix for 2d drawing. I've tried two different versions of the prepare2Ddrawing and can't seam to get it to work. First here is my 3d prep...
private void prepare3Ddrawing(GL10 gl)
{
gl.glLoadIdentity();
gl.glViewport(0, 0, getWidth(), getHeight());
gl.glDisable(GL10.GL_DITHER);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl, 45.0f, (float)getWidth()/(float)getHeight(),0.1f,100.0f);
}
And here is the version of my 2D that puts the origin at the bottom left instead of the top left...
private void prepare2Ddrawing(GL10 gl)
{
gl.glDisable(GL10.GL_DEPTH_TEST);
gl.glLoadIdentity();
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluOrtho2D(gl,0,getWidth(), 0, getHeight());
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
I believe I need the right combination of GLU.gluOrtho2D(gl...) to get the right result. Any ideas? Thanks!
GLU.gluOrtho2D(gl,0,getWidth(), 0, getHeight()) has parameters left, right, bottom, top. If you want "screen-like" coordinates you need to set top to "0.0" and bottom to "getHeight()". This method is basically used to setup your coordinate system. For instance, if you need to draw some graph you would want the (0,0) in center, left if (-1,0) and top is (0,1) so the parameters would be (-1, 1,-1, 1) but if you want to have something like most common screen coordinate system you will want to have (0,0) in top left corner and (width, height) in bottom right corner (0, width, height, 0). Or for the sake of simplicity: Just insert the coordinates you want at top-left and bottom-right points of your view.