Back to project page Cardboard.
The source code is released under:
Apache License
If you think the Android project Cardboard listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.google.vrtoolkit.cardboard; /*from w ww .j a v a2 s . com*/ import android.opengl.GLES20; import android.util.Log; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; public class DistortionRenderer { private static final String TAG = "DistortionRenderer"; private int mTextureId; private int mRenderbufferId; private int mFramebufferId; private IntBuffer mOriginalFramebufferId; private IntBuffer mCullFaceEnabled; private IntBuffer mScissorTestEnabled; private IntBuffer mViewport; private float mResolutionScale; private DistortionMesh mLeftEyeDistortionMesh; private DistortionMesh mRightEyeDistortionMesh; private HeadMountedDisplay mHmd; private FieldOfView mLeftEyeFov; private FieldOfView mRightEyeFov; private ProgramHolder mProgramHolder; private final String VERTEX_SHADER = "attribute vec2 aPosition;\nattribute float aVignette;\nattribute vec2 aTextureCoord;\nvarying vec2 vTextureCoord;\nvarying float vVignette;\nuniform float uTextureCoordScale;\nvoid main() {\n gl_Position = vec4(aPosition, 0.0, 1.0);\n vTextureCoord = aTextureCoord.xy * uTextureCoordScale;\n vVignette = aVignette;\n}\n"; private final String FRAGMENT_SHADER = "precision mediump float;\nvarying vec2 vTextureCoord;\nvarying float vVignette;\nuniform sampler2D uTextureSampler;\nvoid main() {\n gl_FragColor = vVignette * texture2D(uTextureSampler, vTextureCoord);\n}\n"; public DistortionRenderer() { this.mTextureId = -1; this.mRenderbufferId = -1; this.mFramebufferId = -1; this.mOriginalFramebufferId = IntBuffer.allocate(1); this.mCullFaceEnabled = IntBuffer.allocate(1); this.mScissorTestEnabled = IntBuffer.allocate(1); this.mViewport = IntBuffer.allocate(4); this.mResolutionScale = 1.0F; } private class ProgramHolder { public int program; public int aPosition; public int aVignette; public int aTextureCoord; public int uTextureCoordScale; public int uTextureSampler; private ProgramHolder() { } } private class EyeViewport { public float x; public float y; public float width; public float height; public float eyeX; public float eyeY; private EyeViewport() { } public String toString() { return "EyeViewport {x:" + this.x + " y:" + this.y + " width:" + this.width + " height:" + this.height + " eyeX: " + this.eyeX + " eyeY: " + this.eyeY + "}"; } } public void beforeDrawFrame() { GLES20.glGetIntegerv(36006, this.mOriginalFramebufferId); GLES20.glBindFramebuffer(36160, this.mFramebufferId); } public void afterDrawFrame() { GLES20.glBindFramebuffer(36160, this.mOriginalFramebufferId.array()[0]); GLES20.glViewport(0, 0, this.mHmd.getScreen().getWidth(), this.mHmd .getScreen().getHeight()); GLES20.glGetIntegerv(2978, this.mViewport); GLES20.glGetIntegerv(2884, this.mCullFaceEnabled); GLES20.glGetIntegerv(3089, this.mScissorTestEnabled); GLES20.glDisable(3089); GLES20.glDisable(2884); GLES20.glClearColor(0.0F, 0.0F, 0.0F, 1.0F); GLES20.glClear(16640); GLES20.glUseProgram(this.mProgramHolder.program); GLES20.glEnable(3089); GLES20.glScissor(0, 0, this.mHmd.getScreen().getWidth() / 2, this.mHmd .getScreen().getHeight()); renderDistortionMesh(this.mLeftEyeDistortionMesh); GLES20.glScissor(this.mHmd.getScreen().getWidth() / 2, 0, this.mHmd .getScreen().getWidth() / 2, this.mHmd.getScreen().getHeight()); renderDistortionMesh(this.mRightEyeDistortionMesh); GLES20.glDisableVertexAttribArray(this.mProgramHolder.aPosition); GLES20.glDisableVertexAttribArray(this.mProgramHolder.aVignette); GLES20.glDisableVertexAttribArray(this.mProgramHolder.aTextureCoord); GLES20.glUseProgram(0); GLES20.glBindBuffer(34962, 0); GLES20.glBindBuffer(34963, 0); GLES20.glDisable(3089); if (this.mCullFaceEnabled.array()[0] == 1) { GLES20.glEnable(2884); } if (this.mScissorTestEnabled.array()[0] == 1) { GLES20.glEnable(3089); } GLES20.glViewport(this.mViewport.array()[0], this.mViewport.array()[1], this.mViewport.array()[2], this.mViewport.array()[3]); } public void setResolutionScale(float scale) { this.mResolutionScale = scale; } public void onProjectionChanged(HeadMountedDisplay hmd, EyeParams leftEye, EyeParams rightEye, float zNear, float zFar) { this.mHmd = new HeadMountedDisplay(hmd); this.mLeftEyeFov = new FieldOfView(leftEye.getFov()); this.mRightEyeFov = new FieldOfView(rightEye.getFov()); ScreenParams screen = this.mHmd.getScreen(); CardboardDeviceParams cdp = this.mHmd.getCardboard(); if (this.mProgramHolder == null) { this.mProgramHolder = createProgramHolder(); } EyeViewport leftEyeViewport = initViewportForEye(leftEye, 0.0F); EyeViewport rightEyeViewport = initViewportForEye(rightEye, leftEyeViewport.width); leftEye.getFov().toPerspectiveMatrix(zNear, zFar, leftEye.getTransform().getPerspective(), 0); rightEye.getFov().toPerspectiveMatrix(zNear, zFar, rightEye.getTransform().getPerspective(), 0); float textureWidthM = leftEyeViewport.width + rightEyeViewport.width; float textureHeightM = Math.max(leftEyeViewport.height, rightEyeViewport.height); float xPxPerM = screen.getWidth() / screen.getWidthMeters(); float yPxPerM = screen.getHeight() / screen.getHeightMeters(); int textureWidthPx = Math.round(textureWidthM * xPxPerM); int textureHeightPx = Math.round(textureHeightM * yPxPerM); float xEyeOffsetMScreen = screen.getWidthMeters() / 2.0F - cdp.getInterpupillaryDistance() / 2.0F; float yEyeOffsetMScreen = cdp.getVerticalDistanceToLensCenter() - screen.getBorderSizeMeters(); this.mLeftEyeDistortionMesh = createDistortionMesh(leftEye, leftEyeViewport, textureWidthM, textureHeightM, xEyeOffsetMScreen, yEyeOffsetMScreen); xEyeOffsetMScreen = screen.getWidthMeters() - xEyeOffsetMScreen; this.mRightEyeDistortionMesh = createDistortionMesh(rightEye, rightEyeViewport, textureWidthM, textureHeightM, xEyeOffsetMScreen, yEyeOffsetMScreen); setupRenderTextureAndRenderbuffer(textureWidthPx, textureHeightPx); } private EyeViewport initViewportForEye(EyeParams eye, float xOffsetM) { ScreenParams screen = this.mHmd.getScreen(); CardboardDeviceParams cdp = this.mHmd.getCardboard(); float eyeToScreenDistanceM = cdp.getEyeToLensDistance() + cdp.getScreenToLensDistance(); float leftM = (float) Math.tan(Math.toRadians(eye.getFov().getLeft())) * eyeToScreenDistanceM; float rightM = (float) Math .tan(Math.toRadians(eye.getFov().getRight())) * eyeToScreenDistanceM; float bottomM = (float) Math.tan(Math.toRadians(eye.getFov() .getBottom())) * eyeToScreenDistanceM; float topM = (float) Math.tan(Math.toRadians(eye.getFov().getTop())) * eyeToScreenDistanceM; EyeViewport vp = new EyeViewport(); vp.x = xOffsetM; vp.y = 0.0F; vp.width = (leftM + rightM); vp.height = (bottomM + topM); vp.eyeX = (leftM + xOffsetM); vp.eyeY = bottomM; float xPxPerM = screen.getWidth() / screen.getWidthMeters(); float yPxPerM = screen.getHeight() / screen.getHeightMeters(); eye.getViewport().x = Math.round(vp.x * xPxPerM); eye.getViewport().y = Math.round(vp.y * xPxPerM); eye.getViewport().width = Math.round(vp.width * xPxPerM); eye.getViewport().height = Math.round(vp.height * xPxPerM); return vp; } private DistortionMesh createDistortionMesh(EyeParams eye, EyeViewport eyeViewport, float textureWidthM, float textureHeightM, float xEyeOffsetMScreen, float yEyeOffsetMScreen) { return new DistortionMesh(eye, this.mHmd.getCardboard().getDistortion(), this.mHmd.getScreen() .getWidthMeters(), this.mHmd.getScreen() .getHeightMeters(), xEyeOffsetMScreen, yEyeOffsetMScreen, textureWidthM, textureHeightM, eyeViewport.eyeX, eyeViewport.eyeY, eyeViewport.x, eyeViewport.y, eyeViewport.width, eyeViewport.height); } private void renderDistortionMesh(DistortionMesh mesh) { GLES20.glBindBuffer(34962, mesh.mArrayBufferId); mesh.getClass(); mesh.getClass(); GLES20.glVertexAttribPointer(this.mProgramHolder.aPosition, 3, 5126, false, 20, 0 * 4); GLES20.glEnableVertexAttribArray(this.mProgramHolder.aPosition); mesh.getClass(); mesh.getClass(); GLES20.glVertexAttribPointer(this.mProgramHolder.aVignette, 1, 5126, false, 20, 2 * 4); GLES20.glEnableVertexAttribArray(this.mProgramHolder.aVignette); mesh.getClass(); mesh.getClass(); GLES20.glVertexAttribPointer(this.mProgramHolder.aTextureCoord, 2, 5126, false, 20, 3 * 4); GLES20.glEnableVertexAttribArray(this.mProgramHolder.aTextureCoord); GLES20.glActiveTexture(33984); GLES20.glBindTexture(3553, this.mTextureId); GLES20.glUniform1i(this.mProgramHolder.uTextureSampler, 0); GLES20.glUniform1f(this.mProgramHolder.uTextureCoordScale, this.mResolutionScale); GLES20.glBindBuffer(34963, mesh.mElementBufferId); GLES20.glDrawElements(5, mesh.nIndices, 5125, 0); } private float computeDistortionScale(Distortion distortion, float screenWidthM, float interpupillaryDistanceM) { return distortion .distortionFactor((screenWidthM / 2.0F - interpupillaryDistanceM / 2.0F) / (screenWidthM / 4.0F)); } private int createTexture(int width, int height) { int[] textureIds = new int[1]; GLES20.glGenTextures(1, textureIds, 0); GLES20.glBindTexture(3553, textureIds[0]); GLES20.glTexParameteri(3553, 10242, 33071); GLES20.glTexParameteri(3553, 10243, 33071); GLES20.glTexParameteri(3553, 10240, 9729); GLES20.glTexParameteri(3553, 10241, 9729); GLES20.glTexImage2D(3553, 0, 6407, width, height, 0, 6407, 33635, null); return textureIds[0]; } private int setupRenderTextureAndRenderbuffer(int width, int height) { if (this.mTextureId != -1) { GLES20.glDeleteTextures(1, new int[] { this.mTextureId }, 0); } if (this.mRenderbufferId != -1) { GLES20.glDeleteRenderbuffers(1, new int[] { this.mRenderbufferId }, 0); } if (this.mFramebufferId != -1) { GLES20.glDeleteFramebuffers(1, new int[] { this.mFramebufferId }, 0); } this.mTextureId = createTexture(width, height); checkGlError("setupRenderTextureAndRenderbuffer: create texture"); int[] renderbufferIds = new int[1]; GLES20.glGenRenderbuffers(1, renderbufferIds, 0); GLES20.glBindRenderbuffer(36161, renderbufferIds[0]); GLES20.glRenderbufferStorage(36161, 33189, width, height); this.mRenderbufferId = renderbufferIds[0]; checkGlError("setupRenderTextureAndRenderbuffer: create renderbuffer"); int[] framebufferIds = new int[1]; GLES20.glGenFramebuffers(1, framebufferIds, 0); GLES20.glBindFramebuffer(36160, framebufferIds[0]); this.mFramebufferId = framebufferIds[0]; GLES20.glFramebufferTexture2D(36160, 36064, 3553, this.mTextureId, 0); GLES20.glFramebufferRenderbuffer(36160, 36096, 36161, renderbufferIds[0]); int status = GLES20.glCheckFramebufferStatus(36160); if (status != 36053) { throw new RuntimeException("Framebuffer is not complete: " + Integer.toHexString(status)); } GLES20.glBindFramebuffer(36160, 0); return framebufferIds[0]; } 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, 35713, compiled, 0); if (compiled[0] == 0) { Log.e("DistortionRenderer", "Could not compile shader " + shaderType + ":"); Log.e("DistortionRenderer", GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); shader = 0; } } return shader; } private int createProgram(String vertexSource, String fragmentSource) { int vertexShader = loadShader(35633, vertexSource); if (vertexShader == 0) { return 0; } int pixelShader = loadShader(35632, 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, 35714, linkStatus, 0); if (linkStatus[0] != 1) { Log.e("DistortionRenderer", "Could not link program: "); Log.e("DistortionRenderer", GLES20.glGetProgramInfoLog(program)); GLES20.glDeleteProgram(program); program = 0; } } return program; } private ProgramHolder createProgramHolder() { ProgramHolder holder = new ProgramHolder(); holder.program = createProgram( "attribute vec2 aPosition;\nattribute float aVignette;\nattribute vec2 aTextureCoord;\nvarying vec2 vTextureCoord;\nvarying float vVignette;\nuniform float uTextureCoordScale;\nvoid main() {\n gl_Position = vec4(aPosition, 0.0, 1.0);\n vTextureCoord = aTextureCoord.xy * uTextureCoordScale;\n vVignette = aVignette;\n}\n", "precision mediump float;\nvarying vec2 vTextureCoord;\nvarying float vVignette;\nuniform sampler2D uTextureSampler;\nvoid main() {\n gl_FragColor = vVignette * texture2D(uTextureSampler, vTextureCoord);\n}\n"); if (holder.program == 0) { throw new RuntimeException("Could not create program"); } holder.aPosition = GLES20.glGetAttribLocation(holder.program, "aPosition"); checkGlError("glGetAttribLocation aPosition"); if (holder.aPosition == -1) { throw new RuntimeException( "Could not get attrib location for aPosition"); } holder.aVignette = GLES20.glGetAttribLocation(holder.program, "aVignette"); checkGlError("glGetAttribLocation aVignette"); if (holder.aVignette == -1) { throw new RuntimeException( "Could not get attrib location for aVignette"); } holder.aTextureCoord = GLES20.glGetAttribLocation(holder.program, "aTextureCoord"); checkGlError("glGetAttribLocation aTextureCoord"); if (holder.aTextureCoord == -1) { throw new RuntimeException( "Could not get attrib location for aTextureCoord"); } holder.uTextureCoordScale = GLES20.glGetUniformLocation(holder.program, "uTextureCoordScale"); checkGlError("glGetUniformLocation uTextureCoordScale"); if (holder.uTextureCoordScale == -1) { throw new RuntimeException( "Could not get attrib location for uTextureCoordScale"); } holder.uTextureSampler = GLES20.glGetUniformLocation(holder.program, "uTextureSampler"); checkGlError("glGetUniformLocation uTextureSampler"); if (holder.uTextureSampler == -1) { throw new RuntimeException( "Could not get attrib location for uTextureSampler"); } return holder; } private void checkGlError(String op) { int error; if ((error = GLES20.glGetError()) != 0) { Log.e("DistortionRenderer", op + ": glError " + error); throw new RuntimeException(op + ": glError " + error); } } private static float clamp(float val, float min, float max) { return Math.max(min, Math.min(max, val)); } private class DistortionMesh { private static final String TAG = "DistortionMesh"; public static final int BYTES_PER_FLOAT = 4; public static final int BYTES_PER_INT = 4; public final int COMPONENTS_PER_VERT = 5; public final int DATA_STRIDE_BYTES = 20; public final int DATA_POS_OFFSET = 0; public final int DATA_VIGNETTE_OFFSET = 2; public final int DATA_UV_OFFSET = 3; public final int ROWS = 40; public final int COLS = 40; public final float VIGNETTE_SIZE_M_SCREEN = 0.0020F; public int nIndices; public int mArrayBufferId = -1; public int mElementBufferId = -1; public DistortionMesh(EyeParams eye, Distortion distortion, float screenWidthM, float screenHeightM, float xEyeOffsetMScreen, float yEyeOffsetMScreen, float textureWidthM, float textureHeightM, float xEyeOffsetMTexture, float yEyeOffsetMTexture, float viewportXMTexture, float viewportYMTexture, float viewportWidthMTexture, float viewportHeightMTexture) { float mPerUScreen = screenWidthM; float mPerVScreen = screenHeightM; float mPerUTexture = textureWidthM; float mPerVTexture = textureHeightM; float[] vertexData = new float['?']; int vertexOffset = 0; for (int row = 0; row < 40; row++) { for (int col = 0; col < 40; col++) { float uTexture = col / 39.0F * (viewportWidthMTexture / textureWidthM) + viewportXMTexture / textureWidthM; float vTexture = row / 39.0F * (viewportHeightMTexture / textureHeightM) + viewportYMTexture / textureHeightM; float xTexture = uTexture * mPerUTexture; float yTexture = vTexture * mPerVTexture; float xTextureEye = xTexture - xEyeOffsetMTexture; float yTextureEye = yTexture - yEyeOffsetMTexture; float rTexture = (float) Math.sqrt(xTextureEye * xTextureEye + yTextureEye * yTextureEye); float textureToScreen = rTexture > 0.0F ? distortion .distortInverse(rTexture) / rTexture : 1.0F; float xScreen = xTextureEye * textureToScreen + xEyeOffsetMScreen; float yScreen = yTextureEye * textureToScreen + yEyeOffsetMScreen; float uScreen = xScreen / mPerUScreen; float vScreen = yScreen / mPerVScreen; float vignetteSizeMTexture = 0.0020F / textureToScreen; float dxTexture = xTexture - DistortionRenderer.clamp(xTexture, viewportXMTexture + vignetteSizeMTexture, viewportXMTexture + viewportWidthMTexture - vignetteSizeMTexture); float dyTexture = yTexture - DistortionRenderer.clamp(yTexture, viewportYMTexture + vignetteSizeMTexture, viewportYMTexture + viewportHeightMTexture - vignetteSizeMTexture); float drTexture = (float) Math.sqrt(dxTexture * dxTexture + dyTexture * dyTexture); float vignette = 1.0F - DistortionRenderer.clamp(drTexture / vignetteSizeMTexture, 0.0F, 1.0F); vertexData[(vertexOffset + 0)] = (2.0F * uScreen - 1.0F); vertexData[(vertexOffset + 1)] = (2.0F * vScreen - 1.0F); vertexData[(vertexOffset + 2)] = vignette; vertexData[(vertexOffset + 3)] = uTexture; vertexData[(vertexOffset + 4)] = vTexture; vertexOffset += 5; } } this.nIndices = 3158; int[] indexData = new int[this.nIndices]; int indexOffset = 0; vertexOffset = 0; for (int row = 0; row < 39; row++) { if (row > 0) { indexData[indexOffset] = indexData[(indexOffset - 1)]; indexOffset++; } for (int col = 0; col < 40; col++) { if (col > 0) { if (row % 2 == 0) { vertexOffset++; } else { vertexOffset--; } } indexData[(indexOffset++)] = vertexOffset; indexData[(indexOffset++)] = (vertexOffset + 40); } vertexOffset += 40; } FloatBuffer vertexBuffer = ByteBuffer .allocateDirect(vertexData.length * 4) .order(ByteOrder.nativeOrder()).asFloatBuffer(); vertexBuffer.put(vertexData).position(0); IntBuffer indexBuffer = ByteBuffer .allocateDirect(indexData.length * 4) .order(ByteOrder.nativeOrder()).asIntBuffer(); indexBuffer.put(indexData).position(0); int[] bufferIds = new int[2]; GLES20.glGenBuffers(2, bufferIds, 0); this.mArrayBufferId = bufferIds[0]; this.mElementBufferId = bufferIds[1]; GLES20.glBindBuffer(34962, this.mArrayBufferId); GLES20.glBufferData(34962, vertexData.length * 4, vertexBuffer, 35044); GLES20.glBindBuffer(34963, this.mElementBufferId); GLES20.glBufferData(34963, indexData.length * 4, indexBuffer, 35044); GLES20.glBindBuffer(34962, 0); GLES20.glBindBuffer(34963, 0); } } }