I'm testing out OpenTok for use in a VR app we are building. Using the opentok android SDK samples as a staring point I have modified the CustomVideoRenderer class to support CardboardView and corresponding StereoRenderer interface instead of GLSurfaceView and its Render interface. The switch between CardboardView and GLSurfaceView is almost trivial and works fine in other cases (eg in other apps that do not use opentok). In this case it didn't work at all and only after some tinkering with shaders and texture setup properties I was actually able to get anything to appear on the screen! However, much to my disappointment, my attempt at the renderer code(pasted below) results in a weird half-way-there VR view. The stereoscopic view is rendered with the desired distortion on each eye however the underlying frame images are not in duplicated for each eye -instead it the full screen single image is rendered with the the eye portals sitting over the top! Also the frames are rendered in grayscale! If anyone has got this to work properly I would really appreciate some help :)
Here is the code:
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.concurrent.locks.ReentrantLock;
import javax.microedition.khronos.egl.EGLConfig;
import android.content.Context;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;
import android.view.View;
import com.google.vrtoolkit.cardboard.CardboardView;
import com.google.vrtoolkit.cardboard.EyeTransform;
import com.google.vrtoolkit.cardboard.HeadTransform;
import com.google.vrtoolkit.cardboard.Viewport;
import com.opentok.android.BaseVideoRenderer;
public class CustomVideoStereoRendererWorking extends BaseVideoRenderer {
Context mContext;
CardboardView mView;
MyRenderer mRenderer;
static class MyRenderer implements CardboardView.StereoRenderer {
int mTextureIds[] = new int[3];
float[] mScaleMatrix = new float[16];
private FloatBuffer mVertexBuffer;
private FloatBuffer mTextureBuffer;
private ShortBuffer mDrawListBuffer;
boolean mVideoFitEnabled = true;
boolean mVideoDisabled = false;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static final int TEXTURECOORDS_PER_VERTEX = 2;
static float mXYZCoords[] = { -1.0f, 1.0f, 0.0f, // top left
-1.0f, -1.0f, 0.0f, // bottom left
1.0f, -1.0f, 0.0f, // bottom right
1.0f, 1.0f, 0.0f // top right
};
static float mUVCoords[] = { 0, 0, // top left
0, 1, // bottom left
1, 1, // bottom right
1, 0 }; // top right
private short mVertexIndex[] = { 0, 1, 2, 0, 2, 3 }; // order to draw
// vertices
private final String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
//"attribute vec4 aPosition;\n" +
"attribute vec2 aPosition;\n" +
"attribute vec2 aTextureCoord;\n" +
"varying vec2 vTextureCoord;\n" +
"void main() {\n" +
//" gl_Position = uMVPMatrix * aPosition;\n" +
" gl_Position = vec4 ( aPosition.x, aPosition.y, 0.0, 1.0 );\n" + //from working renderer
" vTextureCoord = aTextureCoord;\n" +
"}\n";
/*"attribute vec2 vPosition;\n" +
* "attribute vec2 vTexCoord;\n" +
* "varying vec2 texCoord;\n" +
* "void main() {\n" +
* " texCoord = vTexCoord;\n" +
* " gl_Position = vec4 ( vPosition.x, vPosition.y, 0.0, 1.0 );\n" +
* "}";*/
private final String fragmentShaderCode =
"precision mediump float;\n" +
"uniform sampler2D Ytex;\n" +
"uniform sampler2D Utex,Vtex;\n" +
"varying vec2 vTextureCoord;\n" +
"void main(void) {\n" +
" float nx,ny,r,g,b,y,u,v;\n" +
" mediump vec4 txl,ux,vx;" +
" nx=vTextureCoord[0];\n" +
" ny=vTextureCoord[1];\n" +
" y=texture2D(Ytex,vec2(nx,ny)).r;\n" +
" u=texture2D(Utex,vec2(nx,ny)).r;\n" +
" v=texture2D(Vtex,vec2(nx,ny)).r;\n" +
//" y=1.0-1.1643*(y-0.0625);\n" + // Invert effect
//" y=1.1643*(y-0.0625);\n" + // Normal renderer
" u=u-0.5;\n" +
" v=v-0.5;\n" +
" r=y+1.5958*v;\n" +
" g=y-0.39173*u-0.81290*v;\n" +
" b=y+2.017*u;\n" +
" gl_FragColor=vec4(r,g,b,1.0);\n" +
"}\n";
/*
private final String fragmentShaderCode =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" +
"uniform samplerExternalOES Ytex;\n" +
"uniform samplerExternalOES Utex,Vtex;\n" +
"varying vec2 vTextureCoord;\n" +
"void main(void) {\n" +
" float nx,ny,r,g,b,y,u,v;\n" +
" mediump vec4 txl,ux,vx;" +
" nx=vTextureCoord[0];\n" +
" ny=vTextureCoord[1];\n" +
" y=texture2D(Ytex,vec2(nx,ny)).r;\n" +
" u=texture2D(Utex,vec2(nx,ny)).r;\n" +
" v=texture2D(Vtex,vec2(nx,ny)).r;\n" +
//" y=1.0-1.1643*(y-0.0625);\n" + // Invert effect
//" y=1.1643*(y-0.0625);\n" + // Normal renderer
" u=u-0.5;\n" +
" v=v-0.5;\n" +
" r=y+1.5958*v;\n" +
" g=y-0.39173*u-0.81290*v;\n" +
" b=y+2.017*u;\n" +
" gl_FragColor=vec4(r,g,b,1.0);\n" +
"}\n";
*/
/*"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" +
"uniform samplerExternalOES sTexture;\n" +
"varying vec2 texCoord;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(sTexture,texCoord);\n" +
"}"*/
ReentrantLock mFrameLock = new ReentrantLock();
Frame mCurrentFrame;
private int mProgram;
private int mTextureWidth;
private int mTextureHeight;
private int mViewportWidth;
private int mViewportHeight;
public MyRenderer() {
ByteBuffer bb = ByteBuffer.allocateDirect(mXYZCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
mVertexBuffer = bb.asFloatBuffer();
mVertexBuffer.put(mXYZCoords);
mVertexBuffer.position(0);
ByteBuffer tb = ByteBuffer.allocateDirect(mUVCoords.length * 4);
tb.order(ByteOrder.nativeOrder());
mTextureBuffer = tb.asFloatBuffer();
mTextureBuffer.put(mUVCoords);
mTextureBuffer.position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(mVertexIndex.length * 2);
dlb.order(ByteOrder.nativeOrder());
mDrawListBuffer = dlb.asShortBuffer();
mDrawListBuffer.put(mVertexIndex);
mDrawListBuffer.position(0);
}
public void onSurfaceCreated(EGLConfig config) {
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES
// Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex
// shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment
// shader to
// program
GLES20.glLinkProgram(mProgram);
int positionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
int textureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, COORDS_PER_VERTEX * 4, mVertexBuffer);
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glVertexAttribPointer(textureHandle, TEXTURECOORDS_PER_VERTEX, GLES20.GL_FLOAT, false, TEXTURECOORDS_PER_VERTEX * 4,
mTextureBuffer);
GLES20.glEnableVertexAttribArray(textureHandle);
GLES20.glUseProgram(mProgram);
int i = GLES20.glGetUniformLocation(mProgram, "Ytex");
GLES20.glUniform1i(i, 0); /* Bind Ytex to texture unit 0 */
i = GLES20.glGetUniformLocation(mProgram, "Utex");
GLES20.glUniform1i(i, 1); /* Bind Utex to texture unit 1 */
i = GLES20.glGetUniformLocation(mProgram, "Vtex");
GLES20.glUniform1i(i, 2); /* Bind Vtex to texture unit 2 */
mTextureWidth = 0;
mTextureHeight = 0;
}
static void initializeTexture(int name, int id, int width, int height) {
GLES20.glActiveTexture(name);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, id);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, width, height, 0, GLES20.GL_LUMINANCE,
GLES20.GL_UNSIGNED_BYTE, null);
}
void setupTextures(Frame frame) {
if (mTextureIds[0] != 0) {
GLES20.glDeleteTextures(3, mTextureIds, 0);
}
GLES20.glGenTextures(3, mTextureIds, 0);
int w = frame.getWidth();
int h = frame.getHeight();
int hw = (w + 1) >> 1;
int hh = (h + 1) >> 1;
initializeTexture(GLES20.GL_TEXTURE0, mTextureIds[0], w, h);
initializeTexture(GLES20.GL_TEXTURE1, mTextureIds[1], hw, hh);
initializeTexture(GLES20.GL_TEXTURE2, mTextureIds[2], hw, hh);
mTextureWidth = frame.getWidth();
mTextureHeight = frame.getHeight();
}
void updateTextures(Frame frame) {
int width = frame.getWidth();
int height = frame.getHeight();
int half_width = (width + 1) >> 1;
int half_height = (height + 1) >> 1;
int y_size = width * height;
int uv_size = half_width * half_height;
ByteBuffer bb = frame.getBuffer();
// If we are reusing this frame, make sure we reset position and
// limit
bb.clear();
if (bb.remaining() == y_size + uv_size * 2) {
bb.position(0);
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
GLES20.glPixelStorei(GLES20.GL_PACK_ALIGNMENT, 1);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[0]);
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, width, height, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, bb);
bb.position(y_size);
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[1]);
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, half_width, half_height, GLES20.GL_LUMINANCE,
GLES20.GL_UNSIGNED_BYTE, bb);
bb.position(y_size + uv_size);
GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[2]);
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, half_width, half_height, GLES20.GL_LUMINANCE,
GLES20.GL_UNSIGNED_BYTE, bb);
} else {
mTextureWidth = 0;
mTextureHeight = 0;
}
}
public void onSurfaceChanged(int width, int height) {
GLES20.glViewport(0, 0, width, height);
mViewportWidth = width;
mViewportHeight = height;
}
public void onDrawEye(EyeTransform arg0) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
mFrameLock.lock();
if (mCurrentFrame != null && !mVideoDisabled) {
GLES20.glUseProgram(mProgram);
if (mTextureWidth != mCurrentFrame.getWidth() || mTextureHeight != mCurrentFrame.getHeight()) {
setupTextures(mCurrentFrame);
}
updateTextures(mCurrentFrame);
Matrix.setIdentityM(mScaleMatrix, 0);
float scaleX = 1.0f, scaleY = 1.0f;
float ratio = (float) mCurrentFrame.getWidth() / mCurrentFrame.getHeight();
float vratio = (float) mViewportWidth / mViewportHeight;
if (mVideoFitEnabled) {
if (ratio > vratio) {
scaleY = vratio / ratio;
} else {
scaleX = ratio / vratio;
}
} else {
if (ratio < vratio) {
scaleY = vratio / ratio;
} else {
scaleX = ratio / vratio;
}
}
Matrix.scaleM(mScaleMatrix, 0, scaleX * (mCurrentFrame.isMirroredX() ? -1.0f : 1.0f), scaleY, 1);
int mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mScaleMatrix, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, mVertexIndex.length, GLES20.GL_UNSIGNED_SHORT, mDrawListBuffer);
}
mFrameLock.unlock();
}
public void displayFrame(Frame frame) {
mFrameLock.lock();
if (this.mCurrentFrame != null) {
this.mCurrentFrame.recycle();
}
this.mCurrentFrame = frame;
mFrameLock.unlock();
}
public static int loadShader(int type, String shaderCode) {
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e("Shader", "Could not compile vshader");
Log.v("Shader", "Could not compile vshader:" + GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
return shader;
}
public void disableVideo(boolean b) {
mFrameLock.lock();
mVideoDisabled = b;
if (mVideoDisabled) {
if (this.mCurrentFrame != null) {
this.mCurrentFrame.recycle();
}
this.mCurrentFrame = null;
}
mFrameLock.unlock();
}
public void enableVideoFit(boolean enableVideoFit) {
mVideoFitEnabled = enableVideoFit;
}
public void onFinishFrame(Viewport arg0) {
// TODO Auto-generated method stub
}
public void onNewFrame(HeadTransform arg0) {
// TODO Auto-generated method stub
}
public void onRendererShutdown() {
// TODO Auto-generated method stub
}
};
public CustomVideoStereoRendererWorking(Context context) {
this.mContext = context;
mView = new CardboardView(context);
mView.setEGLContextClientVersion(2);
mView.setVRModeEnabled(true);
mRenderer = new MyRenderer();
mView.setRenderer(mRenderer);
mView.setRenderMode(CardboardView.RENDERMODE_WHEN_DIRTY);
}
#Override
public void onFrame(Frame frame) {
mRenderer.displayFrame(frame);
mView.requestRender();
}
#Override
public void setStyle(String key, String value) {
if (BaseVideoRenderer.STYLE_VIDEO_SCALE.equals(key)) {
if (BaseVideoRenderer.STYLE_VIDEO_FIT.equals(value)) {
mRenderer.enableVideoFit(true);
} else if (BaseVideoRenderer.STYLE_VIDEO_FILL.equals(value)) {
mRenderer.enableVideoFit(false);
}
}
}
#Override
public void onVideoPropertiesChanged(boolean videoEnabled) {
mRenderer.disableVideo(!videoEnabled);
}
#Override
public View getView() {
return mView;
}
#Override
public void onPause() {
mView.onPause();
}
#Override
public void onResume() {
mView.onResume();
}
}
Related
I am working on Vr app using virocore library in android. I have to show video over sphere. The video which i have to implement is not actually video but the two frames provided are the colour frame (left) and the alpha mask (right) frame. I have not worked with openGl but seems like I will need to provide a shader function to perform the alpha masking.
I have used this for shader Adding transparency to a video from black and white (and gray) alpha information video images
but how can i use it with OpenGL in on draw method? or If there is any way in virocore using which i can do alpha masking. I have tried chroma filtering method in virocore but that makes whole video transparent.
public class VideoSurfaceView extends GLSurfaceView {
VideoRender mRenderer;
private MediaPlayer mMediaPlayer = null;
public VideoSurfaceView(Context context, MediaPlayer mp) {
super(context);
setEGLContextClientVersion(2);
mMediaPlayer = mp;
mRenderer = new VideoRender(context);
this.getHolder().setFormat(PixelFormat.RGB_565);
this.getHolder().setFormat(PixelFormat.TRANSPARENT);
setEGLConfigChooser(8,8,8,8,16,0);
setEGLContextClientVersion(2);
setRenderer(mRenderer);
}
#Override
public void onResume() {
Log.e("onResume ", "onResume");
queueEvent(new Runnable(){
public void run() {
Log.e("runnable ", "runnable");
mRenderer.setMediaPlayer(mMediaPlayer);
}});
super.onResume();
}
private static class VideoRender
implements Renderer, SurfaceTexture.OnFrameAvailableListener, MediaPlayer.OnPreparedListener {
private static String TAG = "VideoRender";
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 mVertexShader =
"uniform mat4 uMVPMatrix;\n" +
"uniform mat4 uSTMatrix;\n" +
"attribute vec4 position;\n" +
"attribute vec4 inputTextureCoordinate;\n" +
" \n" +
"varying vec2 textureCoordinate;\n" +
"varying vec2 textureCoordinate2;\n" +
" \n" +
"void main()\n" +
"{\n" +
" gl_Position = uMVPMatrix * position;\n" +
" vec4 texCoord = uSTMatrix * inputTextureCoordinate;\n"+
"textureCoordinate = vec2(inputTextureCoordinate.x * 0.5, inputTextureCoordinate.y);\n" +
" textureCoordinate2 = vec2(inputTextureCoordinate.x * 0.5 + 0.5, inputTextureCoordinate.y);\n" +
"}";
public static final String mFragmentShader = "#extension GL_OES_EGL_image_external : require\n"+
"varying highp vec2 textureCoordinate;\n"+
"varying highp vec2 textureCoordinate2;\n"+
"uniform samplerExternalOES inputImageTexture;\n" +
"void main() {\n"+
" lowp vec4 rgbcolor = texture2D(inputImageTexture, textureCoordinate);\n"+
" lowp vec4 alphaValue = texture2D(inputImageTexture, textureCoordinate2);\n"+
" if (alphaValue.g < 0.5)\n"+
" discard;\n"+
" gl_FragColor = vec4(rgbcolor.rgb, 1.0);\n"+
"}";
private float[] mMVPMatrix = new float[16];
private float[] mSTMatrix = new float[16];
private int mProgram;
private int mTextureID;
private int muMVPMatrixHandle;
private int muSTMatrixHandle;
private int maPositionHandle;
private int maTextureHandle;
private SurfaceTexture mSurface;
private boolean updateSurface = false;
private static int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
private MediaPlayer mMediaPlayer;
public VideoRender(Context context) {
mTriangleVertices = ByteBuffer.allocateDirect(
mTriangleVerticesData.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTriangleVertices.put(mTriangleVerticesData).position(0);
Matrix.setIdentityM(mSTMatrix, 0);
}
public void setMediaPlayer(MediaPlayer player) {
mMediaPlayer = player;
}
#Override
public void onDrawFrame(GL10 glUnused) {
synchronized(this) {
if (updateSurface) {
mSurface.updateTexImage();
mSurface.getTransformMatrix(mSTMatrix);
updateSurface = false;
}
}
GLES20.glClearColor(0.0f, 0.0f, 0.0f, .0f);
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT|GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
GLES20.glUseProgram(mProgram);
checkGlError("glUseProgram");
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
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, 3, 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();
}
#Override
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
}
#Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
mProgram = createProgram(mVertexShader, mFragmentShader);
if (mProgram == 0) {
return;
}
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "position");
checkGlError("glGetAttribLocation aPosition");
if (maPositionHandle == -1) {
throw new RuntimeException("Could not get attrib location for aPosition");
}
maTextureHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");
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(GL_TEXTURE_EXTERNAL_OES, mTextureID);
checkGlError("glBindTexture mTextureID");
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_NEAREST);
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR);
/*
* Create the SurfaceTexture that will feed this textureID,
* and pass it to the MediaPlayer
*/
mSurface = new SurfaceTexture(mTextureID);
mSurface.setOnFrameAvailableListener(this);
Log.e("surface ", "surface");
Surface surface = new Surface(mSurface);
mMediaPlayer.setSurface(surface);
mMediaPlayer.setScreenOnWhilePlaying(true);
surface.release();
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.prepareAsync();
synchronized(this) {
updateSurface = false;
}
}
synchronized public void onFrameAvailable(SurfaceTexture surface) {
updateSurface = true;
}
private int loadShader(int shaderType, String source) {
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
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();
if (program != 0) {
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;
}
private 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);
}
}
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
} // End of class VideoRender.
} // End of class VideoSurfaceView.
With this code, video looks inverted
If you want to discard fragments, then you can use the discard keyword in the fragment shader.
e.g. discard all fragments with an alpha value less than 0.5:
void main()
{
lowp vec4 rgbcolor = texture2D(inputImageTexture, textureCoordinate);
lowp vec4 alphaValue = texture2D(inputImageTexture, textureCoordinate2);
if (alphaValue.g < 0.5)
discard;
gl_FragColor = vec4(rgbcolor.rgb, 1.0);
}
See also OpenGL ES Shading Language 1.00 Specification; 6.4 Jumps; page 58:
The discard keyword is only allowed within fragment shaders. It can be used within a fragment shader to abandon the operation on the current fragment. This keyword causes the fragment to be discarded and no updates to any buffers will occur. It would typically be used within a conditional statement, for example:
if (intensity < 0.0)
discard;
In reference to the comment
As there is no matrix so what will i use in GLES20.glGetUniformLocation?
Of course you can add the matrices to the vertex shader:
attribute vec4 inputTextureCoordinate;
varying vec2 textureCoordinate;
varying vec2 textureCoordinate2;
uniform mat4 matMVP;
uniform mat4 matST;
void main()
{
gl_Position = matMVP * position;
vec4 texCoord = matST * inputTextureCoordinate;
textureCoordinate = vec2(texCoord.x * 0.5, 1.0 - texCoord.y);
textureCoordinate2 = vec2(texCoord.x * 0.5 + 0.5, 1.0 - texCoord.y);
}
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "matMVP");
muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "matST");
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
As I understood, from GLES30 there is no more gl_FragColor buffer (I saw it HERE)
Since I can't read a "Special Variables ", how can I read an "out" buffer?
This is my code:
private static final String FRAGMENT_SHADER =
"#version 300 es\n"+
"#extension GL_OES_EGL_image_external_essl3 : require\n" +
"precision mediump float;\n" + // highp here doesn't seem to matter
"in vec2 vTextureCoord;\n" +
"uniform sampler2D sTexture;\n" +
"out vec4 fragColor ;\n" +
"void main() {\n" +
" vec4 tc = texture(sTexture, vTextureCoord);\n" +
" fragColor.r = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;\n" +
" fragColor.g = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;\n" +
" fragColor.b = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;\n" +
"}\n";
Here I tried to read the data:
ByteBuffer mPixelBuf = ByteBuffer.allocateDirect(mWidth * mHeight * 4);
mPixelBuf.order(ByteOrder.LITTLE_ENDIAN);
GLES30.glReadPixels(startX, startY, frameWidth, frameHeight, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, mPixelBuf);
There are no gl errors in the code.
The output mPixelBuf only zeroes.
How can I make sure that fragColor is reading?
Thanks
Update1- Full Texture Render Code:
package com.MES.YOtm.AnalyzingAdapter;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLES30;
import android.opengl.Matrix;
import android.util.Log;
/**
* Code for rendering a texture onto a surface using OpenGL ES 2.0.
*/
public class STextureRender {
private static final String TAG = "Myopengl";
private int zoom;
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 =
"#version 300 es\n"+
"uniform mat4 uMVPMatrix;\n" +
"uniform mat4 uSTMatrix;\n" +
"in vec4 aPosition;\n" +
"in vec4 aTextureCoord;\n" +
"out vec2 vTextureCoord;\n" +
"void main() {\n" +
" gl_Position = uMVPMatrix * aPosition;\n" +
" vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
"}\n";
//Smapler2D
private static final String FRAGMENT_SHADER =
"#version 300 es\n"+
"#extension GL_OES_EGL_image_external_essl3 : require\n" +
"precision mediump float;\n" + // highp here doesn't seem to matter
"in vec2 vTextureCoord;\n" +
"uniform mediump sampler2D sTexture;\n" +
"layout(location = 0) out mediump vec4 fragColor ;\n" +
"void main() {\n" +
" vec4 tc = texture(sTexture, vTextureCoord);\n" +
" fragColor.r = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;\n" +
" fragColor.g = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;\n" +
" fragColor.b = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;\n" +
"}\n";
private float[] mMVPMatrix = new float[16];
private float[] mSTMatrix = new float[16];
private int mProgram;
private int mTextureID = -12345;
private int muMVPMatrixHandle;
private int muSTMatrixHandle;
private int maPositionHandle;
private int maTextureHandle;
public STextureRender(int _zoom) {
Log.v("My Error", "Start STextureRender constructor");
try
{
mTriangleVertices = ByteBuffer.allocateDirect(
mTriangleVerticesData.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTriangleVertices.put(mTriangleVerticesData).position(0);
Matrix.setIdentityM(mSTMatrix, 0);
zoom = _zoom;
}
catch(Exception ex)
{
Log.v("My Error", "STextureRender Error = " + ex.toString());
}
Log.v("My Error", "End STextureRender constructor");
}
public int getTextureId() {
return mTextureID;
}
/**
* Draws the external texture in SurfaceTexture onto the current EGL surface.
*/
public void drawFrame(SurfaceTexture st, boolean invert) {
checkGlError("onDrawFrame start");
try
{
st.getTransformMatrix(mSTMatrix);
if (invert) {
mSTMatrix[5] = -mSTMatrix[5];
mSTMatrix[13] = 1.0f - mSTMatrix[13];
}
GLES30.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
GLES30.glUseProgram(mProgram);
checkGlError("glUseProgram");
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
GLES30.glVertexAttribPointer(maPositionHandle, 3, GLES30.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
checkGlError("glVertexAttribPointer maPosition");
GLES30.glEnableVertexAttribArray(maPositionHandle);
checkGlError("glEnableVertexAttribArray maPositionHandle");
mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
GLES30.glVertexAttribPointer(maTextureHandle, 2, GLES30.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
checkGlError("glVertexAttribPointer maTextureHandle");
GLES30.glEnableVertexAttribArray(maTextureHandle);
checkGlError("glEnableVertexAttribArray maTextureHandle");
Matrix.setIdentityM(mMVPMatrix, 0);
GLES30.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES30.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
checkGlError("glDrawArrays");
}
catch(Exception ex)
{
Log.v("My Error", "drawFrame Error = " + ex.toString());
}
}
/**
* Initializes GL state. Call this after the EGL surface has been created and made current.
*/
public void surfaceCreated() {
try
{
mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
if (mProgram == 0) {
throw new RuntimeException("failed creating program");
}
maPositionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition");
checkGlError("glGetAttribLocation aPosition");
if (maPositionHandle == -1) {
throw new RuntimeException("Could not get attrib location for aPosition");
}
maTextureHandle = GLES30.glGetAttribLocation(mProgram, "aTextureCoord");
checkGlError("glGetAttribLocation aTextureCoord");
if (maTextureHandle == -1) {
throw new RuntimeException("Could not get attrib location for aTextureCoord");
}
muMVPMatrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix");
checkGlError("glGetUniformLocation uMVPMatrix");
if (muMVPMatrixHandle == -1) {
throw new RuntimeException("Could not get attrib location for uMVPMatrix");
}
muSTMatrixHandle = GLES30.glGetUniformLocation(mProgram, "uSTMatrix");
checkGlError("glGetUniformLocation uSTMatrix");
if (muSTMatrixHandle == -1) {
throw new RuntimeException("Could not get attrib location for uSTMatrix");
}
int[] textures = new int[1];
GLES30.glGenTextures(1, textures, 0);
mTextureID = textures[0];
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
checkGlError("glBindTexture mTextureID");
GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MIN_FILTER,
GLES30.GL_NEAREST);
GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MAG_FILTER,
GLES30.GL_LINEAR);
GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_S,
GLES30.GL_CLAMP_TO_EDGE);
GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_T,
GLES30.GL_CLAMP_TO_EDGE);
checkGlError("glTexParameter");
}
catch(Exception ex)
{
Log.v("My Error", "surfaceCreated Error = " + ex.toString());
}
}
/**
* Replaces the fragment shader. Pass in null to reset to default.
*/
public void changementShader(String fragmentShader) {
try
{
if (fragmentShader == null) {
fragmentShader = FRAGMENT_SHADER;
}
GLES30.glDeleteProgram(mProgram);
mProgram = createProgram(VERTEX_SHADER, fragmentShader);
if (mProgram == 0) {
Log.v("My Error", "failed creating program");
throw new RuntimeException("failed creating program");
}
}
catch(Exception ex)
{
Log.v("My Error", " changementShader Error = " + ex.toString());
}
}
private int loadShader(int shaderType, String source) {
try
{
int shader = GLES30.glCreateShader(shaderType);
checkGlError("glCreateShader type=" + shaderType);
GLES30.glShaderSource(shader, source);
GLES30.glCompileShader(shader);
int[] compiled = new int[1];
GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e(TAG, "Could not compile shader " + shaderType + ":");
Log.e(TAG, " " + GLES30.glGetShaderInfoLog(shader));
GLES30.glDeleteShader(shader);
shader = 0;
}
return shader;
}
catch(Exception ex)
{
Log.v("My Error", "loadShader Error = " + ex.toString());
return 0;
}
}
private int createProgram(String vertexSource, String fragmentSource) {
try
{
int vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
int pixelShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0) {
return 0;
}
int program = GLES30.glCreateProgram();
checkGlError("glCreateProgram");
if (program == 0) {
Log.e(TAG, "Could not create program");
}
GLES30.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
GLES30.glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
GLES30.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES30.GL_TRUE) {
Log.e(TAG, "Could not link program: ");
Log.e(TAG, GLES30.glGetProgramInfoLog(program));
GLES30.glDeleteProgram(program);
program = 0;
}
return program;
}
catch(Exception ex)
{
Log.v("My Error", "createProgram Error = " + ex.toString());
return 0;
}
}
public void checkGlError(String op) {
int error;
while ((error = GLES30.glGetError()) != GLES30.GL_NO_ERROR) {
Log.e(TAG, op + ": glError " + error);
throw new RuntimeException(op + ": glError " + error);
}
}
}
Your shader code is totally irrelevant to what glReadPixels reads, and it has nothing to do with special variable names. It reads from the currently bound read framebuffer; i.e. glReadPixels in ES 3.0 works in exactly the same way as it used to work in ES 2.0.
The only exception is multiple render target support, but that's not relevant in this case.
How can I make sure that fragColor is reading?
glClearColor(some interesting color)
glClear(COLOR_BUFFER_BIT)
glReadPixels()
assert color == some interesting color
What I have learned is that your GLES30.glReadPixels call needs to be done before eglSwapBuffers because glReadBuffer is initially set to GL_BACK in double-buffered configurations according to document of glReadBuffer. Once eglSwapBuffers calls glReadPixels reads nothing back to main memory.
I am Using OpenTok Android SDK 2.4+
(https://tokbox.com/developer/sdks/android/)
Currently in SDK it's Showing the Publisher(Camera Preview) in Square Area but I wants it in Round Shape (Publisher Camera view in Round shape).
Note: PublisherView using OpenGL GLSurfaceView to show Camera Preview.
I am using attached "CustomVideoRenderer.java" Class by Extending class "BaseVideoRenderer".
I am not familiar with the OpenGL so not able to understand what I should change in Code.
So Please help me to Get Out of it..
TO Display Publisher View in Round Shape.
public class CustomVideoRenderer extends BaseVideoRenderer {
private Context mContext;
private GLSurfaceView mView;
private MyRenderer mRenderer;
static class MyRenderer implements GLSurfaceView.Renderer {
int mTextureIds[] = new int[3];
float[] mScaleMatrix = new float[16];
private FloatBuffer mVertexBuffer;
private FloatBuffer mTextureBuffer;
private ShortBuffer mDrawListBuffer;
boolean mVideoFitEnabled = true;
boolean mVideoDisabled = false;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static final int TEXTURECOORDS_PER_VERTEX = 2;
static float mXYZCoords[] = {-1.0f, 1.0f, 0.0f, // top left
-1.0f, -1.0f, 0.0f, // bottom left
1.0f, -1.0f, 0.0f, // bottom right
1.0f, 1.0f, 0.0f // top right
};
static float mUVCoords[] = {0, 0, // top left
0, 1, // bottom left
1, 1, // bottom right
1, 0}; // top right
private short mVertexIndex[] = {0, 1, 2, 0, 2, 3}; // order to draw
// vertices
private final String vertexShaderCode = "uniform mat4 uMVPMatrix;"
+ "attribute vec4 aPosition;\n"
+ "attribute vec2 aTextureCoord;\n"
+ "varying vec2 vTextureCoord;\n" + "void main() {\n"
+ " gl_Position = uMVPMatrix * aPosition;\n"
+ " vTextureCoord = aTextureCoord;\n" + "}\n";
private final String fragmentShaderCode = "precision mediump float;\n"
+ "uniform sampler2D Ytex;\n"
+ "uniform sampler2D Utex,Vtex;\n"
+ "varying vec2 vTextureCoord;\n"
+ "void main(void) {\n"
+ " float nx,ny,r,g,b,y,u,v;\n"
+ " mediump vec4 txl,ux,vx;"
+ " nx=vTextureCoord[0];\n"
+ " ny=vTextureCoord[1];\n"
+ " y=texture2D(Ytex,vec2(nx,ny)).r;\n"
+ " u=texture2D(Utex,vec2(nx,ny)).r;\n"
+ " v=texture2D(Vtex,vec2(nx,ny)).r;\n"
+ " y=1.0-1.1643*(y-0.0625);\n" // Invert effect
// + " y=1.1643*(y-0.0625);\n" // Normal renderer
+ " u=u-0.5;\n" + " v=v-0.5;\n" + " r=y+1.5958*v;\n"
+ " g=y-0.39173*u-0.81290*v;\n" + " b=y+2.017*u;\n"
+ " gl_FragColor=vec4(r,g,b,1.0);\n" + "}\n";
ReentrantLock mFrameLock = new ReentrantLock();
Frame mCurrentFrame;
private int mProgram;
private int mTextureWidth;
private int mTextureHeight;
private int mViewportWidth;
private int mViewportHeight;
public MyRenderer()
{
ByteBuffer bb = ByteBuffer.allocateDirect(mXYZCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
mVertexBuffer = bb.asFloatBuffer();
mVertexBuffer.put(mXYZCoords);
mVertexBuffer.position(0);
ByteBuffer tb = ByteBuffer.allocateDirect(mUVCoords.length * 4);
tb.order(ByteOrder.nativeOrder());
mTextureBuffer = tb.asFloatBuffer();
mTextureBuffer.put(mUVCoords);
mTextureBuffer.position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(mVertexIndex.length * 2);
dlb.order(ByteOrder.nativeOrder());
mDrawListBuffer = dlb.asShortBuffer();
mDrawListBuffer.put(mVertexIndex);
mDrawListBuffer.position(0);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
gl.glClearColor(0, 0, 0, 1);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES
// Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex
// shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment
// shader to
// program
GLES20.glLinkProgram(mProgram);
int positionHandle = GLES20.glGetAttribLocation(mProgram,
"aPosition");
int textureHandle = GLES20.glGetAttribLocation(mProgram,
"aTextureCoord");
GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false, COORDS_PER_VERTEX * 4,
mVertexBuffer);
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glVertexAttribPointer(textureHandle,
TEXTURECOORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
TEXTURECOORDS_PER_VERTEX * 4, mTextureBuffer);
GLES20.glEnableVertexAttribArray(textureHandle);
GLES20.glUseProgram(mProgram);
int i = GLES20.glGetUniformLocation(mProgram, "Ytex");
GLES20.glUniform1i(i, 0); /* Bind Ytex to texture unit 0 */
i = GLES20.glGetUniformLocation(mProgram, "Utex");
GLES20.glUniform1i(i, 1); /* Bind Utex to texture unit 1 */
i = GLES20.glGetUniformLocation(mProgram, "Vtex");
GLES20.glUniform1i(i, 2); /* Bind Vtex to texture unit 2 */
mTextureWidth = 0;
mTextureHeight = 0;
}
static void initializeTexture(int name, int id, int width, int height) {
GLES20.glActiveTexture(name);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, id);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
width, height, 0, GLES20.GL_LUMINANCE,
GLES20.GL_UNSIGNED_BYTE, null);
}
void setupTextures(Frame frame) {
if (mTextureIds[0] != 0) {
GLES20.glDeleteTextures(3, mTextureIds, 0);
}
GLES20.glGenTextures(3, mTextureIds, 0);
int w = frame.getWidth();
int h = frame.getHeight();
int hw = (w + 1) >> 1;
int hh = (h + 1) >> 1;
initializeTexture(GLES20.GL_TEXTURE0, mTextureIds[0], w, h);
initializeTexture(GLES20.GL_TEXTURE1, mTextureIds[1], hw, hh);
initializeTexture(GLES20.GL_TEXTURE2, mTextureIds[2], hw, hh);
mTextureWidth = frame.getWidth();
mTextureHeight = frame.getHeight();
}
void updateTextures(Frame frame) {
int width = frame.getWidth();
int height = frame.getHeight();
int half_width = (width + 1) >> 1;
int half_height = (height + 1) >> 1;
int y_size = width * height;
int uv_size = half_width * half_height;
ByteBuffer bb = frame.getBuffer();
// If we are reusing this frame, make sure we reset position and
// limit
bb.clear();
if (bb.remaining() == y_size + uv_size * 2) {
bb.position(0);
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
GLES20.glPixelStorei(GLES20.GL_PACK_ALIGNMENT, 1);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[0]);
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, width,
height, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE,
bb);
bb.position(y_size);
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[1]);
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0,
half_width, half_height, GLES20.GL_LUMINANCE,
GLES20.GL_UNSIGNED_BYTE, bb);
bb.position(y_size + uv_size);
GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIds[2]);
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0,
half_width, half_height, GLES20.GL_LUMINANCE,
GLES20.GL_UNSIGNED_BYTE, bb);
} else {
mTextureWidth = 0;
mTextureHeight = 0;
}
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
mViewportWidth = width;
mViewportHeight = height;
}
#Override
public void onDrawFrame(GL10 gl) {
mFrameLock.lock();
if (mCurrentFrame != null && !mVideoDisabled) {
GLES20.glUseProgram(mProgram);
if (mTextureWidth != mCurrentFrame.getWidth()
|| mTextureHeight != mCurrentFrame.getHeight()) {
setupTextures(mCurrentFrame);
}
updateTextures(mCurrentFrame);
Matrix.setIdentityM(mScaleMatrix, 0);
float scaleX = 1.0f, scaleY = 1.0f;
float ratio = (float) mCurrentFrame.getWidth()
/ mCurrentFrame.getHeight();
float vratio = (float) mViewportWidth / mViewportHeight;
if (mVideoFitEnabled) {
if (ratio > vratio) {
scaleY = vratio / ratio;
} else {
scaleX = ratio / vratio;
}
} else {
if (ratio < vratio) {
scaleY = vratio / ratio;
} else {
scaleX = ratio / vratio;
}
}
Matrix.scaleM(mScaleMatrix, 0,
scaleX * (mCurrentFrame.isMirroredX() ? -1.0f : 1.0f),
scaleY, 1);
int mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram,
"uMVPMatrix");
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false,
mScaleMatrix, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, mVertexIndex.length,
GLES20.GL_UNSIGNED_SHORT, mDrawListBuffer);
} else {
//black frame when video is disabled
gl.glClearColor(0, 0, 0, 1);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
}
mFrameLock.unlock();
}
public void displayFrame(Frame frame) {
mFrameLock.lock();
if (this.mCurrentFrame != null) {
this.mCurrentFrame.recycle();
}
this.mCurrentFrame = frame;
mFrameLock.unlock();
}
public static int loadShader(int type, String shaderCode) {
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
public void disableVideo(boolean b) {
mFrameLock.lock();
mVideoDisabled = b;
if (mVideoDisabled) {
if (this.mCurrentFrame != null) {
this.mCurrentFrame.recycle();
}
this.mCurrentFrame = null;
}
mFrameLock.unlock();
}
public void enableVideoFit(boolean enableVideoFit) {
mVideoFitEnabled = enableVideoFit;
}
}
public CustomVideoRenderer(Context context) {
this.mContext = context;
mView = new GLSurfaceView(context);
mView.setEGLContextClientVersion(2);
mRenderer = new MyRenderer();
mView.setRenderer(mRenderer);
mView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
#Override
public void onFrame(Frame frame) {
mRenderer.displayFrame(frame);
mView.requestRender();
}
#Override
public void setStyle(String key, String value) {
if (BaseVideoRenderer.STYLE_VIDEO_SCALE.equals(key)) {
if (BaseVideoRenderer.STYLE_VIDEO_FIT.equals(value)) {
mRenderer.enableVideoFit(true);
} else if (BaseVideoRenderer.STYLE_VIDEO_FILL.equals(value)) {
mRenderer.enableVideoFit(false);
}
}
}
#Override
public void onVideoPropertiesChanged(boolean videoEnabled) {
mRenderer.disableVideo(!videoEnabled);
}
#Override
public View getView() {
return mView;
}
#Override
public void onPause() {
mView.onPause();
}
#Override
public void onResume() {
mView.onResume();
}
}
https://tokbox.com/developer/sdks/android/reference/com/opentok/android/Session.SessionOptions.html
You can have returned a TextureViews with TextureViews() and then the usual methods to have rounded corner seems to work.
I am new to the world of Open Gl and I have googled a lot but i am unable to find a way to implement Effects on a video being played. After some research i have finally found a class that can be used to play video on a GLSurfaceView. And i know from Google documentation and that we can apply effects on a video.
By following this post i was able to successfully apply effects on bitmaps. Now i want to do that for my video so any help or pointers is appreciated.
Here is the VideoSurfaceView that i am using to Render Video that is being played
package me.crossle.demo.surfacetexture;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.media.MediaPlayer;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
import android.view.Surface;
#SuppressLint("ViewConstructor")
class VideoSurfaceView extends GLSurfaceView {
VideoRender mRenderer;
private MediaPlayer mMediaPlayer = null;
public VideoSurfaceView(Context context, MediaPlayer mp) {
super(context);
setEGLContextClientVersion(2);
mMediaPlayer = mp;
mRenderer = new VideoRender(context);
setRenderer(mRenderer);
}
#Override
public void onResume() {
queueEvent(new Runnable(){
public void run() {
mRenderer.setMediaPlayer(mMediaPlayer);
}});
super.onResume();
}
private static class VideoRender
implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {
private static String TAG = "VideoRender";
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 final String mVertexShader =
"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 final String mFragmentShader =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" +
"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;
private int muMVPMatrixHandle;
private int muSTMatrixHandle;
private int maPositionHandle;
private int maTextureHandle;
private SurfaceTexture mSurface;
private boolean updateSurface = false;
private static int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
private MediaPlayer mMediaPlayer;
public VideoRender(Context context) {
mTriangleVertices = ByteBuffer.allocateDirect(
mTriangleVerticesData.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTriangleVertices.put(mTriangleVerticesData).position(0);
Matrix.setIdentityM(mSTMatrix, 0);
}
public void setMediaPlayer(MediaPlayer player) {
mMediaPlayer = player;
}
#Override
public void onDrawFrame(GL10 glUnused) {
synchronized(this) {
if (updateSurface) {
mSurface.updateTexImage();
mSurface.getTransformMatrix(mSTMatrix);
updateSurface = false;
}
}
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");
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
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, 3, 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();
}
#Override
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
}
#Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
mProgram = createProgram(mVertexShader, mFragmentShader);
if (mProgram == 0) {
return;
}
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(GL_TEXTURE_EXTERNAL_OES, mTextureID);
checkGlError("glBindTexture mTextureID");
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_NEAREST);
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR);
/*
* Create the SurfaceTexture that will feed this textureID,
* and pass it to the MediaPlayer
*/
mSurface = new SurfaceTexture(mTextureID);
mSurface.setOnFrameAvailableListener(this);
Surface surface = new Surface(mSurface);
mMediaPlayer.setSurface(surface);
mMediaPlayer.setScreenOnWhilePlaying(true);
surface.release();
try {
mMediaPlayer.prepare();
} catch (IOException t) {
Log.e(TAG, "media player prepare failed");
}
synchronized(this) {
updateSurface = false;
}
mMediaPlayer.start();
}
synchronized public void onFrameAvailable(SurfaceTexture surface) {
updateSurface = true;
}
private int loadShader(int shaderType, String source) {
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
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();
if (program != 0) {
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;
}
private 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);
}
}
} // End of class VideoRender.
} // End of class VideoSurfaceView.
And Here is my MainActivity
package me.crossle.demo.surfacetexture;
import java.io.File;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
protected Resources mResources;
private VideoSurfaceView mVideoView = null;
private MediaPlayer mMediaPlayer = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mResources = getResources();
mMediaPlayer = new MediaPlayer();
try {
File dir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File file = new File(dir,
"video.mp4");
mMediaPlayer.setDataSource(file.getAbsolutePath());
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
mVideoView = new VideoSurfaceView(this, mMediaPlayer);
setContentView(mVideoView);
}
#Override
protected void onResume() {
super.onResume();
mVideoView.onResume();
}
}
I have solved the issue and i am posting the answer in case anyone else is also looking for a way to apply different Filters on their video.
After being pointed out in the right direction by Lunero and Fadden i am now able to apply almost all EffectFactory effects to the video being played. Though these effects are only meant for preview purpose and do not change the original video but still they do the job for me.
What i did was that I changed the FragmentShaders code that was applied to the video being rendered and i was able to achieve different effects.
Here is the code for some fragmentShaders.
Black and White Effect
String fragmentShader = "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "varying vec2 vTextureCoord;\n"
+ "uniform samplerExternalOES sTexture;\n"
+ "void main() {\n"
+ " vec4 color = texture2D(sTexture, vTextureCoord);\n"
+ " float colorR = (color.r + color.g + color.b) / 3.0;\n"
+ " float colorG = (color.r + color.g + color.b) / 3.0;\n"
+ " float colorB = (color.r + color.g + color.b) / 3.0;\n"
+ " gl_FragColor = vec4(colorR, colorG, colorB, color.a);\n"
+ "}\n";
Negative Effect
String fragmentShader = "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "varying vec2 vTextureCoord;\n"
+ "uniform samplerExternalOES sTexture;\n"
+ "void main() {\n"
+ " vec4 color = texture2D(sTexture, vTextureCoord);\n"
+ " float colorR = (1.0 - color.r) / 1.0;\n"
+ " float colorG = (1.0 - color.g) / 1.0;\n"
+ " float colorB = (1.0 - color.b) / 1.0;\n"
+ " gl_FragColor = vec4(colorR, colorG, colorB, color.a);\n"
+ "}\n";
Original Video without any Effect
Video with Black and White Effect
Video with Negative Effect
If you like to apply more effects then i suggest you look at VidEffects on github. It will help you apply many different effects on your video.
I'm trying to write my own shader class and matrix handling class for an android application using GLES20. However, right now I'm only getting the background color.
I've been pulling my hair out over this for a couple of days. I wanted to compare matrix operations between my class and GL10 stuff to make sure they spit out the same results, but I couldn't for the life of me get the api example MatrixGrabber and MatrixTrackingGL to spit anything out besides the identity matrix (despite rendering correctly).
So I'm going to post some code, and please let me know if you see anything that could be the problem in these two classes!
Here is my MatrixHandler class:
import java.util.Stack;
import android.opengl.Matrix;
public class MatrixHandler
{
public static float[] mMVPMatrix = new float[16];
public static float[] mProjMatrix = new float[16];
public static float[] mViewMatrix = new float[16];
public static float[] mRotationMatrix = new float[16];
public static Stack<float[]> mvpStack = new Stack<float[]>();
public static Stack<float[]> projStack = new Stack<float[]>();
public static Stack<float[]> viewStack = new Stack<float[]>();
public static Stack<float[]> rotationStack = new Stack<float[]>();
public static void setViewIdentity()
{
Matrix.setIdentityM(mViewMatrix, 0);
}
public static void setProjIdentity()
{
Matrix.setIdentityM(mProjMatrix, 0);
}
public static void setViewport(float left, float right, float bottom, float top, float near, float far)
{
Matrix.frustumM(mProjMatrix, 0,
left, right,
bottom, top,
near, far
);
}
public static void setLookAt(
float eyeX, float eyeY, float eyeZ,
float posX, float posY, float posZ,
float upX, float upY, float upZ
)
{
Matrix.setLookAtM(
mViewMatrix, 0,
eyeX, eyeY, eyeZ,
posX, posY, posZ,
upX, upY, upZ
);
//Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mViewMatrix, 0);
}
public static void translate(float x, float y, float z)
{
Matrix.translateM(mViewMatrix, 0,
x, y, z
);
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mMVPMatrix, 0);
}
public static void rotate(float a, float x, float y, float z)
{
Matrix.rotateM(mRotationMatrix, 0,
a, x, y, z
);
Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
}
public static void pushMatrix()
{
mvpStack.push(mMVPMatrix);
projStack.push(mProjMatrix);
viewStack.push(mViewMatrix);
rotationStack.push(mRotationMatrix);
}
public static void popMatrix()
{
mMVPMatrix = mvpStack.pop();
mProjMatrix = projStack.pop();
mViewMatrix = viewStack.pop();
mRotationMatrix = rotationStack.pop();
}
public static void printMatrix(String label, float[] m)
{
System.err.print(label + " : {");
for(float i : m)
{
System.err.print(i + ", ");
}
System.err.println("}");
}
}
And here is my Shader class:
import java.nio.FloatBuffer;
import com.bradsproject.appName.MatrixHandler;
import android.opengl.GLES20;
public class Shader
{
private static int mProgram;
static int maPositionHandle;
static int maColorHandle;
static int maTextureHandle;
static int muMVPMatrixHandle;
static int maTexture;
private final static String mVertexShader =
"uniform mat4 uMVPMatrix;" +
"attribute vec3 aPosition;" +
"attribute vec2 aTextureCoord;" +
"attribute vec4 aColor;" +
"varying vec4 vColor;" +
"varying vec2 vTextureCoord;" +
"void main() {" +
" gl_Position = uMVPMatrix * vec4(aPosition, 1.0);" +
" vTextureCoord = aTextureCoord;" +
" vColor = aColor;" +
"}";
private final static String mFragmentShader =
"precision mediump float;" +
"varying vec2 vTextureCoord;" +
"uniform sampler2D sTexture;" +
"varying vec4 vColor;" +
"void main() {" +
" gl_FragColor = texture2D(sTexture, vTextureCoord);" +
"}";
public static void init()
{
mProgram = createProgram(mVertexShader, mFragmentShader);
if (mProgram == 0)
return;
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
maColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
maTexture = GLES20.glGetUniformLocation(mProgram, "sTexture");
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
}
public static void drawArrays(FloatBuffer mPosition, FloatBuffer mColor, FloatBuffer mTexture, int textureId, int mode)
{
GLES20.glUseProgram(mProgram);
mPosition.position(0);
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, mPosition);
GLES20.glEnableVertexAttribArray(maPositionHandle);
mColor.position(0);
GLES20.glVertexAttribPointer(maColorHandle, 4, GLES20.GL_FLOAT, false, 4 * 4, mColor);
GLES20.glEnableVertexAttribArray(maColorHandle);
mTexture.position(0);
GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false, 2 * 4, mTexture);
GLES20.glEnableVertexAttribArray(maTextureHandle);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glUniform1i(maTexture, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixHandler.mMVPMatrix, 0);
GLES20.glDrawArrays(mode, 0, mPosition.capacity() / 3);
}
private static int createProgram(String vertexSource, String fragmentSource)
{
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0)
{
System.err.println("Failed to load vertex shader.");
return 0;
}
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0)
{
System.err.println("Failed to load fragment shader.");
return 0;
}
int program = GLES20.glCreateProgram();
if (program != 0)
{
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)
{
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
private static int loadShader(int shaderType, String source)
{
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0)
{
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)
{
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
private static void checkGlError(String op)
{
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR)
{
throw new RuntimeException(op + ": glError " + error);
}
}
}
You are missing "\n" at the end
Avoid those matrix stack, soon you will run short of speed with those heavy stuff when your code becomes larger than 1000 lines of code whether it is openglgame or openglapp
For speed use const, local variables in shader code and perform more calculations inside the fragment shader
Here is a shader set
private static final String vertexShaderCodeLight =
"uniform vec4 uVPosition; \n"
+ "uniform mat4 uP; \n"
+ "void main(){ \n"
+ " gl_PointSize = 15.0; \n"
+ " gl_Position = uP * uVPosition; \n"
+ "} \n";
private static final String fragmentShaderCodeLight =
"#ifdef GL_FRAGMENT_PRECISION_HIGH \n"
+ "precision highp float; \n"
+ "#else \n"
+ "precision mediump float; \n"
+ "#endif \n"
+ "void main(){ \n"
+ " gl_FragColor = vec4(1.0,1.0,1.0,1.0); \n"
+ "} \n";