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);
Related
I build a simple app that display a 3d cube on android. The problem occurs when I try to add a texture.
My texture is a png image. I realize that if I want to display the image with their original colors, I lose the cube background color.
I made different combinations in order to achieve my goal, but I failed.
"void main() {" +
" gl_FragColor = texture2D(u_Texture, v_TexCoordinate) + aColor;" +
"}"
Result:
Combination of texture an color
void main() {" +
" gl_FragColor = texture2D(u_Texture, v_TexCoordinate);" +
"}"
only texture
What I want is keep the color of the cube and display the image with their original colors.
Any help will be appreciated, Thanks
This is my Cubo class:
public class Cubo {
private Context context;
private final String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"attribute vec4 aColor;" +
"varying vec4 vColor;" +
"attribute vec2 a_TexCoordinate;" +
"varying vec2 v_TexCoordinate;" +
"void main() {" +
" vColor=aColor;" +
" gl_Position = uMVPMatrix * vPosition;" +
" v_TexCoordinate = a_TexCoordinate;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform sampler2D u_Texture;" +
"uniform vec4 aColor;" +
"varying vec2 v_TexCoordinate;" +
"void main() {" +
" gl_FragColor = aColor + texture2D(u_Texture, v_TexCoordinate);" +
"}";
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private FloatBuffer mTextureBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
private int mTextureUniformHandle;
private int mTextureCoordinateHandle;
private final int mTextureCoordinateDataSize = 2;
static final int COORDS_PER_VERTEX = 3;
static float cubeCoords[] = {
-0.5f, 0.5f, 0.5f, // top left
-0.5f, -0.5f, 0.5f, // bottom left
0.5f, -0.5f, 0.5f, // bottom right
0.5f, 0.5f, 0.5f, // top right
-0.5f, 0.5f, -0.5f, // top left back
-0.5f, -0.5f, -0.5f, // bottom left back
0.5f, -0.5f, -0.5f, // bottom right back
0.5f, 0.5f, -0.5f // top right back
};
private final short drawOrder[] = {0, 1, 2, 0, 2, 3,
3, 2, 6, 3, 6, 7,
0, 3, 7, 0, 7, 4,
1, 5, 6, 1, 6, 2,
4, 5, 1, 4, 1, 0,
7, 6, 5, 7, 5, 4};
private final int vertexStride = COORDS_PER_VERTEX * 4;
float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 0.0f};
private float texture[] = {
// Mapping coordinates for the vertices
// Front face
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
0.0f, 1.0f
};
private int[] textures = new int[1];
public Cubo(Context context) {
this.context = context;
ByteBuffer bb = ByteBuffer.allocateDirect(
cubeCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(cubeCoords);
vertexBuffer.position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
ByteBuffer byteBuf = ByteBuffer.allocateDirect(texture.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
mTextureBuffer = byteBuf.asFloatBuffer();
mTextureBuffer.put(texture);
mTextureBuffer.position(0);
int vertexShader = MyGLRenderer.loadShader(
GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(
GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
public void draw(float[] mvpMatrix) {
GLES20.glUseProgram(mProgram);
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(
mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
mColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
//---------------textura-------------------
mTextureUniformHandle = GLES20.glGetUniformLocation(mProgram, "u_Texture");
mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgram, "a_TexCoordinate");
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize, GLES20.GL_FLOAT, false,
0, mTextureBuffer);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
GLES20.glUniform1i(mTextureUniformHandle, 0);
//------------------------------------------
GLES20.glDrawElements(
GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
public void loadTexture() {
GLES20.glGenTextures(1, textures, 0);
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.casilla27, options);
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);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
This is my renderer:
public class MyGLRenderer implements GLSurfaceView.Renderer {
private Cubo cubo;
private Context context;
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_x = new float[16];
private final float[] mRotationMatrix_y = new float[16];
private final float[] mRotationMatrix = new float[16];
private float mXAngle;
private float mYAngle;
public MyGLRenderer(Context context) {
this.context = context;
}
#Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
GLES20.glClearDepthf(1.0f);
GLES20.glEnable(GL10.GL_DEPTH_TEST);
cubo = new Cubo(context);
cubo.loadTexture();
}
#Override
public void onSurfaceChanged(GL10 gl10, int i, int i1) {
GLES20.glViewport(0, 0, i, i1);
float ratio = (float) i / i1;
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
#Override
public void onDrawFrame(GL10 gl10) {
float[] scratch = new float[16];
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -4, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
Matrix.setRotateM(mRotationMatrix_x, 0, mYAngle, 1.0f, 0, 0);
Matrix.setRotateM(mRotationMatrix_y, 0, mXAngle, 0, 1.0f, 0);
Matrix.multiplyMM(mRotationMatrix, 0, mRotationMatrix_y, 0, mRotationMatrix_x, 0);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
cubo.draw(scratch);
}
public static int loadShader(int type, String shaderCode) {
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
public float getmXAngle() {
return mXAngle;
}
public void setmXAngle(float mXAngle) {
this.mXAngle = mXAngle;
}
public float getmYAngle() {
return mYAngle;
}
public void setmYAngle(float mYAngle) {
this.mYAngle = mYAngle;
}
You must mix the attribute color and the texture color depending on the alpha channel of the texture:
void main() {
vec4 textureColor = texture2D(u_Texture, v_TexCoordinate);
gl_FragColor = vec4(mix(aColor.rgb, textureColor.rgb, textureColor.a), 1.0);
}
I am relatively new to OpenGL, so this question might seem a bit trivial. I have this code in my Main.java, which is supposed to output a rectangle with the texture of the image on it:
float vertices[] = {
// positions // colors // texture coords
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left
};
int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
int VBO = glGenBuffers(), VAO = glGenVertexArrays(), EBO = glGenBuffers();
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, false, 8, 0);
glEnableVertexAttribArray(0);
// color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, false, 8, 2);
glEnableVertexAttribArray(1);
// texture coord attribute
glVertexAttribPointer(2, 2, GL_FLOAT, false, 8, 5);
glEnableVertexAttribArray(2);
Texture texture = null;
try {
log.info("Loading texture");
texture = Texture.loadTexture("texture.png");
} catch (IOException e) {
log.severe("Texture loading failed due to IOException: " + e.getMessage());
e.printStackTrace();
}
while (!glfwWindowShouldClose(window))
{
glClear(GL_COLOR_BUFFER_BIT);
if(mainProgram != null) {
mainProgram.use();
}
glBindTexture(GL_TEXTURE_2D, texture.getId());
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//...
}
My shaders are the following:
Vertex Shader:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout(location = 2) in vec2 aTexCoord;
out vec3 vertexColor;
out vec2 texCoord;
void main()
{
gl_Position = vec4(aPos , 1.0);
vertexColor = aColor;
texCoord = aTexCoord;
}
Fragment Shader:
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
// texture sampler
uniform sampler2D texture1;
void main()
{
FragColor = texture(texture1, TexCoord);
}
The Texture class referenced above is the following:
public class Texture {
public static Texture loadTexture(String fileName) throws IOException{
//load png file
PNGDecoder decoder = new PNGDecoder(new java.io.FileInputStream(new File("C:/Users/Using/Absolute/Paths/For/Demonstration/texture.png")));
//create a byte buffer big enough to store RGBA values
ByteBuffer buffer = BufferUtils.createByteBuffer(4 * decoder.getWidth() * decoder.getHeight());
//decode
decoder.decode(buffer, decoder.getWidth() * 4, PNGDecoder.Format.RGBA);
//flip the buffer so its ready to read
buffer.flip();
//create a texture
int id = glGenTextures();
//bind the texture
glBindTexture(GL_TEXTURE_2D, id);
//tell opengl how to unpack bytes
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//set the texture parameters, can be GL_LINEAR or GL_NEAREST
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//upload texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, decoder.getWidth(), decoder.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
// Generate Mip Map
glGenerateMipmap(GL_TEXTURE_2D);
return new Texture(id);
}
private int id;
public Texture(int id){
this.id = id;
}
public int getId(){
return id;
}
}
Note that the path of the texture image is not the problem, as the code works without any Exceptions or Compiler errors. The PNGDecoder i used can be found here.
The stride and offset argument must be set in bytes when specifying the array of generic vertex attribute data by glVertexAttribPointer.
Furthermore, the uv coordinates are the 7th and 8th element in the attribute tuple:
glVertexAttribPointer(0, 3, GL_FLOAT, false, 8, 0);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 8*4, 0);
glVertexAttribPointer(1, 3, GL_FLOAT, false, 8, 2);
glVertexAttribPointer(1, 3, GL_FLOAT, false, 8*4, 3*4);
glVertexAttribPointer(2, 2, GL_FLOAT, false, 8, 5);
glVertexAttribPointer(2, 2, GL_FLOAT, false, 8*4, 6*4);
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)
}
}
I have started learning OpenGL with LWJGL3 and some tutorials. Finally I have found the Anton's one on github and I started with most basic one:
Hello Triangle
My problem is kinda similar to this one: Triangle not showing up
This is my problem. I create window with black background but triangle doesn't appear. I would be grateful for any info about my mistakes. Here's my code:
public class HelloWorld
{
public static void main(String[] args)
{
long window = NULL;
int vao, vbo;
final float[] points =
{
0.0f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, -0.5f, 0.0f
};
final String vertex_shader =
"#version 410\n" +
"in vec3 vp;" +
"void main () {" +
" gl_Position = vec4 (vp, 1.0);" +
"}";
final String fragment_shader =
"#version 410\n" +
"out vec4 frag_colour;" +
"void main () {" +
" frag_colour = vec4 (0.5, 0.0, 0.5, 1.0);" +
"}";
int vs, fs, shader_programme;
if( glfwInit() == GL_FALSE )
{
System.err.println("Can't initialize glfw!");
return;
}
window = glfwCreateWindow (640, 480, "Hello Triangle", NULL, NULL);
if (window == 0) {
glfwTerminate();
return;
}
glfwMakeContextCurrent (window);
GL.createCapabilities();
glEnable (GL_DEPTH_TEST);
glDepthFunc (GL_LESS);
vbo = glGenBuffers();
glBindBuffer (GL_ARRAY_BUFFER, vbo);
glBufferData (GL_ARRAY_BUFFER, FloatBuffer.wrap(points),
GL_STATIC_DRAW);
vao = glGenVertexArrays();
glBindVertexArray (vao);
glEnableVertexAttribArray (0);
glBindBuffer (GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer (0, 3, GL_FLOAT, false, 0, 0);
vs = glCreateShader (GL_VERTEX_SHADER);
glShaderSource (vs, vertex_shader);
glCompileShader (vs);
fs = glCreateShader (GL_FRAGMENT_SHADER);
glShaderSource (fs, fragment_shader);
glCompileShader (fs);
shader_programme = glCreateProgram ();
glAttachShader (shader_programme, fs);
glAttachShader (shader_programme, vs);
glLinkProgram (shader_programme);
while ( glfwWindowShouldClose(window) == GL_FALSE ) {
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram (shader_programme);
glBindVertexArray (vao);
glDrawArrays (GL_TRIANGLES, 0, 3);
glfwPollEvents ();
glfwSwapBuffers (window);
}
glfwTerminate();
return;
}
}
So the solution of my problem was using
FloatBuffer buffer = BufferUtils.createFloatBuffer(points.length);
buffer.put(points);
buffer.rewind();
glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
instead of
glBufferData(GL_ARRAY_BUFFER, FloatBuffer.wrap(points),GL_STATIC_DRAW);
It works even without suggested change to shader (layout = 0). Thank you for all answers. I obtained solution posting the question on the LWJGL forum.
It is glEnable(GL_DEPTH_TEST); you are drawing a 2d shape and the depth test is removing your shape. Get rid of that and it should work.
I've been following the tutorial at http://developer.android.com/resources/tutorials/opengl/opengl-es20.html for OpenGL ES on android. I've gotten to the, "Apply Projection and Camera View" section however I always seem to get a blank screen with no triangle, the previous section worked perfectly fine. I also tried just copy pasting the entire tutorial into my code but got the same result. Changing the line:
gl_Position = uMVPMatrix * vPosition;
to:
gl_Position = vPosition;
puts the application back to the first section (triangle stretches depending on screen orientation). Any idea what the problem is? Here's the code I have so far just in case I missed something:
public class GLTest20Renderer implements Renderer {
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix; \n" +
"attribute vec4 vPosition; \n" +
"void main(){ \n" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = uMVPMatrix * vPosition; \n" +
"} \n";
private final String fragmentShaderCode =
"precision mediump float; \n" +
"void main(){ \n" +
" gl_FragColor = vec4 (0.63671875, 0.76953125, 0.22265625, 1.0); \n" +
"} \n";
private FloatBuffer triangleVB;
private int mProgram;
private int maPositionHandle;
private int muMVPMatrixHandle;
private float[] mMVPMatrix = new float[16];
private float[] mMMatrix = new float[16];
private float[] mVMatrix = new float[16];
private float[] mProjMatrix = new float[16];
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
initShapes();
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // creates OpenGL program executables
// get handle to the vertex shader's vPosition member
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
}
public void onDrawFrame(GL10 unused) {
GLES20.glClear( GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT );
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// Prepare the triangle data
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 12, triangleVB);
GLES20.glEnableVertexAttribArray(maPositionHandle);
// Apply a ModelView Projection transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
}
private void initShapes() {
float triangleCoords[] = {
// X, Y, Z
-0.5f, -0.25f, 0,
0.5f, -0.25f, 0,
0.0f, 0.559016994f, 0
};
// initialize vertex Buffer for triangle
ByteBuffer vbb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
triangleCoords.length * 4);
vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order
triangleVB = vbb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer
triangleVB.put(triangleCoords); // add the coordinates to the FloatBuffer
triangleVB.position(0); // set the buffer to read the first coordinate
}
private int loadShader(int type, String shaderCode) {
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
I'm running all this on a Samsung Galaxy S2.
Fixed, just changed the near point in the lookat to be under 3:
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 2, 7);