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.)
Related
So, I'm trying to make a simple 3d engine based on OpenGL ES 2 (Android). First (apart from many tutorials and such), I copy-pasted code from developer.android.com tutorial step-by-step. Everything worked fine. Then I started to modify it. I changed Mesh class' fields and constructor so that vertex coordinates, shader codes, color and number of vertices are not preset (all the code is at the end of the question).
Changed a Mesh field in GL20Renderer class to private List<Mesh> meshs = new ArrayList<Mesh>();. Created functions for adding and removing meshs. The only add function used at the moment in my code is the following:
public void addMesh( Mesh meshToAdd ) {
meshs.add( meshToAdd );
}
The only change in GL20Renderer.onDrawFrame() is that the code there loops through mesh list and calls Mesh.draw() method for every mesh:
for( Mesh meshToDraw : meshs ) {
meshToDraw.draw(scratch);
}
Then I tried to add one mesh from GL20Renderer.onSurfaceCreated() method - it worked fine. Add another mesh from the same method - works fine.
And then I tried to add a mesh from GL20Activity.onCreate() method (while removing the same code from GL20Renderer.onSurfaceCreated()):
float[] vertexCoords = { 0.0f, 0.622008459f, 0.0f, -0.5f, -0.311004243f, 0.0f, 0.5f, -0.311004243f, 0.0f };
int[] drawOrder = { 1, 2, 3 };
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
String vertexShaderCode = "uniform mat4 uMVPMatrix;" + "attribute vec4 vPosition;" + "void main() {" + " gl_Position = uMVPMatrix * vPosition;" + "}";
String fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}";
renderer.addMesh( new Mesh( 3, vertexCoords, drawOrder, 1, color, vertexShaderCode, fragmentShaderCode) );
Mesh gets added to mesh list but doesn't get displayed for some reason (it's properties are the same as the tutorial's mesh's ones). If meshs are added from both GL20Activity.onCreate() and Renderer.onSurfaceCreated() - none of them gets displayed, but they're still on mesh list. Then I added onRendererInitialized() method to GL20Activity, which is called from the very end of GL20Renderer.onCreate() method. onRendererInited() just adds a mesh, and it gets displayed, but only if no mesh is added from GL20Activity.onCreate().
The question is: Why does that mesh not get drawn if it's added from GL20Activity.onCreate() method? I tried changing vertices' coordinates so they are not the cause of a problem. I have also tried to add some code to GL20Renderer.onDrawFrame() to make sure that meshToDraw.draw( scratch ); line of code runs properly, and it does run properly, but still mesh doesn't get drawn. But since mesh with same vertices' coordinates and color added from GL20Activity.onRendererInitialized() and/or from GL20Renderer.onSurfaceCreated() gets drawn, there shouldn't be any problem with drawing mesh, which is added from GL20Activity.onCreate().
MainActivity.java:
package com.Reaper.VisionEngine;
import android.app.*;
import android.content.*;
import android.os.*;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState {
super.onCreate(savedInstanceState);
Intent intent = new Intent( this, GL20Activity.class );
startActivity( intent );
}
}
GL20Activity.java:
package com.Reaper.VisionEngine;
import android.app.*;
import android.os.*;
import android.widget.*;
import com.Reaper.VisionEngine.GLRenderer.*;
import com.Reaper.VisionEngine.GLSurfaceView.*;
import com.Reaper.VisionEngine.Mesh.*;
import java.io.*;
public class GL20Activity extends Activity {
GL20SurfaceView GLView;
GL20Renderer renderer;
#Override
protected void onCreate( Bundle savedInstanceState ) {
super.onCreate(savedInstanceState);
GLView = new GL20SurfaceView(this);
setContentView(GLView);
renderer = GLView.getRenderer();
float[] vertexCoords = { 0.0f, 0.622008459f, 0.0f, -0.5f, -0.311004243f, 0.0f, 0.5f, -0.311004243f, 0.0f };
int[] drawOrder = { 1, 2, 3 };
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
String vertexShaderCode = "uniform mat4 uMVPMatrix;" + "attribute vec4 vPosition;" + "void main() {" + " gl_Position = uMVPMatrix * vPosition;" + "}";
String fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}";
renderer.addMesh( new Mesh( 3, vertexCoords, drawOrder, 1, color, vertexShaderCode, fragmentShaderCode) );
}
GL20Renderer.java:
package com.Reaper.VisionEngine.GLRenderer;
import android.opengl.*;
import com.Reaper.VisionEngine.*;
import com.Reaper.VisionEngine.Mesh.*;
import java.util.*;
import javax.microedition.khronos.egl.*;
import javax.microedition.khronos.opengles.*;
import javax.microedition.khronos.egl.EGLConfig;
public class GL20Renderer implements GLSurfaceView.Renderer {
private List<Mesh> meshs = new ArrayList<Mesh>();
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private float[] mRotationMatrix = new float[16];
public volatile float mAngle;
private List<Mesh> meshQueue = new ArrayList<Mesh>();
private GL20Activity activity;
public void onSurfaceCreated(GL10 gl, EGLConfig glConfig) {
GLES20.glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
}
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
float[] scratch = new float[16];
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
for( Mesh meshToDraw : meshs ) {
meshToDraw.draw(scratch);
}
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
public static int loadShader(int type, String shaderCode){
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
public void addMesh( Mesh meshToAdd ) {
meshs.add( meshToAdd );
}
GL20SurfaceView.java:
package com.Reaper.VisionEngine.GLSurfaceView;
import android.content.*;
import android.opengl.*;
import android.view.*;
import com.Reaper.VisionEngine.*;
import com.Reaper.VisionEngine.GLRenderer.*;
public class GL20SurfaceView extends GLSurfaceView {
private final GL20Renderer Renderer;
public GL20SurfaceView(Context context) {
super(context);
setEGLContextClientVersion(2);
Renderer = new GL20Renderer();
setRenderer(Renderer);
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
public boolean onTouchEvent(MotionEvent e) { // TODO: CHANGE THIS
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
requestRender();
}
return true;
}
public GL20Renderer getRenderer() {
return Renderer;
}
}
Mesh.java:
package com.Reaper.VisionEngine.Mesh;
import android.opengl.*;
import com.Reaper.VisionEngine.GLRenderer.*;
import java.nio.*;
public class Mesh {
private FloatBuffer vertexBuffer;
public final int GL20Program;
private int PositionHandle;
private int ColorHandle;
private int MVPMatrixHandle;
private int colorOverridesTexture = 0;
private int vertexCount = 1;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
private int[] drawOrder; // TODO: Implement
static final int COORDS_PER_VERTEX = 3;
private float vertexCoords[];
float color[] = new float[3];
private String vertexShaderCode;
private String fragmentShaderCode;
private final String defaultVertexShaderCode = "uniform mat4 uMVPMatrix;" + "attribute vec4 vPosition;" + "void main() }" + " gl_Position = uMVPMatrix * vPosition;" + "}";
private final String defaultFragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() }" + " gl_FragColor = vColor;" + "}";
public Mesh( int newVertexCount, float[] newVertexCoords, int[] newDrawOrder, int useColor, float[] newColor, String newVertexShaderCode, String newFragmentShaderCode ) {
vertexCount = newVertexCount;
vertexCoords = newVertexCoords;
drawOrder = newDrawOrder;
colorOverridesTexture = useColor;
color = newColor;
vertexShaderCode = newVertexShaderCode;
fragmentShaderCode = newFragmentShaderCode;
ByteBuffer bb = ByteBuffer.allocateDirect( vertexCoords.length * 4 );
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put( vertexCoords );
vertexBuffer.position( 0 );
int vertexShader = GL20Renderer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = GL20Renderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
GL20Program = GLES20.glCreateProgram();
GLES20.glAttachShader(GL20Program, vertexShader);
GLES20.glAttachShader(GL20Program, fragmentShader);
GLES20.glLinkProgram(GL20Program);
}
public void draw(float[] mvpMatrix) {
MVPMatrixHandle = GLES20.glGetUniformLocation(GL20Program, "uMVPMatrix");
GLES20.glUniformMatrix4fv(MVPMatrixHandle, 1, false, mvpMatrix, 0);
GLES20.glUseProgram(GL20Program);
PositionHandle = GLES20.glGetAttribLocation(GL20Program, "vPosition");
GLES20.glEnableVertexAttribArray(PositionHandle);
GLES20.glVertexAttribPointer(PositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
ColorHandle = GLES20.glGetUniformLocation(GL20Program, "vColor");
GLES20.glUniform4fv(ColorHandle, 1, color, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
GLES20.glDisableVertexAttribArray(PositionHandle);
}
}
Sorry for bad english.
I think at your GL20Activity.onCreate() stage, the EGL context is not available or not made current. Hence the mesh added there, the gl* commands in Mesh's constructor will fail. Even though the mesh object is added to the list, the corresponding OpenGL elements are not initialized so the draw will fail, or draw nothing. You can verify this by checking for OpenGL errors after gl* statements in Mesh's constructor when called from GL20Activity.onCreate().
Now when you do the same at the GL20Renderer.onSurfaceCreated(), (I think that's what you mean by GL20Renderer.onCreated()) the EGL context is available and valid so all gl* commands in Mesh's constructor will execute and hence the mesh's OpenGL components exist, so it is displayed correctly.
Since this method is called at the beginning of rendering, as well as
every time the EGL context is lost, this method is a convenient place
to put code to create resources that need to be created when the
rendering starts, and that need to be recreated when the EGL context
is lost. Textures are an example of a resource that you might want to
create here.
refer https://developer.android.com/reference/android/opengl/GLSurfaceView.Renderer.html for more info.
I developed a simple JOGL rendering a Cube on the screen, followed the same procedure that would work on OpenGL ES 2 from IPhone. but this piece of code does not displaying anything when it is ran.
can someone help to take a browse and point out what is the problem?
Here is my Code
import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.*;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.FPSAnimator;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import javax.swing.*;
import java.awt.*;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
public class TestScene extends JFrame implements GLEventListener {
private static final float[] CUBE_POSITIONS = {
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
};
private static final short[] INDICIES = {
0, 1, 2, 0, 2, 3,//front
2, 1, 5, 6, 2, 5,//right
7, 6, 4, 6, 5, 4,//back
7, 4, 3, 3, 4, 0,//right
6, 7, 2, 7, 3, 2,//top
0, 4, 1, 1, 4, 5,//bottom
};
private GL4 gl;
private int program;
private Matrix4f projection = new Matrix4f();
private Matrix4f viewing = new Matrix4f();
private Matrix4f translation = new Matrix4f();
private int projectionIndex;
private int viewingMatrix;
private int transIndex;
private int positionIndex;
private int vbo;
private int vio;
public TestScene() throws HeadlessException {
super("Testing");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final GLCanvas canvas = new GLCanvas(new GLCapabilities(GLProfile.get(GLProfile.GL4)));
final FPSAnimator animator = new FPSAnimator(25);
animator.add(canvas);
animator.start();
canvas.addGLEventListener(this);
getContentPane().add(canvas);
setSize(800, 600);
setVisible(true);
}
#Override
public void init(final GLAutoDrawable drawable) {
gl = drawable.getGL().getGL4();
try {
//init program and shader
final int vertexShader = createShader("/Users/gang_liu/Develop/Java/ideaProject/KLM/JOGL_Studies_1/vertex.glsl", GL4.GL_VERTEX_SHADER);
final int fragmentShader = createShader("/Users/gang_liu/Develop/Java/ideaProject/KLM/JOGL_Studies_1/fragment.glsl", GL4.GL_VERTEX_SHADER);
program = gl.glCreateProgram();
gl.glAttachShader(program, vertexShader);
gl.glAttachShader(program, fragmentShader);
gl.glLinkProgram(program);
//generate vbo
final IntBuffer intBuffer = IntBuffer.allocate(1);
gl.glGenBuffers(1, intBuffer);
vbo = intBuffer.get();
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo);
gl.glBufferData(GL.GL_ARRAY_BUFFER, CUBE_POSITIONS.length * Buffers.SIZEOF_FLOAT, FloatBuffer.wrap(CUBE_POSITIONS), GL.GL_STATIC_DRAW);
intBuffer.flip();
//generate vio
gl.glGenBuffers(1, intBuffer);
vio = intBuffer.get();
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, vio);
gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, INDICIES.length * Buffers.SIZEOF_SHORT, ShortBuffer.wrap(INDICIES), GL.GL_STATIC_DRAW);
//get program indexes
positionIndex = gl.glGetAttribLocation(program, "position");
projectionIndex = gl.glGetUniformLocation(program, "projection");
viewingMatrix = gl.glGetUniformLocation(program, "view");
transIndex = gl.glGetUniformLocation(program, "trans");
//init projection matrix;
final int width = getWidth();
final int height = getHeight();
projection.perspective(45.0f, (float)width/height, 1.0f, 100.0f);
viewing.lookAt(new Vector3f(0.0f, 0.0f, 10.0f), new Vector3f(0.0f, 0.0f, 0.0f), new Vector3f(0.0f, 1.0f, 0.0f));
} catch (final Exception ex){
ex.printStackTrace();
}
}
#Override
public void dispose(GLAutoDrawable drawable) {
}
#Override
public void display(final GLAutoDrawable drawable) {
gl.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
gl.glEnable(GL4.GL_CULL_FACE);
gl.glEnable(GL4.GL_DEPTH_TEST);
gl.glClearDepth(1.0);
gl.glClear(GL4.GL_COLOR_BUFFER_BIT | GL4.GL_DEPTH_BUFFER_BIT);
gl.glUseProgram(program);
gl.glBindBuffer(GL4.GL_ARRAY_BUFFER, vbo);
gl.glEnableVertexAttribArray(positionIndex);
gl.glVertexAttribPointer(positionIndex, 3, gl.GL_FLOAT, false, Buffers.SIZEOF_FLOAT * 3, 0);
gl.glUniformMatrix4fv(projectionIndex, 1, false, FloatBuffer.wrap(projection.get(new float[16])));
gl.glUniformMatrix4fv(viewingMatrix, 1, false, FloatBuffer.wrap(viewing.get(new float[16])));
gl.glUniformMatrix4fv(transIndex, 1, false, FloatBuffer.wrap(translation.get(new float[16])));
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, vio);
gl.glDrawElements(gl.GL_TRIANGLES, INDICIES.length, gl.GL_UNSIGNED_SHORT, 0);
gl.glUseProgram(0);
}
#Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
}
private int createShader(final String fileName, final int type) throws IOException {
final int shaderID = gl.glCreateShader(type);
final StringBuilder sb = new StringBuilder();
final BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
gl.glShaderSource(shaderID, 1, new String[]{sb.toString()}, null, 0);
gl.glCompileShader(shaderID);
final IntBuffer intBuffer = IntBuffer.allocate(1);
gl.glGetShaderiv(shaderID, gl.GL_COMPILE_STATUS, intBuffer);
if (intBuffer.get() == gl.GL_FALSE) {
intBuffer.flip();
gl.glGetShaderiv(shaderID, gl.GL_INFO_LOG_LENGTH, intBuffer);
final int logLength = intBuffer.get();
final ByteBuffer byteBuffer = ByteBuffer.allocate(logLength);
gl.glGetShaderInfoLog(shaderID, logLength, null, byteBuffer);
System.out.println("Filed to compile " +
(type == GL4.GL_VERTEX_SHADER ? "vertex shader" : "fragment shader")
+ " shader :\n" + new String(byteBuffer.array()));
}
return shaderID;
}
public static void main(String[] args) {
new TestScene();
}
}
here are the shader files
vertex shader
#version 400
uniform lowp mat4 trans;
uniform lowp mat4 view;
uniform lowp mat4 projection;
in lowp vec3 position;
out lowp vec3 out_color;
void main(){
gl_Position = projection * view * trans * vec4(position, 1.0);
out_color = position;
}
fragment shader
#version 400
in lowp vec3 out_color;
out vec4 fragColor;
void main(){
fragColor = vec4(out_color, 1.0);
}
I know I should calculate uniform matrix in CPU. again, this is just good for debugging.
beside of this problem, I have another question. it seems some opengl functions related of a program like glGetAttribLocation and glGetUniformLocation has program parameter, so does that mean we do not have to call glUseProgram ahead of these function invocations? is that assumption correct? any opinion regarding on this question is welcome.
Thanks
I have been following the Android documentation showing you how to display openGL graphics using Android. I am pretty sure I have followed the code correctly but nothing is displaying. Here is my code:
OpenGLES20Activity.java
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.content.Context;
import android.opengl.EGLConfig;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
public class OpenGLES20Activity extends Activity {
private GLSurfaceView mGLView;
private MyGLRenderer renderer;
private static Triangle mTriangle;
private static Square mSquare;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create a GLSurfaceView instance and set it
// as the ContentView for this Activity.
mGLView = new MyGLSurfaceView(this);
setContentView(mGLView);
}
class MyGLSurfaceView extends GLSurfaceView {
public MyGLSurfaceView(Context context){
super(context);
// Create an OpenGL ES 2.0 context
setEGLContextClientVersion(2);
// Set the Renderer for drawing on the GLSurfaceView
renderer = new MyGLRenderer();
setRenderer(renderer);
}
}
public static class MyGLRenderer implements GLSurfaceView.Renderer {
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
}
public void onDrawFrame(GL10 unused) {
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
mTriangle.draw();
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
#Override
public void onSurfaceCreated(GL10 gl,
javax.microedition.khronos.egl.EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// initialize a triangle
mTriangle = new Triangle();
}
public static int loadShader(int type, String shaderCode){
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
}
Triangle.java
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.opengl.GLES20;
import com.example.test3d.OpenGLES20Activity.MyGLRenderer;
public class Triangle {
private FloatBuffer vertexBuffer;
int mProgram;
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float triangleCoords[] = { // in counterclockwise order:
0.0f, 0.622008459f, 0.0f, // top
0f, 0.311004243f, 0.0f, // bottom left
0f, 0.311004243f, 0.0f // bottom right
};
// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
public Triangle() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (number of coordinate values * 4 bytes per float)
triangleCoords.length * 4);
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
// create a floating point buffer from the ByteBuffer
vertexBuffer = bb.asFloatBuffer();
// add the coordinates to the FloatBuffer
vertexBuffer.put(triangleCoords);
// set the buffer to read the first coordinate
vertexBuffer.position(0);
int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
int mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // creates OpenGL ES program executables
}
public void draw() {
int vertexStride = 10;
int vertexCount = 10;
// Add program to OpenGL ES environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
int mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
int mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
I have also specified in my manifest file:
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
EDIT: Just to be clear, I'm not getting any errors at all. My screen is just black and it remains black. I have used some print statements and I have confirmed that Triangle.draw() is being called. So my guess would be that the draw implementation is wrong but I followed the documentation so maybe it is something else. Any help is appreciated.
I see two main problems. First of all, your triangle coordinates:
static float triangleCoords[] = { // in counterclockwise order:
0.0f, 0.622008459f, 0.0f, // top
0f, 0.311004243f, 0.0f, // bottom left
0f, 0.311004243f, 0.0f // bottom right
};
The second and third vertex have the same coordinates. A triangle with two identical vertices is degenerate, and will not render any pixels.
Then these values look questionable:
int vertexStride = 10;
int vertexCount = 10;
You have 3 floats per vertex, so vertexStride should be 3 * sizeof(float), which is 12. And I only see 3 vertices, so vertexCount should be 3.
Current situation doesn't allow me to use a computer. And it will be like this for a while. I use Android IDE (AIDE) to program on my phone.
In my code, I used glGetShaderiv() to get the compile status, and noticed the status value is 0. However, there is no indication to suggest the vertex shader code contains an error or something is wrong with loading the GLSL code from a text file.
Below are the codes. Note that I jumbled all the codes together so that the execution of the code is as iterative as possible. Meaning the code doesn't jump around a lot using function calls, for easier debugging.
RenderView class:
package p.e;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import static android.opengl.GLES20.*;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;
public class RenderView extends GLSurfaceView implements GLSurfaceView.Renderer{
private Context context;
private Table table;
private int texture;
private final float[] projectionMatrix=new float[16];
private final float[] modelMatrix=new float[16];
private Shader shader;
public RenderView(Context context){
super(context);
this.context=context;
}
#Override
public void onSurfaceCreated(GL10 p1, EGLConfig p2)
{
// TODO: Implement this method
glClearColor(1f,0f,1f,1f);
this.shader=new Shader(context);
this.table=new Table();
final int[] textureID=new int[1];
glGenTextures(1,textureID,0);
texture=textureID[0];
BitmapFactory.Options options=new BitmapFactory.Options();
options.inScaled=false;
Bitmap bitmap=BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher, options);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
GLUtils.texImage2D(GL_TEXTURE_2D,0,bitmap,0);
glGenerateMipmap(GL_TEXTURE_2D);
bitmap.recycle();
glBindTexture(GL_TEXTURE_2D,0);
}
#Override
public void onSurfaceChanged(GL10 p1, int width, int height)
{
// TODO: Implement this method
glViewport(0,0,width,height);
perspectiveM(projectionMatrix,45f, (float)width/(float)height,1f,10f);
Matrix.setIdentityM(modelMatrix, 0);
Matrix.translateM(modelMatrix,0,0f,0f,-2.5f);
Matrix.rotateM(modelMatrix,0,-60f,1f,0f,0f);
final float[] temp=new float[16];
Matrix.multiplyMM(temp,0,projectionMatrix,0,modelMatrix,0);
System.arraycopy(temp,0,projectionMatrix,0,temp.length);
}
#Override
public void onDrawFrame(GL10 p1)
{
// TODO: Implement this method
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader.getProgram());
glUniformMatrix4fv(shader.uMatrixLocation, 1, false, projectionMatrix,0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,texture);
glUniform1i(shader.uTextureUnitLocation,0);
table.getVertexBuffer().position(0);
glVertexAttribPointer(shader.aPositionLocation, 2, GL_FLOAT,false,2*4,table.getVertexBuffer());
//glBindBuffer(GL_ARRAY_BUFFER, table.vertexBufferPointer);
//table.getVertexBuffer().position(0);
glEnableVertexAttribArray(shader.aPositionLocation);
//table.getVertexBuffer().rewind();
table.getVertexBuffer().position(0);
//table.getTexBuffer().position(0);
//glBindBuffer(GL_ARRAY_BUFFER, table.texBufferPointer);
table.getTexBuffer().position(0);
glVertexAttribPointer(shader.aTexPositionLocaton,2,GL_FLOAT,false,2*4,table.getTexBuffer());
glEnableVertexAttribArray(shader.aTexPositionLocaton);
//table.getTexBuffer().rewind();
table.getTexBuffer().position(0);
glDrawArrays(GL_TRIANGLE_FAN,0,4);
//glDisableVertexAttribArray(shader.aPositionLocation);
//glDisableVertexAttribArray(shader.aTexPositionLocaton);
}
public static void perspectiveM(float[] m, float yFovInDegrees, float aspect, float n, float f) {
final float angleInRadians = (float) (yFovInDegrees * Math.PI / 180.0);
final float a = (float) (1.0 / Math.tan(angleInRadians / 2.0));
m[0] = a / aspect;
m[1] = 0f;
m[2] = 0f;
m[3] = 0f;
m[4] = 0f;
m[5] = a;
m[6] = 0f;
m[7] = 0f;
m[8] = 0f;
m[9] = 0f;
m[10] = -((f + n) / (f - n));
m[11] = -1f;
m[12] = 0f;
m[13] = 0f;
m[14] = -((2f * f * n) / (f - n));
m[15] = 0f;
}
}
`
Table class:
package p.e;
import java.nio.*;
import static android.opengl.GLES20.*;
public class Table
{
private FloatBuffer vertexBuffer;
private FloatBuffer texBuffer;
public int vertexBufferPointer;
public int texBufferPointer;
private final float[] vertexData={
-0.5f,-0.5f,
0.5f,-0.5f,
0.5f,0.5f,
-0.5f,0.5f
};
private final float[] texData={
0f,1f,
1f,1f,
1f,0f,
0f,0f
};
public Table(){
int[] bufferPointer=new int[1];
glGenBuffers(1,bufferPointer,0);
vertexBuffer=ByteBuffer.allocateDirect(vertexData.length*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
vertexBuffer.put(vertexData);
vertexBuffer.flip();
glBindBuffer(GL_ARRAY_BUFFER, bufferPointer[0]);
glBufferData(GL_ARRAY_BUFFER, vertexData.length*4, vertexBuffer, GL_STATIC_DRAW);
vertexBufferPointer=bufferPointer[0];
texBuffer=ByteBuffer.allocateDirect(texData.length*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
texBuffer.put(texData);
texBuffer.flip();
glGenBuffers(1, bufferPointer,0);
glBindBuffer(GL_ARRAY_BUFFER,bufferPointer[0]);
glBufferData(GL_ARRAY_BUFFER,texData.length*4, texBuffer, GL_STATIC_DRAW);
}
public FloatBuffer getVertexBuffer(){
return vertexBuffer;
}
public FloatBuffer getTexBuffer(){
return texBuffer;
}
}
Shader class:
package p.e;
import java.io.*;
import android.content.Context;
import static android.opengl.GLES20.*;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;
import android.util.Log;
import java.nio.ByteOrder;
public class Shader
{
private int program;
private final String U_MATRIX="u_matrix";
private final String U_TEXTUREUNIT="u_texUnit";
private final String A_POSITION="a_position";
private final String A_TEXCOORDS="a_texPos";
public int uMatrixLocation;
public int uTextureUnitLocation;
public int aPositionLocation;
public int aTexPositionLocaton;
public Shader(Context context){
int vertex=glCreateShader(GL_VERTEX_SHADER);
IntBuffer intBuf=ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer();
Log.wtf("Code",Shader.loadString(context,R.raw.tex_vert));
glShaderSource(vertex,Shader.loadString(context,R.raw.tex_vert));
glCompileShader(vertex);
//check
int status;
glGetShaderiv(vertex, GL_COMPILE_STATUS, intBuf);
status=intBuf.get(0);
if(status==0){
glGetShaderiv(vertex,GL_INFO_LOG_LENGTH,intBuf);
status=intBuf.get(0);
if (status>1){
Log.i("Shader","Vertex Shader: "+glGetShaderInfoLog(vertex));
}
glDeleteShader(vertex);
Log.w("Shader","Vertex Shader error.");
return;
}
//check end
int fragment=glCreateShader(GL_FRAGMENT_SHADER);
Log.wtf("Code",Shader.loadString(context,R.raw.tex_frag));
glShaderSource(fragment, Shader.loadString(context,R.raw.tex_frag));
glCompileShader(fragment);
//check
glGetShaderiv(fragment, GL_COMPILE_STATUS, intBuf);
status=intBuf.get(0);
Log.i("Shader","Fragment Shader: "+glGetShaderInfoLog(fragment));
if(status==0){
glDeleteShader(fragment);
Log.w("Shader","Fragment Shader error.");
return;
}
//check end
program=glCreateProgram();
//check
Log.i("Shader","Program: "+glGetProgramInfoLog(program));
if(program==0){
Log.w("Shader","Program not created.");
return;
}
//check end
glAttachShader(program,vertex);
glAttachShader(program,fragment);
glLinkProgram(program);
//check
glValidateProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, intBuf);
status=intBuf.get(0);
if(status==0){
glDeleteProgram(program);
Log.w("Shader","Program unable to link.");
return;
}
//check end
//check
glGetProgramiv(program, GL_VALIDATE_STATUS, intBuf);
status=intBuf.get(0);
Log.i("Shader","Program validation: "+glGetProgramInfoLog(program));
if(status==0){
glDeleteProgram(program);
Log.w("Shader","Program validation failed.");
return;
}
//check end
uMatrixLocation=glGetUniformLocation(program,U_MATRIX);
uTextureUnitLocation=glGetUniformLocation(program,U_TEXTUREUNIT);
aPositionLocation=glGetAttribLocation(program,A_POSITION);
aTexPositionLocaton=glGetAttribLocation(program,A_TEXCOORDS);
}
public void setVertexAttributePointer(int location, int offset, int componentCount, int type, boolean isTranspose, int stride, FloatBuffer buffer){
buffer.position(offset);
glVertexAttribPointer(location, componentCount,type,isTranspose,stride,buffer);
glEnableVertexAttribArray(location);
buffer.rewind();
}
public void setup(FloatBuffer vertexBuffer, FloatBuffer texBuffer){
vertexBuffer.position(0);
glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT,false,0,vertexBuffer);
glEnableVertexAttribArray(aPositionLocation);
vertexBuffer.rewind();
texBuffer.position(0);
glVertexAttribPointer(aTexPositionLocaton,2,GL_FLOAT,false,0,texBuffer);
glEnableVertexAttribArray(aTexPositionLocaton);
texBuffer.rewind();
}
public int getProgram(){
return program;
}
public void bind(int texture, float[] matrix){
glUniformMatrix4fv(uMatrixLocation, 1, false, matrix,0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,texture);
glUniform1i(uTextureUnitLocation,0);
}
private static String loadString(Context context, int resourceID){
StringBuilder builder=new StringBuilder();
try{
BufferedReader reader=new BufferedReader(new InputStreamReader(context.getResources().openRawResource(resourceID)));
String line;
while((line=reader.readLine())!=null){
builder.append(line);
builder.append('\n');
}
}
catch(Exception e){
}
return builder.toString();
}
}
Below are the shader source codes.
Vertex Shader:
uniform mat4 u_matrix;
attribute vec4 a_position;
attribute vec2 a_texPos;
varying vec2 v_texPos;
void main{
v_texPos=a_texPos;
gl_Position=u_matrix*a_position;
}
Fragment Shader:
precision mediump float;
uniform sampler2D u_texUnit;
varying vec2 v_texPos;
void main(){
gl_FragColor=texture2D(u_texUnit,v_texPos);
}
I will post a screenshot of Logcat displaying nothing but the phrase "Vertex shader error" debug message. The GL_INVALID_OPERATION is caused by the vertex shader status being 0, was deleted and returned back to the onSurfaceCreated(), and not being able to point to the matrix location, as the vertex shader is nonexistent at that point in time.
I think it's just a simple typo. The vertex shader is missing () after main:
void main{
needs to be:
void main(){
I'm trying to learn OpenGL ES as part of my foray into Android development.
So far, I've created the following Android application by cutting and pasting from various tutorials I found.
The application is supposed to create 2 coloured squares (1 red square and 1 blue square) and rotate them around a central point.
So during part of the rotation, the red square should be in front while during another part of the rotation, the blue square should be in front.
When I run my application in the Android simulator however, it only shows the blue square in front.
Does anyone know what I'm missing?
package hello.world;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
public class HelloActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new HelloView(this));
}
private class HelloView extends GLSurfaceView {
private HelloRenderer renderer;
public HelloView(Context context) {
super(context);
renderer = new HelloRenderer(context);
setRenderer(renderer);
}
}
private class HelloRenderer implements GLSurfaceView.Renderer {
public float xrot; //X Rotation ( NEW )
public float yrot; //Y Rotation ( NEW )
public float zrot; //Z Rotation ( NEW )
private List<ColoredQuad> quads;
public HelloRenderer(Context context) {
quads = new ArrayList<ColoredQuad>();
quads.add(new ColoredQuad(
new Vertex3D(-1.0f, -1.0f, 1.0f),
new Vertex3D(1.0f, -1.0f, 1.0f),
new Vertex3D(-1.0f, 1.0f, 1.0f),
new Vertex3D(1.0f, 1.0f, 1.0f),
new RGBA(1.0f, 0.0f, 0.0f)));
quads.add(new ColoredQuad(
new Vertex3D(-1.0f, -1.0f, -1.0f),
new Vertex3D(1.0f, -1.0f, -1.0f),
new Vertex3D(-1.0f, 1.0f, -1.0f),
new Vertex3D(1.0f, 1.0f, -1.0f),
new RGBA(0.0f, 0.0f, 1.0f)));
}
/**
* Called whenever drawing is needed.
*/
public void onDrawFrame(GL10 gl) {
// clear screen and depth buffer
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
//Drawing
gl.glTranslatef(0.0f, 0.0f, -5.0f); //move 5 units into the screen
gl.glScalef(0.5f, 0.5f, 0.5f); //scale the objects to 50 percent of original size
//Rotate around the axis based on the rotation matrix (rotation, x, y, z)
gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f); //X
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f); //Y
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f); //Z
for (ColoredQuad quad : quads) {
quad.draw(gl);
}
//Change rotation factors (nice rotation)
xrot += 3.0f;
yrot += 2.0f;
zrot += 1.0f;
}
/**
* Called when the surface has changed.
* For example, when switching from portrait to landscape view.
*/
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glEnable(GL10.GL_SMOOTH); // enable smooth shading
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // black background
gl.glClearDepthf(GL10.GL_DEPTH_TEST); // enable depth testing
gl.glDepthFunc(GL10.GL_LEQUAL); // type of depth testing to do
//Really Nice Perspective Calculations
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
}
}
private class Vertex3D {
public float x;
public float y;
public float z;
public Vertex3D(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
}
public class RGBA {
public float red;
public float blue;
public float green;
public float alpha;
public RGBA(float red, float green, float blue) {
this.red = red;
this.blue = blue;
this.green = green;
this.alpha = 1.0f;
}
}
private ByteBuffer makeByteBuffer(byte[] array)
{
ByteBuffer bb = ByteBuffer.allocateDirect(array.length);
bb.put(array);
bb.position(0);
return bb;
}
private FloatBuffer makeFloatBuffer(float[] array)
{
ByteBuffer bb = ByteBuffer.allocateDirect(array.length * 4);
bb.order(ByteOrder.nativeOrder());
FloatBuffer fb = bb.asFloatBuffer();
fb.put(array);
fb.position(0);
return fb;
}
private class ColoredQuad {
private FloatBuffer vertexBuffer;
private FloatBuffer colorBuffer;
private ByteBuffer indexBuffer;
private float[] vertices = new float[12]; // 4 vertices * XYZ (12)
private float[] colors = new float[16]; // 4 vertices * RGBA (16)
private byte[] indices = {
0, 1, 2, 1, 2, 3
};
public ColoredQuad(Vertex3D bottomLeft, Vertex3D bottomRight, Vertex3D topLeft, Vertex3D topRight, RGBA color) {
vertices[0] = bottomLeft.x; vertices[1] = bottomLeft.y; vertices[2] = bottomLeft.z;
vertices[3] = bottomRight.x; vertices[4] = bottomRight.y; vertices[5] = bottomRight.z;
vertices[6] = topLeft.x; vertices[7] = topLeft.y; vertices[8] = topLeft.z;
vertices[9] = topRight.x; vertices[10]= topRight.y; vertices[11]= topRight.z;
colors[0] = color.red; colors[1] = color.green; colors[2] = color.blue; colors[3] = color.alpha;
colors[4] = color.red; colors[5] = color.green; colors[6] = color.blue; colors[7] = color.alpha;
colors[8] = color.red; colors[9] = color.green; colors[10]= color.blue; colors[11]= color.alpha;
colors[12]= color.red; colors[13]= color.green; colors[14]= color.blue; colors[15]= color.alpha;
vertexBuffer = makeFloatBuffer(vertices);
colorBuffer = makeFloatBuffer(colors);
indexBuffer = makeByteBuffer(indices);
}
public void draw(GL10 gl) {
//Point to our buffers
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
//Set the face rotation
gl.glFrontFace(GL10.GL_CCW);
//Enable the vertex and texture state
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);
//Draw the vertices as triangles, based on the Index Buffer information
gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_BYTE, indexBuffer);
//Disable the client state before leaving
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
}
}
The reason you only see the blue one in front is because something is going wrong with the depth testing, and the blue one is simply drawn last (over everything else).
Where you say
gl.glClearDepthf(GL10.GL_DEPTH_TEST); // enable depth testing
you probably mean
gl.glEnable(GL10.GL_DEPTH_TEST);
and possibly
gl.glClearDepthf(1.0f);
but that's the default anyway.
Cheers, Aert.