I'm learning OpenGL ES. When I'm using texture for triangle, I meet error. Here is my code:
package com.test;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.GLUtils;
import android.os.Bundle;
public class TexttureTriangleTest extends Activity{
GLSurfaceView glView;
ByteBuffer byteBuffer;
FloatBuffer vertices;
AssetManager assetManager;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
assetManager = getAssets();
int VERTEX_SIZE = (2+2)*4;
byteBuffer = ByteBuffer.allocateDirect(3*VERTEX_SIZE);
byteBuffer.order(ByteOrder.nativeOrder());
vertices = byteBuffer.asFloatBuffer();
vertices.put(new float[] { 0.0f, 0.0f, 1, 0, 0, 1,
319.0f, 0.0f, 0, 1, 0, 1,
160.0f, 479.0f, 0, 0, 1, 1});
vertices.flip();
glView = new GLSurfaceView(this);
glView.setRenderer(new Render());
setContentView(glView);
}
class Render implements Renderer{
#Override
public void onDrawFrame(GL10 gl) {
try { //I think error in this block of code
Bitmap bitmap = BitmapFactory.decodeStream(assetManager.open("bobrgb888.png"));
int textureIds[] = new int[1];
gl.glGenTextures(1, textureIds, 0);
int textureId = textureIds[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);
gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
bitmap.recycle();
} catch (IOException e) {
throw new RuntimeException("couldn't load asset!");
}
gl.glViewport(0, 0, glView.getWidth(), glView.getHeight());
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrthof(340, 0, 420, 0, 0, 0);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
int VERTEX_SIZE = (2+2)*4;
vertices.position(0);
gl.glVertexPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, vertices);
vertices.position(2);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, vertices);
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// TODO Auto-generated method stub
}
}
}
I think I have met error in function onDrawFrame and in block code try-catch. Who can verify it for me and teach me how to correct it, please.
thanks :)
I'm not a great expert in OpenGL but as far as I can see here are some mistakes in yor code.
At first it's a wrong size for byteBuffer
int VERTEX_SIZE = (2+2)*4;
byteBuffer = ByteBuffer.allocateDirect(3*VERTEX_SIZE);
For your vertices array your need 18 * 4 byte size. 18 is the number of floats in the vertices and 4 is number of bytes for every float in tha array.
In the second why do you think your error in try-catch?
Look for the error line in the log in IDE. Post it here and we can see the problem.
bobrgb888.png file must be present in assets folder.
Related
This is my code, before some changes it was drawing a blue circle on green background. I don't have any idea how to fix that problem. I was trying many things. The first problem is that it looks distorted, it it more oval that circle, first idea was to create seperate function which was called out in onSurfaceChanged
DrawCircle(gl, width, height) it helped me to get correct shape of circle, but I was not able to move it like in the pattern shown below.
package pl.varkame;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.os.SystemClock;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class MyRenderer implements GLSurfaceView.Renderer {
private float kat=0;
public FloatBuffer vertexBuffer;
private DrawCircle Circlek;
private int n_seg=720; // number of segments
float[] vertices = new float[n_seg*2+2];
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
Circlek = new DrawCircle();
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
float ratio = (float) width/(float) height;
GLU.gluPerspective(gl,45.0f,(float)width/(float)height,-1.0f, -10.0f);
gl.glOrthof(-ratio, ratio, -1.0f, 1.0f, -1.0f, 1.0f);
gl.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
}
#Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glColor4f(0.0f , 0.0f , 1.0f , 1.0f);
gl.glLoadIdentity();
Circlek.draw();
}
}
class DrawCircle{
public FloatBuffer vertexBuffer;
public int c=0;
private int n_seg=720; // number of segments
float[] vertices = new float[n_seg*2+2];
public float width = 100;
public float height= 50;
float ratio = width/height;
public void Circle(GL10 gl) {
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(2 , GL10.GL_FLOAT , 0 , vertexBuffer);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDrawArrays(GL10.GL_TRIANGLE_FAN , 0 , vertices.length / 2);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
}
public void draw(){
for (int i=0; i<n_seg;i++){
float radius=0.1f ;
double rad=(Math.PI*2*i/n_seg);
vertices[c]=(float)Math.cos(rad)*radius;
vertices[c+1]=(float) Math.sin(rad)*(radius/ratio);
c+=2;
}
}
}
I was trying to animate this movement with any success
This was last working code, circle was distorted in it
package pl.varkame;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class MyRenderer implements GLSurfaceView.Renderer {
private float kat=0;
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl,45.0f,(float)width/(float)height,-1.0f, -10.0f);
gl.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
}
#Override
public void onDrawFrame(GL10 gl) {
FloatBuffer vertexBuffer;
int c=0;
int n_seg=720; // number of segments
float[] vertices = new float[n_seg*2+2];
for (int i=0; i<n_seg;i++){
float radius=0.1f ; // 10% z szerokości ekranu
double rad=(Math.PI*2*i/n_seg);
vertices[c]=(float)Math.cos(rad)*radius;
vertices[c+1]=(float) Math.sin(rad)*radius;
c+=2;
}
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
gl.glLoadIdentity();
gl.glTranslatef(0.0f,0.0f,0.0f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(2, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDrawArrays(GL10.GL_TRIANGLE_FAN,0,vertices.length/2);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
}
}
I am trying to display a png as a texture in Eclipse using the LWJGL library. I made sure to bind the texture and to set the coordinates BEFORE drawing the vertexes, but the image still isn't displaying. What could be the problem?
package javagame;
import static org.lwjgl.opengl.GL11.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.Color;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;
#SuppressWarnings("unused")
public class ImageLWJGL {
public static Texture p;
public static void main(String[] args) {
createDisplay();
createGL();
render();
cleanUp();
}
private static void createDisplay(){
try {
Display.setDisplayMode(new DisplayMode(800,600));
Display.create();
Display.setVSyncEnabled(true);
} catch (LWJGLException e) {
e.printStackTrace();
}
}
public static void createGL(){
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, Display.getWidth(), 0, Display.getHeight(), -1, 1);
glMatrixMode(GL_MODELVIEW);
glClearColor(0,0,0,1);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
}
private static void render(){
while(!Display.isCloseRequested()){
try {
p = TextureLoader.getTexture("PNG",
new FileInputStream(new File("res/wood.png")));
} catch (IOException e) {
e.printStackTrace();
}
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glColor3f(0.25f,0.75f,0.5f);
p.bind();
glBegin(GL_QUADS);
glTexCoord2f(0,0);
glVertex2f(0,0); // (origin, origin)
glTexCoord2f(0,1);
glVertex2f(0,p.getHeight()); // (origin, y-axis)-height
glTexCoord2f(1,1);
glVertex2f(p.getWidth(),p.getHeight()); // (x-axis, y-axis)
glTexCoord2f(1,0);
glVertex2f(p.getWidth(),0); // (x-axis, origin)-width
glEnd();
Display.update();
}
}
private static void cleanUp(){
Display.destroy();
}
}
Your problem is your Ortho Projection Matrix.
glOrtho(0, Display.getWidth(), 0, Display.getHeight(), -1, 1);
It goes from 0 to Display Width and 0 to Display Height, but you render your Quad from 0 to 1. So your Quad will be rendered, but to Small, that you can see it.
To solve the Problem Change the glOrtho to:
glOrtho(0, 1, 0, 1, -1, 1);
Solution below
I'm trying to disable the clamping of colors, but it does not work. There are the same OpenGL calls, such as a comparable c++ program where they work. To disable the clamping I use the following code:
GL30.glClampColor(GL30.GL_CLAMP_READ_COLOR, GL11.GL_FALSE);
GL30.glClampColor(GL30.GL_CLAMP_VERTEX_COLOR, GL11.GL_FALSE);
GL30.glClampColor(GL30.GL_CLAMP_FRAGMENT_COLOR, GL11.GL_FALSE);
For a better understanding the full code below and also a test class. As you can see, there is a test in swap if the values are below zero. I think the buffer is a floating point buffer and also the clamping is disabled. So I have no clue why it doesn't work. Any suggestions?
Canvas.java:
package render;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import javax.imageio.ImageIO;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.Pbuffer;
import org.lwjgl.opengl.PixelFormat;
import org.lwjgl.util.glu.GLU;
public class Canvas {
private final int m_width;
private final int m_height;
private Pbuffer m_buffer;
private final FloatBuffer m_readBuffer;
private final float[] m_data;
private final FloatBuffer m_dataBuffer;
public Canvas(final int width, final int height) {
this.m_width = width;
this.m_height = height;
this.m_data = new float[this.m_width * this.m_height * 4];
this.m_dataBuffer = FloatBuffer.wrap(this.m_data);
this.m_readBuffer = BufferUtils.createFloatBuffer(this.m_data.length);
}
public void disable() {
GL11.glPopClientAttrib();
GL11.glPopAttrib();
}
public void disable2D() {
GL11.glPopAttrib();
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPopMatrix();
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glPopMatrix();
}
public void enable() {
GL11.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GL30.glClampColor(GL30.GL_CLAMP_READ_COLOR, GL11.GL_FALSE);
GL30.glClampColor(GL30.GL_CLAMP_VERTEX_COLOR, GL11.GL_FALSE);
GL30.glClampColor(GL30.GL_CLAMP_FRAGMENT_COLOR, GL11.GL_FALSE);
}
public void enable2D(final int x, final int y, final int width,
final int height) {
GL11.glViewport(0, 0, this.m_width, this.m_height);
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPushMatrix();
GL11.glLoadIdentity();
GLU.gluOrtho2D(0, this.m_width, 0, this.m_height);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glPushMatrix();
GL11.glLoadIdentity();
GL11.glPushAttrib(GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glDisable(GL11.GL_LIGHTING);
}
public void init() {
try {
if ((Pbuffer.getCapabilities() & Pbuffer.PBUFFER_SUPPORTED) == 0) {
System.out.println("Canvas:: No PBuffer support!");
System.exit(-1);
}
final PixelFormat pf = new PixelFormat();
pf.withBitsPerPixel(32);
pf.withFloatingPoint(true);
this.m_buffer = new Pbuffer(this.m_width, this.m_height, pf, null);
this.m_buffer.makeCurrent();
} catch (final LWJGLException e) {
System.err.println("Canvas:: Cannot create buffer!");
e.printStackTrace();
System.exit(-1);
}
if (this.m_buffer.isBufferLost()) {
this.m_buffer.destroy();
System.err.println("Canvas:: Buffer was lost!");
System.exit(-1);
}
System.out.printf("Canvas:: Created canvas (%d %d)\n", this.m_width,
this.m_height);
}
public void swap() {
synchronized (this.m_dataBuffer) {
GL11.glReadPixels(0, 0, this.m_width, this.m_height, GL11.GL_RGBA,
GL11.GL_FLOAT, this.m_readBuffer);
while (this.m_readBuffer.remaining() > 0) {
final float t = this.m_readBuffer.get();
if (t < 0) {
System.out.println(t);
}
this.m_dataBuffer.put(t);
}
this.m_dataBuffer.rewind();
this.m_readBuffer.rewind();
}
}
}
CanvasTest.java:
package OpenGLTests;
import org.lwjgl.opengl.GL11;
import render.Canvas;
public class CanvasTest {
public static void main(final String[] args) {
final Canvas canvas = new Canvas(100, 100);
canvas.init();
canvas.enable2D(0, 0, 100, 100);
canvas.enable();
GL11.glClearColor(-1, -1, -1, 1);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glColor4f(-1, -1, 1, 1);
GL11.glBegin(GL11.GL_TRIANGLES);
GL11.glVertex2f(0.f, 0.f);
GL11.glVertex2f(100.f, 0.f);
GL11.glVertex2f(50.f, 100.f);
GL11.glEnd();
canvas.swap();
canvas.disable();
canvas.disable2D();
System.out.println("end");
}
}
Solution
Now I fixed the problem by using a FBO. I haven't known the difference between FBO and PBO (Pbuffer). But the PBO is apparently outdated and old, and don't support values, which are out of range. So you can see the initialization code of the fbo in the init() method below. Don't forget to bind the FBO when you use it.
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, this.m_fbo);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.m_color);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL30.GL_RGBA32F, this.m_width,
this.m_height, 0, GL11.GL_RGBA, GL11.GL_FLOAT,
(FloatBuffer) null);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
GL11.GL_NEAREST);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER,
GL11.GL_NEAREST);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S,
GL11.GL_CLAMP);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T,
GL11.GL_CLAMP);
GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER,
GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, this.m_color, 0);
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
Try using:
glClampColorARB(GL_CLAMP_READ_COLOR_ARB, GL_FALSE);
glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, GL_FALSE);
glClampColorARB(GL_CLAMP_FRAGMENT_COLOR_ARB, GL_FALSE);
I'm also having problems with lwjgl/unclamped color/floating point framebuffers, but at least
for using glColor3f(2.0F, 1.0F, 1.0F) or such this seems to work (without framebuffers being
involved.) However, as far as I can tell you don't actually have a floating point framebuffer in there, so what this code will do is carry out the color multiplication and then clamp it to [0F, 1F]. This is desired because anything else will be undisplayable, but if you do end up creating an unclamped float framebuffer then you'll need to use GL_RGBA16F as the format and GL_FLOAT as the data type. I have a bit of a lack of understanding of this floating point
FBO stuff (as is shown by the fact I haven't figured it out myself) but nonetheless this has worked for me up to a point. I know some people only need the vertex color set to false, but I'm using all three calls to be sure it works. By the way, for lwjgl these can be found in org.lwjgl.opengl.ARBColorBufferFloat.
GL_FIXED_ONLY_ARB can be used in place of GL_FALSE. Just make sure that your buffer you're rendering to is floating point, since fixed-only will only unclamp if your buffer is floating point - integer (unsigned byte to be precise) is the default.
Just to reiterate once more, however, you won't be able to actually find the outside-of-bounds color results by reading the main display buffer - it'll be clamped, however you'll be able to see the results. As I've tried with those GL30 functions on my mac and it gave me errors to the tune of "unsupported", ARB works for me - but not with framebuffers. I don't understand why but for your purposes I think this will work.
If anyone knows I'm drastically wrong, please correct me.
EDIT: My problems with framebuffers were simply due to my own stupidity. HDR floating-point framebuffers can have HDR textures attached like so:
glTexImage2D(GL_TEXTURE_2D, 0, hdrEnabled ? hdrFormat : GL_RGBA8, width, height, 0, GL_RGB, hdrEnabled ? hdrDataType : GL_UNSIGNED_BYTE, (java.nio.ByteBuffer) null);
There's a little snippet from my code, hdrFormat is an int set to GL_RGBA16F and hdrDataType is set to GL_HALF_FLOAT.
I'm starting with open GL and have a little problem, I'm trying to paint the screen with a solid (and random) color, but it shown a high refresh with the color. I inserted a sleep in the method onDrawFrame for seeing whats happening and the result is: black screen - colored screen - black screen - colored screen ... refreshing every second. What am I doing wrong? Thats my code:
package com.example.opengltest;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.Random;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
public class MainActivity extends Activity implements Renderer {
GLSurfaceView glView;
GL10 gl;
Object stateChanged = new Object();
Random rand = new Random();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
glView = new GLSurfaceView(this);
glView.setRenderer(this);
setContentView(glView);
}
private void paint() {
Log.i("OPENGL","paint");
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
gl.glClearColor(rand.nextFloat(), rand.nextFloat(), rand.nextFloat(),
1);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
gl.glViewport(0, 0, glView.getWidth(), glView.getHeight());
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrthof(0, 320, 0, 480, 1, -1);
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(3 * 2 * 4);
byteBuffer.order(ByteOrder.nativeOrder());
FloatBuffer vertices = byteBuffer.asFloatBuffer();
vertices.put(new float[] { 0.0f, 0.0f,
319.0f, 0.0f,
160.0f, 479.0f });
vertices.flip();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
#Override
protected void onPause() {
super.onPause();
glView.onPause();
}
#Override
protected void onResume() {
super.onResume();
glView.onResume();
}
public void onDrawFrame(GL10 gl) {
Log.i("OPENGL","onDrawFrame");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
Log.i("OPENGL","onSurfaceChanged");
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
this.gl = gl;
Log.i("OPENGL","onSurfaceCreated");paint();
}
}
You are calling your paint() method in onSurfaceChanged whereas you should call some of its contents in onDrawFrame(), and never use the sleep function inside a GL thread, it will only cause black screens flickering all over.
In particular, try moving
gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // Clear the color buffer
gl.glClearColor(rand.nextFloat(), rand.nextFloat(), rand.nextFloat(), 1); // Clear it with a random color
to the onDrawFrame function, and I don't know exactly why you're calling glClear() twice in paint(), but that's a mistake, you're clearing what you just painted with a random color! So delete that second call to onClear() and I think you should be fine. Actually, I don't even think you need the first glClear() call, because you're already clearing it with glClearColor, although if you do introduce some alpha transparency then you're better off having both (or else they will sum up).
Let us know if that helped!
I'm new to OpenGL and I'm teaching myself by making a 2D game for Android with ES 2.0. I am starting off by creating a "Sprite" class that creates a plane and renders a texture to it. To practice, I have two Sprite objects that are drawn alternating in the same place. I got this much working fine and well with ES 1.0, but now that I've switched to 2.0, I am getting a black screen with no errors. I'm exhausted trying to figure out what I'm doing wrong, but I have a strong feeling it has to do with my shaders. I'm going to dump all the relevant code here and hopefully somebody can give me an answer or some advice as to what I'm doing wrong. And if it's not immediately apparent what I'm doing wrong, perhaps some advice on how to figure it out? Thanks in advance for looking through all the code I'm about to post.
The three classes I'm posting are:
GameRenderer - the renderer for my GLSurfaceView
Shader - creates a shader program object
Sprite - creates a square and draws a texture on it
Also, I'll post my vertex and fragment shader source.
Related classes I didn't think were relevant enough to post:
GameActivity
GameView - A GLSurfaceView
GameLoopThread - My main game loop
FPSCounter - outputs the average FPS to logcat every 100 frames.
GameRender class:
package com.detour.raw;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.graphics.Bitmap;
import android.opengl.GLES20;
import android.opengl.GLU;
import android.opengl.Matrix;
import android.opengl.GLSurfaceView;
public class GameRenderer implements GLSurfaceView.Renderer{
private static final String LOG_TAG = GameRenderer.class.getSimpleName();
Context mContext;
Bitmap bitmap;
private float red = 0.0f;
private float green = 0.0f;
private float blue = 0.0f;
Shader shader;
FPSCounter fps;
Sprite sprite;
Sprite sprite2;
int x = 0;
private float[] mProjMatrix = new float[16];
private float[] mVMatrix = new float[16];
//int[] vertexShader;
//int[] fragmentShader;
//int program;
//String vShaderSource = "";
//String fShaderSource = "";
public GameRenderer(Context context){
mContext = context;
//create objects/sprites
sprite = new Sprite(mContext);
sprite2 = new Sprite(mContext);
fps = new FPSCounter();
}
#Override
public void onDrawFrame(GL10 gl) {
GLES20.glClearColor(red, green, blue, 1.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
if(x>3){
x=0;
}
if(x%2==0){
sprite.draw(gl);
}else{
sprite2.draw(gl);
}
x++;
fps.calculate();
//fps.draw(gl);
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float)(width/height);
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 0.5f, 10);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// TODO Auto-generated method stub
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glClearDepthf(1.0f);
GLES20.glDepthFunc(GLES20.GL_LEQUAL);
GLES20.glDepthMask(true);
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glCullFace(GLES20.GL_BACK);
GLES20.glClearColor(red, green, blue, 1.0f);
//load sprite/object textures (preferably loop through an array of all sprites).
sprite.loadGLTexture(gl, mContext, R.drawable.raw1);
sprite2.loadGLTexture(gl, mContext, R.drawable.raw2);
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -5.0f, 0.0f, 0f, 0f, 0f, 0.0f, 0.0f);
System.gc();
}
}
Shader class:
package com.detour.raw;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import android.content.Context;
import android.opengl.GLES20;
import android.util.Log;
public class Shader {
public static final String TAG = Shader.class.getSimpleName();
int program;
int vertexShader;
int fragmentShader;
String vShaderSource;
String fShaderSource;
public Shader(){
//blank constructor
//createProgram();
}
public Shader(String vs_source, String fs_source){
this.vShaderSource = vs_source;
this.fShaderSource = fs_source;
createProgram();
}
public Shader(int vs_source_id, int fs_source_id, Context context) {
StringBuffer vs = new StringBuffer();
StringBuffer fs = new StringBuffer();
try{
InputStream inputStream = context.getResources().openRawResource(vs_source_id);
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
String read = in.readLine();
while (read != null) {
vs.append(read + "\n");
read = in.readLine();
}
vs.deleteCharAt(vs.length() - 1);
inputStream = context.getResources().openRawResource(fs_source_id);
in = new BufferedReader(new InputStreamReader(inputStream));
read = in.readLine();
while (read != null) {
fs.append(read + "\n");
read = in.readLine();
}
fs.deleteCharAt(fs.length() - 1);
}catch (Exception e){
Log.d("ERROR-readingShader", "Could not read shader: " + e.getLocalizedMessage());
}
this.vShaderSource = vs.toString();
this.fShaderSource = fs.toString();
createProgram();
}
private void createProgram(){
program = GLES20.glCreateProgram();
if(program!=0){
vertexShader = createShader(GLES20.GL_VERTEX_SHADER, vShaderSource);
fragmentShader = createShader(GLES20.GL_FRAGMENT_SHADER, fShaderSource);
GLES20.glAttachShader(program, vertexShader);
GLES20.glAttachShader(program, fragmentShader);
GLES20.glLinkProgram(program);
}else{
Log.e(TAG, "Couldn't create program.");
}
}
private int createShader(int type, String source){
int shader = GLES20.glCreateShader(type);
if(shader!=0){
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
}
return shader;
}
public int getProgram(){
return program;
}
Sprite class:
package com.detour.raw;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
public class Sprite {
//public static final int FRAME_WIDTH = 64;
//public static final int FRAME_HEIGHT = 64;
private static final String LOG_TAG = Sprite.class.getSimpleName();
Context mContext;
Bitmap bitmap;
private int textureLoc;
private int vertexLoc;
private int[] textures = new int[1];
//private int[] pixels;
/*private float textureCoordinates[] = {
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f};*/
private float vertices[] = {
-1.0f, 1.0f,// 0.0f,
-1.0f, -1.0f,// 0.0f,
1.0f, -1.0f,// 0.0f,
1.0f, 1.0f// 0.0f
};
private short[] indices = {
0, 1, 2,
0, 2, 3};
private FloatBuffer vertexBuffer;
//private IntBuffer textureBuffer;
private ShortBuffer indexBuffer;
Shader shader;
int program;
String vShaderSource = "";
String fShaderSource = "";
public Sprite(Context context){
mContext = context;
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
ibb.order(ByteOrder.nativeOrder());
indexBuffer = ibb.asShortBuffer();
indexBuffer.put(indices);
indexBuffer.position(0);
}
public void draw(GL10 gl) {
GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length, GLES20.GL_FLOAT, indexBuffer);
}
public void loadGLTexture(GL10 gl, Context context, int id){
shader = new Shader(R.raw.sprite_vs, R.raw.sprite_fs, mContext);
program = shader.getProgram();
GLES20.glUseProgram(program);
vertexLoc = GLES20.glGetAttribLocation(program, "a_position");
textureLoc = GLES20.glGetUniformLocation(program, "u_texture"); //texture
InputStream is = context.getResources().openRawResource(id);
try {
bitmap = BitmapFactory.decodeStream(is);
} finally {
try {
is.close();
is = null;
} catch (IOException e) {
}
}
//pixels = new int[(bitmap.getWidth()*bitmap.getHeight())];
//bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
/*ByteBuffer byteBuf = ByteBuffer.allocateDirect(pixels.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
textureBuffer = byteBuf.asIntBuffer();
textureBuffer.put(pixels);
textureBuffer.position(0);*/
GLES20.glDeleteTextures(1, textures, 0);
GLES20.glGenTextures(1, textures, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
GLES20.glUniform1i(textureLoc, 0);
GLES20.glEnableVertexAttribArray(vertexLoc);
GLES20.glVertexAttribPointer(vertexLoc, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
//GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, FRAME_WIDTH, FRAME_HEIGHT, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, byteBuf);//(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
}
Vertex shader (sprite_vs.txt):
#version 110
attribute vec2 a_position;
varying vec2 v_texcoord;
void main()
{
gl_Position = vec4(a_position, 0.0, 1.0);
v_texcoord = a_position * vec2(0.5) + vec2(0.5);
}
Fragment (pixel) shader (sprite_fs.txt):
#version 110
uniform sampler2D u_texture;
varying vec2 v_texcoord;
void main()
{
gl_FragColor = texture2D(u_texture, v_texcoord);
}
Thank you so much if you actually took the time to look through this! Hopefully someone else can use this as a resource for themselves in the future, also.
A few observations/questions:
I don't know how you changed the fragment shader, but the version that is currently posted needs a precision specifier. Just add:
precision mediump float;
to the top, and it should work. Now regarding the black screen here are some questions:
When you change the glClearColor to something not black and comment out all the draw commands, does it still look black? If so, then you have a bigger problem than textures.
Second, if you ignore the texture output and try drawing each sprite as just a flat colored rectangle with no texture data, what do you get? You should be able to see some colored rectangle on the screen.
Finally, you need to bind the texture before you call glDrawElements. (Though this shouldn't matter in this example since you haven't changed the state yet.)