I recently started coding with OpenGL ES 2.0 and ran into a (for me) quite challenging problem. This is my first try, streaming VBO Buffer Objects dynamically ( at least that's what I think I am doing). My application should draw 2 triangles in a specific color, but instead they are just black. I think it's possible that I mixed up some GL commands, but I can't find the problem.
Here's a snippet from my GLRenderer class:
#Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
/* Draw black background */
GLES20.glClearColor(0.8f, 0.6f, 0.4f, 1.0f);
{...}
viewMatrix = camera.getMatrix();
/* Create and compile shaders */
int vertexShaderHandle = loadShader(vertexShader, GLES20.GL_VERTEX_SHADER);
int fragmentShaderHandle = loadShader(fragmentShader, GLES20.GL_FRAGMENT_SHADER);
/* Create and link program */
programHandle = loadProgram(vertexShaderHandle, fragmentShaderHandle);
/* Set references for drawing input */
mVPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "uMVPMatrix");
positionHandle = GLES20.glGetAttribLocation(programHandle, "vPosition");
colorHandle = GLES20.glGetUniformLocation(programHandle, "vColor");
checkGlError("glGetUniformLocation");
/* Create 2 Triangles for testing purposes. */
final float[] triangle1Vertex = { 0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,};
final float[] triangle2Vertex = { -1.0f, 1.0f, 0.0f,
-1.0f, -0.5f, 0.0f,
-0.5f, 1.0f, 0.0f};
/* Color */
final float[] color = { 0.63671875f, 0.76953125f, 0.22265625f, 0.0f};
/* Init triangles */
Triangle triangle1 = new Triangle(triangle1Vertex, color);
Triangle triangle2 = new Triangle(triangle2Vertex, color);
/* Add triangles to be drawn */
TriangleCollection.add(triangle1);
TriangleCollection.add(triangle2);
/* Create buffer objects in GPU, 2 buffers are needed */
final int buffers[] = new int[2];
GLES20.glGenBuffers(2, buffers, 0); //Generate GPUSide Buffers
/* Allocate GPU memory space for vertices */
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[0]);
GLES20.glBufferData(
GLES20.GL_ARRAY_BUFFER,
TriangleCollection.MAX_NUMBER_OF_VERTICES * TriangleCollection.BYTES_PER_FLOAT,
TriangleCollection.publishVerticesBuffer(),
GLES20.GL_STREAM_DRAW);
checkGlError("glBufferData");
/* Allocate GPU memory space for color data */
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[1]);
GLES20.glBufferData(
GLES20.GL_ARRAY_BUFFER,
TriangleCollection.NUMBER_OF_COLOR_ELEMENTS * TriangleCollection.BYTES_PER_FLOAT,
TriangleCollection.publishColorBuffer(),
GLES20.GL_STREAM_DRAW);
checkGlError("glBufferData");
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
/* Reference the GPU Buffers */
triangleVerticesIdx = buffers[0];
triangleColorsIdx = buffers[1];
GLES20.glFlush();
startTime = System.nanoTime();
}
#Override
public void onDrawFrame(GL10 unused) {
FloatBuffer vertices = TriangleCollection.publishVerticesBuffer();
FloatBuffer colors = TriangleCollection.publishColorBuffer();
/* Upload triangle data */
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, triangleVerticesIdx);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertices.capacity() * Triangle.BYTES_PER_FLOAT, vertices);
checkGlError("glBufferSubData");
/* Upload color data */
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, triangleColorsIdx);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, colors.capacity() * Triangle.BYTES_PER_FLOAT, colors);
checkGlError("glBufferSubData");
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
/* Clear Screen */
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glUseProgram(programHandle);
/*Matrix calculations */
Matrix.setIdentityM(modelMatrix, 0);
Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
GLES20.glUniformMatrix4fv(mVPMatrixHandle, 1, false, mvpMatrix, 0);
checkGlError("glUniformMatrix4fv");
/* Pass the position information */
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, triangleVerticesIdx);
checkGlError("glBindBuffer");
GLES20.glEnableVertexAttribArray(positionHandle);
checkGlError("glEnableVertexAttribArray");
GLES20.glVertexAttribPointer(positionHandle, Triangle.COORDINATES_PER_VERTEX, GLES20.GL_FLOAT, false, 0, 0);
checkGlError("glVertexAttribPointer");
/* Pass the color information */
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, triangleColorsIdx);
checkGlError("glBindBuffer");
GLES20.glEnableVertexAttribArray(colorHandle);
checkGlError("glEnableVertexAttribArray");
GLES20.glVertexAttribPointer(colorHandle, TriangleCollection.COLOR_SIZE_FLOAT, GLES20.GL_FLOAT, false, 0, 0);
checkGlError("glVertexAttribPointer");
/* Clear currently bound buffer */
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
checkGlError("glBindBuffer");
//Draw
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, TriangleCollection.MAX_NUMBER_OF_VERTICES);
checkGlError("glDrawArrays");
}
This code runs without errors and I already checked the FloatBuffers in Debug Mode. They contain the required information.
I would also appeciate any feedback on the general concept of my drawing / rendering pipeline. I'm not sure weather this is a good solution but at least I get 30 FPS #8000 Triangles on my Nexus 5.
Edit 1
After some testing I got the following results:
According to the log I'm using EGL 1.4. I do not intend to use OpenGL ES 3.0 for now ( Provided that this is possible).
2.Replacing vColor element of the fragment shader with a constant value works. The triangles are red:
final String fragmentShader =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vec4(1.0,0.0,0.0,1.0);" +
"}";
When using the normal non-static fragment shader, removing this part of the code changes absolutely nothing:
/* Pass the color information */
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, triangleColorsIdx);
checkGlError("glBindBuffer");
GLES20.glEnableVertexAttribArray(colorHandle);
checkGlError("glEnableVertexAttribArray");
GLES20.glVertexAttribPointer(colorHandle, TriangleCollection.COLOR_SIZE_FLOAT, GLES20.GL_FLOAT, false, 0, 0);
checkGlError("glVertexAttribPointer");
Removing colorHandle = GLES20.glGetUniformLocation(programHandle, "vColor"); from surfaceCreated() works as usual, no triangle is drawn.
Edit 2
I still can't find my mistake. While using glGetUniformLocation worked for one triangle, it doesn't work for many. I stripped down my project to a simple test application so I can show the complete code:
public class MainActivity extends Activity {
private MySurfaceView mySurfaceView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* Create SurfaceView and add it to Activity */
MySurfaceView = new MySurfaceView(this);
setContentView(mySurfaceView);
}
#Override
protected void onPause() {
super.onPause();
MySurfaceView.onPause();
}
#Override
protected void onResume() {
super.onResume();
MySurfaceView.onResume();
}
}
public class MySurfaceView extends GLSurfaceView {
private final GLRenderer renderer;
/**
* Creates the SurfaceView
* #param context Application context
*/
public MySurfaceView(Context context) {
super(context);
setEGLConfigChooser(8, 8, 8, 8, 16, 0);
/* OpenGl Version GLES 2.0 min */
setEGLContextClientVersion(2);
/* Add Renderer for drawing */
renderer = new GLRenderer();
setRenderer(renderer);
}
}
public class GLRenderer implements GLSurfaceView.Renderer {
/* Frame Counter */
private int nbFrame = 0;
private long startTime;
/* Vertex Shader */
final String vertexShader =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
// Note that the uMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition;" +
"}";
/* Fragment Shader*/
final String fragmentShader =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
/* Reference for the program */
private int programHandle;
/* References to pass data into shader */
private int mVPMatrixHandle, positionHandle, colorHandle;
/* Projection matrix, used for projection 3D scene to 2D viewport. */
private float[] projectionMatrix = new float[16];
/* Model matrix used for moving Models around */
private float[] modelMatrix = new float[16];
/* Combined Matrix */
private float[] mvpMatrix = new float[16];
/* Matrix of the camera position and perspective */
private float[] viewMatrix;
/* Reference to the buffer of the triangle vertices in the GPU DDR */
private int triangleVerticesIdx;
/* Reference to the buffer of the triangle colors in the GPU DDR */
private int triangleColorsIdx;
/**
* Load shader
*/
static int loadShader(final String shader, int type) {
int shaderHandle = GLES20.glCreateShader(type);
if (shaderHandle != 0) {
GLES20.glShaderSource(shaderHandle, shader);
checkGlError("glShaderSource");
GLES20.glCompileShader(shaderHandle);
checkGlError("glCompileShader");
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == 0) {
GLES20.glDeleteShader(shaderHandle);
shaderHandle = 0;
}
}
if (shaderHandle == 0) {
throw new RuntimeException("Error while creating shader");
}
return shaderHandle;
}
/**
* Loads a OpenGL ES 2.0 program with a vertex and a fragment shader.
* #param vertexShader
* #param fragmentShader
* #return
*/
public static int loadProgram(int vertexShader, int fragmentShader) {
int programHandle;
/* Load program */
programHandle = GLES20.glCreateProgram();
if (programHandle != 0) {
/* Bind shaders to program */
GLES20.glAttachShader(programHandle, vertexShader);
checkGlError("glAttachShader");
GLES20.glAttachShader(programHandle, fragmentShader);
checkGlError("glAttachShader");
/* Bind Attributes */
GLES20.glBindAttribLocation(programHandle, 0, "vPosition");
checkGlError("glBindAttribLocation");
GLES20.glBindAttribLocation(programHandle, 1, "vColor");
checkGlError("glBindAttribLocation");
/* Link shaders */
GLES20.glLinkProgram(programHandle);
/* Get link status... */
final int[] linkStatus = new int[1];
GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] == 0) {
GLES20.glDeleteProgram(programHandle);
programHandle = 0;
}
}
if (programHandle == 0) {
throw new RuntimeException("Error creating program.");
}
return programHandle;
}
#Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
/* Draw black background */
GLES20.glClearColor(0.8f, 0.6f, 0.4f, 1.0f);
/* Create Camera and define values -> calculate Matrix */
Camera camera = new Camera();
camera.setPosition(0.0f, 0.0f, 1.5f);
camera.setPerspective(0.0f, 0.0f, -5.0f);
camera.setUpVector(0.0f, 1.0f, 0.0f);
camera.setMatrix();
viewMatrix = camera.getMatrix();
/* Create and compile shaders */
int vertexShaderHandle = loadShader(vertexShader, GLES20.GL_VERTEX_SHADER);
int fragmentShaderHandle = loadShader(fragmentShader, GLES20.GL_FRAGMENT_SHADER);
/* Create and link program */
programHandle = loadProgram(vertexShaderHandle, fragmentShaderHandle);
/* Set references for drawing input */
mVPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "uMVPMatrix");
positionHandle = GLES20.glGetAttribLocation(programHandle, "vPosition");
colorHandle = GLES20.glGetUniformLocation(programHandle, "vColor");
checkGlError("glGetUniformLocation");
/* Create 2 Triangles for testing purposes. */
final float[] triangle1Vertex = { 0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,};
/* Color */
final float[] color = { 0.0f, 0.76953125f, 0.22265625f, 1.0f,
0.0f, 0.76953125f, 0.22265625f, 1.0f,
0.0f, 0.76953125f, 0.22265625f, 1.0f};
/* Create Vertex Buffer */
ByteBuffer bb = ByteBuffer.allocateDirect(triangle1Vertex.length*4);
bb.order(ByteOrder.nativeOrder());
FloatBuffer vert1 = bb.asFloatBuffer();
vert1.put(triangle1Vertex);
vert1.position(0);
/* Create Color Buffer */
ByteBuffer bb1 = ByteBuffer.allocateDirect(color.length*4);
bb1.order(ByteOrder.nativeOrder());
FloatBuffer color1 = bb1.asFloatBuffer();
color1.put(color);
color1.position(0);
/* Create buffer objects in GPU, 2 buffers are needed */
final int buffers[] = new int[2];
GLES20.glGenBuffers(2, buffers, 0); //Generate GPUSide Buffers
/* Allocate GPU memory space for vertices */
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[0]);
GLES20.glBufferData(
GLES20.GL_ARRAY_BUFFER,
1*9*4,// 9 floats for triangle and 4 bytes per float
vert1,
GLES20.GL_STATIC_DRAW);
checkGlError("glBufferData");
/* Upload FPU memory space for color data */
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[1]);
GLES20.glBufferData(
GLES20.GL_ARRAY_BUFFER,
1*3*4*4,
color1,
GLES20.GL_STATIC_DRAW);
checkGlError("glBufferData");
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
/* Reference the GPU Buffers */
triangleVerticesIdx = buffers[0];
triangleColorsIdx = buffers[1];
GLES20.glFlush();
startTime = System.nanoTime();
}
/**
* Not needed. Device must be in landscape mode all the time.
*
* #param unused -
*/
#Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
/* Define Viewport */
GLES20.glViewport(0, 0, width, height);
/* Create perspective projection */
final float ratio = (float) width / height;
final float left = -ratio;
final float right = ratio;
final float bottom = -1.0f;
final float top = 1.0f;
final float near = 1.0f;
final float far = 10.0f;
Matrix.frustumM(projectionMatrix, 0, left, right, bottom, top, near, far);
}
#Override
public void onDrawFrame(GL10 unused) {
/* Measure FPS */
nbFrame++;
if(System.nanoTime()-startTime >= 1000000000) {
Log.d("FPS", Integer.toString(nbFrame));
nbFrame = 0;
startTime = System.nanoTime();
}
/* Clear Screen */
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glUseProgram(programHandle);
/*Matrix calculations */
Matrix.setIdentityM(modelMatrix, 0);
Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
GLES20.glUniformMatrix4fv(mVPMatrixHandle, 1, false, mvpMatrix, 0);
checkGlError("glUniformMatrix4fv");
/* Pass the position information */
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, triangleVerticesIdx);
checkGlError("glBindBuffer");
GLES20.glEnableVertexAttribArray(positionHandle);
checkGlError("glEnableVertexAttribArray");
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 0, 0);
checkGlError("glVertexAttribPointer");
/* Pass the color information */
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, triangleColorsIdx);
checkGlError("glBindBuffer");
GLES20.glEnableVertexAttribArray(colorHandle);
checkGlError("glEnableVertexAttribArray");
GLES20.glVertexAttribPointer(colorHandle, 4, GLES20.GL_FLOAT, false, 0, 0);
checkGlError("glVertexAttribPointer");
/* Clear currently bound buffer */
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
checkGlError("glBindBuffer");
//Draw
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 1*9);
checkGlError("glDrawArrays");
}
/**
* Utility method for debugging OpenGL calls. Provide the name of the call
* just after making it:
*
* <pre>
* mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
* MyGLRenderer.checkGlError("glGetUniformLocation");</pre>
*
* If the operation is not successful, the check throws an error.
*
* #param glOperation - Name of the OpenGL call to check.
*/
public static void checkGlError(String glOperation) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e("OPEN_GL", glOperation + ": glError " + error);
throw new RuntimeException(glOperation + ": glError " + error);
}
}
}
Solution
Finally I was able to solve the problem (with help of your clues). For all other people who have similar problems, check your shaders and don't just copy & paste them like me. Also this helped me alot. Also here are my now working shaders:
final String vertexShader =
"uniform mat4 uMVPMatrix; \n"
+ "attribute vec4 aPosition; \n"
+ "attribute vec4 aColor; \n"
+ "varying vec4 vColor; \n"
+ "void main() \n"
+ "{ \n"
+ " vColor = aColor; \n"
+ " gl_Position = uMVPMatrix * aPosition; \n"
+ "} \n";
/* Fragment Shader*/
final String fragmentShader =
"precision mediump float;" +
"varying vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
I'd have to see your Fragment shader to know for certain, but from here, it looks like you're setting the alpha component to 0 in your color array, which means that your colors won't show up. Set the alpha component to 1.
You need to check and ensure that your fragment shader is compiling correctly. According to the GLSL_ES specification, shaders need to contain a line at the beginning indicating which version you're using. (Section 3.3, page 9). Unless you're building for ESGL1.0 (which seems unlikely given your liberal use of Vertex Buffer Objects) that directive has to be present in any valid shader code.
Related
I'm in troubles with Android, GL and Textures, I have a little example and I want to load two different textures and paint it on screen, my problem is that always draws the last texture loaded.
I'm noob with GL in android and have copied the code from some tutorials.
Here is the code:
GLRenderer:
public class GLRenderer implements Renderer {
// Our matrices
private final float[] mtrxProjection = new float[16];
private final float[] mtrxView = new float[16];
private final float[] mtrxProjectionAndView = new float[16];
// Our screenresolution
float mScreenWidth = 1280;
float mScreenHeight = 768;
// Misc
Context mContext;
long mLastTime;
int mProgram;
CardView card1;
CardView card2;
public GLRenderer(Context c)
{
mContext = c;
mLastTime = System.currentTimeMillis() + 100;
}
public void onPause()
{
/* Do stuff to pause the renderer */
}
public void onResume()
{
/* Do stuff to resume the renderer */
mLastTime = System.currentTimeMillis();
}
#Override
public void onDrawFrame(GL10 unused) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Get the current time
long now = System.currentTimeMillis();
// We should make sure we are valid and sane
if (mLastTime > now) return;
// Get the amount of time the last frame took.
long elapsed = now - mLastTime;
// Update our example
// Render our example
card1.Draw(mtrxProjectionAndView);
card2.Draw(mtrxProjectionAndView);
// Save the current time to see how long it took :).
mLastTime = now;
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// We need to know the current width and height.
mScreenWidth = width;
mScreenHeight = height;
// Redo the Viewport, making it fullscreen.
GLES20.glViewport(0, 0, (int)mScreenWidth, (int)mScreenHeight);
// Clear our matrices
for(int i=0;i<16;i++)
{
mtrxProjection[i] = 0.0f;
mtrxView[i] = 0.0f;
mtrxProjectionAndView[i] = 0.0f;
}
// Setup our screen width and height for normal sprite translation.
Matrix.orthoM(mtrxProjection, 0, 0f, mScreenWidth, 0.0f, mScreenHeight, 0, 50);
// Set the camera position (View matrix)
Matrix.setLookAtM(mtrxView, 0, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mtrxProjectionAndView, 0, mtrxProjection, 0, mtrxView, 0);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
card1 = new CardView(mContext, 1);
card2 = new CardView(mContext, 2);
}
}
public class CardView {
// Misc
private Context mContext;
// Geometric variables
// number of coordinates per vertex in this array
private static final int COORDS_PER_VERTEX = 3;
private static float vertices[] = {
10.0f, 300f, 0.0f,
10.0f, 100f, 0.0f,
450f, 100f, 0.0f,
450f, 300f, 0.0f,
};
private static float vertices2[] = {
450.0f, 600f, 0.0f,
450.0f, 300f, 0.0f,
900f, 300f, 0.0f,
900f, 600f, 0.0f,
};
private final short indices[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
private static float uvs[] = {
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f
};
private FloatBuffer vertexBuffer;
private ShortBuffer drawListBuffer;
private FloatBuffer uvBuffer;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
public CardView(Context c, int index)
{
// Save context reference
mContext = c;
// Create the triangles
SetupCard(index);
// Create the image information
SetupImage(index);
// Set the clear color to black
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1);
// Create the shaders, solid color
int vertexShader = riGraphicTools.loadShader(GLES20.GL_VERTEX_SHADER, riGraphicTools.vs_SolidColor);
int fragmentShader = riGraphicTools.loadShader(GLES20.GL_FRAGMENT_SHADER, riGraphicTools.fs_SolidColor);
riGraphicTools.sp_SolidColor = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(riGraphicTools.sp_SolidColor, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(riGraphicTools.sp_SolidColor, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(riGraphicTools.sp_SolidColor); // creates OpenGL ES program executables
// Create the shaders, images
vertexShader = riGraphicTools.loadShader(GLES20.GL_VERTEX_SHADER, riGraphicTools.vs_Image);
fragmentShader = riGraphicTools.loadShader(GLES20.GL_FRAGMENT_SHADER, riGraphicTools.fs_Image);
riGraphicTools.sp_Image = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(riGraphicTools.sp_Image, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(riGraphicTools.sp_Image, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(riGraphicTools.sp_Image); // creates OpenGL ES program executables
// Set our shader programm
GLES20.glUseProgram(riGraphicTools.sp_Image);
}
public void SetupImage(int index)
{
// The texture buffer
ByteBuffer bb = ByteBuffer.allocateDirect(uvs.length * 4);
bb.order(ByteOrder.nativeOrder());
uvBuffer = bb.asFloatBuffer();
uvBuffer.put(uvs);
uvBuffer.position(0);
// Generate Textures, if more needed, alter these numbers.
int[] texturenames = new int[1];
GLES20.glGenTextures(1, texturenames, 0);
// Retrieve our image from resources.
//int id = mContext.getResources().getIdentifier("drawable/test_backbground1", null, mContext.getPackageName());
if(index == 1) {
int id = riGraphicTools.loadTexture(mContext, R.drawable.test_backbground1, index);
}else{
int id = riGraphicTools.loadTexture(mContext, R.drawable.test_backbground2, index);
}
}
public void SetupCard(int index)
{
if( index == 1) {
// The vertex buffer.
ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
}else{
// The vertex buffer.
ByteBuffer bb = ByteBuffer.allocateDirect(vertices2.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(vertices2);
vertexBuffer.position(0);
}
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(indices.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(indices);
drawListBuffer.position(0);
}
/**
* Encapsulates the OpenGL ES instructions for drawing this shape.
*
* #param mvpMatrix - The Model View Project matrix in which to draw
* this shape.
*/
public void Draw(float[] mvpMatrix){
// clear Screen and Depth Buffer, we have set the clear color as black.
//GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// get handle to vertex shader's vPosition member
int mPositionHandle = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "vPosition");
// Enable generic vertex attribute array
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// Get handle to texture coordinates location
int mTexCoordLoc = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "a_texCoord" );
// Enable generic vertex attribute array
GLES20.glEnableVertexAttribArray ( mTexCoordLoc );
// Prepare the texturecoordinates
GLES20.glVertexAttribPointer ( mTexCoordLoc, 2, GLES20.GL_FLOAT,
false,
0, uvBuffer);
// Get handle to shape's transformation matrix
int mtrxhandle = GLES20.glGetUniformLocation(riGraphicTools.sp_Image, "uMVPMatrix");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mtrxhandle, 1, false, mvpMatrix, 0);
// Get handle to textures locations
int mSamplerLoc = GLES20.glGetUniformLocation (riGraphicTools.sp_Image, "s_texture" );
// Set the sampler texture unit to 0, where we have saved the texture.
GLES20.glUniform1i ( mSamplerLoc, 0);
// Draw the triangle
GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTexCoordLoc);
}
}
public class riGraphicTools {
private static final String TAG = "GraphicTools";
// Program variables
public static int sp_SolidColor;
public static int sp_Image;
/* SHADER Solid
*
* This shader is for rendering a colored primitive.
*
*/
public static final String vs_SolidColor =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
"}";
public static final String fs_SolidColor =
"precision mediump float;" +
"void main() {" +
" gl_FragColor = vec4(0.5,0,0,1);" +
"}";
/* SHADER Image
*
* This shader is for rendering 2D images straight from a texture
* No additional effects.
*
*/
public static final String vs_Image =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"attribute vec2 a_texCoord;" +
"varying vec2 v_texCoord;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
" v_texCoord = a_texCoord;" +
"}";
public static final String fs_Image =
"precision mediump float;" +
"varying vec2 v_texCoord;" +
"uniform sampler2D s_texture;" +
"void main() {" +
" gl_FragColor = texture2D( s_texture, v_texCoord );" +
"}";
/**
* Utility method for compiling a OpenGL shader.
*
* <p><strong>Note:</strong> When developing shaders, use the checkGlError()
* method to debug shader coding errors.</p>
*
* #param type - Vertex or fragment shader type.
* #param shaderCode - String containing the shader code.
* #return - Returns an id for the shader.
*/
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 the shader
return shader;
}
/**
* Utility method for debugging OpenGL calls. Provide the name of the call
* just after making it:
*
* <pre>
* mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
* FandeckRenderer.checkGlError("glGetUniformLocation");</pre>
*
* If the operation is not successful, the check throws an error.
*
* #param glOperation - Name of the OpenGL call to check.
*/
public static void checkGlError(String glOperation) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e(TAG, glOperation + ": glError " + error);
throw new RuntimeException(glOperation + ": glError " + error);
}
}
public static int loadTexture(final Context context, final int resourceId, int[] textureHandler, int index)
{
//GLES20.glGenTextures(1, textureHandler, index);
if (textureHandler[index] != 0)
{
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false; // No pre-scaling
// Read in the resource
final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);
// Bind to the texture in OpenGL
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandler[index]);
// Set filtering
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Recycle the bitmap, since its data has been loaded into OpenGL.
bitmap.recycle();
}
if (textureHandler[index] == 0)
{
throw new RuntimeException("Error loading texture.");
}
return textureHandler[0];
}
}
Ok, the problem was the textureHandle, I have moved it to upper class to keep data, and in the method Draw I have added:
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[textureIndex]);
and it works.
the final code is the next one:
public class GLRenderer implements Renderer {
// Our matrices
private final float[] mtrxProjection = new float[16];
private final float[] mtrxView = new float[16];
private final float[] mtrxProjectionAndView = new float[16];
// Our screenresolution
float mScreenWidth = 1280;
float mScreenHeight = 768;
// Misc
Context mContext;
long mLastTime;
int mProgram;
CardView card1;
CardView card2;
public GLRenderer(Context c)
{
mContext = c;
mLastTime = System.currentTimeMillis() + 100;
}
public void onPause()
{
/* Do stuff to pause the renderer */
}
public void onResume()
{
/* Do stuff to resume the renderer */
mLastTime = System.currentTimeMillis();
}
#Override
public void onDrawFrame(GL10 unused) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Get the current time
long now = System.currentTimeMillis();
// We should make sure we are valid and sane
if (mLastTime > now) return;
// Get the amount of time the last frame took.
long elapsed = now - mLastTime;
// Update our example
// Render our example
card1.Draw(mtrxProjectionAndView, 0);
card2.Draw(mtrxProjectionAndView , 1);
// Save the current time to see how long it took :).
mLastTime = now;
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// We need to know the current width and height.
mScreenWidth = width;
mScreenHeight = height;
// Redo the Viewport, making it fullscreen.
GLES20.glViewport(0, 0, (int)mScreenWidth, (int)mScreenHeight);
// Clear our matrices
for(int i=0;i<16;i++)
{
mtrxProjection[i] = 0.0f;
mtrxView[i] = 0.0f;
mtrxProjectionAndView[i] = 0.0f;
}
// Setup our screen width and height for normal sprite translation.
Matrix.orthoM(mtrxProjection, 0, 0f, mScreenWidth, 0.0f, mScreenHeight, 0, 50);
// Set the camera position (View matrix)
Matrix.setLookAtM(mtrxView, 0, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mtrxProjectionAndView, 0, mtrxProjection, 0, mtrxView, 0);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
card1 = new CardView(mContext, 0);
card2 = new CardView(mContext, 1);
}
}
public class CardView {
// Misc
private Context mContext;
// Geometric variables
// number of coordinates per vertex in this array
private static final int COORDS_PER_VERTEX = 3;
private static float vertices[] = {
10.0f, 300f, 0.0f,
10.0f, 100f, 0.0f,
450f, 100f, 0.0f,
450f, 300f, 0.0f,
};
private static float vertices2[] = {
450.0f, 600f, 0.0f,
450.0f, 300f, 0.0f,
900f, 300f, 0.0f,
900f, 600f, 0.0f,
};
private final short indices[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
private static float uvs[] = {
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f
};
private FloatBuffer vertexBuffer;
private ShortBuffer drawListBuffer;
private FloatBuffer uvBuffer;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
final int[] textureHandle;
public CardView(Context c, int index)
{
textureHandle = new int[2];
// Save context reference
mContext = c;
// Create the triangles
SetupCard(index);
// Create the image information
SetupImage(index);
// Set the clear color to black
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1);
// Create the shaders, solid color
int vertexShader = riGraphicTools.loadShader(GLES20.GL_VERTEX_SHADER, riGraphicTools.vs_SolidColor);
int fragmentShader = riGraphicTools.loadShader(GLES20.GL_FRAGMENT_SHADER, riGraphicTools.fs_SolidColor);
riGraphicTools.sp_SolidColor = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(riGraphicTools.sp_SolidColor, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(riGraphicTools.sp_SolidColor, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(riGraphicTools.sp_SolidColor); // creates OpenGL ES program executables
// Create the shaders, images
vertexShader = riGraphicTools.loadShader(GLES20.GL_VERTEX_SHADER, riGraphicTools.vs_Image);
fragmentShader = riGraphicTools.loadShader(GLES20.GL_FRAGMENT_SHADER, riGraphicTools.fs_Image);
riGraphicTools.sp_Image = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(riGraphicTools.sp_Image, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(riGraphicTools.sp_Image, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(riGraphicTools.sp_Image); // creates OpenGL ES program executables
// Set our shader programm
GLES20.glUseProgram(riGraphicTools.sp_Image);
}
public void SetupImage(int index)
{
GLES20.glGenTextures(2, textureHandle, 0);
// The texture buffer
ByteBuffer bb = ByteBuffer.allocateDirect(uvs.length * 4);
bb.order(ByteOrder.nativeOrder());
uvBuffer = bb.asFloatBuffer();
uvBuffer.put(uvs);
uvBuffer.position(0);
// Generate Textures, if more needed, alter these numbers.
int[] texturenames = new int[1];
GLES20.glGenTextures(1, texturenames, 0);
// Retrieve our image from resources.
//int id = mContext.getResources().getIdentifier("drawable/test_backbground1", null, mContext.getPackageName());
if(index == 1) {
int id = riGraphicTools.loadTexture(mContext, R.drawable.test_backbground1, textureHandle, index);
}else{
int id = riGraphicTools.loadTexture(mContext, R.drawable.test_backbground2, textureHandle, index);
}
}
public void SetupCard(int index)
{
if( index == 1) {
// The vertex buffer.
ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
}else{
// The vertex buffer.
ByteBuffer bb = ByteBuffer.allocateDirect(vertices2.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(vertices2);
vertexBuffer.position(0);
}
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(indices.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(indices);
drawListBuffer.position(0);
}
/**
* Encapsulates the OpenGL ES instructions for drawing this shape.
*
* #param mvpMatrix - The Model View Project matrix in which to draw
* this shape.
*/
public void Draw(float[] mvpMatrix, int textureIndex){
// clear Screen and Depth Buffer, we have set the clear color as black.
//GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[textureIndex]);
// get handle to vertex shader's vPosition member
int mPositionHandle = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "vPosition");
// Enable generic vertex attribute array
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// Get handle to texture coordinates location
int mTexCoordLoc = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "a_texCoord" );
// Enable generic vertex attribute array
GLES20.glEnableVertexAttribArray ( mTexCoordLoc );
// Prepare the texturecoordinates
GLES20.glVertexAttribPointer ( mTexCoordLoc, 2, GLES20.GL_FLOAT,
false,
0, uvBuffer);
// Get handle to shape's transformation matrix
int mtrxhandle = GLES20.glGetUniformLocation(riGraphicTools.sp_Image, "uMVPMatrix");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mtrxhandle, 1, false, mvpMatrix, 0);
// Get handle to textures locations
int mSamplerLoc = GLES20.glGetUniformLocation (riGraphicTools.sp_Image, "s_texture" );
// Set the sampler texture unit to 0, where we have saved the texture.
GLES20.glUniform1i ( mSamplerLoc, 0);
// Draw the triangle
GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTexCoordLoc);
}
}
public class riGraphicTools {
// Program variables
public static int sp_SolidColor;
public static int sp_Image;
/* SHADER Solid
*
* This shader is for rendering a colored primitive.
*
*/
public static final String vs_SolidColor =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
"}";
public static final String fs_SolidColor =
"precision mediump float;" +
"void main() {" +
" gl_FragColor = vec4(0.5,0,0,1);" +
"}";
/* SHADER Image
*
* This shader is for rendering 2D images straight from a texture
* No additional effects.
*
*/
public static final String vs_Image =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"attribute vec2 a_texCoord;" +
"varying vec2 v_texCoord;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
" v_texCoord = a_texCoord;" +
"}";
public static final String fs_Image =
"precision mediump float;" +
"varying vec2 v_texCoord;" +
"uniform sampler2D s_texture;" +
"void main() {" +
" gl_FragColor = texture2D( s_texture, v_texCoord );" +
"}";
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 the shader
return shader;
}
public static int loadTexture(final Context context, final int resourceId, int[] textureHandle , int index)
{
if (textureHandle[index] != 0)
{
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false; // No pre-scaling
// Read in the resource
final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);
// Bind to the texture in OpenGL
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[index]);
// Set filtering
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Recycle the bitmap, since its data has been loaded into OpenGL.
bitmap.recycle();
}
if (textureHandle[index] == 0)
{
throw new RuntimeException("Error loading texture.");
}
return textureHandle[index];
}
}
I hope that will help someone.
I am new to OpenGL and I have a Triangle that gets created to my surfaceView which in turns shows on my screen. I have been able to add Rotation to make my Triangle rotate however that is not what I need. I have been looking around a lot into OpenGL and have read into Transformation and then into Translation which I believe to be correct for adding vertical movement to my Triangle, however with my current understanding I have been unable to implement this.
Example:
Triangle is created at the top of the screen and moves down the Y axis towards the bottom of the screen.
This translation is on an angle however it shows what I mean, I would just like to move in y-Axis.
I have a Render as follows:
public class MyGLRenderer implements GLSurfaceView.Renderer {
//Project Matrix
private float mMatrix[] = new float[16];
private Triangle mTriangle;
private final float[] mMVPMatrix = new float[16];
private final float[] mModelMatrix = new float[16];
private float[] mTempMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
// Called once to set up the view's opengl es environment
public void onSurfaceCreated(GL10 unused, EGLConfig config){
//Set the background frame color
GLES30.glClearColor(208.0f,208.0f,208.0f,1.0f);
mTriangle= new Triangle();
}
// Called for each redraw of the view
public void onDrawFrame(GL10 gl){
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
//Redraw background color
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
// Apply transformation, start with translation
Matrix.setIdentityM(mModelMatrix, 0); // initialize to identity matrix
Matrix.translateM(mModelMatrix, 0, -0.5f, 0, 0); // translation to the left
// Create a rotation transformation for the triangle
long time = SystemClock.uptimeMillis() % 4000L;
float mAngle = 0.090f * ((int) time);
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
Matrix.orthoM(mMatrix, 0, -1, 1, -1, 1, -1, 1);
// Combine Rotation and Translation matrices
mTempMatrix = mModelMatrix.clone();
Matrix.multiplyMM(mModelMatrix, 0, mTempMatrix, 0, mRotationMatrix, 0);
// Combine the model matrix with the projection and camera view
mTempMatrix = mMVPMatrix.clone();
Matrix.multiplyMM(mMVPMatrix, 0, mTempMatrix, 0, mModelMatrix, 0);
mTriangle.draw(mMatrix);
}
// Called if the geometry of the view changes (example is when the screen orientation changes from landscape to portrait
public void onSurfaceChanged(GL10 unused, int width, int height){
// Called if the geometry of the viewport changes
GLES30.glViewport(0, 0, width, height);
}
public static int loadShader(int type, String shaderCode){
// create a vertex shader type (GLES30.GL_VERTEX_SHADER)
// or a fragment shader type (GLES30.GL_FRAGMENT_SHADER)
int shader = GLES30.glCreateShader(type);
// add the source code to the shader and compile it
GLES30.glShaderSource(shader, shaderCode);
GLES30.glCompileShader(shader);
return shader;
}
}
I have a triangle as follows:
public class Triangle {
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
// Note that the uMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private final FloatBuffer vertexBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// 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
-0.5f, -0.311004243f, 0.0f, // bottom left
0.5f, -0.311004243f, 0.0f // bottom right
};
private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 0.0f };
/**
* Sets up the drawing object data for use in an OpenGL ES context.
*/
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);
// prepare shaders and OpenGL program
int vertexShader = MyGLRenderer.loadShader(
GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(
GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
/**
* Encapsulates the OpenGL ES instructions for drawing this shape.
*
* #param mvpMatrix - The Model View Project matrix in which to draw
* this shape.
*/
public void draw(float[] mvpMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
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
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
I have a Surface View as follows:
public class MyGLSurfaceView extends GLSurfaceView {
private final MyGLRenderer mRenderer;
public MyGLSurfaceView(Context context, AttributeSet attrs){
super(context, attrs);
//Create an OpenGl 3.0 context
setEGLContextClientVersion(3);
mRenderer = new MyGLRenderer();
//Set the Renderer for drawing on the GLSurfaceView
setRenderer(mRenderer);
}
}
I am looking for a bare bones example/answer/URL that can accomplish moving a drawn Triangle on the y axis (I am assuming using Translation).
I have read and tried to implement the following links (some way above my current understanding).
http://www.swiftless.com/tutorials/opengl/rotation.html
http://www.songho.ca/opengl/gl_transform.html
https://open.gl/transformations
http://www.informit.com/articles/article.aspx?p=2111395&seqNum=3
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/
As well as a few others however these seem to be the best sources I have found.
Yes, it would be a translation. It is quite simple, all you would need to do is to modify mvpMatrix in the draw() method of Triangle.
It is important to keep track of the triangle's position to make it move, however. You would add private member to the class resembling this:
private Point mPosition;
Keep in mind, the Point in that code is a simple point/vector class I am assuming you have since I am pretty sure this is on android? Check out this class.
Then, you would need to increment the y-position of the triangle each frame. You could do this in draw(), but I would add another method for movement.
mPosition.y += 0.01;
Note: I see you have an MVP matrix as an argument to draw. Right now, since this is 2D and orthogonic, it is not that important, but think about moving your rotation from the onDrawFrame() function into the Triangle.draw(), since the M stands for Model which would normally be per model/triangle. Normally, you would only pass the VP (view and projection) to the Triangle.draw() method. Check this out.
Finally, you would translate the mvp matrix in the Triangle.draw() method by the triangle's position. So, before passing the matrix to the shader (before GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);), translate it:
Matrix.translateM(mvpMatrix, mPosition.x, mPosition.y);
// Or whatever translate method you have, I am not sure
I'm making an app for android, and I have drawn a triangle, I can rotate it but not move it!
my question why?
Say if you need more info!
Source Code:
package com.uraniumdevs.projectx;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
public class MyGL20Renderer implements GLSurfaceView.Renderer {
private static final String TAG = "MyGLRenderer";
private Triangle mTriangle;
private Square mSquare;
private final float[] mMVPMatrix = new float[16];
private final float[] mProjMatrix = new float[16];
private final float[] mVMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
private final float[] mTranslationMatrix = new float[16];
// Declare as volatile because we are updating it from another thread
public volatile float Angle;
public volatile float Tempx;
public volatile float Tempy;
#Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
mTriangle = new Triangle();
mSquare = new Square();
}
#Override
public void onDrawFrame(GL10 unused) {
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
// Create a rotation for the triangle
Matrix.setRotateM(mRotationMatrix, 0, Angle, 0, 0, -1.0f);
Matrix.translateM(mTranslationMatrix, 0, Tempx, Tempy, 0);
// Combine the rotation matrix with the projection and camera view
Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTranslationMatrix, 0, mMVPMatrix, 0);
mTriangle.draw(mMVPMatrix);
}
#Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
// Adjust the viewport based on geometry changes,
// such as screen rotation
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
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;
}
/**
* Utility method for debugging OpenGL calls. Provide the name of the call
* just after making it:
*
* <pre>
* mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
* MyGLRenderer.checkGlError("glGetUniformLocation");
* </pre>
*
* If the operation is not successful, the check throws an error.
*
* #param glOperation
* - Name of the OpenGL call to check.
*/
public static void checkGlError(String glOperation) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e(TAG, glOperation + ": glError " + error);
throw new RuntimeException(glOperation + ": glError " + error);
}
}
}
class Triangle {
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" + "void main() {" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = vPosition * uMVPMatrix;" + "}";
private final String fragmentShaderCode = "precision mediump float;"
+ "uniform vec4 vColor;" + "void main() {"
+ " gl_FragColor = vColor;" + "}";
private final FloatBuffer vertexBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
public static float triangleCoords[] = { // in counterclockwise order:
0.0f, 0.1f, 0.0f, // top
-0.1f, -0.1f, 0.0f, // bottom left
0.1f, -0.1f, 0.0f // bottom right
};
private final int vertexCount;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per
// vertex
// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
public Triangle() {
vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
// 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);
// prepare shaders and OpenGL program
int vertexShader = MyGL20Renderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGL20Renderer.loadShader(
GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader
// to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment
// shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
public void draw(float[] mvpMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
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
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGL20Renderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGL20Renderer.checkGlError("glUniformMatrix4fv");
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
class Square {
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" + "void main() {" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = vPosition * uMVPMatrix;" + "}";
private final String fragmentShaderCode = "precision mediump float;"
+ "uniform vec4 vColor;" + "void main() {"
+ " gl_FragColor = vColor;" + "}";
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float squareCoords[] = { -0.5f, 0.5f, 0.0f, // top left
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f, // bottom right
0.5f, 0.5f, 0.0f }; // top right
private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw
// vertices
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per
// vertex
// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.2f, 0.709803922f, 0.898039216f, 1.0f };
public Square() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (# of coordinate values * 2 bytes per short)
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
// prepare shaders and OpenGL program
int vertexShader = MyGL20Renderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGL20Renderer.loadShader(
GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader
// to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment
// shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
public void draw(float[] mvpMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
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
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGL20Renderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGL20Renderer.checkGlError("glUniformMatrix4fv");
// Draw the square
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
I think we are modifying the same OpenGLES20Activity from the Android developer training guide
http://developer.android.com/training/graphics/opengl/environment.html
In order to move and rotate the triangle, you may do this:
(In order to make the variables accessible from other class object, I made them public.)
Add a mModelMatrix for the Triangle object. This is used to set the location and direction of the triangle. Initialize it in the constructor.
public class Triangle {
...
public float[] mModelMatrix = new float[16];
...
public Triangle() {
...
Matrix.setIdentityM(mModelMatrix, 0);
...
}
Use Matrix.translateM and Matrix.rotateM (not Matrix.setRotateM) to move and rotate the triangle. You can do these as many times as you like.
Matrix.translateM(mTriangle.mModelMatrix, 0, 0.5f, 0f, 0f);
Matrix.rotateM(mTriangle.mModelMatrix, 0, -45f, 0, 0, -1.0f);
Multiply the mViewMatrix and the mProjectMatrix to create a mMVPMatrix, same as in the example. Then multiply the mMVPMatrix with the triangle's mModelMatrix.
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 1, 10);
}
public void onDrawFrame(GL10 unused) {
float[] scratch = new float[16];
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 5f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
// Combine the model's translation & rotation matrix
// with the projection and camera view
// Note that the mMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mTriangle.mModelMatrix, 0);
// Draw triangle
mTriangle.draw(scratch);
}
I am surprised this works at all. In OpenGL if you use proper column-major matrices, you should multiply: ModelViewProjectionMatrix * VertexPosition for transforming your vertices.
This is very likely the cause of your problem.
In other words, in both of your shaders
gl_Position = vPosition * uMVPMatrix;
should probably be:
gl_Position = uMVPMatrix * vPosition;
Your original matrix multiplication would work in Direct3D, because it uses row-major matrices. This is something to keep in mind if you are porting code from HLSL.
I've just started learning OpenGL for Android and I'm having a weird problem when drawing a circle. some of its vertices stick to the left and top wall making lines go out from the circle a bit randomly. Every time I restart the app they have a different position.
My DrawScreen class where the circle is drawn:
public class DrawScreen implements GLSurfaceView.Renderer {
Ball ball;
public float mAngle;
private int mProgram;
private int maPositionHandle;
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix; \n" +
"attribute vec4 vPosition; \n" +
"void main(){ \n" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = uMVPMatrix * vPosition; \n" +
"} \n";
private final String fragmentShaderCode =
"precision mediump float; \n" +
"void main(){ \n" +
" gl_FragColor = vec4 (0.63671875, 0.76953125, 0.22265625, 1.0); \n" +
"} \n";
private int muMVPMatrixHandle;
private float[] mMVPMatrix = new float[16];
private float[] mVMatrix = new float[16];
private float[] mProjMatrix = new float[16];
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
ball = new Ball();
// Set the background frame color
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
ball.initShapes(240, 360, 50);
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL 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 program executables
// get handle to the vertex shader's vPosition member
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
}
public void onDrawFrame(GL10 unused) {
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// Prepare the circle data
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 0, ball.ballVB);
GLES20.glEnableVertexAttribArray(maPositionHandle);
// Apply a ModelView Projection transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
// Draw the circle
GLES20.glDrawArrays(GLES20.GL_LINE_LOOP, 0, (int) (ball.getNumSeg() * 3));
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
// this projection matrix is applied to object coodinates
// in the onDrawFrame() method
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
}
private 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;
}
}
And my Ball class where the circle is created:
public class Ball {
public FloatBuffer ballVB;
private float cx, cy, r;
float numSegments = 360;
public void initShapes(float tx, float ty, float tr){
cx = (tx / 240.f) - 1.f;
cy = (ty / 360.f) - 1.f;
r = (tr / 240.f);
float ballCoords[] = new float[(int) (numSegments * 3)];
double theta = (2 * 3.1415926 / numSegments);
float c = (float) Math.cos(theta);//precalculate the sine and cosine
float s = (float) Math.sin(theta);
float t;
float x = r;//we start at angle = 0
float y = 0;
for(int i = 0; i < (numSegments * 3); i = i + 3 ) {
ballCoords[i] = (x + cx);
ballCoords[i + 1] = (y + cy);
ballCoords[i + 2] = (0);
//apply the rotation matrix
t = x;
x = c * x - s * y;
y = s * t + c * y;
}
// initialize vertex Buffer for triangle
ByteBuffer vbb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
ballCoords.length * 4);
vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order
ballVB = vbb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer
ballVB.put(ballCoords); // add the coordinates to the FloatBuffer
ballVB.position(0); // set the buffer to read the first coordinate
}
public float getNumSeg(){
return numSegments;
}
}
I've been scouring the internet for hours but haven't found anything. Hope you guys can help me.
GLES20.glDrawArrays(GLES20.GL_LINE_LOOP, 0, (int) (ball.getNumSeg() * 3));
I'm suspicious of this, are segments referring to individual vertices?
The final argument to glDrawArrays is the number of vertices to draw, not the number of floats. You should probably remove the * 3 multiplier from glDrawArrays.
Your extra lines are probably from drawing garbage data because you're drawing 3 times as many vertices as you've actually allocated.
I've been following the tutorial at http://developer.android.com/resources/tutorials/opengl/opengl-es20.html for OpenGL ES on android. I've gotten to the, "Apply Projection and Camera View" section however I always seem to get a blank screen with no triangle, the previous section worked perfectly fine. I also tried just copy pasting the entire tutorial into my code but got the same result. Changing the line:
gl_Position = uMVPMatrix * vPosition;
to:
gl_Position = vPosition;
puts the application back to the first section (triangle stretches depending on screen orientation). Any idea what the problem is? Here's the code I have so far just in case I missed something:
public class GLTest20Renderer implements Renderer {
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix; \n" +
"attribute vec4 vPosition; \n" +
"void main(){ \n" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = uMVPMatrix * vPosition; \n" +
"} \n";
private final String fragmentShaderCode =
"precision mediump float; \n" +
"void main(){ \n" +
" gl_FragColor = vec4 (0.63671875, 0.76953125, 0.22265625, 1.0); \n" +
"} \n";
private FloatBuffer triangleVB;
private int mProgram;
private int maPositionHandle;
private int muMVPMatrixHandle;
private float[] mMVPMatrix = new float[16];
private float[] mMMatrix = new float[16];
private float[] mVMatrix = new float[16];
private float[] mProjMatrix = new float[16];
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
initShapes();
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL 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 program executables
// get handle to the vertex shader's vPosition member
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
}
public void onDrawFrame(GL10 unused) {
GLES20.glClear( GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT );
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// Prepare the triangle data
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 12, triangleVB);
GLES20.glEnableVertexAttribArray(maPositionHandle);
// Apply a ModelView Projection transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
}
private void initShapes() {
float triangleCoords[] = {
// X, Y, Z
-0.5f, -0.25f, 0,
0.5f, -0.25f, 0,
0.0f, 0.559016994f, 0
};
// initialize vertex Buffer for triangle
ByteBuffer vbb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
triangleCoords.length * 4);
vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order
triangleVB = vbb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer
triangleVB.put(triangleCoords); // add the coordinates to the FloatBuffer
triangleVB.position(0); // set the buffer to read the first coordinate
}
private 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;
}
}
I'm running all this on a Samsung Galaxy S2.
Fixed, just changed the near point in the lookat to be under 3:
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 2, 7);