Source: stage/renderer/VRRenderer.js

/**
 * jslint browser: true
 */

/**
 * Creates renderer for VR
 * @param {Canvas} canvas
 * @param {Boolean} distorted
 * @param {Ayce.CameraManager} cameraController
 * @class
 * @constructor
 */
Ayce.VRRenderer = function (canvas, distorted, cameraController) {

    Ayce.Renderer.call(this, canvas);

    var scope = this;

    var i = 0;

    var gl;

    var framebufferL, framebufferR;
    var textureL, textureR;
    var framebufferWidth;
    var framebufferHeight;

    var shader;
    var stride = 0;
    var interlacedBuffer;

    var vrSquare;

    var VALUES_PER_POSITION = 3;
    var VALUES_PER_TEXTURE_COORD = 2;


    var cameraDistortion;

    /*********************************************
     *
     *      Renderer initialization
     *
     *********************************************/

    /**
     * Handles canvas resizing
     */
    this.resize = function(){
        if(cameraController){
            canvas.width  = cameraController.cameraProperties.renderRectWidth;
            canvas.height = cameraController.cameraProperties.renderRectHeight;
        }
        else{
            console.warn("Can't get renderRect from cameraProperties.");
            canvas.width  = this.width *window.devicePixelRatio;
            canvas.height = this.height*window.devicePixelRatio;
        }

        canvas.style.width = this.width + 'px';
        canvas.style.height = this.height + 'px';

        console.log("Resolution: " + canvas.width +"x" + canvas.height, " Style: " + canvas.style.width +" - " + canvas.style.height);

        //gl stuff
        gl.viewportWidth =  canvas.width/2.0;
        gl.viewportHeight = canvas.height;
        //TODO doesn't do anything?
        scope.setViewportAndScissor(0, 0, gl.viewportWidth, gl.viewportHeight);
    };
    
    var init = this.init;
    /**
     * Initializes VR renderer
     */
    this.init = function(){
        console.log("Initializing VRRenderer");
        init(); //Ayce.Renderer.init();

        gl = this.getGL();
        var temp = initTextureFramebuffer();
        framebufferL = temp[0];
        textureL = temp[1];
        temp = initTextureFramebuffer();
        framebufferR = temp[0];
        textureR = temp[1];

        vrSquare = new Ayce.VRSquare();
        var vert = Ayce.VRRenderer.prototype.vrCanvasVert;//Ayce.XMLLoader.getSourceSynch(shaderPath + vrSquare.shader + ".vert");
        var frag = Ayce.VRRenderer.prototype.vrCanvasFrag;//Ayce.XMLLoader.getSourceSynch(shaderPath + vrSquare.shader + ".frag");
        shader = new Ayce.Shader(gl, vert, frag);
        shader.pMatrixUniform = shader.getUniformLocation("uPMatrix");
        shader.mvMatrixUniform = shader.getUniformLocation("uMVMatrix");
        shader.vertexPositionAttribute = shader.getAttribLocation("aVertexPosition");
        shader.textureCoordAttribute = shader.getAttribLocation("aTextureCoord");
        shader.samplerUniform = shader.getUniformLocation("uSampler");

        var config = new Ayce.CameraModifier();
        config.getPosition().set(0, 0, 2.3);
        config.getOrientation().set(0, 0, 0, 1);
        
        var controller = new Ayce.CameraManager();
        controller.modifiers.push(config);
        cameraDistortion = new Ayce.Camera(controller);
        cameraDistortion.isOrtho = true;
        cameraDistortion.updateProjectionMatrix();
        cameraDistortion.update();

        initBuffers();

        gl.enable(gl.SCISSOR_TEST);
    };

    /**
     * Initializes framebuffer for scene rendering for one eye
     * @return {Array} framebufferAndTexture
     */
    function initTextureFramebuffer() {
        var framebuffer = gl.createFramebuffer();
        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
        framebufferWidth = framebuffer.width = 1024;//TODO calc correct dimensions
        framebufferHeight = framebuffer.height = 1024;

        var texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, framebuffer.width, framebuffer.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

        var renderbuffer = gl.createRenderbuffer();
        gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
        gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, framebuffer.width, framebuffer.height);

        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);

        gl.bindTexture(gl.TEXTURE_2D, null);
        gl.bindRenderbuffer(gl.RENDERBUFFER, null);
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        return [framebuffer, texture];
    }

    /**
     * Initializes buffers for squares with texture (later converted to frame buffer)
     */
    function initBuffers() {
        var interlacedArray = vrSquare.vertices.slice();
        var i;
        for(i=VALUES_PER_POSITION; i<=vrSquare.vertices.length; i+=VALUES_PER_POSITION){    // Join Object3D arrays
            interlacedArray.splice(i+(i/VALUES_PER_POSITION-1)*VALUES_PER_TEXTURE_COORD, 0,
                vrSquare.textureCoords[(i/VALUES_PER_POSITION-1)*VALUES_PER_TEXTURE_COORD],    // x
                vrSquare.textureCoords[(i/VALUES_PER_POSITION-1)*VALUES_PER_TEXTURE_COORD+1]); // y
        }

        stride = (VALUES_PER_POSITION+VALUES_PER_TEXTURE_COORD)*4;
        interlacedBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, interlacedBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(interlacedArray), gl.STATIC_DRAW);
        interlacedBuffer.numItems = 4;

        gl.enableVertexAttribArray(shader.vertexPositionAttribute);
        gl.vertexAttribPointer(shader.vertexPositionAttribute, VALUES_PER_POSITION, gl.FLOAT, false, stride, 0);
        gl.enableVertexAttribArray(shader.textureCoordAttribute);
        gl.vertexAttribPointer(shader.textureCoordAttribute, VALUES_PER_TEXTURE_COORD, gl.FLOAT, false, stride, VALUES_PER_POSITION*4);
    }

    /*********************************************
     *
     *      Render current Scene
     *
     *********************************************/

    /**
     * Updates objects
     * @param {Camera} camera
     * @param {Ayce.Object3D[]} objects
     * @param {Ayce.Object3D[]} transparentObjects
     */
    this.update = function (camera, objects, transparentObjects) {
        for(i=0; i < objects.length; i++){
            objects[i].buffer.updateVR(camera);
        }
        
        for(i=0; i < transparentObjects.length; i++){
            transparentObjects[i].buffer.updateVR(camera);
        }
    };

    /**
     * Renders objects
     * @param {Camera} camera
     * @param {Ayce.Object3D[]} objects
     * @param {Ayce.Object3D[]} transparentObjects
     */
    this.render = function (camera, objects, transparentObjects) {
        if(distorted){
            vrRenderDistorted(objects, transparentObjects);
        }
        else{
            vrRender(objects, transparentObjects);
        }
    };

    /**
     * Renders scene to frame buffer with shader for barrel distortion correction and collor abberation
     * @param {Ayce.Object3D[]} objects
     * @param {Ayce.Object3D[]} transparentObjects
     */
    function vrRenderDistorted(objects, transparentObjects) {
        //Render Left Eye
        gl.bindFramebuffer(gl.FRAMEBUFFER, framebufferL);
        scope.setViewportAndScissor(0, 0, framebufferWidth, framebufferHeight);

        renderObjects(objects, "left");
        renderObjects(transparentObjects, "left");

        //Render Right Eye
        gl.bindFramebuffer(gl.FRAMEBUFFER, framebufferR);
        scope.setViewportAndScissor(0, 0, framebufferWidth, framebufferHeight);

        renderObjects(objects, "right");
        renderObjects(transparentObjects, "right");

        //Apply Distortion
        //Render Both Eyes to Canvas
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        shader.initShaders();

        gl.bindBuffer(gl.ARRAY_BUFFER, interlacedBuffer);
//        gl.enableVertexAttribArray(shader.vertexPositionAttribute);
//        gl.enableVertexAttribArray(shader.textureCoordAttribute);
        gl.vertexAttribPointer(shader.vertexPositionAttribute, VALUES_PER_POSITION, gl.FLOAT, false, stride, 0);
        gl.vertexAttribPointer(shader.textureCoordAttribute, VALUES_PER_TEXTURE_COORD, gl.FLOAT, false, stride, VALUES_PER_POSITION*4);

        //Left
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, textureL);
        gl.uniform1i(shader.samplerUniform, 0);
        scope.setViewportAndScissor(0, 0, gl.viewportWidth, gl.viewportHeight);
        
        gl.uniformMatrix4fv(shader.pMatrixUniform, false, cameraDistortion.getPerspectiveMatrix().data);
        gl.uniformMatrix4fv(shader.mvMatrixUniform, false, cameraDistortion.getViewMatrix().data);

        gl.drawArrays(gl.TRIANGLE_STRIP, 0, interlacedBuffer.numItems);

        //Right
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, textureR);
        gl.uniform1i(shader.samplerUniform, 0);
        scope.setViewportAndScissor(gl.viewportWidth, 0, gl.viewportWidth, gl.viewportHeight);

        gl.uniformMatrix4fv(shader.pMatrixUniform, false, cameraDistortion.getPerspectiveMatrix().data);
        gl.uniformMatrix4fv(shader.mvMatrixUniform, false, cameraDistortion.getViewMatrix().data);

        gl.drawArrays(gl.TRIANGLE_STRIP, 0, interlacedBuffer.numItems);

        gl.bindTexture(gl.TEXTURE_2D, null);
    }

    /**
     * Renders scene to frame buffers
     * @param {Ayce.Object3D[]} objects
     * @param {Ayce.Object3D[]} transparentObjects
     */
    function vrRender(objects, transparentObjects) {
        //Render Left Eye
        scope.setViewportAndScissor(0, 0, gl.viewportWidth, gl.viewportHeight);

        renderObjects(objects, "left");
        renderObjects(transparentObjects, "left");

        //Render Right Eye
        scope.setViewportAndScissor(gl.viewportWidth, 0, gl.viewportWidth, gl.viewportHeight);

        renderObjects(objects, "right");
        renderObjects(transparentObjects, "right");
    }

    /**
     * Renders Objects
     * @param {Ayce.Object3D[]} objects
     * @param {String} eye
     */
    function renderObjects (objects, eye) {
        for (var i=0; i<objects.length; i++) {
            objects[i].buffer.renderVR(eye);
        }
    }
};

Ayce.VRRenderer.prototype = new Ayce.Renderer();
Ayce.VRRenderer.prototype.vrCanvasVert = 
    "attribute vec3 aVertexPosition;"+
    "attribute vec2 aTextureCoord;"+

    "uniform mat4 uMVMatrix;"+
    "uniform mat4 uPMatrix;"+

    "varying vec2 vTextureCoord;"+


    "void main(void) {"+
      "gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);"+
      "vTextureCoord = aTextureCoord;"+
    "}";
Ayce.VRRenderer.prototype.vrCanvasFrag =
"precision mediump float;"+

"uniform sampler2D uSampler;"+
"uniform bool uUseTextures;"+

"varying vec2 vTextureCoord;"+
"varying vec4 vColor;"+

"void main(void) {"+
    // Abstand zwischen 0.5|0.5 und S|T
"    float distance = sqrt(abs(0.5-vTextureCoord.s)*abs(0.5-vTextureCoord.s)+abs(0.5-vTextureCoord.t)*abs(0.5-vTextureCoord.t));"+
    // X-/Y-Abstand
"    float distanceS = (0.5-vTextureCoord.s);"+
"    float distanceT = (0.5-vTextureCoord.t);"+
    // 0.22 und 0.24 sind die Koeffizienten von den Linsen des Oculus
"    float factor = 1.0+0.22*distance*distance+0.24*distance*distance*distance*distance;"+
    // Umwandlung der Texturkoordinaten in OpenGL-Koordinaten (-0.5<x<0.5|-0.5<y<0.5), Multiplizierung mit dem Faktor
"    float newCoordS = (vTextureCoord.s-0.5)*factor;"+
"    float newCoordT = (vTextureCoord.t-0.5)*factor;"+

    // Chromatic abberation (Farbkorrektur)
    //1.0 => keine Korrektur
"    float offsetR = 1.0;"+
"    float offsetG = 0.995;"+
"    float offsetB = 0.980;"+

    // Chromatic abberation und Rückumwandlung in Texturkoordinaten
"    float newCoordSR = newCoordS/offsetR+0.5;"+
"    float newCoordSG = newCoordS/offsetG+0.5;"+
"    float newCoordSB = newCoordS/offsetB+0.5;"+

"    float newCoordTR = newCoordT/offsetR+0.5;"+
"    float newCoordTG = newCoordT/offsetG+0.5;"+
"    float newCoordTB = newCoordT/offsetB+0.5;"+

"    if(newCoordSR<0.0||newCoordSR>1.0"+
"    ||newCoordSG<0.0||newCoordSG>1.0"+
"    ||newCoordSB<0.0||newCoordSB>1.0"+
"    ||newCoordTR<0.0||newCoordTR>1.0"+
"    ||newCoordTG<0.0||newCoordTG>1.0"+
"    ||newCoordTB<0.0||newCoordTB>1.0){"+
        // Wenn Koordinaten außerhalb der Textur, schwarzer Pixel
        //gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
"        gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);"+
"    }else{"+

"        float r = texture2D(uSampler, vec2(newCoordSR, newCoordTR)).r;"+
"        float g = texture2D(uSampler, vec2(newCoordSG, newCoordTG)).g;"+
"        float b = texture2D(uSampler, vec2(newCoordSB, newCoordTB)).b;"+
"        gl_FragColor = vec4(r, g, b, 1.0);"+
"    }"+
"}";