I recently looked at the OpenGL ES 2.0 Tutorial provided by developers.android.com. I succesfully finished it (even if it wasn't very clear) but then I bumped into a problem. Once I finished it, I was never told how to translate or scale objects. I tried different options that seemed logical at the moment but they didn't work. I am not very expirienced in OpenGL ES 2.0 in android.
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
long time = SystemClock.uptimeMillis() % 4000L;
mAngle = 0.090f * ((int) time);
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
t.draw(mMVPMatrix);
All of these matrices are size 16 float arrays. My question is, how could I do a translation with an x and y position and sam for scale (a float with the scale)? There seems to be mo setTranslateM method and, when I tried the alternative methods I was not able to make them work. What do I do?
To translate/scale/rotate your objects, you should apply these operations to your Model Matrix.
Example:
Matrix.setIdentityM(mMMatrix, 0); // reset transformations
Matrix.setRotateM(mMMatrix, 0, 0, 1.0f, 0, 0); // set rotation
Matrix.translateM(mMMatrix, 0, 10.0f, 20.0f, 0); // apply translation
Matrix.scaleM(mMMatrix, 0, 0.25f, 0.25f, 0.25f); // apply scale
Please note that certain static methods of Matrix class do memory allocations so if you do objects transformation on each frame you may want not to use them. Read more about this here http://groups.google.com/group/android-developers/browse_thread/thread/b30dd2a437cfb076?pli=1 and in my blog post here http://androidworks-kea.blogspot.com/2012/05/developers-notes-about-opengl-es.html
Related
I have a problem with skybox textures. If you twist the camera, it creates a feeling that one texture overlays another, as in the screenshot:
Skybox show code:
private void drawSkybox(int texId){
glColor4f(1,1,1,1);
glDepthMask(false);
glEnable(GL_TEXTURE_CUBE_MAP);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, texId);
glBindVertexArray(vao[0]);
glBindBuffer (GL_ARRAY_BUFFER, vbo[0]);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glBindBuffer (GL_ARRAY_BUFFER, 0);
glDepthMask(true);
glDisable(GL_TEXTURE_CUBE_MAP);
}
My opengl paramters:
glEnable(GL_ALPHA_TEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glEnable(GL_NORMALIZE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadeModel(GL_SMOOTH);
glColorMask (true, true, true, true);
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
And my call drawSkybox:
glViewport(0, 0, WIDTH, HEIGHT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-max, max, -1, 1, 10, -10);
glRotated(cameraX, 1f, 0f, 0);
glRotated(cameraY, 0f, 1f, 0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
drawSkybox(texId);
How i can fix the problem?
I understand that the problem is glDepthMask(false); but how can it be replaced?
If I just remove glDepthMask(false); and replace it with an example with glDepthFunc(GL_LEQUAL); and glDepthFunc(GL_LESS); then the skybox will overlap all other objects and only it will be visible –
Do not change the depth mask or the depth test when you draw the skybox. Keep the depth test function GL_LESS.
private void drawSkybox(int texId){
glColor4f(1,1,1,1);
// glDepthMask(false); <---- DELETE
// [...]
}
But clear the depth buffer again, after drawing the skybox. Draw the skybox first, then the skybox covers the entire screen. Then clear the depth buffer, thus all objects which are draw after, will cover the skybox:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// [...]
drawSkybox(texId);
glClear(GL_DEPTH_BUFFER_BIT);
// render other objects
// [...]
I am developing a terrain generation app on android with Opengl ES, and I am having problems rotating the camera around itself ( the FPS effect to make it clear).
what I am doing is to move the view with Matrix.setLookAtMand then rotating the view after translating it to the origin.This is a snippet of the code:
Matrix.setLookAtM(mViewMatrix, 0, xrot, eyeY, yrot, xrot, lookY, yrot,0.0f, 1.0f, 0.0f);
Matrix.translateM(mViewMatrix,0,-xrot,0f,-yrot);
Matrix.rotateM(mViewMatrix, 0, mAngleX+mAngleY, 0.0f, 1.0f,0.0f);
Matrix.translateM(mViewMatrix,0,xrot,0f,yrot);
where xrot,yrot,mAngleX+mAngleY are input from the touchscreen.
This code works only in the origin, but when you move it rotates around the y axis of the world and not the camera one. I guess I am not doing it right but I didn't find a way to do it that works anywhere.
I found a way to do it:
Matrix.setIdentityM(mCurrentRotation, 0);
Matrix.rotateM(mCurrentRotation, 0, mAngleY+mAngleX, 0.0f,1.0f, 0.0f);
Matrix.rotateM(mCurrentRotation, 0, mAngleX, 0.0f,0.0f, 1.0f);
mAngleX = 0.0f;
mAngleY = 0.0f;
Matrix.multiplyMM(mTemporaryMatrix, 0, mCurrentRotation, 0, mAccumulatedRotation, 0);
System.arraycopy(mTemporaryMatrix, 0, mAccumulatedRotation, 0, 16);
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
// multiply the rotation just for model*view
Matrix.multiplyMM(mMVPMatrix, 0, mTemporaryMatrix, 0, mMVPMatrix, 0);
//than add projection as usual
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
Where mTemporaryMatrix, mCurrentRotation,mAccumulatedRotation, are matrices to calculate total rotation, keep frame rotation and store the whole rotation of the camera, respectively. This solution works for rotation but it doesn't make the camera move in the direction it is pointing.
Basicly I want to open a Door and I thought of using glRotatef.
My Problem is that it is affecting every object which is drawn after it.
Does anyone know how to stop that ?
Door.class
public static void draw(Texture door) {
door.bind();
if(Door_Test.state == "out" && d != 90){
glRotatef(i, 0, 1, 0);
i+=5;
}
glBegin(GL_QUADS);
glColor3f(1f, 1f, 1f);glTexCoord2f(0,0);glVertex3f(-2,3, -15);
glColor3f(1f, 1f, 1f);glTexCoord2f(0,1);glVertex3f(-2,-3, -15);
glColor3f(1f, 1f, 1f);glTexCoord2f(1,1);glVertex3f(2,-3, -15);
glColor3f(1f, 1f, 1f);glTexCoord2f(1,0);glVertex3f(2,3, -15);
glEnd();
}
Common way to do it:
glPushMatrix();
glRotatef(/*...*/);
// Drawing commands here
glPopMatrix();
When you rotate, it rotates the entire scene. So to rotate a single object, you rotate the entire scene, draw your object, then rotate your screen back.
glRotatef(i, 0, 1, 0);
// Draw object.
glRotatef(-i, 0, 1, 0);
As Reto Koradi pointed out, if you continually do this you might have floating point rounding errors that accumulate over time. HolyBlackCat's answer offers a better solution.
I am making a platformer which I started developing with default java functions, but now am switching to OpenGL for everything. I have done everything like I always do with OpenGL, and what I did works fine in my other OpenGL projects. Now my problem is that LWJGL/OpenGL is scaling my textures in a very strange way.
It seems to be related to my screen's aspect ratio. (8:5)
I already had to flip the screen to make it the right way round, but as you can see the text is working fine, it's just the textured rect, and it isn't even straight on the bottom.
Here are the most important snippets from the two classes which actually use OpenGL:
Metamorph.java (main class)
public static void initGL()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, Display.getWidth(), 0, Display.getHeight(), 1, -1);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glMatrixMode(GL_MODELVIEW);
glClearColor(0, 0, 0, 1);
glDisable(GL_DEPTH_TEST);
}
public void render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glScalef(1.0f, -1.0f, 1.0f);
glTranslatef(0f, -720f, 0f);
//glScalef(1280f/800, 720f/500, 1f);
renderer.render();
Display.update();
Display.sync(60);
}
Renderer.java (rendering stuff)
private void renderMainMenuWithGL()
{
//System.out.println("Main Menu!");
glColor4f(1, 1, 1, 1);
try
{
Texture bg = loadTexture("mockery");
bg.bind();
} catch (Exception e)
{
e.printStackTrace();
}
//drawQuad(0, 0, 1280, 720, 0, 0, 1280, 720);
glPushMatrix();
{
glBegin(GL_QUADS);
glTexCoord2f(0, 0);glVertex2f(0, 0);
glTexCoord2f(0, 1);glVertex2f(0, 720);
glTexCoord2f(1, 1);glVertex2f(1280, 720);
glTexCoord2f(1, 0);glVertex2f(1280, 0);
glEnd();
}
glPopMatrix();
TrueTypeFont f = loadFont(MAINFONT, Font.PLAIN, 50);
TrueTypeFont fb = loadFont(MAINFONT, Font.PLAIN, 48);
int sel = -1;
if(Mouse.getX() > 1000 && Mouse.getX() < 1240 && Mouse.getY() > 282.5F && Mouse.getY()< 737.5F)
sel = Math.round((Mouse.getY() - 337.5F)/75F);
if(sel == 0)
drawStringRight(fb, 1240, 350, "Story", new Color(0xff516b6b));
else
drawStringRight(f, 1240, 350, "Story", new Color(0xff516b6b));
}
private void drawStringRight(TrueTypeFont f, int x, int y, String s, Color c)
{
glPushMatrix();
f.drawString(x-f.getWidth(s), y, s, c);
glPopMatrix();
}
I am also open to advice on file structure/what I did wrong elsewhere, but keep in mind this is heavily WIP
The only possible problem I can see from what you have posted is the scaling and translating you are doing prior to rendering. You should not need to do this with your projection matrix setup the way it is. Other possibilities are that either the dimensions are not really 1280x720 as you think or you have modified one of the matrices further in the code you have not posted. I would try setting both the modelview and projection matrices to the identity matrix and then use glOrtho as you have above immediatly before drawing your quad, and use Display.getWidth and Display.getHeight instead of 1280/720 for the vertex coords.
This works fine for me:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, Display.getWidth(), 0, Display.getHeight(), 1, -1);
glBegin(GL_QUADS);
glTexCoord2f(0,0);
glVertex2i(0, 0);
glTexCoord2f(1,0);
glVertex2i(Display.getWidth(), 0);
glTexCoord2f(1,1);
glVertex2i(Display.getWidth(), Display.getHeight());
glTexCoord2f(0,1);
glVertex2i(0, Display.getHeight());
glEnd();
If this still doesnt work, make sure that the viewport is also set to the entire display: glViewport(0,0,Display.getWidth(),Display.getHeight()).
Aswell, I notice that you are using glPushMatrix() and glPopMatrix before and after drawing, which does nothing and is not needed. Push and pop are used to save the current projection or modelview matrix and then reload it later, so that you can apply transformations inbetween and undo them when needed.
You're setting the projection matrix such that the coordinates of the corners of the window are (0,0) through (1680, 1050), and those are mapped into an area that covers 1280x800 pixels. Then you're drawing a 1280x720 image into it, so the screen coordinates of the image come out to span only 800x500 pixels. If you use 1280x720 in your glOrtho() call, I believe it will fix the issue. That is, you want the size of the window, not the size of the display in that call.
Update: Turns out this is a driver issue with the powerVR SGX in my Nexus S. Code works fluidly on all other devices I've tested it on.
I'll be making a smaller test case and submitting a bug report... to someone. Don't know who.
Hey guys,
First off, I'm working on a port of AndAR (ARToolkit for Android) to GLES 2.0 using the Java GLES2.0 bindings. The entirety of my code can be found here if you're curious, but I'll try to sum up the problem in this question. AndARShaders
I'm attempting to implement this paper to generate AR renderings which reflect and refract believably: Virtual Reflections in Augmented Reality Environments. In order to do this, the screen space bounding box of the object to be rendered is determined, then used to generate texture coordinates for planes representing each face of the cubemap. This means rendering a cubemap for each frame for each model. I'm only rendering one model at a time right now. I'm trying to use framebuffer objects to render the cubemap based on the method described in the paper.
Anyway, to the problem.
I have it mostly implemented. As far as I can tell, at least the front face of the cubemap has vertices and UV coordinates generated correctly. I can render my front face to the system provided frame buffer for the screen and it renders just fine without problems for as long as I'd like. The problem comes in with rendering it to a framebuffer object.
When I render my cubemap faces to a framebuffer object linked to a cubemap texture, GL eats ALL of my memory within a few seconds and crashes with a GLError 1285 (OUT OF MEMORY). If I don't bind the FBO, I can render the cubemap faces to the screen without any memory issues. Cube map texture size is 128 PX square, which should be reasonable for a mobile device. Somehow, GL is leaking memory
Here's the rough order I'm doing things. This is the entry to the render for this frame. (src/edu/dhbw/andar/ARGLES20Renderer.java ~Line 179)
// BEGIN TO DRAW FRAME. DRAW BACKGROUND CAMERA IMAGE TO QUAD
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glUseProgram(mProgram);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0); // Camera image is stored in Texture0
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureName);
//load new preview frame as a texture, if needed
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, previewFrameWidth, previewFrameHeight, mode, GLES20.GL_UNSIGNED_BYTE, frameData);
//draw camera preview frame:
squareBuffer.position(0);
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, GraphicsUtil.TRIANGLE_VERTICES_DATA_STRIDE_BYTES, squareBuffer);
GLES20.glEnableVertexAttribArray(maPositionHandle);
textureBuffer.position(0);
GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false, GraphicsUtil.TRIANGLE_VERTICES_UV_STRIDE_BYTES, textureBuffer);
GLES20.glEnableVertexAttribArray(maTextureHandle);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glUniform1i(mSamplerLoc, 0);
//draw camera square
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glDisableVertexAttribArray(maPositionHandle);
GLES20.glDisableVertexAttribArray(maTextureHandle);
DRAW_OBJECTS();
And the code to draw the objects goes somewhat like this:
( src/edu/dhbw/andar/ARGLES20Object.java ~ Line 36 )
( src/edu/dhbw/andar/pub/CustomGLES20Object.java ~Line 55 )
// Use the new program for the object (Refract/reflect shader)
GLES20.glUseProgram( mProgram );
// Transform to where the marker is
Matrix.multiplyMM(mMVPMatrix, 0, glCameraMatrix, 0, glMatrix, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
// Create a cubemap for this object from vertices
GENERATE_CUBEMAP( box.vertArray() );
// Feed in Verts
box.verts().position(0);
box.normals().position(0);
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, VERTEX_NORMAL_DATA_STRIDE, box.verts());
GLES20.glEnableVertexAttribArray(maPositionHandle);
GLES20.glVertexAttribPointer(maNormalHandle, 3, GLES20.GL_FLOAT, false, VERTEX_NORMAL_DATA_STRIDE, box.normals());
GLES20.glEnableVertexAttribArray(maNormalHandle);
// Set Uniforms...
GLES20.glUniform4f(muColor, 0.0f, 1.0f, 0.0f, 1.0f);
...
// Draw the cube faces
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
...
GLES20.glDisableVertexAttribArray(maPositionHandle);
GLES20.glDisableVertexAttribArray(maNormalHandle);
Note the GENERATE_CUBEMAP( Vertices ) Toward the beginning of rendering the object. Here's what that does. Screen space bounding box [ssbb] has been calculated from vertices.
(src/edu/dhbw/andar/ARGLES20Renderer.java ~Line 280)
// Grab the current viewport and program for restoration later
int[] OldViewport = new int[4], OldProgram = new int[1];
GLES20.glGetIntegerv(GLES20.GL_VIEWPORT, OldViewport, 0);
GLES20.glGetIntegerv(GLES20.GL_CURRENT_PROGRAM, OldProgram, 0);
// Update dynamic cubemap based on screen space bounding box for this frame
mDC.UpdateUVs( DynamicCubemap.CorrectSSBB( ssbb ), widthcorrection, heightcorrection );
// Set up the program used to render to the texture
GLES20.glUseProgram(mProgram);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureName);
float[] projmatrix = new float[16]; // Projection Matrix
Matrix.orthoM(projmatrix, 0, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
Matrix.multiplyMM(mMVPMatrix, 0, projmatrix, 0, mVMatrix, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glUniform1i(mSamplerLoc, 0); // Use the camera texture (bound in unit zero)
// Render to the front face of the cubemap
// Note: If I don't bind the new Framebuffer, this
// renders the face to the screen very nicely without memory issues
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, 0); // Ensure we aren't rendering to the same texture we're using
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[5]);
GLES20.glViewport( 0, 0, edu.dhbw.andar.Config.CUBEMAP_SIZE, edu.dhbw.andar.Config.CUBEMAP_SIZE);
GLES20.glClear( GLES20.GL_COLOR_BUFFER_BIT );
mDC.DrawFace( 5, maPositionHandle, maTextureHandle ); // Draw the front face with glDrawArrays
// Unbind the framebuffer, we no longer need to render to textures.
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
// Ensure the newly generated cubemap is bound to the correct texture unit
GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, mCubeMapTexture);
// Bind the old program and viewport
GLES20.glUseProgram( OldProgram[0] );
GLES20.glViewport( OldViewport[0], OldViewport[1], OldViewport[2], OldViewport[3] );
And That's it... Here's how I initialize my FBOs and Cubemap Textures when the program starts.
(src/edu/dhbw/andar/ARGLES20Renderer.java ~Line 128)
// Generate Cubemap Textures
int[] cubemaptextures = new int[1];
GLES20.glGenTextures(1, cubemaptextures, 0 );
mCubeMapTexture = cubemaptextures[0];
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, mCubeMapTexture);
for( int i = 0; i < 6; i++ ) {
GLES20.glTexImage2D(GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, mode, CUBEMAP_SIZE, CUBEMAP_SIZE, 0, mode, GLES20.GL_UNSIGNED_BYTE, ByteBuffer.wrap(frame));
}
GLES20.glTexParameterf(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, 0);
// Create a set of FrameBuffers for the cubemap
mFrameBuffers = new int[6];
GLES20.glGenFramebuffers(6, mFrameBuffers, 0);
for( int i = 0; i < 6; i++ ) {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[i]);
GLES20.glFramebufferTexture2D( GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, mCubeMapTexture, 0 );
GLES20.glCheckFramebufferStatus( GLES20.GL_FRAMEBUFFER );
}
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
Perhaps my ordering is wrong, or my setup is incorrect?
Sorry for the LONG post. I really did everything in my power to make this as short as possible while still giving enough information to solve the problem. I cut out a lot of extra code which is application specific. If you're interested, or you think the problem might be caused elsewhere, I included links to the actual source files so you can take a quick peek.
Thanks for your time! I've wasted FAR too much time on this.
-Griff
Edit: clarified texture size