I' ve been working on Google Cardboard for some research.
I faced some problem when I was trying to open camera on Google Cardboard. The problem is aspect ratio. When I was looking the camera with Google Cardboard, it looks like more thinner then reality. As you can see the images below...
Normal image:
Cardboard image:
And these are the codes.
CardboardOverlayView.java
public class CardboardOverlayView extends LinearLayout {
private static final String TAG = CardboardOverlayView.class.getSimpleName();
private final CardboardOverlayEyeView mLeftView;
private final CardboardOverlayEyeView mRightView;
private AlphaAnimation mTextFadeAnimation;
public CardboardOverlayView(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(HORIZONTAL);
LayoutParams params = new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1.0f);
params.setMargins(0, 0, 0, 0);
mLeftView = new CardboardOverlayEyeView(context, attrs);
mLeftView.setLayoutParams(params);
addView(mLeftView);
mRightView = new CardboardOverlayEyeView(context, attrs);
mRightView.setLayoutParams(params);
addView(mRightView);
// Set some reasonable defaults.
setDepthOffset(0.016f);
setColor(Color.rgb(150, 255, 180));
setVisibility(View.VISIBLE);
mTextFadeAnimation = new AlphaAnimation(1.0f, 0.0f);
mTextFadeAnimation.setDuration(5000);
}
public void show3DToast(String message) {
setText(message);
setTextAlpha(1f);
mTextFadeAnimation.setAnimationListener(new EndAnimationListener() {
#Override
public void onAnimationEnd(Animation animation) {
setTextAlpha(0f);
}
});
startAnimation(mTextFadeAnimation);
}
private abstract class EndAnimationListener implements Animation.AnimationListener {
#Override public void onAnimationRepeat(Animation animation) {}
#Override public void onAnimationStart(Animation animation) {}
}
private void setDepthOffset(float offset) {
mLeftView.setOffset(offset);
mRightView.setOffset(-offset);
}
private void setText(String text) {
mLeftView.setText(text);
mRightView.setText(text);
}
private void setTextAlpha(float alpha) {
mLeftView.setTextViewAlpha(alpha);
mRightView.setTextViewAlpha(alpha);
}
private void setColor(int color) {
mLeftView.setColor(color);
mRightView.setColor(color);
}
/**
* A simple view group containing some horizontally centered text underneath a horizontally
* centered image.
*
* This is a helper class for CardboardOverlayView.
*/
private class CardboardOverlayEyeView extends ViewGroup {
private final ImageView imageView;
private final TextView textView;
private float offset;
public CardboardOverlayEyeView(Context context, AttributeSet attrs) {
super(context, attrs);
imageView = new ImageView(context, attrs);
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageView.setAdjustViewBounds(true); // Preserve aspect ratio.
addView(imageView);
textView = new TextView(context, attrs);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14.0f);
textView.setTypeface(textView.getTypeface(), Typeface.BOLD);
textView.setGravity(Gravity.CENTER);
textView.setShadowLayer(3.0f, 0.0f, 0.0f, Color.DKGRAY);
addView(textView);
}
public void setColor(int color) {
imageView.setColorFilter(color);
textView.setTextColor(color);
}
public void setText(String text) {
textView.setText(text);
}
public void setTextViewAlpha(float alpha) {
textView.setAlpha(alpha);
}
public void setOffset(float offset) {
this.offset = offset;
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// Width and height of this ViewGroup.
final int width = right - left;
final int height = bottom - top;
// The size of the image, given as a fraction of the dimension as a ViewGroup. We multiply
// both width and heading with this number to compute the image's bounding box. Inside the
// box, the image is the horizontally and vertically centered.
final float imageSize = 0.12f;
// The fraction of this ViewGroup's height by which we shift the image off the ViewGroup's
// center. Positive values shift downwards, negative values shift upwards.
final float verticalImageOffset = -0.07f;
// Vertical position of the text, specified in fractions of this ViewGroup's height.
final float verticalTextPos = 0.52f;
// Layout ImageView
float imageMargin = (1.0f - imageSize) / 2.0f;
float leftMargin = (int) (width * (imageMargin + offset));
float topMargin = (int) (height * (imageMargin + verticalImageOffset));
imageView.layout(
(int) leftMargin, (int) topMargin,
(int) (leftMargin + width * imageSize), (int) (topMargin + height * imageSize));
// Layout TextView
leftMargin = offset * width;
topMargin = height * verticalTextPos;
textView.layout(
(int) leftMargin, (int) topMargin,
(int) (leftMargin + width), (int) (topMargin + height * (1.0f - verticalTextPos)));
}
}
}
MainActivity.java
public class MainActivity extends CardboardActivity implements CardboardView.StereoRenderer, OnFrameAvailableListener {
private static final String TAG = "VRCamMtMMainAc";
private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
private Camera camera;
private final String vertexShaderCode =
"attribute vec4 position;" +
"attribute vec2 inputTextureCoordinate;" +
"varying vec2 textureCoordinate;" +
"void main()" +
"{" +
"gl_Position = position;" +
"textureCoordinate = inputTextureCoordinate;" +
"}";
private final String fragmentShaderCode =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;" +
"varying vec2 textureCoordinate; \n" +
"uniform samplerExternalOES s_texture; \n" +
"void main(void) {" +
" gl_FragColor = texture2D( s_texture, textureCoordinate );\n" +
//" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" +
"}";
private FloatBuffer vertexBuffer, textureVerticesBuffer, vertexBuffer2;
private ShortBuffer drawListBuffer, buf2;
private int mProgram;
private int mPositionHandle, mPositionHandle2;
private int mColorHandle;
private int mTextureCoordHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 2;
static float squareVertices[] = { // in counterclockwise order:
-1.0f, -1.0f, // 0.left - mid
1.0f, -1.0f, // 1. right - mid
-1.0f, 1.0f, // 2. left - top
1.0f, 1.0f, // 3. right - top
};
private short drawOrder[] = {0, 2, 1, 1, 2, 3}; // order to draw vertices
private short drawOrder2[] = {2, 0, 3, 3, 0, 1}; // order to draw vertices
static float textureVertices[] = {
0.0f, 1.0f, // A. left-bottom
1.0f, 1.0f, // B. right-bottom
0.0f, 0.0f, // C. left-top
1.0f, 0.0f // D. right-top
};
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
private ByteBuffer indexBuffer; // Buffer for index-array
private int texture;
private CardboardOverlayView mOverlayView;
private CardboardView cardboardView;
private SurfaceTexture surface;
private float[] mView;
private float[] mCamera;
public void startCamera(int texture) {
surface = new SurfaceTexture(texture);
surface.setOnFrameAvailableListener(this);
camera = Camera.open();
Camera.Parameters params = camera.getParameters();
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
// Check what resolutions are supported by your camera
/*List<Camera.Size> sizes = params.getSupportedPictureSizes();
// Iterate through all available resolutions and choose one.
// The chosen resolution will be stored in mSize.
Camera.Size mSize = null;
for (Camera.Size size : sizes) {
Log.i(TAG, "Available resolution: " + size.width + "x" + size.height);
mSize = size;
}
params.setPictureSize(5312,2988);*/
camera.setParameters(params);
try {
camera.setPreviewTexture(surface);
camera.startPreview();
} catch (IOException ioe)
{
Log.i(TAG, "CAM LAUNCH FAILED");
}
}
static private int createTexture() {
int[] texture = new int[1];
GLES20.glGenTextures(1, texture, 0);
GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture[0]);
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
return texture[0];
}
private int loadGLShader(int type, String code) {
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, code);
GLES20.glCompileShader(shader);
// Get the compilation status.
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
// If the compilation failed, delete the shader.
if (compileStatus[0] == 0) {
Log.e(TAG, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
if (shader == 0) {
throw new RuntimeException("Error creating shader.");
}
return shader;
}
/**
* Checks if we've had an error inside of OpenGL ES, and if so what that error is.
*
* #param func
*/
private static void checkGLError(String func) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e(TAG, func + ": glError " + error);
throw new RuntimeException(func + ": glError " + error);
}
}
/**
* Sets the view to our CardboardView and initializes the transformation matrices we will use
* to render our scene.
*
* #param savedInstanceState
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cardboardView = (CardboardView) findViewById(R.id.cardboard_view);
cardboardView.setRenderer(this);
setCardboardView(cardboardView);
mCamera = new float[16];
mView = new float[16];
mOverlayView = (CardboardOverlayView) findViewById(R.id.overlay);
//mOverlayView.show3DToast("Pull the magnet when you find an object.");
}
#Override
public void onRendererShutdown() {
Log.i(TAG, "onRendererShutdown");
}
#Override
public void onSurfaceChanged(int width, int height) {
Log.i(TAG, "onSurfaceChanged");
}
/**
* Creates the buffers we use to store information about the 3D world. OpenGL doesn't use Java
* arrays, but rather needs data in a format it can understand. Hence we use ByteBuffers.
*
* #param config The EGL configuration used when creating the surface.
*/
#Override
public void onSurfaceCreated(EGLConfig config) {
Log.i(TAG, "onSurfaceCreated");
GLES20.glClearColor(0.1f, 0.1f, 0.1f, 0.5f); // Dark background so text shows up well
ByteBuffer bb = ByteBuffer.allocateDirect(squareVertices.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareVertices);
vertexBuffer.position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
ByteBuffer bb2 = ByteBuffer.allocateDirect(textureVertices.length * 4);
bb2.order(ByteOrder.nativeOrder());
textureVerticesBuffer = bb2.asFloatBuffer();
textureVerticesBuffer.put(textureVertices);
textureVerticesBuffer.position(0);
int vertexShader = loadGLShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadGLShader(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);
texture = createTexture();
startCamera(texture);
}
/**
* Prepares OpenGL ES before we draw a frame.
*
* #param headTransform The head transformation in the new frame.
*/
#Override
public void onNewFrame(HeadTransform headTransform) {
float[] mtx = new float[16];
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
surface.updateTexImage();
surface.getTransformMatrix(mtx);
}
#Override
public void onFrameAvailable(SurfaceTexture arg0) {
this.cardboardView.requestRender();
}
/**
* Draws a frame for an eye. The transformation for that eye (from the camera) is passed in as
* a parameter.
*
* #param transform The transformations to apply to render this eye.
*/
#Override
public void onDrawEye(EyeTransform transform) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glUseProgram(mProgram);
GLES20.glActiveTexture(GL_TEXTURE_EXTERNAL_OES);
GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "position");
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
false, vertexStride, vertexBuffer);
mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");
GLES20.glEnableVertexAttribArray(mTextureCoordHandle);
GLES20.glVertexAttribPointer(mTextureCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
false, vertexStride, textureVerticesBuffer);
mColorHandle = GLES20.glGetAttribLocation(mProgram, "s_texture");
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTextureCoordHandle);
Matrix.multiplyMM(mView, 0, transform.getEyeView(), 0, mCamera, 0);
}
#Override
public void onFinishFrame(Viewport viewport) {
}
#Override
public void onCardboardTrigger() {
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ui_layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.google.vrtoolkit.cardboard.CardboardView
android:id="#+id/cardboard_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true" />
<com.oculab.mehmettaha.vrcamera.CardboardOverlayView
android:id="#+id/overlay"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
</RelativeLayout>
Hence how can I handle this problem. I' ve tried to change camera resolution, but it didn' t work.
Cheers!
The problem is the camera preview size. Its expecting to be at or near full screen size but you're trying to make it half the width with the same height. After trying this I found the aspect much improved: https://stackoverflow.com/a/19592492/922339. It set the optimal preview size to 352 by 288 on my Nexus 5
You probably want the camera views to fill the screen, and don't mind if the preview is cropped on left and right. So, when you set the texture coordinates (textureVeryices), use left=0.25 and right=0.75.
Related
I was following a OpenGL tutorial (https://developer.android.com/training/graphics/opengl/touch.html#angle) and I did everything right I think. My problem is that the triangle doesnt rotate on the display. I tried to debug and mGLView.requestRender(); is getting the values but it doesnt update the image on the screen.
public class OpenGL extends Fragment {
private GLSurfaceView mGLView;
private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
private float mPreviousX;
private float mPreviousY;
private OpenGLRenderer mRenderer;
public int height = 0;
public int width = 0;
public OpenGL() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mGLView = new GLSurfaceView(getActivity());
mGLView.setEGLContextClientVersion(2);
mRenderer = new OpenGLRenderer();
mGLView.setRenderer(mRenderer);
// Render the view only when there is a change in the drawing data
mGLView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager()
.getDefaultDisplay()
.getMetrics(displayMetrics);
height = displayMetrics.heightPixels;
width = displayMetrics.widthPixels;
mGLView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent e) {
// MotionEvent reports input details from the touch screen
// and other input controls. In this case, you are only
// interested in events where the touch position changed.
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
float dy = y - mPreviousY;
// reverse direction of rotation above the mid-line
if (y > height / 2) {
dx = dx * -1;
}
// reverse direction of rotation to left of the mid-line
if (x < width / 2) {
dy = dy * -1;
}
mRenderer.setAngle(
mRenderer.getAngle() +
((dx + dy) * TOUCH_SCALE_FACTOR));
mGLView.requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
});
return mGLView;
}
#Override
public void onResume() {
super.onResume();
mGLView.onResume();
}
#Override
public void onPause() {
super.onPause();
mGLView.onPause();
}
}
class OpenGLRenderer implements GLSurfaceView.Renderer {
private Triangle mRectangle;
private OpenGL mOpenGL = new OpenGL();
// mMVPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private float[] mRotationMatrix = new float[16];
float[] scratch = new float[16];
public volatile float mAngle;
public float getAngle() {
return mAngle;
}
public void setAngle(float angle) {
mAngle = angle;
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Set the background frame color
//GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLES20.glViewport(0, 0, mOpenGL.width, mOpenGL.height);
float ratio = (float) mOpenGL.width / mOpenGL.height;
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
mRectangle = new Triangle();
}
public void onDrawFrame(GL10 gl) {
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mViewMatrix, 0);
// Create a rotation for the triangle
// long time = SystemClock.uptimeMillis() % 4000L;
// float angle = 0.090f * ((int) time);
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
// Combine the rotation matrix with the projection and camera view
// Note that the mMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
// Draw triangle
mRectangle.drawMatrix(scratch);
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
public static int loadShader(int type, String shaderCode){
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
class Triangle {
private final String vertexShaderCodeMatrix =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
// Note that the uMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition;" +
"}";
// Use to access and set the view transformation
private int mMVPMatrixHandle;
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float triangleCoords[] = { // in counterclockwise order:
0.0f, 0.622008459f, 0.0f, // top
-0.5f, -0.311004243f, 0.0f, // bottom left
0.5f, -0.311004243f, 0.0f // bottom right
};
// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.5f, 0.5f, 0.5f, 1.0f };
private final int mProgram;
private short[] indices = {0,1,2,0,2,3};
private FloatBuffer vertexBuffer;
private ShortBuffer indexBuffer;
public Triangle() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (number of coordinate values * 4 bytes per float)
triangleCoords.length * 4);
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
// create a floating point buffer from the ByteBuffer
vertexBuffer = bb.asFloatBuffer();
// add the coordinates to the FloatBuffer
vertexBuffer.put(triangleCoords);
// set the buffer to read the first coordinate
vertexBuffer.position(0);
int vertexShader = OpenGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = OpenGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
// create empty OpenGL ES Program
mProgram = GLES20.glCreateProgram();
// add the vertex shader to program
GLES20.glAttachShader(mProgram, vertexShader);
// add the fragment shader to program
GLES20.glAttachShader(mProgram, fragmentShader);
// creates OpenGL ES program executables
GLES20.glLinkProgram(mProgram);
}
private int mPositionHandle;
private int mColorHandle;
private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
public void draw() {
// Add program to OpenGL ES environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
public void drawMatrix(float[] mvpMatrix) { // pass in the calculated transformation matrix
// Add program to OpenGL ES environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
// Pass the projection and view transformation to the shader
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
You loaded 'vertexShaderCode' instead of 'vertexShaderCodeMatrix' in the Triangle class.
I tried to develop a mobile cardboard application, which renders 3d objects into a camera view (some kind of ar).
I used this project and tried to render a simple cube in the camera:
https://github.com/Sveder/CardboardPassthrough/
I didn't get it working, the background is always black or the app wrecked.
I would be very grateful for any help or suggestions.
Thanks
Thats what i have
Origin CardboardPassthrough
here is the working code, with the cubes
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.os.Bundle;
import android.os.Vibrator;
import android.util.Log;
import android.view.KeyEvent;
import com.google.vrtoolkit.cardboard.*;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
public class Card extends CardboardActivity implements CardboardView.StereoRenderer, SurfaceTexture.OnFrameAvailableListener {
private static final float CAMERA_Z = 0.01f;
private static final float TIME_DELTA = 0.3f;
private static final float YAW_LIMIT = 0.12f;
private static final float PITCH_LIMIT = 0.12f;
//---------------------------------------------------
private int intCurrentI = -1;
private int intCurrentI1 = -1;
//---------------------------------------------------
// We keep the light always position just above the user.
private final float[] mLightPosInWorldSpace = new float[]{0.0f, 2.0f, 0.0f, 1.0f};
private final float[] mLightPosInEyeSpace = new float[4];
private static final int COORDS_PER_VERTEX = 3;
private final WorldLayoutData DATA = new WorldLayoutData();
private FloatBuffer mCubeVertices;
private FloatBuffer mCubeColors;
private FloatBuffer mCubeFoundColors;
private FloatBuffer mCubeNormals;
private int mGlProgram;
private int mPositionParam;
private int mNormalParam;
private int mColorParam;
private int mModelViewProjectionParam;
private int mLightPosParam;
private int mModelViewParam;
private int mModelParam;
private int mIsFloorParam;
private float[] mModelCube;
private float[] mCamera;
private float[] mView;
private float[] mHeadView;
private float[] mModelViewProjection;
private float[] mModelView;
private float[] mModelCube2;
private float[] mModelFloor;
private float mObjectDistance = 12f;
private float mFloorDepth = 20f;
private Vibrator mVibrator;
private CardboardOverlayView mOverlayView;
private SurfaceTexture surface;
private Camera camera;
private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
private final String vertexShaderCode =
"attribute vec4 position;" +
"attribute vec2 inputTextureCoordinate;" +
"varying vec2 textureCoordinate;" +
"void main()" +
"{" +
"gl_Position = position;" +
"textureCoordinate = inputTextureCoordinate;" +
"}";
private final String fragmentShaderCode =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;" +
"varying vec2 textureCoordinate; \n" +
"uniform samplerExternalOES s_texture; \n" +
"void main(void) {" +
" gl_FragColor = texture2D( s_texture, textureCoordinate );\n" +
//" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" +
"}";
private int texture;
private CardboardView cardboardView;
/**
* Converts a raw text file, saved as a resource, into an OpenGL ES shader
*
* #param type The type of shader we will be creating.
* #param resId The resource ID of the raw text file about to be turned into a shader.
* #return
*/
private int loadGLShader(int type, int resId) {
String code = readRawTextFile(resId);
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, code);
GLES20.glCompileShader(shader);
// Get the compilation status.
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
// If the compilation failed, delete the shader.
if (compileStatus[0] == 0) {
GLES20.glDeleteShader(shader);
shader = 0;
}
if (shader == 0) {
throw new RuntimeException("Error creating shader.");
}
return shader;
}
/**
* Checks if we've had an error inside of OpenGL ES, and if so what that error is.
*
* #param func
*/
private static void checkGLError(String func) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
throw new RuntimeException(func + ": glError " + error);
}
}
/**
* Sets the view to our CardboardView and initializes the transformation matrices we will use
* to render our scene.
*
* #param savedInstanceState
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.common_ui);
cardboardView = (CardboardView) findViewById(R.id.cardboard_view);
/*********************/
cardboardView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
/*********************/
cardboardView.setRenderer(this);
setCardboardView(cardboardView);
/* 2014-10-16 */
mModelCube2 = new float[16];
/* 2014-10-16 */
mModelCube = new float[16];
mCamera = new float[16];
mView = new float[16];
mModelViewProjection = new float[16];
mModelView = new float[16];
mModelFloor = new float[16];
mHeadView = new float[16];
mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
mOverlayView = (CardboardOverlayView) findViewById(R.id.overlay);
mOverlayView.show3DToast("VR-Test");
}
#Override
public void onRendererShutdown() {
}
#Override
public void onSurfaceChanged(int width, int height) {
}
/**
* Creates the buffers we use to store information about the 3D world. OpenGL doesn't use Java
* arrays, but rather needs data in a format it can understand. Hence we use ByteBuffers.
*
* #param config The EGL configuration used when creating the surface.
*/
#Override
public void onSurfaceCreated(EGLConfig config) {
GLES20.glClearColor(0.1f, 0.1f, 0.1f, 0.5f); // Dark background so text shows up well
ByteBuffer bbVertices = ByteBuffer.allocateDirect(DATA.CUBE_COORDS.length * 4);
bbVertices.order(ByteOrder.nativeOrder());
mCubeVertices = bbVertices.asFloatBuffer();
mCubeVertices.put(DATA.CUBE_COORDS);
mCubeVertices.position(0);
ByteBuffer bbColors = ByteBuffer.allocateDirect(DATA.CUBE_COLORS.length * 4);
bbColors.order(ByteOrder.nativeOrder());
mCubeColors = bbColors.asFloatBuffer();
mCubeColors.put(DATA.CUBE_COLORS);
mCubeColors.position(0);
ByteBuffer bbFoundColors = ByteBuffer.allocateDirect(DATA.CUBE_FOUND_COLORS.length * 4);
bbFoundColors.order(ByteOrder.nativeOrder());
mCubeFoundColors = bbFoundColors.asFloatBuffer();
mCubeFoundColors.put(DATA.CUBE_FOUND_COLORS);
mCubeFoundColors.position(0);
ByteBuffer bbNormals = ByteBuffer.allocateDirect(DATA.CUBE_NORMALS.length * 4);
bbNormals.order(ByteOrder.nativeOrder());
mCubeNormals = bbNormals.asFloatBuffer();
mCubeNormals.put(DATA.CUBE_NORMALS);
mCubeNormals.position(0);
int vertexShader = loadGLShader(GLES20.GL_VERTEX_SHADER, R.raw.light_vertex);
int gridShader = loadGLShader(GLES20.GL_FRAGMENT_SHADER, R.raw.grid_fragment);
mGlProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mGlProgram, vertexShader);
GLES20.glAttachShader(mGlProgram, gridShader);
GLES20.glLinkProgram(mGlProgram);
texture = createTexture();
startCamera(texture);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
// Object first appears directly in front of user
Matrix.setIdentityM(mModelCube, 0);
Matrix.translateM(mModelCube, 0, 0, 0, -mObjectDistance);
Matrix.setIdentityM(mModelCube2, 0);
Matrix.translateM(mModelCube2, 0, -10.0f, -10.0f, -mObjectDistance - 12.0f);
Matrix.setIdentityM(mModelFloor, 0);
Matrix.translateM(mModelFloor, 0, 0, -mFloorDepth, 0); // Floor appears below user
checkGLError("onSurfaceCreated");
}
/**
* Converts a raw text file into a string.
*
* #param resId The resource ID of the raw text file about to be turned into a shader.
* #return
*/
private String readRawTextFile(int resId) {
InputStream inputStream = getResources().openRawResource(resId);
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
reader.close();
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
/**
* Prepares OpenGL ES before we draw a frame.
*
* #param headTransform The head transformation in the new frame.
*/
#Override
public void onNewFrame(HeadTransform headTransform) {
GLES20.glUseProgram(mGlProgram);
mModelViewProjectionParam = GLES20.glGetUniformLocation(mGlProgram, "u_MVP");
mLightPosParam = GLES20.glGetUniformLocation(mGlProgram, "u_LightPos");
mModelViewParam = GLES20.glGetUniformLocation(mGlProgram, "u_MVMatrix");
mModelParam = GLES20.glGetUniformLocation(mGlProgram, "u_Model");
mIsFloorParam = GLES20.glGetUniformLocation(mGlProgram, "u_IsFloor");
// Build the Model part of the ModelView matrix.
Matrix.rotateM(mModelCube, 0, TIME_DELTA, 0.5f, 0.5f, 1.0f);
Matrix.rotateM(mModelCube2, 0, TIME_DELTA, 0.5f, 0.5f, 1.0f);
//--------------------------------------
// Build the camera matrix and apply it to the ModelView.
Matrix.setLookAtM(mCamera, 0, 0.0f, 0.0f, CAMERA_Z, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
headTransform.getHeadView(mHeadView, 0);
checkGLError("onReadyToDraw");
}
/**
* Draws a frame for an eye. The transformation for that eye (from the camera) is passed in as
* a parameter.
*
* #param transform The transformations to apply to render this eye.
*/
#Override
public void onDrawEye(EyeTransform transform) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
mPositionParam = GLES20.glGetAttribLocation(mGlProgram, "a_Position");
mNormalParam = GLES20.glGetAttribLocation(mGlProgram, "a_Normal");
mColorParam = GLES20.glGetAttribLocation(mGlProgram, "a_Color");
GLES20.glEnableVertexAttribArray(mPositionParam);
GLES20.glEnableVertexAttribArray(mNormalParam);
GLES20.glEnableVertexAttribArray(mColorParam);
checkGLError("mColorParam");
// Apply the eye transformation to the camera.
Matrix.multiplyMM(mView, 0, transform.getEyeView(), 0, mCamera, 0);
// Set the position of the light
Matrix.multiplyMV(mLightPosInEyeSpace, 0, mView, 0, mLightPosInWorldSpace, 0);
GLES20.glUniform3f(mLightPosParam, mLightPosInEyeSpace[0], mLightPosInEyeSpace[1],
mLightPosInEyeSpace[2]);
// Build the ModelView and ModelViewProjection matrices
// for calculating cube position and light.
Matrix.multiplyMM(mModelView, 0, mView, 0, mModelCube, 0);
Matrix.multiplyMM(mModelViewProjection, 0, transform.getPerspective(), 0,
mModelView, 0);
drawCube(1);
//--------------------------------------
Matrix.multiplyMM(mModelView, 0, mView, 0, mModelCube2, 0);
Matrix.multiplyMM(mModelViewProjection, 0, transform.getPerspective(), 0,
mModelView, 0);
drawCube(0);
//--------------------------------------
}
#Override
public void onFinishFrame(Viewport viewport) {
}
public void drawCube(int i1) {
// This is not the floor!
GLES20.glUniform1f(mIsFloorParam, 0f);
// Set the Model in the shader, used to calculate lighting
if (i1 == 1) {
GLES20.glUniformMatrix4fv(mModelParam, 1, false, mModelCube, 0);
} else if (i1 == 0) {
//--2014-10-16 ??--------------------------------
GLES20.glUniformMatrix4fv(mModelParam, 1, false, mModelCube2, 0);
//-------------------------------------------------
}
// Set the ModelView in the shader, used to calculate lighting
GLES20.glUniformMatrix4fv(mModelViewParam, 1, false, mModelView, 0);
// Set the position of the cube
GLES20.glVertexAttribPointer(mPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
false, 0, mCubeVertices);
// Set the ModelViewProjection matrix in the shader.
GLES20.glUniformMatrix4fv(mModelViewProjectionParam, 1, false, mModelViewProjection, 0);
// Set the normal positions of the cube, again for shading
GLES20.glVertexAttribPointer(mNormalParam, 3, GLES20.GL_FLOAT,
false, 0, mCubeNormals);
if (isLookingAtObject(i1)) {
GLES20.glVertexAttribPointer(mColorParam, 4, GLES20.GL_FLOAT, false,
0, mCubeFoundColors);
if (i1 == 1)
intCurrentI1 = i1;
else
intCurrentI1 = -1;
intCurrentI = i1;
System.out.println("drawCube->intCurrentI2:" + intCurrentI);
} else {
GLES20.glVertexAttribPointer(mColorParam, 4, GLES20.GL_FLOAT, false,
0, mCubeColors);
intCurrentI = -1;
}
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 36);
checkGLError("Drawing cube");
if (intCurrentI1 != -1)
intCurrentI = intCurrentI1;
System.out.println("drawCube_out_if->intCurrentI4:" + intCurrentI);
}
private boolean isLookingAtObject(int i1) {
float[] initVec = {0, 0, 0, 1.0f};
float[] objPositionVec = new float[4];
System.out.println("isLookingAtObject1->i1:" + i1);
// Convert object space to camera space. Use the headView from onNewFrame.
if (i1 == 1) {
Matrix.multiplyMM(mModelView, 0, mHeadView, 0, mModelCube, 0);
Matrix.multiplyMV(objPositionVec, 0, mModelView, 0, initVec, 0);
intCurrentI = i1;
} else if (i1 == 0) {
Matrix.multiplyMM(mModelView, 0, mHeadView, 0, mModelCube2, 0);
Matrix.multiplyMV(objPositionVec, 0, mModelView, 0, initVec, 0);
intCurrentI = i1;
}
float pitch = (float) Math.atan2(objPositionVec[1], -objPositionVec[2]);
float yaw = (float) Math.atan2(objPositionVec[0], -objPositionVec[2]);
boolean bool1 = (Math.abs(pitch) < PITCH_LIMIT) && (Math.abs(yaw) < YAW_LIMIT);
return bool1;
}
public void startCamera(int texture) {
surface = new SurfaceTexture(texture);
surface.setOnFrameAvailableListener(this);
camera = Camera.open();
try {
camera.setPreviewTexture(surface);
camera.startPreview();
} catch (IOException ioe) {
Log.w("MainActivity", "CAM LAUNCH FAILED");
}
}
static private int createTexture() {
int[] texture = new int[1];
GLES20.glGenTextures(1, texture, 0);
GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture[0]);
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
return texture[0];
}
#Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
this.cardboardView.requestRender();
}
}
Edit 11.07.16
I cut the vertex in half, to see posible vertex behind it.
But i think the problem is vertex and fragment-shader
The Cube is only showing up, when im using this vertex and fragment-shader
simple_fragment.shader
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
light_vertex.shader
uniform mat4 u_MVP;
uniform mat4 u_MVMatrix;
uniform mat4 u_Model;
uniform vec3 u_LightPos;
uniform float u_IsFloor;
attribute vec4 a_Position;
attribute vec4 a_Color;
attribute vec3 a_Normal;
varying vec4 v_Color;
varying vec3 v_Grid;
varying float v_isFloor;
void main()
{
vec3 modelVertex = vec3(u_Model * a_Position);
v_Grid = modelVertex;
vec3 modelViewVertex = vec3(u_MVMatrix * a_Position);
vec3 modelViewNormal = vec3(u_MVMatrix * vec4(a_Normal, 0.0));
float distance = length(u_LightPos - modelViewVertex);
vec3 lightVector = normalize(u_LightPos - modelViewVertex);
float diffuse = max(dot(modelViewNormal, lightVector), 0.5 );
diffuse = diffuse * (1.0 / (1.0 + (0.00001 * distance * distance)));
v_Color = a_Color * diffuse;
gl_Position = u_MVP * a_Position;
v_isFloor = u_IsFloor;
}
and the camera is only showing up when im using this shaders
vertex.shader
attribute vec4 position;
attribute vec2 inputTextureCoordinate;
varying vec2 textureCoordinate;
void main()
{
gl_Position = position;
textureCoordinate = inputTextureCoordinate;
}
fragment.shader
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 textureCoordinate;
varying vec4 v_Color;
uniform samplerExternalOES s_texture;
void main(void) {
gl_FragColor = texture2D( s_texture, textureCoordinate );
}
i don't know how to fix the shaders
I would suggest that you disable glEnable(GL_DEPTH_TEST) to render background objects in the foreground then switch between shaders using:
GLES20.glUseProgram();
For your example this might be:
#Override
public void onSurfaceCreated(EGLConfig config) {
Log.i(TAG, "onSurfaceCreated");
GLES20.glClearColor(0.1f, 0.1f, 0.1f, 0.5f); // Dark background so text shows up well
ByteBuffer bb = ByteBuffer.allocateDirect(squareVertices.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareVertices);
vertexBuffer.position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
ByteBuffer bb2 = ByteBuffer.allocateDirect(textureVertices.length * 4);
bb2.order(ByteOrder.nativeOrder());
textureVerticesBuffer = bb2.asFloatBuffer();
textureVerticesBuffer.put(textureVertices);
textureVerticesBuffer.position(0);
//Cube
ByteBuffer bbVertices = ByteBuffer.allocateDirect(DATA.CUBE_COORDS.length * 4);
bbVertices.order(ByteOrder.nativeOrder());
mCubeVertices = bbVertices.asFloatBuffer();
mCubeVertices.put(DATA.CUBE_COORDS);
mCubeVertices.position(0);
ByteBuffer bbColors = ByteBuffer.allocateDirect(DATA.CUBE_COLORS.length * 4);
bbColors.order(ByteOrder.nativeOrder());
mCubeColors = bbColors.asFloatBuffer();
mCubeColors.put(DATA.CUBE_COLORS);
mCubeColors.position(0);
ByteBuffer bbFoundColors = ByteBuffer.allocateDirect(DATA.CUBE_FOUND_COLORS.length * 4);
bbFoundColors.order(ByteOrder.nativeOrder());
mCubeFoundColors = bbFoundColors.asFloatBuffer();
mCubeFoundColors.put(DATA.CUBE_FOUND_COLORS);
mCubeFoundColors.position(0);
ByteBuffer bbNormals = ByteBuffer.allocateDirect(DATA.CUBE_NORMALS.length * 4);
bbNormals.order(ByteOrder.nativeOrder());
mCubeNormals = bbNormals.asFloatBuffer();
mCubeNormals.put(DATA.CUBE_NORMALS);
mCubeNormals.position(0);
int vertexShader = loadGLShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadGLShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mCameraProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(mCameraProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mCameraProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mCameraProgram);
vertexShader = loadGLShader(GLES20.GL_VERTEX_SHADER, R.raw.light_vertex);
fragmentShader = loadGLShader(GLES20.GL_FRAGMENT_SHADER, R.raw.grid_fragment);
mCubeProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(mCubeProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mCubeProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mCubeProgram);
texture = createTexture();
startCamera(texture);
Matrix.setIdentityM(mModelCube, 0);
Matrix.translateM(mModelCube, 0, 0, 0, -mObjectDistance);
checkGLError("onSurfaceCreated");
}
#Override
public void onNewFrame(HeadTransform headTransform) {
GLES20.glUseProgram(mCubeProgram);
mModelViewProjectionParam = GLES20.glGetUniformLocation(mCubeProgram, "u_MVP");
mLightPosParam = GLES20.glGetUniformLocation(mCubeProgram, "u_LightPos");
mModelViewParam = GLES20.glGetUniformLocation(mCubeProgram, "u_MVMatrix");
mModelParam = GLES20.glGetUniformLocation(mCubeProgram, "u_Model");
mIsFloorParam = GLES20.glGetUniformLocation(mCubeProgram, "u_IsFloor");
// Build the Model part of the ModelView matrix.
Matrix.rotateM(mModelCube, 0, TIME_DELTA, 0.5f, 0.5f, 1.0f);
// Build the camera matrix and apply it to the ModelView.
Matrix.setLookAtM(mCamera, 0, 0.0f, 0.0f, CAMERA_Z, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
headTransform.getHeadView(mHeadView, 0);
GLES20.glUseProgram(mCameraProgram);
float[] mtx = new float[16];
//GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
surface.updateTexImage();
surface.getTransformMatrix(mtx);
}
#Override
public void onDrawEye(EyeTransform transform) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
//Camera
GLES20.glUseProgram(mCameraProgram);
GLES20.glActiveTexture(GL_TEXTURE_EXTERNAL_OES);
GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
mPositionHandle = GLES20.glGetAttribLocation(mCameraProgram, "position");
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
false, vertexStride, vertexBuffer);
mTextureCoordHandle = GLES20.glGetAttribLocation(mCameraProgram, "inputTextureCoordinate");
GLES20.glEnableVertexAttribArray(mTextureCoordHandle);
GLES20.glVertexAttribPointer(mTextureCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
false, vertexStride, textureVerticesBuffer);
mColorHandle = GLES20.glGetAttribLocation(mCameraProgram, "s_texture");
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTextureCoordHandle);
//cube
GLES20.glUseProgram(mCubeProgram);
mPositionParam = GLES20.glGetAttribLocation(mCubeProgram, "a_Position");
mNormalParam = GLES20.glGetAttribLocation(mCubeProgram, "a_Normal");
mColorParam = GLES20.glGetAttribLocation(mCubeProgram, "a_Color");
GLES20.glEnableVertexAttribArray(mPositionParam);
GLES20.glEnableVertexAttribArray(mNormalParam);
GLES20.glEnableVertexAttribArray(mColorParam);
// Set the position of the light
Matrix.multiplyMV(mLightPosInEyeSpace, 0, mView, 0, mLightPosInWorldSpace, 0);
GLES20.glUniform3f(mLightPosParam, mLightPosInEyeSpace[0], mLightPosInEyeSpace[1],
mLightPosInEyeSpace[2]);
Matrix.multiplyMM(mModelView, 0, mView, 0, mModelCube, 0);
Matrix.multiplyMM(mModelViewProjection, 0, transform.getPerspective(), 0,
mModelView, 0);
drawCube(1);
Matrix.multiplyMM(mView, 0, transform.getEyeView(), 0, mCamera, 0);
Just an open suggestion. I developed an AR project for a University assignment I had, a couple of months ago. In my case I used a tool called Vuforia and integrated it with Unity for it to work on mobile devices. You can get your app to work on both Android and iOS devices. The latest releases of both Unity and Vuforia both help in the development of AR projects, since it is currently at its hype.
Depending on the work you need you AR project to perform, and your experience with Unity, the learning curve increases. In my case, I augmented the construction of a roof for a neolithical site. I also used a third party software called makehuman and Blender to create a walking human being. In all of my project, I didn't need to touch a line of code at all :)
Hope this helps.
I want to add another Texture from a PNG Image, it is more like a logo so I wanted it to be in center and in the background shows the GLSurfaceView with all its effects and models. Please see my nicely done illustrations.
---------------------------
| GLSurfaceView * |
| |
| |
| |
| * ------------- |
| | | |
| | PNG | |
| | Texture | * |
| * | | |
| ------------- |
| * |
| |
| |
| * * |
---------------------------
LEGEND: * = some good effects :D (pweew! pweew!)
I am trying to make a live wallpaper using WallpaperService which has a subclass that extends Engine class, that class has a subclass that extends GLSurfaceView, what I have so far is the GLSurfaceView and it is working great!
-Added- FlashWallpaperService
public class FlashWallpaperService extends WallpaperService {
#Override
public Engine onCreateEngine() {
return new GLEngine();
}
public class GLEngine extends Engine {
private FlashGLSurfaceView flashGLSurfaceView;
#Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
flashGLSurfaceView = new FlashGLSurfaceView(FlashWallpaperService.this);
}
#Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (visible) {
flashGLSurfaceView.onResume();
} else {
flashGLSurfaceView.onPause();
}
}
#Override
public void onDestroy() {
super.onDestroy();
flashGLSurfaceView.onDestroy();
}
class FlashGLSurfaceView extends GLSurfaceView {
private ActivityManager activityManager;
private ConfigurationInfo configurationInfo;
private boolean supportsEs2;
public FlashGLSurfaceView(Context context) {
super(context);
if (!isInEditMode()) {
activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
configurationInfo = activityManager.getDeviceConfigurationInfo();
supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000;
if (supportsEs2) {
// Request an OpenGL ES 2.0 compatible context.
this.setEGLContextClientVersion(2);
// Set the renderer to our demo renderer, defined below.
FlashSystemRenderer mRenderer = new FlashSystemRenderer(this);
this.setRenderer(mRenderer);
this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
} else {
if (!isInEditMode()) throw new UnsupportedOperationException();
}
}
}
public FlashGLSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setEGLContextClientVersion(2);
this.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
this.setZOrderOnTop(false);
if (!isInEditMode()) {
activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
configurationInfo = activityManager.getDeviceConfigurationInfo();
supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000;
if (supportsEs2) {
// Request an OpenGL ES 2.0 compatible context.
this.setEGLContextClientVersion(2);
// Set the renderer to our demo renderer, defined below.
FlashSystemRenderer mRenderer = new FlashSystemRenderer(this);
this.setRenderer(mRenderer);
this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
} else {
if (!isInEditMode()) throw new UnsupportedOperationException();
}
}
}
#Override
public SurfaceHolder getHolder() {
return getSurfaceHolder();
}
public void onDestroy() {
super.onDetachedFromWindow();
}
}
}
}
-Added- Renderer class
public class FlashSystemRenderer implements GLSurfaceView.Renderer {
public float ratio;
public int mvpMatrixHandle;
public int mvMatrixHandle = -1;
public int positionHandle;
public int normalHandle;
public int textureCoordinateHandle;
public int programHandle;
public int miscHandle;
public int sizeX = 35;
public int sizeY = 70;
public float mTime;
private GLSurfaceView mGlSurfaceView;
/**
* Store the model matrix. This matrix is used to move models from object space (where each model can be thought
* of being located at the center of the universe) to world space.
*/
private float[] mModelMatrix = new float[16];
/**
* Store the view matrix. This can be thought of as our camera. This matrix transforms world space to eye space;
* it positions things relative to our eye.
*/
private float[] mViewMatrix = new float[16];
/** Store the projection matrix. This is used to project the scene onto a 2D viewport. */
private float[] mProjectionMatrix = new float[16];
/** Allocate storage for the final combined matrix. This will be passed into the shader program. */
private float[] mMVPMatrix = new float[16];
private float[] mTemporaryMatrix = new float[16];
private int timeHandle;
private long mStartTime;
private int frames;
private long startTime;
private boolean mStart;
private long timePassed;
private float dt;
private long t_current;
private long t_prev;
private float dt_prev = 1;
private ValueAnimator animator;
private Bitmap mBitmap;
private FlashSystem mFlashSystem;
private Context context;
private int resolutionHandle;
private int mWidth;
private int mHeight;
private int timesRepeated;
private float delta;
private ExecutorService mExecutor = Executors.newSingleThreadExecutor();
public FlashSystemRenderer(GLSurfaceView glSurfaceView) {
mGlSurfaceView = glSurfaceView;
context = glSurfaceView.getContext();
}
#Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
// Use culling to remove back faces.
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glFrontFace(GLES20.GL_CW);
// Enable depth testing
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
// Position the eye in front of the origin.
final float eyeX = 0.0f;
final float eyeY = 0.0f;
final float eyeZ = 0.0f;
// We are looking toward the distance
final float lookX = 0.0f;
final float lookY = 0.0f;
final float lookZ = 1.0f;
// Set our up vector. This is where our head would be pointing were we holding the camera.
final float upX = 0.0f;
final float upY = 1.0f;
final float upZ = 0.0f;
Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
final String vertexShader = RawResourceReader.readTextFileFromRawResource(context, R.raw.flash_vert);
final String fragmentShader = RawResourceReader.readTextFileFromRawResource(context, R.raw.flash_frag);
final int vertexShaderHandle = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader);
final int fragmentShaderHandle = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);
programHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle,
new String[]{"a_Position", "a_TexCoordinate", "a_TileXY"});
}
#Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
// Set the OpenGL viewport to the same size as the surface.
GLES20.glViewport(0, 0, width, height);
mWidth = width;
mHeight = height;
// Create a new perspective projection matrix. The height will stay the same
// while the width will vary as per aspect ratio.
final float ratio = (float) width / height;
final float left = -ratio;
#SuppressWarnings("UnnecessaryLocalVariable")
final float right = ratio;
final float bottom = -1.0f;
final float top = 1.0f;
final float near = 1.0f;
final float far = 10.0f;
this.ratio = ratio;
Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
mStartTime = System.currentTimeMillis();
mExecutor.execute(new FlashsGenerator(this));
}
#Override
public void onDrawFrame(GL10 gl10) {
logFrame();
drawGl();
if (mFlashSystem != null) {
mFlashSystem.render();
}
}
private void drawGl() {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glUseProgram(programHandle);
// Set program handles
mvpMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVPMatrix");
mvMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVMatrix");
timeHandle = GLES20.glGetUniformLocation(programHandle, "u_Time");
resolutionHandle = GLES20.glGetUniformLocation(programHandle, "u_Resolution");
positionHandle = GLES20.glGetAttribLocation(programHandle, "a_Position");
normalHandle = GLES20.glGetAttribLocation(programHandle, "a_Normal");
textureCoordinateHandle = GLES20.glGetAttribLocation(programHandle, "a_TexCoordinate");
miscHandle = GLES20.glGetAttribLocation(programHandle, "a_Misc");
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 0.0f, 0.0f, 5f);
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
// Pass in the modelview matrix.
GLES20.glUniformMatrix4fv(mvMatrixHandle, 1, false, mMVPMatrix, 0);
Matrix.multiplyMM(mTemporaryMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
System.arraycopy(mTemporaryMatrix, 0, mMVPMatrix, 0, 16);
// Pass in the combined matrix.
GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, mMVPMatrix, 0);
// Pass in u_Time
GLES20.glUniform1f(timeHandle, (System.currentTimeMillis() - mStartTime) / 3500f);
// u_Resolution
GLES20.glUniform2f(resolutionHandle, mWidth, mHeight);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
GLES20.glEnable(GLES20.GL_BLEND);
}
public void logFrame() {
frames++;
timePassed = (System.nanoTime() - startTime) / 1_000_000;
if(timePassed >= 10_000) {
frames = 0;
startTime = System.nanoTime();
}
}
public void onTouchEvent() {
if (mStart) {
reset();
}
mStart = !mStart;
mStartTime = System.nanoTime();
}
private void reset() {
if (animator != null) {
animator.cancel();
}
mStartTime = 0;
dt = 0;
t_prev = 0;
}
public FlashSystem getFlashSystem() {
return mFlashSystem;
}
public void setFlashSystem(final FlashSystem flashSystem) {
mFlashSystem = flashSystem;
}
public void queue(Runnable runnable) {
mGlSurfaceView.queueEvent(runnable);
}
}
-Added- flash_vert.glsl
uniform mat4 u_MVPMatrix; // A constant representing the combined model/view/projection matrix.
uniform mat4 u_MVMatrix; // A constant representing the combined model/view matrix.
uniform float u_Time;
uniform vec2 u_Resolution;
attribute vec4 a_Position; //initial
attribute vec2 a_TexCoordinate;
attribute vec4 a_Misc; //initial
varying vec2 v_TexCoordinate;
varying float v_Radius;
#define RADIUS 3.5
float rand( vec2 co )
{
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
void rotate( in float angle, inout vec2 vector )
{
mat2 rotationMatrix = mat2( cos( angle ), -sin( angle ),
sin( angle ), cos( angle ));
vector *= rotationMatrix;
}
void main()
{
// Transform the vertex into eye space.
//v_Position = vec3(u_MVMatrix * a_Position);
float aspect = u_Resolution.x / u_Resolution.y;
// Pass through the texture coordinate.
v_TexCoordinate = a_TexCoordinate;
vec2 centerPos = a_Position.xy;
float f = mix(1.0, a_Misc.t, u_Time);
centerPos *= mod(f, RADIUS);
float size = a_Misc.s;
size = mix(0.0, size, mod(f, RADIUS)/RADIUS);
vec2 relativePos = vec2(
(a_TexCoordinate.s - 0.5) * 2.0 * size,
(a_TexCoordinate.t - 0.5) * 2.0 * size
);
vec2 v = vec2(0.0, 1.0);
vec4 pos = vec4(
relativePos + centerPos,
0.0,
1.0
);
gl_Position = u_MVPMatrix * pos;
v_Radius = size * 2.5;
}
-Added- flash_frag.glsl
precision mediump float;
uniform sampler2D uTexture;
varying vec2 vTexPosition;
void main() {
gl_FragColor = texture2D(uTexture, vTexPosition);
}
How can I add the PNG texture from my existing renderer to render them together given that the PNG texture having transparency/alpha is in the top of all other objects? All of what I did find from searching did not work, some does but it show the PNG texture only
Hi i want to show a live camera preview side by side on my Android Phone.
Like this:
https://lh6.googleusercontent.com/-Ngo4tD4AvWY/U8JOHxxQ9KI/AAAAAAAACK8/NZjnw1Yqg8A/w384-h287/2.jpg
It works if just the left half screen shows the live preview or just the right side,
but if i want to show the left and the rigth side simultaneously i just get a black screen
And i dont know why.
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.test.normaltest.sbscameraview" >
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
MainActivity
public class MainActivity extends Activity {
private CameraView cameraView1;
private CameraView cameraView2;
private Camera mCamera1;
private Camera mCamera2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCamera1 = getCameraInstance();
cameraView1 = new CameraView(this, mCamera1);
cameraView2 = new CameraView(this, mCamera1);
FrameLayout preview1 = (FrameLayout) findViewById(R.id.camera_preview1);
preview1.addView(cameraView1);
FrameLayout preview2 = (FrameLayout) findViewById(R.id.camera_preview2);
preview2.addView(cameraView2);
}
/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
Log.i("camera", "null");
}
return c; // returns null if camera is unavailable
}
}
CameraView
public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraView(Context context, Camera camera) {
super(context);
mHolder = getHolder();
mHolder.addCallback(this);
mCamera = camera;
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i("CameraView", "surfaceChanged");
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
You can use these codes to open camera on Google Cardboard.
Manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.oculab.mehmettaha.vrcamera" >
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.front" android:required="false" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
CardboardOverlayView.java
public class CardboardOverlayView extends LinearLayout {
private static final String TAG = CardboardOverlayView.class.getSimpleName();
private final CardboardOverlayEyeView mLeftView;
private final CardboardOverlayEyeView mRightView;
private AlphaAnimation mTextFadeAnimation;
public CardboardOverlayView(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(HORIZONTAL);
LayoutParams params = new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1.0f);
params.setMargins(0, 0, 0, 0);
mLeftView = new CardboardOverlayEyeView(context, attrs);
mLeftView.setLayoutParams(params);
addView(mLeftView);
mRightView = new CardboardOverlayEyeView(context, attrs);
mRightView.setLayoutParams(params);
addView(mRightView);
// Set some reasonable defaults.
setDepthOffset(0.016f);
setColor(Color.rgb(150, 255, 180));
setVisibility(View.VISIBLE);
mTextFadeAnimation = new AlphaAnimation(1.0f, 0.0f);
mTextFadeAnimation.setDuration(5000);
}
public void show3DToast(String message) {
setText(message);
setTextAlpha(1f);
mTextFadeAnimation.setAnimationListener(new EndAnimationListener() {
#Override
public void onAnimationEnd(Animation animation) {
setTextAlpha(0f);
}
});
startAnimation(mTextFadeAnimation);
}
private abstract class EndAnimationListener implements Animation.AnimationListener {
#Override public void onAnimationRepeat(Animation animation) {}
#Override public void onAnimationStart(Animation animation) {}
}
private void setDepthOffset(float offset) {
mLeftView.setOffset(offset);
mRightView.setOffset(-offset);
}
private void setText(String text) {
mLeftView.setText(text);
mRightView.setText(text);
}
private void setTextAlpha(float alpha) {
mLeftView.setTextViewAlpha(alpha);
mRightView.setTextViewAlpha(alpha);
}
private void setColor(int color) {
mLeftView.setColor(color);
mRightView.setColor(color);
}
/**
* A simple view group containing some horizontally centered text underneath a horizontally
* centered image.
*
* This is a helper class for CardboardOverlayView.
*/
private class CardboardOverlayEyeView extends ViewGroup {
private final ImageView imageView;
private final TextView textView;
private float offset;
public CardboardOverlayEyeView(Context context, AttributeSet attrs) {
super(context, attrs);
imageView = new ImageView(context, attrs);
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageView.setAdjustViewBounds(true); // Preserve aspect ratio.
addView(imageView);
textView = new TextView(context, attrs);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14.0f);
textView.setTypeface(textView.getTypeface(), Typeface.BOLD);
textView.setGravity(Gravity.CENTER);
textView.setShadowLayer(3.0f, 0.0f, 0.0f, Color.DKGRAY);
addView(textView);
}
public void setColor(int color) {
imageView.setColorFilter(color);
textView.setTextColor(color);
}
public void setText(String text) {
textView.setText(text);
}
public void setTextViewAlpha(float alpha) {
textView.setAlpha(alpha);
}
public void setOffset(float offset) {
this.offset = offset;
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// Width and height of this ViewGroup.
final int width = right - left;
final int height = bottom - top;
// The size of the image, given as a fraction of the dimension as a ViewGroup. We multiply
// both width and heading with this number to compute the image's bounding box. Inside the
// box, the image is the horizontally and vertically centered.
final float imageSize = 0.12f;
// The fraction of this ViewGroup's height by which we shift the image off the ViewGroup's
// center. Positive values shift downwards, negative values shift upwards.
final float verticalImageOffset = -0.07f;
// Vertical position of the text, specified in fractions of this ViewGroup's height.
final float verticalTextPos = 0.52f;
// Layout ImageView
float imageMargin = (1.0f - imageSize) / 2.0f;
float leftMargin = (int) (width * (imageMargin + offset));
float topMargin = (int) (height * (imageMargin + verticalImageOffset));
imageView.layout(
(int) leftMargin, (int) topMargin,
(int) (leftMargin + width * imageSize), (int) (topMargin + height * imageSize));
// Layout TextView
leftMargin = offset * width;
topMargin = height * verticalTextPos;
textView.layout(
(int) leftMargin, (int) topMargin,
(int) (leftMargin + width), (int) (topMargin + height * (1.0f - verticalTextPos)));
}
}
}
MainActivity.java
public class MainActivity extends CardboardActivity implements CardboardView.StereoRenderer, OnFrameAvailableListener {
private static final String TAG = "VRCamMtMMainAc";
private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
private Camera camera;
private final String vertexShaderCode =
"attribute vec4 position;" +
"attribute vec2 inputTextureCoordinate;" +
"varying vec2 textureCoordinate;" +
"void main()" +
"{" +
"gl_Position = position;" +
"textureCoordinate = inputTextureCoordinate;" +
"}";
private final String fragmentShaderCode =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;" +
"varying vec2 textureCoordinate; \n" +
"uniform samplerExternalOES s_texture; \n" +
"void main(void) {" +
" gl_FragColor = texture2D( s_texture, textureCoordinate );\n" +
//" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" +
"}";
private FloatBuffer vertexBuffer, textureVerticesBuffer, vertexBuffer2;
private ShortBuffer drawListBuffer, buf2;
private int mProgram;
private int mPositionHandle, mPositionHandle2;
private int mColorHandle;
private int mTextureCoordHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 2;
static float squareVertices[] = { // in counterclockwise order:
-1.0f, -1.0f, // 0.left - mid
1.0f, -1.0f, // 1. right - mid
-1.0f, 1.0f, // 2. left - top
1.0f, 1.0f, // 3. right - top
};
private short drawOrder[] = {0, 2, 1, 1, 2, 3}; // order to draw vertices
private short drawOrder2[] = {2, 0, 3, 3, 0, 1}; // order to draw vertices
static float textureVertices[] = {
0.0f, 1.0f, // A. left-bottom
1.0f, 1.0f, // B. right-bottom
0.0f, 0.0f, // C. left-top
1.0f, 0.0f // D. right-top
};
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
private ByteBuffer indexBuffer; // Buffer for index-array
private int texture;
private CardboardOverlayView mOverlayView;
private CardboardView cardboardView;
private SurfaceTexture surface;
private float[] mView;
private float[] mCamera;
public void startCamera(int texture) {
surface = new SurfaceTexture(texture);
surface.setOnFrameAvailableListener(this);
camera = Camera.open();
Camera.Parameters params = camera.getParameters();
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
// Check what resolutions are supported by your camera
/*List<Camera.Size> sizes = params.getSupportedPictureSizes();
// Iterate through all available resolutions and choose one.
// The chosen resolution will be stored in mSize.
Camera.Size mSize = null;
for (Camera.Size size : sizes) {
Log.i(TAG, "Available resolution: " + size.width + "x" + size.height);
mSize = size;
}
params.setPictureSize(5312,2988);*/
camera.setParameters(params);
try {
camera.setPreviewTexture(surface);
camera.startPreview();
} catch (IOException ioe)
{
Log.i(TAG, "CAM LAUNCH FAILED");
}
}
static private int createTexture() {
int[] texture = new int[1];
GLES20.glGenTextures(1, texture, 0);
GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture[0]);
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
return texture[0];
}
private int loadGLShader(int type, String code) {
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, code);
GLES20.glCompileShader(shader);
// Get the compilation status.
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
// If the compilation failed, delete the shader.
if (compileStatus[0] == 0) {
Log.e(TAG, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
if (shader == 0) {
throw new RuntimeException("Error creating shader.");
}
return shader;
}
/**
* Checks if we've had an error inside of OpenGL ES, and if so what that error is.
*
* #param func
*/
private static void checkGLError(String func) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e(TAG, func + ": glError " + error);
throw new RuntimeException(func + ": glError " + error);
}
}
/**
* Sets the view to our CardboardView and initializes the transformation matrices we will use
* to render our scene.
*
* #param savedInstanceState
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cardboardView = (CardboardView) findViewById(R.id.cardboard_view);
cardboardView.setRenderer(this);
setCardboardView(cardboardView);
mCamera = new float[16];
mView = new float[16];
mOverlayView = (CardboardOverlayView) findViewById(R.id.overlay);
//mOverlayView.show3DToast("Pull the magnet when you find an object.");
}
#Override
public void onRendererShutdown() {
Log.i(TAG, "onRendererShutdown");
}
#Override
public void onSurfaceChanged(int width, int height) {
Log.i(TAG, "onSurfaceChanged");
}
/**
* Creates the buffers we use to store information about the 3D world. OpenGL doesn't use Java
* arrays, but rather needs data in a format it can understand. Hence we use ByteBuffers.
*
* #param config The EGL configuration used when creating the surface.
*/
#Override
public void onSurfaceCreated(EGLConfig config) {
Log.i(TAG, "onSurfaceCreated");
GLES20.glClearColor(0.1f, 0.1f, 0.1f, 0.5f); // Dark background so text shows up well
ByteBuffer bb = ByteBuffer.allocateDirect(squareVertices.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareVertices);
vertexBuffer.position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
ByteBuffer bb2 = ByteBuffer.allocateDirect(textureVertices.length * 4);
bb2.order(ByteOrder.nativeOrder());
textureVerticesBuffer = bb2.asFloatBuffer();
textureVerticesBuffer.put(textureVertices);
textureVerticesBuffer.position(0);
int vertexShader = loadGLShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadGLShader(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);
texture = createTexture();
startCamera(texture);
}
/**
* Prepares OpenGL ES before we draw a frame.
*
* #param headTransform The head transformation in the new frame.
*/
#Override
public void onNewFrame(HeadTransform headTransform) {
float[] mtx = new float[16];
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
surface.updateTexImage();
surface.getTransformMatrix(mtx);
}
#Override
public void onFrameAvailable(SurfaceTexture arg0) {
this.cardboardView.requestRender();
}
/**
* Draws a frame for an eye. The transformation for that eye (from the camera) is passed in as
* a parameter.
*
* #param transform The transformations to apply to render this eye.
*/
#Override
public void onDrawEye(EyeTransform transform) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glUseProgram(mProgram);
GLES20.glActiveTexture(GL_TEXTURE_EXTERNAL_OES);
GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "position");
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
false, vertexStride, vertexBuffer);
mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");
GLES20.glEnableVertexAttribArray(mTextureCoordHandle);
GLES20.glVertexAttribPointer(mTextureCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
false, vertexStride, textureVerticesBuffer);
mColorHandle = GLES20.glGetAttribLocation(mProgram, "s_texture");
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTextureCoordHandle);
Matrix.multiplyMM(mView, 0, transform.getEyeView(), 0, mCamera, 0);
}
#Override
public void onFinishFrame(Viewport viewport) {
}
#Override
public void onCardboardTrigger() {
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ui_layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.google.vrtoolkit.cardboard.CardboardView
android:id="#+id/cardboard_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true" />
<com.oculab.mehmettaha.vrcamera.CardboardOverlayView
android:id="#+id/overlay"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
</RelativeLayout>
Don' t forget the importing libraries such as cardboard.jar and libprotobuf-java-...jar
Finally, you should get the result like this:
Cheers!
I'm working on an Android camera app that modifies the camera feed and displays it live on the screen. I have it working and doing what I want perfectly on my DROID RAZR MAXX running 4.3, and it works perfect on other phones, but unfortunately I have ran into a problem on several phones and am unable to track down the issue.
I've attached a screenshot showing what the issue is.
It's very hard to tell what the green "artifacts" are, but it almost looks like it's blocks from the camera feed from when it first turned on. The colors flicker, but the shapes inside the blocks don't really change.
I've stripped out everything that isn't needed and cleaned up the code as best I can, but I honestly have zero clue as to why this is happening, especially since it seems to work on some phones fine, while other phones it doesn't.
If I need to give more information just comment and I will add it!
CameraActivity.java
public class CameraActivity extends Activity
{
private MyGLSurfaceView glSurfaceView;
private MyCamera mCamera;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mCamera = new MyCamera();
glSurfaceView = new MyGLSurfaceView(this, mCamera);
setContentView(glSurfaceView);
}
#Override
protected void onPause()
{
super.onPause();
mCamera.stop();
}
}
MyCamera.java
public class MyCamera
{
private final static String LOG_TAG = "MyCamera";
private Camera mCamera;
private Parameters mCameraParams;
private Boolean running = false;
void start(SurfaceTexture surface)
{
Log.v(LOG_TAG, "Starting Camera");
mCamera = Camera.open(0);
mCameraParams = mCamera.getParameters();
Log.v(LOG_TAG, mCameraParams.getPreviewSize().width + " x " + mCameraParams.getPreviewSize().height);
try {
mCamera.setPreviewTexture(surface);
mCamera.startPreview();
running = true;
} catch (IOException e) {
e.printStackTrace();
}
}
void stop()
{
if (running) {
Log.v(LOG_TAG, "Stopping Camera");
mCamera.stopPreview();
mCamera.release();
running = false;
}
}
}
MyGLSurfaceView.java
class MyGLSurfaceView extends GLSurfaceView implements Renderer
{
private final static String LOG_TAG = "MyGLSurfaceView";
private MyCamera mCamera;
private SurfaceTexture mSurface;
private DirectVideo mDirectVideo;
public MyGLSurfaceView(Context context, MyCamera camera)
{
super(context);
mCamera = camera;
setEGLContextClientVersion(2);
setRenderer(this);
}
#Override
public void onDrawFrame(GL10 gl)
{
float[] mtx = new float[16];
mSurface.updateTexImage();
mSurface.getTransformMatrix(mtx);
mDirectVideo.draw();
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height)
{
Log.v(LOG_TAG, "Surface Changed");
GLES20.glViewport(0, 0, width, height);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
Log.v(LOG_TAG, "Surface Created");
int texture = createTexture();
mDirectVideo = new DirectVideo(texture);
mSurface = new SurfaceTexture(texture);
mCamera.start(mSurface);
}
private int createTexture()
{
int[] textures = new int[1];
// generate one texture pointer and bind it as an external texture.
GLES20.glGenTextures(1, textures, 0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]);
// No mip-mapping with camera source.
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
// Clamp to edge is only option.
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
return textures[0];
}
public static int loadShader(int type, String shaderCode){
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
DirectVideo.java
public class DirectVideo
{
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"attribute vec2 inputTextureCoordinate;" +
"varying vec2 textureCoordinate;" +
"void main()" +
"{"+
"gl_Position = vPosition;"+
"textureCoordinate = inputTextureCoordinate;" +
"}";
private final String fragmentShaderCode =
"#extension GL_OES_EGL_image_external : require\n"+
"precision mediump float;" +
"varying vec2 textureCoordinate;\n" +
"uniform samplerExternalOES s_texture;\n" +
"void main() {" +
" gl_FragColor = texture2D( s_texture, textureCoordinate );\n" +
"}";
private FloatBuffer vertexBuffer, textureVerticesBuffer;
private ShortBuffer drawListBuffer;
private final int mProgram;
private int mPositionHandle;
private int mTextureCoordHandle;
private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
// number of coordinates per vertex in this array
private static final int COORDS_PER_VERTEX = 2;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
static float squareCoords[] = {
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f,
1.0f, 1.0f,
};
static float textureVertices[] = {
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
};
private int texture;
public DirectVideo(int texture)
{
this.texture = texture;
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
ByteBuffer bb2 = ByteBuffer.allocateDirect(textureVertices.length * 4);
bb2.order(ByteOrder.nativeOrder());
textureVerticesBuffer = bb2.asFloatBuffer();
textureVerticesBuffer.put(textureVertices);
textureVerticesBuffer.position(0);
int vertexShader = MyGLSurfaceView.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = MyGLSurfaceView.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); // creates OpenGL ES program executables
}
public void draw()
{
GLES20.glUseProgram(mProgram);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the <insert shape here> coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");
GLES20.glEnableVertexAttribArray(mTextureCoordHandle);
GLES20.glVertexAttribPointer(mTextureCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, textureVerticesBuffer);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTextureCoordHandle);
}
}
In the onDrawFrame method you get a transformation matrix, but you don't use it.
This matrix should be used to transform texture coordinates.
For details see the documentation of the SurfaceTexture class.
Here is the fix:
pass the matrix to the the draw method:
#Override
public void onDrawFrame(GL10 gl)
{
float[] mtx = new float[16];
mSurface.updateTexImage();
mSurface.getTransformMatrix(mtx);
mDirectVideo.draw(mtx);
}
Add the following method to the DirectVideo class:
private float[] transformTextureCoordinates( float[] coords, float[] matrix)
{
float[] result = new float[ coords.length ];
float[] vt = new float[4];
for ( int i = 0 ; i < coords.length ; i += 2 ) {
float[] v = { coords[i], coords[i+1], 0 , 1 };
Matrix.multiplyMV(vt, 0, matrix, 0, v, 0);
result[i] = vt[0];
result[i+1] = vt[1];
}
return result;
}
In the draw method Convert the textureVertices list before adding to the buffer ( you should do this conversion at every paint since the matrix can change):
textureVerticesBuffer.clear();
textureVerticesBuffer.put( transformTextureCoordinates( textureVertices, mtx ));
textureVerticesBuffer.position(0);
An alternative solution is if you pass the matrix to the shader.
implements SurfaceTexture.OnFrameAvailableListener
#Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
// TODO Auto-generated method stub
}
setOnFrameAvailableListener(this);