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 Java.Lang.RuntimeException("Could not create program");
            }

            holder.aPosition = GLES20.GlGetAttribLocation(holder.program, "aPosition");
            checkGlError("glGetAttribLocation aPosition");
            if (holder.aPosition == -1)
            {
                throw new Java.Lang.RuntimeException("Could not get attrib location for aPosition");
            }
            holder.aVignette = GLES20.GlGetAttribLocation(holder.program, "aVignette");
            checkGlError("glGetAttribLocation aVignette");
            if (holder.aVignette == -1)
            {
                throw new Java.Lang.RuntimeException("Could not get attrib location for aVignette");
            }
            holder.aTextureCoord = GLES20.GlGetAttribLocation(holder.program, "aTextureCoord");

            checkGlError("glGetAttribLocation aTextureCoord");
            if (holder.aTextureCoord == -1)
            {
                throw new Java.Lang.RuntimeException("Could not get attrib location for aTextureCoord");
            }
            holder.uTextureCoordScale = GLES20.GlGetUniformLocation(holder.program, "uTextureCoordScale");

            checkGlError("glGetUniformLocation uTextureCoordScale");
            if (holder.uTextureCoordScale == -1)
            {
                throw new Java.Lang.RuntimeException("Could not get attrib location for uTextureCoordScale");
            }
            holder.uTextureSampler = GLES20.GlGetUniformLocation(holder.program, "uTextureSampler");

            checkGlError("glGetUniformLocation uTextureSampler");
            if (holder.uTextureSampler == -1)
            {
                throw new Java.Lang.RuntimeException("Could not get attrib location for uTextureSampler");
            }

            return(holder);
        }
        public void onProjectionChanged(HeadMountedDisplay hmd, EyeParams leftEye, EyeParams rightEye, float zNear, float zFar)
        {
            mHmd         = new HeadMountedDisplay(hmd);
            mLeftEyeFov  = new FieldOfView(leftEye.getFov());
            mRightEyeFov = new FieldOfView(rightEye.getFov());

            ScreenParams          screen = mHmd.getScreen();
            CardboardDeviceParams cdp    = mHmd.getCardboard();

            if (mProgramHolder == null)
            {
                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();

            mLeftEyeDistortionMesh = createDistortionMesh(leftEye, leftEyeViewport, textureWidthM, textureHeightM, xEyeOffsetMScreen, yEyeOffsetMScreen);

            xEyeOffsetMScreen       = screen.getWidthMeters() - xEyeOffsetMScreen;
            mRightEyeDistortionMesh = createDistortionMesh(rightEye, rightEyeViewport, textureWidthM, textureHeightM, xEyeOffsetMScreen, yEyeOffsetMScreen);

            setupRenderTextureAndRenderbuffer(textureWidthPx, textureHeightPx);
        }