Related
If i change the background of GLES20.glClearColor(1.0f, 1.0f, 0.0f, 1.0f) and then try to draw a texture, colors of this last changes unexpectedly. This is the png file:
The result of the application when i try simply to display it is this one:
Im using this code:
public class GLRenderer implements GLSurfaceView.Renderer {
private static final String TAG = "MyGLRenderer";
private float[] vertices = {
-1f, -1f,
1f, -1f,
-1f, 1f,
1f, 1f
};
private float[] textureVertices = {
0f, 1f,
1f, 1f,
0f, 0f,
1f, 0f
};
private final String vertexShaderCode =
"attribute vec4 aPosition;" +
"attribute vec2 aTexPosition;" +
"varying vec2 vTexPosition;" +
"void main() {" +
" gl_Position = aPosition;" +
" vTexPosition = aTexPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform sampler2D uTexture;" +
"varying vec2 vTexPosition;" +
"void main() {\n" +
"vec4 color = texture2D(uTexture, vTexPosition);\n"+
//"if(color.r == 0.0 && color.g == 0.0 && color.b == 0.0)\n"+
// "color = vec4(1.0,0.5,0.5,1.0);"+
// "discard;"+
" gl_FragColor = color;" +
"}";
private FloatBuffer verticesBuffer;
private FloatBuffer textureBuffer;
private int vertexShader;
private int fragmentShader;
private int program;
private Bitmap bmp;
private int textures[] = new int[2];
// mMVPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
public GLRenderer() {
bmp=Bitmap.createBitmap(513,912, Bitmap.Config.ARGB_8888);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
checkGlError("glClearColor");
setup();
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
#Override
public void onDrawFrame(GL10 gl) {
Log.d("Drawing_Frame","Working");
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, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
// Draw Bitmap
drawBinaryImage(bmp,textures[0]);
Matrix.setRotateM(mRotationMatrix, 0, 0, 0, 0, 1.0f);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
}
private void setup(){
GLES20.glGenTextures(2, textures, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);
//GLES20.glBindTexture(GL);
initializeBuffers();
initializeProgram();
}
private void initializeBuffers() {
ByteBuffer buff = ByteBuffer.allocateDirect(vertices.length * 4);
buff.order(ByteOrder.nativeOrder());
verticesBuffer = buff.asFloatBuffer();
verticesBuffer.put(vertices);
verticesBuffer.position(0);
buff = ByteBuffer.allocateDirect(textureVertices.length * 4);
buff.order(ByteOrder.nativeOrder());
textureBuffer = buff.asFloatBuffer();
textureBuffer.put(textureVertices);
textureBuffer.position(0);
}
private void initializeProgram() {
vertexShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
GLES20.glGetShaderInfoLog(vertexShader);
checkGlError("glCreateShader");
GLES20.glShaderSource(vertexShader, vertexShaderCode);
GLES20.glCompileShader(vertexShader);
fragmentShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
GLES20.glShaderSource(fragmentShader, fragmentShaderCode);
GLES20.glCompileShader(fragmentShader);
program = GLES20.glCreateProgram();
GLES20.glAttachShader(program, vertexShader);
GLES20.glAttachShader(program, fragmentShader);
GLES20.glLinkProgram(program);
checkGlError("glLinkProgram");
}
public void updateTexture(Bitmap bmp){
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);
}
private void drawBinaryImage(Bitmap bmp,int texture){
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glUseProgram(program);
//Changes Here original Line GLES20.glDisable(GLES20.GL_BLEND);
GLES20.glDisable(GLES20.GL_CULL_FACE);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE);
int positionHandle = GLES20.glGetAttribLocation(program, "aPosition");
int textureHandle = GLES20.glGetUniformLocation(program, "uTexture");
int texturePositionHandle = GLES20.glGetAttribLocation(program, "aTexPosition");
//Log.d("GL_SETUP",positionHandle+" , "+textureHandle);
GLES20.glVertexAttribPointer(texturePositionHandle, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);
GLES20.glEnableVertexAttribArray(texturePositionHandle);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
Log.d("FILTER_APPLY","Applying");
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
GLES20.glUniform1i(textureHandle, 0);
GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, 0, verticesBuffer);
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
checkGlError("glDrawArrays");
}
public void setBitmap(Bitmap bitmap){
updateTexture(bitmap);
this.bmp = bitmap;
}
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);
}
}
}
The framebuffer is cleared by GLES20.glClearColor(1.0f, 1.0f, 0.0f, 1.0f);.
RGBA(1, 1, 0, 1) is yellow. This causes that before rendering the texture, the entire framebuffer is filled in yellow.
The texture contains a blue color RGBA(0, 0, 1, 1) and a black color RGBA(0, 0, 0, 1).
When the quad with the texture is drawn, then blending is enabled with the following function:
(see Blending and glBlendFunc)
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE);
At blending the current color of the framebuffer is mixed by the actually drawn color. With the above setup this done by the following function:
destinationColor = sourceColor * 1 + destinationColor * 1
In the regions where the texture is blue, the final color becomes white:
(1, 1, 0) * 1 + (0, 0, 1) * 1 = (1, 1, 1)
In the regions where the texture is black, the color in the framebuffer stays yellow:
(1, 1, 0) * 1 + (0, 0, 0) * 1 = (1, 1, 0)
I need to draw a shape in OpenGL from a binary image. For example, I have this image binary
and I need to draw this shape in OpenGL. The fact is that I need to do this "dynamically" so I will have for example a different binary image every second and I need to draw the shapes from every one of those images. Shapes will not be the only triangles like in the example image. I will use this in Android Studio.
Convert this image to Bitmap object and draw it on the GLSurfaceView. you can check condition in shader code like this.
vec4 color = texture2D(uTexture, vTexPosition);
if(color.r == 0.0 && color.g == 0.0 && color.b == 0.0)
color = vec4(1.0,0.5,0.5,1.0);
gl_FragColor = color;
This is the full GLRenderer
public class GLRenderer implements GLSurfaceView.Renderer {
private static final String TAG = "MyGLRenderer";
private float[] vertices = {
-1f, -1f,
1f, -1f,
-1f, 1f,
1f, 1f
};
private float[] textureVertices = {
0f, 1f,
1f, 1f,
0f, 0f,
1f, 0f
};
private final String vertexShaderCode =
"attribute vec4 aPosition;" +
"attribute vec2 aTexPosition;" +
"varying vec2 vTexPosition;" +
"void main() {" +
" gl_Position = aPosition;" +
" vTexPosition = aTexPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform sampler2D uTexture;" +
"varying vec2 vTexPosition;" +
"void main() {\n" +
"vec4 color = texture2D(uTexture, vTexPosition);\n"+
"if(color.r == 0.0 && color.g == 0.0 && color.b == 0.0)\n"+
"color = vec4(1.0,0.5,0.5,1.0);"+
" gl_FragColor = color;" +
"}";
private FloatBuffer verticesBuffer;
private FloatBuffer textureBuffer;
private int vertexShader;
private int fragmentShader;
private int program;
private Bitmap bmp;
private int textures[] = new int[2];
// mMVPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
public GLRenderer() {
bmp=Bitmap.createBitmap(513,912, Bitmap.Config.ARGB_8888);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
checkGlError("glClearColor");
setup();
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
#Override
public void onDrawFrame(GL10 gl) {
Log.d("Drawing_Frame","Working");
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, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
// Draw Bitmap
drawBinaryImage(bmp,textures[0]);
Matrix.setRotateM(mRotationMatrix, 0, 0, 0, 0, 1.0f);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
}
private void setup(){
GLES20.glGenTextures(2, textures, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);
//GLES20.glBindTexture(GL);
initializeBuffers();
initializeProgram();
}
private void initializeBuffers() {
ByteBuffer buff = ByteBuffer.allocateDirect(vertices.length * 4);
buff.order(ByteOrder.nativeOrder());
verticesBuffer = buff.asFloatBuffer();
verticesBuffer.put(vertices);
verticesBuffer.position(0);
buff = ByteBuffer.allocateDirect(textureVertices.length * 4);
buff.order(ByteOrder.nativeOrder());
textureBuffer = buff.asFloatBuffer();
textureBuffer.put(textureVertices);
textureBuffer.position(0);
}
private void initializeProgram() {
vertexShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
GLES20.glGetShaderInfoLog(vertexShader);
checkGlError("glCreateShader");
GLES20.glShaderSource(vertexShader, vertexShaderCode);
GLES20.glCompileShader(vertexShader);
fragmentShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
GLES20.glShaderSource(fragmentShader, fragmentShaderCode);
GLES20.glCompileShader(fragmentShader);
program = GLES20.glCreateProgram();
GLES20.glAttachShader(program, vertexShader);
GLES20.glAttachShader(program, fragmentShader);
GLES20.glLinkProgram(program);
checkGlError("glLinkProgram");
}
public void updateTexture(Bitmap bmp){
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);
}
private void drawBinaryImage(Bitmap bmp,int texture){
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glUseProgram(program);
//Changes Here original Line GLES20.glDisable(GLES20.GL_BLEND);
GLES20.glDisable(GLES20.GL_CULL_FACE);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE);
int positionHandle = GLES20.glGetAttribLocation(program, "aPosition");
int textureHandle = GLES20.glGetUniformLocation(program, "uTexture");
int texturePositionHandle = GLES20.glGetAttribLocation(program, "aTexPosition");
//Log.d("GL_SETUP",positionHandle+" , "+textureHandle);
GLES20.glVertexAttribPointer(texturePositionHandle, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);
GLES20.glEnableVertexAttribArray(texturePositionHandle);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
Log.d("FILTER_APPLY","Applying");
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
GLES20.glUniform1i(textureHandle, 0);
GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, 0, verticesBuffer);
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
checkGlError("glDrawArrays");
}
public void setBitmap(Bitmap bitmap){
updateTexture(bitmap);
this.bmp = bitmap;
}
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);
}
}
}
Use this renderer with GlSurfaceView
Here is main Activity class
public class MainActivity extends AppCompatActivity {
FrameLayout glView;
private GLRenderer renderer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
glView = findViewById(R.id.glview);
setupGL();
}
private void setupGL(){
renderer = new GLRenderer();
//MyGlSurfaceView glSurfaceView = new MyGlSurfaceView(this,renderer);
GLSurfaceView glSurfaceView = new GLSurfaceView(this);
glSurfaceView.setEGLContextClientVersion(2);
glSurfaceView.setRenderer(renderer);
glView.addView(glSurfaceView);
produceFrame();
}
private void produceFrame(){
Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.drawable.sample);
renderer.setBitmap(bmp);
}
You can check this for complete project.
I am testing to draw a textured sprite with opengl es 2.0. on android.
Demo works fine when I set ortho projection from - scr_w/2 to +scr_w/2 and - scr_h/ to +scr_h/2 with origin in middle of screen.
However if I translate to what I expect : 480 width (scr_w) and 720 (scr_h) height coordinate system + origin on top left corner of screen.
then Texture rendering goes wrong.
Matrix.orthoM(mProjectionMatrix, 0, 0, scr_w,0, scr_h, -1, 1); //then texture rendering doesn't work ?
I have tried to set Matrix.setLookAtM differently to point from (240,360,5) to (240,360,0)
//Matrix.setLookAtM(mViewMatrix, 0, scr_w/2, scr_h/2, 5, scr_w/2, scr_h/2, 0f, 0f, 1.0f, 0.0f); // then texture rendering doesn't work ?
The Renderer class:
public class SimpleRenderer implements GLSurfaceView.Renderer{
private final Context ctx;
static int scr_w = 480;
static int scr_h = 720;
private final float[] mViewMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mMVPMatrix = new float[16];
private Sprite spt1;
SimpleRenderer(final Context context)
{
this.ctx = context;
}
public void onSurfaceCreated(GL10 unused, EGLConfig config)
{
//Set the background color
GLES20.glClearColor(0.0f, 0.0f, 2.0f, 1.0f);
//Disable depth test
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
// Set alpha blend on
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
spt1 = new Sprite(ctx);
}
public void onSurfaceChanged(GL10 unused, int width, int height)
{
GLES20.glViewport(0, 0, width, height);
Matrix.orthoM(mProjectionMatrix, 0, -scr_w/2, scr_w/2,-scr_h/2, scr_h/2, -1, 1);
// Matrix.orthoM(mProjectionMatrix, 0, 0, scr_w,0, scr_h, -1, 1); // DOESNT WORK ?
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 5, 0, 0, 0f, 0f, 1.0f, 0.0f);
//Matrix.setLookAtM(mViewMatrix, 0, scr_w/2, scr_h/2, 5, scr_w/2, scr_h/2, 0f, 0f, 1.0f, 0.0f); // DOESN'T WORK
// Mix the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
}
public void onDrawFrame(GL10 unused)
{
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
spt1.Draw(mMVPMatrix);
}}
The Sprite Class maybe mess with shader or fragment in 2D coordinates?
public class Sprite
{
//Reference to Activity Context
private final Context mActivityContext;
//Added for Textures
private final FloatBuffer mCubeTextureCoordinates;
private int mTextureUniformHandle;
private int mTextureCoordinateHandle;
private final int mTextureCoordinateDataSize = 2;
private int mTextureDataHandle;
private final String vertexShaderCode =
"attribute vec2 a_TexCoordinate; \n" +
"varying vec2 v_TexCoordinate; \n" +
"uniform mat4 uMVPMatrix; \n" +
"attribute vec4 vPosition; \n" +
"void main() { \n" +
" gl_Position = vPosition * uMVPMatrix; \n" +
" v_TexCoordinate = a_TexCoordinate; \n" +
"} \n";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"uniform sampler2D u_Texture;" +
"varying vec2 v_TexCoordinate;" +
"void main() {" +
"gl_FragColor = (vColor * texture2D(u_Texture, v_TexCoordinate));" +
"}";
private final int shaderProgram;
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 2;
static float spriteCoords[] = {
-50f, 50f, // top left
-50f, -50f, // bottom left
50f, -50f, // bottom right
50f, 50f //top right
};
private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; //Order to draw vertices
private final int vertexStride = COORDS_PER_VERTEX * 4; //Bytes per vertex
float color[] = { 1f, 1f, 1f, 1.0f };
public Sprite(final Context activityContext)
{
mActivityContext = activityContext;
//Initialize Vertex Byte Buffer
ByteBuffer bb = ByteBuffer.allocateDirect(spriteCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(spriteCoords);
vertexBuffer.position(0);
final float[] TextureCoordinate =
{
1f, 0f,
1f, 1f,
0f, 1f,
0f, 0f
};
mCubeTextureCoordinates = ByteBuffer.allocateDirect(TextureCoordinate.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
mCubeTextureCoordinates.put(TextureCoordinate).position(0);
//Initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(spriteCoords.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
shaderProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(shaderProgram, vertexShader);
GLES20.glAttachShader(shaderProgram, fragmentShader);
//Texture Code
GLES20.glBindAttribLocation(shaderProgram, 0, "a_TexCoordinate");
GLES20.glLinkProgram(shaderProgram);
//Load the texture
mTextureDataHandle = loadTexture(mActivityContext, R.drawable.cathead);
}
public void Draw(float[] mvpMatrix)
{
//Add program to OpenGL ES Environment
GLES20.glUseProgram(shaderProgram);
//Get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(shaderProgram, "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(shaderProgram, "vColor");
//Set the Color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
//Set Texture Handles and bind Texture
mTextureUniformHandle = GLES20.glGetAttribLocation(shaderProgram, "u_Texture");
mTextureCoordinateHandle = GLES20.glGetAttribLocation(shaderProgram, "a_TexCoordinate");
//Set the active texture unit to texture unit 0.
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
//Bind the texture to this unit.
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);
//Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
GLES20.glUniform1i(mTextureUniformHandle, 0);
//Pass in the texture coordinate information
mCubeTextureCoordinates.position(0);
GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize, GLES20.GL_FLOAT, false, 0, mCubeTextureCoordinates);
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
//Get Handle to Shape's Transformation Matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(shaderProgram, "uMVPMatrix");
//Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
GLES20.glEnable(GLES20.GL_BLEND_COLOR);
GLES20.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
GLES20.glDepthMask(false);
//Draw the triangle
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
//Disable Vertex Array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
public static int loadTexture(final Context context, final int resourceId)
{
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
if (textureHandle[0] != 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[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);
// Recycle the bitmap, since its data has been loaded into OpenGL.
bitmap.recycle();
}
if (textureHandle[0] == 0)
{
throw new RuntimeException("Error loading texture.");
}
return textureHandle[0];
}
public static int loadShader(int type, String shaderCode)
{
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
I want to make customized watermark renderer on Android.
I am going to add watermark at bottom right corner above current camera preview screen.
Here is the source code what I made. this code does not work.
Please help me. Thank you.
Function call sequence is :
-setWatermarkTexture
-createBuffers
-configureOpenGL
-draw
public class WatermarkRenderer implements Renderer {
private int[] textureHandles = new int[1];
float vertices[] = {
-1, 0.5f,
-0.5f, -0.5f,
-1, 1,
-0.5f, 1,
};
float texCoords[] = {
0, 0,
1, 0,
0, 1,
1, 1,
};
private Bitmap watermark;
private int[] buffers = new int[2];
private float[] projectionMatrix = new float[16];
private FloatBuffer vertexBuffer;
private FloatBuffer textureBuffer;
#Override
public void configureOpenGL() {
GLES20.glGenTextures(textureHandles.length, textureHandles, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[0]);
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);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glGenBuffers(buffers.length, buffers, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, 8, vertexBuffer, GLES20.GL_STATIC_DRAW );
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[1]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, 8, textureBuffer, GLES20.GL_STATIC_DRAW );
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
}
#Override
public void createBuffers() {
vertexBuffer = FloatBuffer.allocate(8);
vertexBuffer.put(vertices);
vertexBuffer.position(0);
textureBuffer = FloatBuffer.allocate(8);
textureBuffer.put(texCoords);
textureBuffer.position(0);
}
#Override
public void draw() {
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glEnable(GLES20.GL_BLEND);
//GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
//GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA );
if (watermark != null) {
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, watermark, 0);
watermark.recycle();
watermark = null;
}
/*
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[0]);
GLES20.glEnableVertexAttribArray(AttributeLocations.POSITION);
GLES20.glVertexAttribPointer(AttributeLocations.POSITION, 2, GLES20.GL_FLOAT, false, 0, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[1]);
GLES20.glEnableVertexAttribArray(AttributeLocations.TEXTURE_COORDINATES );
GLES20.glVertexAttribPointer(AttributeLocations.TEXTURE_COORDINATES, 2, GLES20.GL_FLOAT, false, 0, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4 );
GLES20.glDisableVertexAttribArray(AttributeLocations.POSITION);
GLES20.glDisableVertexAttribArray(AttributeLocations.TEXTURE_COORDINATES);
*/
GLES20.glDisable(GLES20.GL_BLEND);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
}
#Override
public void setProjectionMatrix(float[] projectionMatrix) {
this.projectionMatrix = projectionMatrix;
}
public void setWatermarkTexture(Bitmap bitmap) {
watermark = bitmap;
}
}
I solved my issue.
Here is the source code.
public class WatermarkRenderer implements Renderer {
private final float[] TEX_VERTICES = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
private final float[] POS_VERTICES = {
0.2f, -1.0f,
1.0f, -1.0f,
0.2f, -0.9f,
1.0f, -0.9f
};
private static final String VERTEX_SHADER =
"attribute vec4 a_position;\n" +
"attribute vec2 a_texcoord;\n" +
"varying vec2 v_texcoord;\n" +
"void main() {\n" +
" gl_Position = a_position;\n" +
" v_texcoord = a_texcoord;\n" +
"}\n";
private static final String FRAGMENT_SHADER =
"precision mediump float;\n" +
"uniform sampler2D tex_sampler;\n" +
"varying vec2 v_texcoord;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(tex_sampler, v_texcoord);\n" +
"}\n";
private final int FLOAT_SIZE_BYTES = 4;
private Bitmap watermark;
private int shaderProgram;
private int texSamplerHandle;
private int texCoordHandle;
private int posCoordHandle;
private FloatBuffer texVertices;
private FloatBuffer posVertices;
private int[] textureHandles = new int[1];
#Override
public void configureOpenGL() {
shaderProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(shaderProgram, OpenGLUtils.loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER));
GLES20.glAttachShader(shaderProgram, OpenGLUtils.loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER));
GLES20.glLinkProgram(shaderProgram);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(shaderProgram, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
String info = GLES20.glGetProgramInfoLog(shaderProgram);
GLES20.glDeleteProgram(shaderProgram);
shaderProgram = 0;
throw new RuntimeException("Could not link program: " + info);
}
texSamplerHandle = GLES20.glGetUniformLocation(shaderProgram, "tex_sampler");
texCoordHandle = GLES20.glGetAttribLocation(shaderProgram, "a_texcoord");
posCoordHandle = GLES20.glGetAttribLocation(shaderProgram, "a_position");
}
#Override
public void createBuffers() {
ByteBuffer byteBuffer;
byteBuffer = ByteBuffer.allocateDirect(TEX_VERTICES.length * FLOAT_SIZE_BYTES);
byteBuffer.order(ByteOrder.nativeOrder());
texVertices = byteBuffer.asFloatBuffer();
texVertices.put(TEX_VERTICES);
texVertices.position(0);
byteBuffer = ByteBuffer.allocateDirect(POS_VERTICES.length * FLOAT_SIZE_BYTES);
byteBuffer.order(ByteOrder.nativeOrder());
posVertices = byteBuffer.asFloatBuffer();
posVertices.put(POS_VERTICES);
posVertices.position(0);
}
#Override
public void draw() {
// Select the program.
GLES20.glUseProgram(shaderProgram);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA );
// Set the vertex attributes
GLES20.glVertexAttribPointer(texCoordHandle, 2, GLES20.GL_FLOAT, false, 0, texVertices);
GLES20.glEnableVertexAttribArray(texCoordHandle);
GLES20.glVertexAttribPointer(posCoordHandle, 2, GLES20.GL_FLOAT, false, 0, posVertices);
GLES20.glEnableVertexAttribArray(posCoordHandle);
// Set the input texture
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[0]);
GLES20.glUniform1i(texSamplerHandle, 0);
GLES20.glGenTextures(textureHandles.length, textureHandles, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[0]);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, watermark, 0);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glDisableVertexAttribArray(texCoordHandle);
GLES20.glDisableVertexAttribArray(posCoordHandle);
GLES20.glDisable(GLES20.GL_BLEND);
GLES20.glUseProgram(0);
}
#Override
public void setProjectionMatrix(float[] projectionMatrix) {
}
public void setWatermarkTexture(Bitmap bitmap) {
watermark = bitmap;
}
}
I've a problem with convertion of camera preview in Android from YUV format to RGB. The purpose of conversion is to apply some effects. I try to convert by fragment shader because convertion by native code is slow (about 14fps). The reference which I've used is http://jyrom.tistory.com/m/post/view/id/187. I try to port this code to Android platform, but the result is black-green rectangles. But, I can watch some form through the output which I get. Could you please try to help me to resolve this issue. I believe this is popular problem: apply effects to camera preview. I also give a link to my project for testing: https://dl.dropbox.com/u/12829395/application/FilterGL/FilterGL.zip.
Thank you.
UPDATED:
This is my onPreviewFrame method:
public void onPreviewFrame(byte[] data, Camera camera) {
yBuffer.put(data);
yBuffer.position(0);
System.arraycopy(data, U_INDEX, uData, 0, LENGTH_4 * 2);
uBuffer.put(uData);
uBuffer.position(0);
System.arraycopy(data, V_INDEX, vData, 0, LENGTH_4);
vBuffer.put(vData);
vBuffer.position(0);
}
This is how I bind byte arrays to OpenGL texture in onDrawFrame method:
GLES20.glUniform1i(yTexture, 1);
GLES20.glTexImage2D( GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
320, 240, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, yBuffer);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glUniform1i(uTexture, 2);
GLES20.glTexImage2D( GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
160, 120, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, uBuffer);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glUniform1i(vTexture, 3);
GLES20.glTexImage2D( GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
160, 120, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, vBuffer);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
And this is my fragment shader code:
#ifdef GL_ES
precision highp float;
#endif
varying vec2 v_texCoord;
uniform sampler2D y_texture;
uniform sampler2D u_texture;
uniform sampler2D v_texture;
void main()
{
float nx,ny,r,g,b,y,u,v;
nx=v_texCoord.x;
ny=v_texCoord.y;
y=texture2D(y_texture,v_texCoord).r;
u=texture2D(u_texture,v_texCoord).r;
v=texture2D(v_texture,v_texCoord).r;
y=1.1643*(y-0.0625);
u=u-0.5;
v=v-0.5;
r=y+1.5958*v;
g=y-0.39173*u-0.81290*v;
b=y+2.017*u;
gl_FragColor = vec4(r,g,b,1.0);
}
I don't know if you solved your problem.
I used your code and I solved in this mode.
public class MyRenderer implements Renderer{
public static final int recWidth = Costanti.recWidth;
public static final int recHeight = Costanti.recHeight;
private static final int U_INDEX = recWidth*recHeight;
private static final int V_INDEX = recWidth*recHeight*5/4;
private static final int LENGTH = recWidth*recHeight;
private static final int LENGTH_4 = recWidth*recHeight/4;
private int previewFrameWidth = 256;
private int previewFrameHeight = 256;
private int[] yTextureNames;
private int[] uTextureNames;
private int[] vTextureNames;
private MainActivity activity;
private FloatBuffer mVertices;
private ShortBuffer mIndices;
private int mProgramObject;
private int mPositionLoc;
private int mTexCoordLoc;
private int yTexture;
private int uTexture;
private int vTexture;
private final float[] mVerticesData = { -1.f, 1.f, 0.0f, // Position 0
0.0f, 0.0f, // TexCoord 0
-1.f, -1.f, 0.0f, // Position 1
0.0f, 1.0f, // TexCoord 1
1.f, -1.f, 0.0f, // Position 2
1.0f, 1.0f, // TexCoord 2
1.f, 1.f, 0.0f, // Position 3
1.0f, 0.0f // TexCoord 3
};
private final short[] mIndicesData = { 0, 1, 2, 0, 2, 3 };
private ByteBuffer yBuffer;
private ByteBuffer uBuffer;
private ByteBuffer vBuffer;
private IntBuffer frameBuffer;
private IntBuffer renderBuffer;
private IntBuffer parameterBufferWidth;
private IntBuffer parameterBufferHeigth;
byte[] ydata = new byte[LENGTH];
byte[] uData = new byte[LENGTH_4];
byte[] vData = new byte[LENGTH_4];
public MyRenderer(MainActivity activity) {
this.activity = activity;
mVertices = ByteBuffer.allocateDirect(mVerticesData.length * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mVertices.put(mVerticesData).position(0);
mIndices = ByteBuffer.allocateDirect(mIndicesData.length * 2)
.order(ByteOrder.nativeOrder()).asShortBuffer();
mIndices.put(mIndicesData).position(0);
yBuffer = MyGraphUtils.makeByteBuffer(LENGTH);
uBuffer = MyGraphUtils.makeByteBuffer(LENGTH_4/* * 2*/);
vBuffer = MyGraphUtils.makeByteBuffer(LENGTH_4);
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glActiveTexture(GLES20.GL_ACTIVE_TEXTURE);
GLES20.glViewport(0, 0, width, height);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
Log.d("debug", "on surface created");
// Define a simple shader program for our point.
final String vShaderStr = readTextFileFromRawResource(activity, R.raw.v_simple);
final String fShaderStr = readTextFileFromRawResource(activity, R.raw.f_convert);
frameBuffer = IntBuffer.allocate(1);
renderBuffer= IntBuffer.allocate(1);
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
GLES20.glGenFramebuffers(1, frameBuffer);
GLES20.glGenRenderbuffers(1, renderBuffer);
GLES20.glActiveTexture(GLES20.GL_ACTIVE_TEXTURE);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer.get(0));
GLES20.glClear(0);
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderBuffer.get(0));
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16,
320, 240);
parameterBufferHeigth = IntBuffer.allocate(1);
parameterBufferWidth = IntBuffer.allocate(1);
GLES20.glGetRenderbufferParameteriv(GLES20.GL_RENDERBUFFER, GLES20.GL_RENDERBUFFER_WIDTH, parameterBufferWidth);
GLES20.glGetRenderbufferParameteriv(GLES20.GL_RENDERBUFFER, GLES20.GL_RENDERBUFFER_HEIGHT, parameterBufferHeigth);
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_RENDERBUFFER, renderBuffer.get(0));
if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER)!=GLES20.GL_FRAMEBUFFER_COMPLETE){
Log.d("debug", "gl frame buffer status != frame buffer complete");
}
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glClear(0);
mProgramObject = loadProgram(vShaderStr, fShaderStr);
// Get the attribute locations
mPositionLoc = GLES20.glGetAttribLocation(mProgramObject, "a_position");
mTexCoordLoc = GLES20.glGetAttribLocation(mProgramObject, "a_texCoord");
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
yTexture = GLES20.glGetUniformLocation(mProgramObject, "y_texture");
yTextureNames = new int[1];
GLES20.glGenTextures(1, yTextureNames, 0);
int yTextureName = yTextureNames[0];
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
uTexture = GLES20.glGetUniformLocation(mProgramObject, "u_texture");
uTextureNames = new int[1];
GLES20.glGenTextures(1, uTextureNames, 0);
int uTextureName = uTextureNames[0];
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
vTexture = GLES20.glGetUniformLocation(mProgramObject, "v_texture");
vTextureNames = new int[1];
GLES20.glGenTextures(1, vTextureNames, 0);
int vTextureName = vTextureNames[0];
GLES20.glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
}
#Override
public final void onDrawFrame(GL10 gl) {
Log.d("debug", "on Draw frame");
// Clear the color buffer
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// Use the program object
GLES20.glUseProgram(mProgramObject);
// Load the vertex position
mVertices.position(0);
GLES20.glVertexAttribPointer(mPositionLoc, 3, GLES20.GL_FLOAT, false, 5*4, mVertices);
// Load the texture coordinate
mVertices.position(3);
GLES20.glVertexAttribPointer(mTexCoordLoc, 2, GLES20.GL_FLOAT, false, 5*4, mVertices);
GLES20.glEnableVertexAttribArray(mPositionLoc);
GLES20.glEnableVertexAttribArray(mTexCoordLoc);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yTextureNames[0]);
GLES20.glTexImage2D( GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
320, 240, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, yBuffer);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yTextureNames[0]);
GLES20.glUniform1i(yTexture, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, uTextureNames[0]);
GLES20.glTexImage2D( GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
160, 120, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, uBuffer);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glActiveTexture(GLES20.GL_TEXTURE1+2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, uTextureNames[0]);
GLES20.glUniform1i(uTexture, 2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, vTextureNames[0]);
GLES20.glTexImage2D( GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
160, 120, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, vBuffer);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glActiveTexture(GLES20.GL_TEXTURE1+1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, vTextureNames[0]);
GLES20.glUniform1i(vTexture, 1);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_SHORT, mIndices);
}
public void setPreviewFrameSize(int realWidth, int realHeight) {
previewFrameHeight = realHeight;
previewFrameWidth = realWidth;
}
public static String readTextFileFromRawResource(final Context context, final int resourceId) {
final InputStream inputStream = context.getResources().openRawResource(resourceId);
final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String nextLine;
final StringBuilder body = new StringBuilder();
try {
while ((nextLine = bufferedReader.readLine()) != null) {
body.append(nextLine);
body.append('\n');
}
} catch (IOException e) {
return null;
}
return body.toString();
}
public static int loadShader(int type, String shaderSrc) {
int shader;
int[] compiled = new int[1];
// Create the shader object
shader = GLES20.glCreateShader(type);
if (shader == 0) {
return 0;
}
// Load the shader source
GLES20.glShaderSource(shader, shaderSrc);
// Compile the shader
GLES20.glCompileShader(shader);
// Check the compile status
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e("ESShader", GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
return 0;
}
return shader;
}
public static int loadProgram(String vertShaderSrc, String fragShaderSrc) {
int vertexShader;
int fragmentShader;
int programObject;
int[] linked = new int[1];
// Load the vertex/fragment shaders
vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertShaderSrc);
if (vertexShader == 0) {
return 0;
}
fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragShaderSrc);
if (fragmentShader == 0) {
GLES20.glDeleteShader(vertexShader);
return 0;
}
// Create the program object
programObject = GLES20.glCreateProgram();
if (programObject == 0) {
return 0;
}
GLES20.glAttachShader(programObject, vertexShader);
GLES20.glAttachShader(programObject, fragmentShader);
// Link the program
GLES20.glLinkProgram(programObject);
// Check the link status
GLES20.glGetProgramiv(programObject, GLES20.GL_LINK_STATUS, linked, 0);
if (linked[0] == 0) {
Log.e("ESShader", "Error linking program:");
Log.e("ESShader", GLES20.glGetProgramInfoLog(programObject));
GLES20.glDeleteProgram(programObject);
return 0;
}
// Free up no longer needed shader resources
GLES20.glDeleteShader(vertexShader);
GLES20.glDeleteShader(fragmentShader);
return programObject;
}
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
System.arraycopy(data, 0, ydata, 0, LENGTH);
yBuffer.put(ydata);
yBuffer.position(0);
System.arraycopy(data, U_INDEX, uData, 0, LENGTH_4);
uBuffer.put(uData);
uBuffer.position(0);
System.arraycopy(data, V_INDEX, vData, 0, LENGTH_4);
vBuffer.put(vData);
vBuffer.position(0);
}
}
Not sure if you have already fixed this problem.My answer
By default Camera output is NV12, but in fragment shader YUV to RGB you are using YV12 -> RGB.
You will have to do
setPreviewFormat(ImageFormat.YV12);, or may be use some other shader
There are 3 textures , make sure you do
GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, muTextureName)
before call to any glTexImage2D. and glTexSubImage2D
You can also use glTexSubImage2D with every frame and glTexImage2D once.
size of U and V is same , atleast for YV12,
System.arraycopy(data, U_INDEX, uData, 0, LENGTH_4 * 2);
should be
System.arraycopy(data, U_INDEX, uData, 0, LENGTH_4);
change the size accordingly in the code.
For the fastest and most optimized way, just use the common GL Extention
//Fragment Shader
#extension GL_OES_EGL_image_external : require
uniform samplerExternalOES u_Texture;
Than in Java
surfaceTexture = new SurfaceTexture(textureIDs[0]);
try {
someCamera.setPreviewTexture(surfaceTexture);
} catch (IOException t) {
Log.e(TAG, "Cannot set preview texture target!");
}
someCamera.startPreview();
private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
In Java GL Thread
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureIDs[0]);
GLES20.glUniform1i(uTextureHandle, 0);
The color conversion is already done for you.
You can do what ever you want right in the Fragment shader.
Hope that saves you some time in your research.
Finally made your project display camera previews.
I found 2 problems:
1. Before binding and changing surface characteristics you have to call GLES20.glActiveTexture(GLES20.surfacenumber);
2. More important and hidden problem is that
GLES20.glTexImage2D() does not work with width and height, which are not power of 2 numbers.
After loading texture with size, for example, 1024X1024, you should call GLES20.glTexSubImage2D()
Good luck!
I applied the solution form How to render Android's YUV-NV21 camera image on the background in libgdx with OpenGLES 2.0 in real-time? to the project shared in the question and got a working project. If you are like me searching for tutorial code that does YUV to RGB conversion by fragment shader, you can simply do the following steps to get a working example.
Download the project https://dl.dropbox.com/u/12829395/application/FilterGL/FilterGL.zip and unzip.
Replace file GLRenderer.java and res/raw/f_convert.glsl entirely by the code shared below.
Open the project in Eclipse, or import the project to Android Studio.
The main issues of the code in the question are:
without GLES20.glActiveTexture(GLES20.GL_TEXTURE1);, yBuffer is not passed to GL.
the YUV data takes YUV-NV21 format, and u_texture and v_texture wasn't passed and handled correctly in the shader. Refer to this post for more information.
Now the corrected code: please replace GLRenderer.java with
package com.filtergl.shader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
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.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView.Renderer;
import android.util.Log;
public class GLRenderer
implements Renderer, PreviewCallback {
private static final int LENGTH = 76800;
private static final int LENGTH_2 = 38400;
private ActivityFilterGL activity;
private FloatBuffer mVertices;
private ShortBuffer mIndices;
private int previewFrameWidth = 256;
private int previewFrameHeight = 256;
private int mProgramObject;
private int mPositionLoc;
private int mTexCoordLoc;
// private int mSamplerLoc;
private int yTexture;
private int uTexture;
private int vTexture;
private final float[] mVerticesData = { -1.f, 1.f, 0.0f, // Position 0
0.0f, 0.0f, // TexCoord 0
-1.f, -1.f, 0.0f, // Position 1
0.0f, 1.0f, // TexCoord 1
1.f, -1.f, 0.0f, // Position 2
1.0f, 1.0f, // TexCoord 2
1.f, 1.f, 0.0f, // Position 3
1.0f, 0.0f // TexCoord 3
};
private final short[] mIndicesData = { 0, 1, 2, 0, 2, 3 };
private ByteBuffer frameData = null;
private ByteBuffer yBuffer;
private ByteBuffer uBuffer;
public GLRenderer(ActivityFilterGL activity) {
this.activity = activity;
mVertices = ByteBuffer.allocateDirect(mVerticesData.length * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mVertices.put(mVerticesData).position(0);
mIndices = ByteBuffer.allocateDirect(mIndicesData.length * 2)
.order(ByteOrder.nativeOrder()).asShortBuffer();
mIndices.put(mIndicesData).position(0);
yBuffer = GraphicsUtil.makeByteBuffer(LENGTH);
uBuffer = GraphicsUtil.makeByteBuffer(LENGTH_2);
}
#Override
public final void onDrawFrame(GL10 gl) {
// Clear the color buffer
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// Use the program object
GLES20.glUseProgram(mProgramObject);
// Load the vertex position
mVertices.position(0);
GLES20.glVertexAttribPointer(mPositionLoc, 3, GLES20.GL_FLOAT, false, 5 * 4, mVertices);
// Load the texture coordinate
mVertices.position(3);
GLES20.glVertexAttribPointer(mTexCoordLoc, 2, GLES20.GL_FLOAT, false, 5 * 4, mVertices);
GLES20.glEnableVertexAttribArray(mPositionLoc);
GLES20.glEnableVertexAttribArray(mTexCoordLoc);
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glUniform1i(yTexture, 1);
GLES20.glTexImage2D( GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
320, 240, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, yBuffer);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
GLES20.glUniform1i(uTexture, 2);
GLES20.glTexImage2D( GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE_ALPHA,
160, 120, 0, GLES20.GL_LUMINANCE_ALPHA, GLES20.GL_UNSIGNED_BYTE, uBuffer);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_SHORT, mIndices);
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Define a simple shader program for our point.
final String vShaderStr = readTextFileFromRawResource(activity, R.raw.v_simple);
final String fShaderStr = readTextFileFromRawResource(activity, R.raw.f_convert);
// Load the shaders and get a linked program object
mProgramObject = loadProgram(vShaderStr, fShaderStr);
// Get the attribute locations
mPositionLoc = GLES20.glGetAttribLocation(mProgramObject, "a_position");
mTexCoordLoc = GLES20.glGetAttribLocation(mProgramObject, "a_texCoord");
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
yTexture = GLES20.glGetUniformLocation(mProgramObject, "y_texture");
int[] yTextureNames = new int[1];
GLES20.glGenTextures(1, yTextureNames, 0);
int yTextureName = yTextureNames[0];
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yTextureName);
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
uTexture = GLES20.glGetUniformLocation(mProgramObject, "u_texture");
int[] uTextureNames = new int[1];
GLES20.glGenTextures(1, uTextureNames, 0);
int uTextureName = uTextureNames[0];
GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, uTextureName);
// Set the background clear color to black.
GLES20.glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
}
public void setPreviewFrameSize(int realWidth, int realHeight) {
previewFrameHeight = realHeight;
previewFrameWidth = realWidth;
// frameData = GraphicsUtil.makeByteBuffer(previewFrameHeight * previewFrameWidth * 3);
}
public static String readTextFileFromRawResource(final Context context, final int resourceId) {
final InputStream inputStream = context.getResources().openRawResource(resourceId);
final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String nextLine;
final StringBuilder body = new StringBuilder();
try {
while ((nextLine = bufferedReader.readLine()) != null) {
body.append(nextLine);
body.append('\n');
}
} catch (IOException e) {
return null;
}
return body.toString();
}
public static int loadShader(int type, String shaderSrc) {
int shader;
int[] compiled = new int[1];
// Create the shader object
shader = GLES20.glCreateShader(type);
if (shader == 0) {
return 0;
}
// Load the shader source
GLES20.glShaderSource(shader, shaderSrc);
// Compile the shader
GLES20.glCompileShader(shader);
// Check the compile status
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e("ESShader", GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
return 0;
}
return shader;
}
public static int loadProgram(String vertShaderSrc, String fragShaderSrc) {
int vertexShader;
int fragmentShader;
int programObject;
int[] linked = new int[1];
// Load the vertex/fragment shaders
vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertShaderSrc);
if (vertexShader == 0) {
return 0;
}
fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragShaderSrc);
if (fragmentShader == 0) {
GLES20.glDeleteShader(vertexShader);
return 0;
}
// Create the program object
programObject = GLES20.glCreateProgram();
if (programObject == 0) {
return 0;
}
GLES20.glAttachShader(programObject, vertexShader);
GLES20.glAttachShader(programObject, fragmentShader);
// Link the program
GLES20.glLinkProgram(programObject);
// Check the link status
GLES20.glGetProgramiv(programObject, GLES20.GL_LINK_STATUS, linked, 0);
if (linked[0] == 0) {
Log.e("ESShader", "Error linking program:");
Log.e("ESShader", GLES20.glGetProgramInfoLog(programObject));
GLES20.glDeleteProgram(programObject);
return 0;
}
// Free up no longer needed shader resources
GLES20.glDeleteShader(vertexShader);
GLES20.glDeleteShader(fragmentShader);
return programObject;
}
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
yBuffer.put(data, 0, LENGTH);
yBuffer.position(0);
uBuffer.put(data, LENGTH, LENGTH/2);
uBuffer.position(0);
}
}
and replace f_convert.glsl with
#ifdef GL_ES
precision highp float;
#endif
varying vec2 v_texCoord;
uniform sampler2D y_texture;
uniform sampler2D u_texture;
void main()
{
float r, g, b, y, u, v;
//We had put the Y values of each pixel to the R,G,B components by
//GL_LUMINANCE, that's why we're pulling it from the R component,
//we could also use G or B
y = texture2D(y_texture, v_texCoord).r;
//We had put the U and V values of each pixel to the A and R,G,B
//components of the texture respectively using GL_LUMINANCE_ALPHA.
//Since U,V bytes are interspread in the texture, this is probably
//the fastest way to use them in the shader
u = texture2D(u_texture, v_texCoord).a - 0.5;
v = texture2D(u_texture, v_texCoord).r - 0.5;
//The numbers are just YUV to RGB conversion constants
r = y + 1.13983*v;
g = y - 0.39465*u - 0.58060*v;
b = y + 2.03211*u;
gl_FragColor = vec4(r,g,b,1.0);
}