Opengl es 2.0 draw bitmap overlay on video - java

I am trying to draw a bitmap as an overlay on every frame of the video. I found an example on how to decode and encode a video and it is working. This example has a TextureRenderer class with a drawFrame function that I need to modify in order to add the bitmap. I am newbie to opengl but I learnt that I need to create a texture with the bitmap and bind it. I tried that in the following code but it is throwing an exception.
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// from: https://android.googlesource.com/platform/cts/+/lollipop-release/tests/tests/media/src/android/media/cts/TextureRender.java
// blob: 4125dcfcfed6ed7fddba5b71d657dec0d433da6a
// modified: removed unused method bodies
// modified: use GL_LINEAR for GL_TEXTURE_MIN_FILTER to improve quality.
package com.example.name.videoeditortest;
/**
* Code for rendering a texture onto a surface using OpenGL ES 2.0.
*/
import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.util.Log;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
/**
* Code for rendering a texture onto a surface using OpenGL ES 2.0.
*/
class TextureRender {
private Bitmap bitmap;
private static final String TAG = "TextureRender";
private static final int FLOAT_SIZE_BYTES = 4;
private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
private final float[] mTriangleVerticesData = {
// X, Y, Z, U, V
-1.0f, -1.0f, 0, 0.f, 0.f,
1.0f, -1.0f, 0, 1.f, 0.f,
-1.0f, 1.0f, 0, 0.f, 1.f,
1.0f, 1.0f, 0, 1.f, 1.f,
};
private FloatBuffer mTriangleVertices;
private static final String VERTEX_SHADER =
"uniform mat4 uMVPMatrix;\n" +
"uniform mat4 uSTMatrix;\n" +
"attribute vec4 aPosition;\n" +
"attribute vec4 aTextureCoord;\n" +
"varying vec2 vTextureCoord;\n" +
"void main() {\n" +
" gl_Position = uMVPMatrix * aPosition;\n" +
" vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
"}\n";
private static final String FRAGMENT_SHADER =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" + // highp here doesn't seem to matter
"varying vec2 vTextureCoord;\n" +
"uniform samplerExternalOES sTexture;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
"}\n";
private float[] mMVPMatrix = new float[16];
private float[] mSTMatrix = new float[16];
private int mProgram;
private int mTextureID = -12345;
private int mTextureBitmapID = -12345;
private int muMVPMatrixHandle;
private int muSTMatrixHandle;
private int maPositionHandle;
private int maTextureHandle;
public TextureRender() {
mTriangleVertices = ByteBuffer.allocateDirect(
mTriangleVerticesData.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTriangleVertices.put(mTriangleVerticesData).position(0);
Matrix.setIdentityM(mSTMatrix, 0);
}
public int getTextureId() {
return mTextureID;
}
public void drawFrame(SurfaceTexture st) {
checkGlError("onDrawFrame start");
st.getTransformMatrix(mSTMatrix);
GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glUseProgram(mProgram);
checkGlError("glUseProgram");
//Bing textrues
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
GLES20.glActiveTexture(GLES20.GL_TEXTURE_2D);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureBitmapID);
mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
checkGlError("glVertexAttribPointer maPosition");
GLES20.glEnableVertexAttribArray(maPositionHandle);
checkGlError("glEnableVertexAttribArray maPositionHandle");
mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
checkGlError("glVertexAttribPointer maTextureHandle");
GLES20.glEnableVertexAttribArray(maTextureHandle);
checkGlError("glEnableVertexAttribArray maTextureHandle");
Matrix.setIdentityM(mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
checkGlError("glDrawArrays");
GLES20.glFinish();
}
/**
* Initializes GL state. Call this after the EGL surface has been created and made current.
*/
public void surfaceCreated() {
mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
if (mProgram == 0) {
throw new RuntimeException("failed creating program");
}
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
checkGlError("glGetAttribLocation aPosition");
if (maPositionHandle == -1) {
throw new RuntimeException("Could not get attrib location for aPosition");
}
maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
checkGlError("glGetAttribLocation aTextureCoord");
if (maTextureHandle == -1) {
throw new RuntimeException("Could not get attrib location for aTextureCoord");
}
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
checkGlError("glGetUniformLocation uMVPMatrix");
if (muMVPMatrixHandle == -1) {
throw new RuntimeException("Could not get attrib location for uMVPMatrix");
}
muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
checkGlError("glGetUniformLocation uSTMatrix");
if (muSTMatrixHandle == -1) {
throw new RuntimeException("Could not get attrib location for uSTMatrix");
}
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
mTextureID = textures[0];
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
checkGlError("glBindTexture mTextureID");
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE);
checkGlError("glTexParameter");
mTextureBitmapID = loadBitmapTexture();
}
private int loadBitmapTexture()
{
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
if (textureHandle[0] != 0)
{
// Bind to the texture in OpenGL
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
// 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);
}
if (textureHandle[0] == 0)
{
throw new RuntimeException("Error loading texture.");
}
return textureHandle[0];
}
/**
* Replaces the fragment shader.
*/
public void changeFragmentShader(String fragmentShader) {
GLES20.glDeleteProgram(mProgram);
mProgram = createProgram(VERTEX_SHADER, fragmentShader);
if (mProgram == 0) {
throw new RuntimeException("failed creating program");
}
}
private int loadShader(int shaderType, String source) {
int shader = GLES20.glCreateShader(shaderType);
checkGlError("glCreateShader type=" + shaderType);
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e(TAG, "Could not compile shader " + shaderType + ":");
Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
return shader;
}
private int createProgram(String vertexSource, String fragmentSource) {
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0) {
return 0;
}
int program = GLES20.glCreateProgram();
checkGlError("glCreateProgram");
if (program == 0) {
Log.e(TAG, "Could not create program");
}
GLES20.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
GLES20.glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
Log.e(TAG, "Could not link program: ");
Log.e(TAG, GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
return program;
}
public void checkGlError(String op) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e(TAG, op + ": glError " + error);
throw new RuntimeException(op + ": glError " + error);
}
}
public void setBitmap(Bitmap bitmap){
this.bitmap = bitmap;
}
/**
* Saves the current frame to disk as a PNG image. Frame starts from (0,0).
* <p>
* Useful for debugging.
*/
public static void saveFrame(String filename, int width, int height) {
// glReadPixels gives us a ByteBuffer filled with what is essentially big-endian RGBA
// data (i.e. a byte of red, followed by a byte of green...). We need an int[] filled
// with native-order ARGB data to feed to Bitmap.
//
// If we implement this as a series of buf.get() calls, we can spend 2.5 seconds just
// copying data around for a 720p frame. It's better to do a bulk get() and then
// rearrange the data in memory. (For comparison, the PNG compress takes about 500ms
// for a trivial frame.)
//
// So... we set the ByteBuffer to little-endian, which should turn the bulk IntBuffer
// get() into a straight memcpy on most Android devices. Our ints will hold ABGR data.
// Swapping B and R gives us ARGB. We need about 30ms for the bulk get(), and another
// 270ms for the color swap.
//
// Making this even more interesting is the upside-down nature of GL, which means we
// may want to flip the image vertically here.
ByteBuffer buf = ByteBuffer.allocateDirect(width * height * 4);
buf.order(ByteOrder.LITTLE_ENDIAN);
GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
buf.rewind();
int pixelCount = width * height;
int[] colors = new int[pixelCount];
buf.asIntBuffer().get(colors);
for (int i = 0; i < pixelCount; i++) {
int c = colors[i];
colors[i] = (c & 0xff00ff00) | ((c & 0x00ff0000) >> 16) | ((c & 0x000000ff) << 16);
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(filename);
Bitmap bmp = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888);
bmp.compress(Bitmap.CompressFormat.PNG, 90, fos);
bmp.recycle();
} catch (IOException ioe) {
throw new RuntimeException("Failed to write file " + filename, ioe);
} finally {
try {
if (fos != null) fos.close();
} catch (IOException ioe2) {
throw new RuntimeException("Failed to close file " + filename, ioe2);
}
}
Log.d(TAG, "Saved " + width + "x" + height + " frame as '" + filename + "'");
}
}
Exception thrown:
E/ExtractDecodeEditEncodeMuxTest: error while releasing muxer
java.lang.IllegalStateException: Can't stop due to wrong state.
at android.media.MediaMuxer.stop(MediaMuxer.java:231)
at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest.extractDecodeEditEncodeMux(ExtractDecodeEditEncodeMuxTest.java 434)
at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest.access$000(ExtractDecodeEditEncodeMuxTest.java:58)
at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest$TestWrapper.run(ExtractDecodeEditEncodeMuxTest.java:171)
at java.lang.Thread.run(Thread.java:841)
If I comment GLES20.glActiveTexture(GLES20.GL_TEXTURE_2D); in the drawFrame the video is rendered correctly but with not bitmap. If I comment GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureBitmapID); in the drawFrame I get the following exception:
java.lang.RuntimeException: glVertexAttribPointer maPosition: glError 1280
at com.example.name.videoeditortest.TextureRender.checkGlError(TextureRender.java:259)
at com.example.name.videoeditortest.TextureRender.drawFrame(TextureRender.java:111)
at com.example.name.videoeditortest.OutputSurface.drawImage(OutputSurface.java:252)
at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest.doExtractDecodeEditEncodeMux(ExtractDecodeEditEncodeMuxTest.java:793)
at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest.extractDecodeEditEncodeMux(ExtractDecodeEditEncodeMuxTest.java:341)
at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest.access$000(ExtractDecodeEditEncodeMuxTest.java:58)
at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest$TestWrapper.run(ExtractDecodeEditEncodeMuxTest.java:171)
at java.lang.Thread.run(Thread.java:841)

I see two things that seems wrong to me.
You are trying to bind everything at the same time, and hope that one call to GLES20.glDrawArrays() will draw everything.
You have only one shader, where you should have two : one for doing video texture rendering, and another for your bitmap layer rendering.
What you must know is that a frame can be draw by multiple call to glDrawArrays, each call will just paint a little part over previously drawn stuff (basically).
The first part of rendering a frame in your case should look to this :
# init
loadShaderForVideo()
loadShaderForBitmapLayer()
prepareYourArraysEtc()
...
#loop
GLClear()
updateVideoTexture()
drawFrame(){
drawVideo()
drawBitmap()
}
drawVideo(){
bindYourActiveTextureToVideo()
setYourVertexAttribAndUniform()
GLES20.glDrawArrays()
}
drawBitmap() {
bindYourActiveTextureToBitmap()
setYourVertexAttribAndUniform() // This should be the same as above for video
// Considering you want to draw above your video, consider activating the blending for transparency :
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
GLES20.glDrawArrays()
}
Concerning the shader, just take a look at these :
A common Vertex Shader for both :
public static final String vertexDefaultShaderCode =
"uniform mat4 uVPMatrix;" +
"uniform mat4 uModelMatrix;" + // uniform = input const
"attribute vec3 aPosition;" + // attribute = input property different for each vertex
"attribute vec2 aTexCoordinate;" +
"varying vec2 vTexCoordinate;" +// varying = output property different for each pixel
"void main() {" +
"vTexCoordinate = aTexCoordinate;" +
"gl_Position = uVPMatrix * uModelMatrix * vec4(aPosition,1.0);" +
"}";
Then a basic fragment shader ( for your bitmap 2D texture ) :
public static final String fragmentDefaultShaderCode =
"precision mediump float;" +
"uniform sampler2D uTexture;" +
"varying vec2 vTexCoordinate;" +
"void main() {" +
" gl_FragColor = texture2D(uTexture, vTexCoordinate);" +
"}";
Then a different version for video rendering :
public static final String fragmentExternalShaderCode =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;" +
"uniform samplerExternalOES sTexture;" +
"varying vec2 vTexCoordinate;" +
"void main() {" +
" gl_FragColor = texture2D(sTexture, vTexCoordinate);" +
"}";
Thus you will need two Programs, one with the defaultVertexShader + defaultFragmentShader and another with defaultVertexShader + fragmentExternalShaderCode.

Buggy but works. Per J.Jacobs-VP, you need to use two programs and two shaders, one for video frame and the other for the bitmap.
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var gles = 2
/**
* Code for rendering a texture onto a surface using OpenGL ES 2.0.
*/
internal class TextureRender {
/** Video Frame **/
private val mTriangleVerticesData = floatArrayOf( // X, Y, Z, U, V
-1.0f, -1.0f, 0f, 0f, 0f,
1.0f, -1.0f, 0f, 1f, 0f,
-1.0f, 1.0f, 0f, 0f, 1f,
1.0f, 1.0f, 0f, 1f, 1f
)
private val mTriangleVertices: FloatBuffer
private val mMVPMatrix = FloatArray(16)
private val mSTMatrix = FloatArray(16)
private var mProgram = 0
var textureId = -12345
private set
private var muMVPMatrixHandle = 0
private var muSTMatrixHandle = 0
private var maPositionHandle = 0
private var maTextureHandle = 0
/** Bitmap Overlay **/
private val mBitmapTriangleVerticesData = floatArrayOf( // X, Y, Z, U, V
-1.0f, -1.0f, 0f, 0f, 0f,
1.0f, -1.0f, 0f, 1f, 0f,
-1.0f, 1.0f, 0f, 0f, 1f,
1.0f, 1.0f, 0f, 1f, 1f
)
private val mBitmapTriangleVertices: FloatBuffer
private var mBitmapProgram = 1
private val mBitmapMVPMatrix = FloatArray(16)
private val mBitmapSTMatrix = FloatArray(16)
private var mBitmapuMVPMatrixHandle = 0
private var mBitmapuSTMatrixHandle = 0
private var mBitmapaPositionHandle = 0
private var mBitmapaTextureHandle = 0
var bitmapTextureId = -12345
private set
fun drawFrame(st: SurfaceTexture, sourceWidth: Int, sourceHeight: Int, targetWidth: Int, targetHeight: Int, flipHorizontally: Boolean, flipVertically: Boolean) {
if (gles == 2) {
/** Draw Frame **/
println("drawFrame")
checkGlError("onDrawFrame start")
st.getTransformMatrix(mSTMatrix)
//GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f)
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT or GLES20.GL_COLOR_BUFFER_BIT)
GLES20.glUseProgram(mProgram)
checkGlError("glUseProgram")
// original
GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId)
// vertices
mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET)
GLES20.glVertexAttribPointer(
maPositionHandle, 3, GLES20.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices
)
checkGlError("glVertexAttribPointer maPosition")
GLES20.glEnableVertexAttribArray(maPositionHandle)
checkGlError("glEnableVertexAttribArray maPositionHandle")
mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET)
GLES20.glVertexAttribPointer(
maTextureHandle, 2, GLES20.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices
)
checkGlError("glVertexAttribPointer maTextureHandle")
GLES20.glEnableVertexAttribArray(maTextureHandle)
checkGlError("glEnableVertexAttribArray maTextureHandle")
Matrix.setIdentityM(mMVPMatrix, 0)
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0)
GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0)
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
checkGlError("glDrawArrays")
/** Draw Bitmap **/
// bitmap
// save to bitmap
val bmp = savePixels(0, 0, sourceWidth, sourceHeight)
// load texture from bitmap
println("bmp: ${bmp?.width} ${bmp?.height}")
if (bmp != null) {
//val monochromeBmp = convertToBlackWhite(bmp)?.rotate(90f)
var monochromeBmp = convertToBlackWhite(bmp)
if (sourceHeight > sourceWidth) {
if (monochromeBmp != null) {
//monochromeBmp.rotate(90f)
}
}
println("monochromeBmp: ${monochromeBmp?.width} ${monochromeBmp?.height}")
println("drawFrame")
checkGlError("onDrawFrame start")
st.getTransformMatrix(mBitmapSTMatrix)
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT or GLES20.GL_COLOR_BUFFER_BIT)
GLES20.glUseProgram(mBitmapProgram)
checkGlError("glUseProgram")
// original
GLES20.glActiveTexture(GLES20.GL_TEXTURE1)
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, bitmapTextureId)
// vertices
mBitmapTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET)
GLES20.glVertexAttribPointer(
mBitmapaPositionHandle, 3, GLES20.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mBitmapTriangleVertices
)
checkGlError("glVertexAttribPointer mBitmapaPosition")
GLES20.glEnableVertexAttribArray(mBitmapaPositionHandle)
checkGlError("glEnableVertexAttribArray mBitmapaPositionHandle")
mBitmapTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET)
GLES20.glVertexAttribPointer(
mBitmapaTextureHandle, 2, GLES20.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mBitmapTriangleVertices
)
checkGlError("glVertexAttribPointer mBitmapaTextureHandle")
GLES20.glEnableVertexAttribArray(mBitmapaTextureHandle)
checkGlError("glEnableVertexAttribArray maTextureHandle")
Matrix.setIdentityM(mBitmapMVPMatrix, 0)
GLES20.glUniformMatrix4fv(mBitmapuMVPMatrixHandle, 1, false, mBitmapMVPMatrix, 0)
GLES20.glUniformMatrix4fv(mBitmapuSTMatrixHandle, 1, false, mBitmapSTMatrix, 0)
if (monochromeBmp != null) {
loadTexture(monochromeBmp)
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
checkGlError("glDrawArrays")
}
}
// finish
GLES20.glFinish()
}
}
/**
* Save Bitmap as Grayscale
**/
private fun convertToBlackWhite(bmp: Bitmap): Bitmap? {
val width = bmp.width
val height = bmp.height
val pixels = IntArray(width * height)
bmp.getPixels(pixels, 0, width, 0, 0, width, height)
val alpha = 0xFF shl 24 // ?bitmap?24?
for (i in 0 until height) {
for (j in 0 until width) {
var grey = pixels[width * i + j]
val red = grey and 0x00FF0000 shr 16
val green = grey and 0x0000FF00 shr 8
val blue = grey and 0x000000FF
grey = (red * 0.3 + green * 0.59 + blue * 0.11).toInt()
grey = alpha or (grey shl 16) or (grey shl 8) or grey
pixels[width * i + j] = grey
}
}
val newBmp = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
newBmp.setPixels(pixels, 0, width, 0, 0, width, height)
return newBmp
}
/**
Rotate Bitmap
*/
fun Bitmap.rotate(degrees: Float): Bitmap {
val matrix = android.graphics.Matrix().apply { postRotate(degrees) }
return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)
}
/**
* Save Texture as Bitmap
**/
private fun savePixels(x: Int, y: Int, w: Int, h: Int): Bitmap? {
val b = IntArray(w * (y + h))
val bt = IntArray(w * h)
val ib: IntBuffer = IntBuffer.wrap(b)
ib.position(0)
GLES20.glReadPixels(0, 0, w, h, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, ib)
var i = 0
var k = 0
while (i < h) {
//remember, that OpenGL bitmap is incompatible with Android bitmap
//and so, some correction need.
for (j in 0 until w) {
val pix = b[i * w + j]
val pb = pix shr 16 and 0xff
val pr = pix shl 16 and 0x00ff0000
val pix1 = pix and -0xff0100 or pr or pb
bt[(h - k - 1) * w + j] = pix1
}
i++
k++
}
return Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888)
}
fun saveImage(finalBitmap: Bitmap) {
val generator = Random()
var n = 10000
n = generator.nextInt(n)
val fname = "Image-$n.jpg"
val file = File(output(fname)!!.path!!)
if (file.exists()) file.delete()
try {
val out = FileOutputStream(file)
finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)
out.flush()
out.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun output(fileName: String) : Uri? {
val root = mainctx!!.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
val myDir = File("$root/Files/")
if (!myDir.exists()) {
Log.d("TAG", "$myDir doesn't exist")
myDir.mkdirs()
}
try {
val f = File(myDir, fileName)
if (f.exists()) {
//println("File Exists! Deleting")
f.delete()
}
f.createNewFile()
// file Uri
//println("fileUri: $fileUri")
return Uri.fromFile(f)
// File Saved
} catch (e: FileNotFoundException) {
//println("FileNotFoundException")
e.printStackTrace()
return null
} catch (e: IOException) {
//println("IOException")
e.printStackTrace()
return null
}
}
/**
* Load Texture from Bitmap
**/
private fun loadTexture(bitmap: Bitmap) {
println("loadTexture")
// Bind to the texture in OpenGL
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, bitmapTextureId)
// 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.
println("bitmap: ${bitmap.width} ${bitmap.height}")
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0)
// check errors
checkGlError("texImage2d");
// Recycle the bitmap, since its data has been loaded into OpenGL.
bitmap.recycle()
}
/**
* Initializes GL state. Call this after the EGL surface has been created and made current.
*/
fun surfaceCreated() {
println("surfaceCreated")
if (gles == 2) {
// video shader
mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER)
if (mProgram == 0) {
throw RuntimeException("failed creating program")
}
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition")
checkGlError("glGetAttribLocation aPosition")
if (maPositionHandle == -1) {
throw RuntimeException("Could not get attrib location for aPosition")
}
maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord")
checkGlError("glGetAttribLocation aTextureCoord")
if (maTextureHandle == -1) {
throw RuntimeException("Could not get attrib location for aTextureCoord")
}
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix")
checkGlError("glGetUniformLocation uMVPMatrix")
if (muMVPMatrixHandle == -1) {
throw RuntimeException("Could not get attrib location for uMVPMatrix")
}
muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix")
checkGlError("glGetUniformLocation uSTMatrix")
if (muSTMatrixHandle == -1) {
throw RuntimeException("Could not get attrib location for uSTMatrix")
}
val textures = IntArray(1)
GLES20.glGenTextures(1, textures, 0)
textureId = textures[0]
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId)
checkGlError("glBindTexture mTextureID")
GLES20.glTexParameterf(
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_NEAREST.toFloat()
)
GLES20.glTexParameterf(
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR.toFloat()
)
GLES20.glTexParameteri(
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE
)
GLES20.glTexParameteri(
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE
)
checkGlError("glTexParameter")
// bitmap shader
mBitmapProgram = createProgram(VERTEX_SHADER, FRAGMENT_BITMAP_SHADER)
if (mBitmapProgram == 0) {
throw RuntimeException("failed creating program")
}
mBitmapaPositionHandle = GLES20.glGetAttribLocation(mBitmapProgram, "aPosition")
checkGlError("glGetAttribLocation aPosition")
if (mBitmapaPositionHandle == -1) {
throw RuntimeException("Could not get attrib location for aPosition")
}
mBitmapaTextureHandle = GLES20.glGetAttribLocation(mBitmapProgram, "aTextureCoord")
checkGlError("glGetAttribLocation aTextureCoord")
if (mBitmapaTextureHandle == -1) {
throw RuntimeException("Could not get attrib location for aTextureCoord")
}
mBitmapuMVPMatrixHandle = GLES20.glGetUniformLocation(mBitmapProgram, "uMVPMatrix")
checkGlError("glGetUniformLocation uMVPMatrix")
if (mBitmapuMVPMatrixHandle == -1) {
throw RuntimeException("Could not get attrib location for uMVPMatrix")
}
mBitmapuSTMatrixHandle = GLES20.glGetUniformLocation(mBitmapProgram, "uSTMatrix")
checkGlError("glGetUniformLocation uSTMatrix")
if (mBitmapuSTMatrixHandle == -1) {
throw RuntimeException("Could not get attrib location for uSTMatrix")
}
val bmpTextures = IntArray(1)
GLES20.glGenTextures(1, bmpTextures, 0)
bitmapTextureId = textures[0]
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, bitmapTextureId)
checkGlError("glBindTexture bitmapTextureId")
GLES20.glTexParameterf(
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_NEAREST.toFloat()
)
GLES20.glTexParameterf(
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR.toFloat()
)
GLES20.glTexParameteri(
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE
)
GLES20.glTexParameteri(
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE
)
checkGlError("glTexParameter")
}
}
/**
* Replaces the fragment shader.
*/
fun changeFragmentShader(fragmentShader: String) {
if (gles == 2) {
GLES20.glDeleteProgram(mProgram)
mProgram = createProgram(VERTEX_SHADER, fragmentShader)
if (mProgram == 0) {
throw RuntimeException("failed creating program")
}
}
}
private fun loadShader(shaderType: Int, source: String): Int {
if (gles == 2) {
var shader = GLES20.glCreateShader(shaderType)
checkGlError("glCreateShader type=$shaderType")
GLES20.glShaderSource(shader, source)
GLES20.glCompileShader(shader)
val compiled = IntArray(1)
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0)
if (compiled[0] == 0) {
Log.e(TAG, "Could not compile shader $shaderType:")
Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader))
GLES20.glDeleteShader(shader)
shader = 0
}
return shader
}
}
private fun createProgram(vertexSource: String, fragmentSource: String): Int {
if (gles == 2) {
val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource)
if (vertexShader == 0) {
return 0
}
val pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource)
if (pixelShader == 0) {
return 0
}
var program = GLES20.glCreateProgram()
checkGlError("glCreateProgram")
if (program == 0) {
Log.e(TAG, "Could not create program")
}
GLES20.glAttachShader(program, vertexShader)
checkGlError("glAttachShader")
GLES20.glAttachShader(program, pixelShader)
checkGlError("glAttachShader")
GLES20.glLinkProgram(program)
val linkStatus = IntArray(1)
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0)
if (linkStatus[0] != GLES20.GL_TRUE) {
Log.e(TAG, "Could not link program: ")
Log.e(TAG, GLES20.glGetProgramInfoLog(program))
GLES20.glDeleteProgram(program)
program = 0
}
return program
}
}
fun checkGlError(op: String) {
if (gles == 2) {
var error: Int
while (GLES20.glGetError().also { error = it } != GLES20.GL_NO_ERROR) {
Log.e(TAG, "$op: glError $error")
throw RuntimeException("$op: glError $error")
}
}
}
companion object {
private const val TAG = "TextureRender"
private const val FLOAT_SIZE_BYTES = 4
private const val TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES
private const val TRIANGLE_VERTICES_DATA_POS_OFFSET = 0
private const val TRIANGLE_VERTICES_DATA_UV_OFFSET = 3
private const val VERTEX_SHADER = "uniform mat4 uMVPMatrix;\n" +
"uniform mat4 uSTMatrix;\n" +
"attribute vec4 aPosition;\n" +
"attribute vec4 aTextureCoord;\n" +
"varying vec2 vTextureCoord;\n" +
"void main() {\n" +
" gl_Position = uMVPMatrix * aPosition;\n" +
" vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
"}\n"
private const val FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" + // highp here doesn't seem to matter
"varying vec2 vTextureCoord;\n" +
"uniform samplerExternalOES sTexture;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
"}\n"
private const val FRAGMENT_BITMAP_SHADER = "#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" + // highp here doesn't seem to matter
"varying vec2 vTextureCoord;\n" +
"uniform sampler2D uTexture;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(uTexture, vTextureCoord);\n" +
"}\n"
}
init {
mTriangleVertices = ByteBuffer.allocateDirect(
mTriangleVerticesData.size * FLOAT_SIZE_BYTES
)
.order(ByteOrder.nativeOrder()).asFloatBuffer()
mTriangleVertices.put(mTriangleVerticesData).position(0)
Matrix.setIdentityM(mSTMatrix, 0)
mBitmapTriangleVertices = ByteBuffer.allocateDirect(
mBitmapTriangleVerticesData.size * FLOAT_SIZE_BYTES
)
.order(ByteOrder.nativeOrder()).asFloatBuffer()
mBitmapTriangleVertices.put(mBitmapTriangleVerticesData).position(0)
Matrix.setIdentityM(mBitmapSTMatrix, 0)
}
}

Related

How to refactor code from Vertex Array to Vertex Array Object methods

I'm studying Opengl and the book i've been using is OpenGL(R) ES 3.0 Programming Guide, 2nd Edition. And at chapter 6 they talk about Vertex arrays and they have a example code that uses Vertex Array methods, which is the code below. Later down that chapter they are talking about Vertex Array Object and what I wanted to try is to take this example code and refactor the code into something that uses the Vertex Array Object methods.
Problem is I have no idea how Vertex Array Object works and would be gratful if anyone could push me in the right direction.
The example code is here:
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
import se.hig.dvg306.modul3app.R;
import se.hig.dvg306.modul3app.tools.ResourceHandler;
public class Modul3Renderer implements GLSurfaceView.Renderer
{
//
// Constructor - loads model data from a res file and creates byte buffers for
// vertex data and for normal data
//
public Modul3Renderer (Context context)
{
appContext = context;
Log.e(TAG, "--->>> Creating ModelLoader...");
ModelLoader modelLoader = new ModelLoaderImpl ();
Log.e(TAG, "--->>> ...finished.");
Log.e(TAG, "--->>> Loading model...");
Log.e(TAG, "--->>> Starting with vertices...");
float[] mVerticesData; //= new float[0];
try {
mVerticesData = modelLoader.loadModel (context, R.raw.torus2, 0, 4, 6);
} catch (IOException e) {
throw new RuntimeException (e);
}
Log.e(TAG, "--->>> ...finished.");
// Process vertex data
// 4: because of 4 elements per vertex position
nbrOfVertices = mVerticesData.length / 4;
mVertices = ByteBuffer.allocateDirect(mVerticesData.length * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mVertices.put(mVerticesData).position(0);
Log.e(TAG, "--->>> Starting with normals...");
float[] mNormalData; //= new float[0];
try {
mNormalData = modelLoader.loadModel (context, R.raw.torus2, 4, 4, 6);
} catch (IOException e) {
throw new RuntimeException (e);
}
Log.e(TAG, "--->>> ...finished.");
// Process normal data
// 4: because of 4 elements per vertex position
nbrOfNormals = mNormalData.length / 4;
mNormals = ByteBuffer.allocateDirect(mNormalData.length * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mNormals.put(mNormalData).position(0);
}
///
// Create a shader object, load the shader source, and
// compile the shader.
//
private int createShader(int type, String shaderSrc )
{
int shader;
int[] compiled = new int[1];
// Create the shader object
shader = GLES30.glCreateShader ( type );
if ( shader == 0 )
{
return 0;
}
// Load the shader source
GLES30.glShaderSource ( shader, shaderSrc );
// Compile the shader
GLES30.glCompileShader ( shader );
// Check the compile status
GLES30.glGetShaderiv ( shader, GLES30.GL_COMPILE_STATUS, compiled, 0 );
if ( compiled[0] == 0 )
{
Log.e ( TAG, GLES30.glGetShaderInfoLog ( shader ) );
GLES30.glDeleteShader ( shader );
return 0;
}
return shader;
}
///
// Initialize the shader and program object
//
public void onSurfaceCreated ( GL10 glUnused, EGLConfig config )
{
int vertexShader;
int fragmentShader;
int programObject;
int[] linked = new int[1];
// Load the source code for the vertex shader program from a res file:
try {
vShaderStr = ResourceHandler.readTextData(appContext, R.raw.vertex_shader);
} catch (IOException e) {
Log.e ( TAG, "--->>> Could not load source code for vertex shader.");
throw new RuntimeException (e);
}
Log.e ( TAG, "--->>> Loaded vertex shader: " + vShaderStr);
// Load the source code for the fragment shader program from a res file:
try {
fShaderStr = ResourceHandler.readTextData(appContext, R.raw.fragment_shader);
} catch (IOException e) {
Log.e ( TAG, "--->>> Could not load source code for fragment shader.");
throw new RuntimeException (e);
}
Log.e ( TAG, "--->>> Loaded fragment shader: " + fShaderStr);
// Create the vertex/fragment shaders
vertexShader = createShader( GLES30.GL_VERTEX_SHADER, vShaderStr );
fragmentShader = createShader( GLES30.GL_FRAGMENT_SHADER, fShaderStr );
// Create the program object
programObject = GLES30.glCreateProgram();
if ( programObject == 0 )
{
return;
}
GLES30.glAttachShader ( programObject, vertexShader );
GLES30.glAttachShader ( programObject, fragmentShader );
// Bind vPosition to attribute 0
GLES30.glBindAttribLocation ( programObject, 0, "vPosition" );
// Bind vNormal to attribute 1
GLES30.glBindAttribLocation ( programObject, 1, "vNormal" );
// Link the program
GLES30.glLinkProgram ( programObject );
// Check the link status
GLES30.glGetProgramiv ( programObject, GLES30.GL_LINK_STATUS, linked, 0 );
if ( linked[0] == 0 )
{
Log.e ( TAG, "Error linking program:" );
Log.e ( TAG, GLES30.glGetProgramInfoLog ( programObject ) );
GLES30.glDeleteProgram ( programObject );
return;
}
// Store the program object
mProgramObject = programObject;
GLES30.glClearColor ( 0.15f, 0.15f, 0.15f, 1.0f );
GLES30.glEnable(GLES30.GL_DEPTH_TEST);
}
//
// Draw a torus using the shader pair created in onSurfaceCreated()
//
public void onDrawFrame ( GL10 glUnused )
{
// Initiate the model-view matrix as identity matrix
Matrix.setIdentityM(mViewMatrix, 0);
// Define a translation transformation
Matrix.translateM(mViewMatrix, 0, 0.0f, 0.0f, -60.0f);
// Define a rotation transformation
Matrix.rotateM(mViewMatrix, 0, 90.0f, 1.0f, 0.0f, 0.0f);
// Calculate the model-view and projection transformation as composite transformation
Matrix.multiplyMM (mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
// Clear the color buffer
GLES30.glClear ( GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT );
// Use the program object
GLES30.glUseProgram ( mProgramObject );
// Make MVP matrix accessible in the vertex shader
mMVPMatrixHandle = GLES30.glGetUniformLocation(mProgramObject, "uMVPMatrix");
GLES30.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
// Light position:
vLightPositionHandle = GLES30.glGetUniformLocation(mProgramObject, "vLightPosition");
GLES30.glUniform4fv(vLightPositionHandle, 1, lightPosition, 0);
// Light color:
vLightColorDfHandle = GLES30.glGetUniformLocation(mProgramObject, "vLightColorDf");
GLES30.glUniform4fv(vLightColorDfHandle, 1, lightColorDf, 0);
// Material color:
vMaterialColorDfHandle = GLES30.glGetUniformLocation(mProgramObject, "vMaterialColorDf");
GLES30.glUniform4fv(vMaterialColorDfHandle, 1, materialColorDf, 0);
// Load the vertex data from mVertices
GLES30.glVertexAttribPointer ( 0, 4, GLES30.GL_FLOAT, false, 0, mVertices );
// Assign vertex data to 'in' variable bound to attribute with index 0:
GLES30.glEnableVertexAttribArray ( 0 );
// Load the normal data from mNormals
GLES30.glVertexAttribPointer ( 1, 4, GLES30.GL_FLOAT, false, 0, mNormals );
// Assign normal data to 'in' variable bound to attribute with index 1:
GLES30.glEnableVertexAttribArray ( 1 );
GLES30.glDrawArrays (GLES30.GL_TRIANGLES, 0, nbrOfVertices);
GLES30.glDisableVertexAttribArray ( 1 );
GLES30.glDisableVertexAttribArray ( 0 );
}
//
// Handle surface changes
//
public void onSurfaceChanged ( GL10 glUnused, int width, int height )
{
mWidth = width;
mHeight = height;
GLES30.glViewport(0, 0, width, height);
float ratio = (float) width / height;
// this projection matrix is applied to object coordinates
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1.0f, 1.0f, 0.5f, 1000.0f);
}
// Member variables
private Context appContext;
private int mWidth;
private int mHeight;
private int nbrOfVertices;
private FloatBuffer mVertices;
private int nbrOfNormals;
private FloatBuffer mNormals;
private int mProgramObject;
private int mMVPMatrixHandle;
// Transformation data:
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
// Light position and color (only diffuse term now):
private int vLightPositionHandle;
private final float lightPosition [] = {175.0f, 75.0f, 125.0f, 0.0f};
// Light color (only diffuse term now):
private int vLightColorDfHandle;
private final float lightColorDf [] = {0.98f, 0.98f, 0.98f, 1.0f};
// Material color (only diffuse term now):
private int vMaterialColorDfHandle;
private final float materialColorDf [] = {0.62f, 0.773f, 0.843f, 1.0f};
// To be read when creating the instance:
private String vShaderStr;
private String fShaderStr;
private static String TAG = "Modul3Renderer";
}
I've tried for the past days now to understand how to write a code that uses the Object methods but I cant wrap my head around it and decided to ask. So I hope by asking I can get some understanding how to begin.
When you want to use a Vertex Array Object, you must create and bind the VAO. The vertex specification is stored in the state vector of the currently bound VAO. Therefore, you must do the vertex specification when the VAO is bound. I also suggest to put the attributes in Vertex Buffer Objects:
int vao;
int vboVertices;
int vboNormals;
vao = GLES30.glGenVertexArray();
GLES30.glBindVertexArray(vao);
vboVertices = GLES30.glGenBuffer();
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboVertices);
GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, mVertices.remaining() * 4, mVertices, GLES30.GL_STATIC_DRAW);
GLES30.glVertexAttribPointer(0, 4, GLES30.GL_FLOAT, false, 0, 0);
GLES30.glEnableVertexAttribArray(0);
vboNormals = GLES30.glGenBuffer();
GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboNormals);
GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, mNormals.remaining() * 4, mNormals, GLES30.GL_STATIC_DRAW);
GLES30.glVertexAttribPointer(1, 4, GLES30.GL_FLOAT, false, 0, 0);
GLES30.glEnableVertexAttribArray(1);
Later you can use the VAO to draw the geometry. For this it is enough to bind the VAO:
GLES30.glBindVertexArray(vao);
GLES30.glDrawArrays (GLES30.GL_TRIANGLES, 0, nbrOfVertices);

Renderning simple square in Android OpenGL

Here are my coordinates to draw a square:
static float vertices_textures[] = {
//vertices //positions
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 0.0f
};
Buffer vertices_textures_buffer = FloatBuffer.wrap(vertices_textures);
Then my initialization code is:
egl = (EGL10) EGLContext.getEGL();
eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
if (eglDisplay == EGL10.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay failed");
}
int[] version = new int[2];
if (!egl.eglInitialize(eglDisplay, version)) {
throw new RuntimeException("eglInitialize failed");
}
EGLConfig eglConfig = chooseEglConfig();
eglContext = createContext(egl, eglDisplay, eglConfig);
eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, surfaceTexture, null);
if (eglSurface == null || eglSurface == EGL10.EGL_NO_SURFACE) {
throw new RuntimeException("GL Error: " + GLUtils.getEGLErrorString(egl.eglGetError()));
}
if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
throw new RuntimeException("GL make current error: " + GLUtils.getEGLErrorString(egl.eglGetError()));
}
int vertexShader = loadShader(GL_VERTEX_SHADER, OrwellShaders.Vertex.video);
int fragmentShader = loadShader(GL_FRAGMENT_SHADER, OrwellShaders.Fragment.color);
program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
vertexInLocation = glGetAttribLocation(program, "aPos");
textureInLocation = glGetAttribLocation(program, "aTexCoord");
glGenVertexArrays(1, vertexArrayObject, 0);
glGenBuffers(1, vertexBufferObject, 0);
glGenBuffers(3, pixelBufferObjects, 0);
glBindVertexArray(vertexArrayObject[0]);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject[0]);
glBufferData(GL_ARRAY_BUFFER, vertices_textures.length, vertices_textures_buffer, GL_STATIC_DRAW);
glVertexAttribPointer(vertexInLocation, 3, GL_FLOAT, false, 5*4 , 0);
glEnableVertexAttribArray(vertexInLocation);
glVertexAttribPointer(textureInLocation, 2, GL_FLOAT, false, 5*4 , 3*4);
glEnableVertexAttribArray(textureInLocation);
This is how I render:
glUseProgram(program);
glClearColor(0.0f, 0.0f, 0.4f, 1f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindVertexArray(vertexArrayObject[0]);
glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0, GLES11Ext.GL_RGBA8_OES, 100, 100,
0, GLES11Ext.GL_RGBA8_OES, GL_UNSIGNED_BYTE, buffer);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
Here is the EGL context creation
private EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE};
return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attribList);
}
private EGLConfig chooseEglConfig() {
int[] configsCount = new int[1];
EGLConfig[] configs = new EGLConfig[1];
int[] configSpec = getConfig();
if (!egl.eglChooseConfig(eglDisplay, configSpec, configs, 1, configsCount)) {
throw new IllegalArgumentException("Failed to choose config: " + GLUtils.getEGLErrorString(egl.eglGetError()));
} else if (configsCount[0] > 0) {
return configs[0];
}
return null;
}
Here are my shaders
public static final String vertex =
"#version 320 es\n " +
"layout (location = 0) in vec3 aPos;\n " +
"layout (location = 1) in vec2 aTexCoord;\n " +
"\n " +
"out vec2 TexCoord;\n " +
"\n " +
"void main()\n " +
"{\n " +
" gl_Position = vec4(aPos, 1.0);\n " +
" TexCoord = vec2(aTexCoord.x, aTexCoord.y);\n " +
"}\n";
and
public static final String color =
"#version 320 es\n " +
"precision mediump float;\n " +
"in vec2 TexCoord;\n " +
"out vec4 FragColor;\n " +
"\n " +
"void main()\n " +
"{\n " +
" FragColor = vec4(1.0f, 0, 0, 1.0f);\n " +
"}\n";
As you can see by the shader I should see a red square, but I see a lot of random pixels binking on the screen. If I use glClearColor, I can see the color I put there, but nothing more happens.
Is there something wrong with my vertex arrays?
UPDATE:
I'm using
import static android.opengl.GLES32.*;
The 4th respectively 5th parameter of glVertexAttribPointer have to be the stride between consecutive attributes respectively the offset to the first component in bytes rather than the number of elements.
Size the size of a float element is 4, it has to be:
glVertexAttribPointer(vextexInLocation, 3, GL_FLOAT, false, 5*4, 0);
glEnableVertexAttribArray(vextexInLocation);
glVertexAttribPointer(textureInLocation, 2, GL_FLOAT, false, 5*4, 3*4);
glEnableVertexAttribArray(textureInLocation);

Android GL How to draw different textures

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.

Open Gl ES 2.0 Triangles always black

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.

random lines when drawing gles20 circle in Android

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.

Categories