constructor(vrDisplay, gl) {
    this.vrDisplay = vrDisplay;
    this.gl = gl;
    if (this.vrDisplay) {
      this.passThroughCamera = vrDisplay.getPassThroughCamera();
      // Depending on the type of passThroughCamera, use a different
      // target for the texture and different shaders.
      if (this.passThroughCamera instanceof Image) {
        this.textureTarget = gl.TEXTURE_2D;
        this.fragmentSource = fragmentSource;
      } else {
        this.textureTarget = gl.TEXTURE_EXTERNAL_OES;
        this.fragmentSource = fragmentSourceOES;
      }
      this.program = getProgram(gl, vertexSource, this.fragmentSource);
    }

    gl.useProgram(this.program);

    // Setup a quad
    this.vertexPositionAttribute = gl.getAttribLocation(
      this.program,
      'aVertexPosition'
    );
    this.textureCoordAttribute = gl.getAttribLocation(
      this.program,
      'aTextureCoord'
    );

    this.samplerUniform = gl.getUniformLocation(this.program, 'uSampler');

    this.vertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexPositionBuffer);
    let vertices = [
      -1.0,
      1.0,
      0.0,
      -1.0,
      -1.0,
      0.0,
      1.0,
      1.0,
      0.0,
      1.0,
      -1.0,
      0.0,
    ];
    let f32Vertices = new Float32Array(vertices);
    gl.bufferData(gl.ARRAY_BUFFER, f32Vertices, gl.STATIC_DRAW);
    this.vertexPositionBuffer.itemSize = 3;
    this.vertexPositionBuffer.numItems = 12;

    this.textureCoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, this.textureCoordBuffer);
    // Precalculate different texture UV coordinates depending on the possible
    // orientations of the device depending if there is a VRDisplay or not
    let textureCoords = null;
    if (this.vrDisplay) {
      // In the case of ARKit camera frame the canvas might not have been
      // created of the correct size so the UV values can't be calculated from it.
      let u = window.WebARonARKitSendsCameraFrames ? 1.0 :
        this.passThroughCamera.width / this.passThroughCamera.textureWidth;
      let v = window.WebARonARKitSendsCameraFrames ? 1.0 :
        this.passThroughCamera.height / this.passThroughCamera.textureHeight;
      textureCoords = [
        [0.0, 0.0, 0.0, v, u, 0.0, u, v],
        [u, 0.0, 0.0, 0.0, u, v, 0.0, v],
        [u, v, u, 0.0, 0.0, v, 0.0, 0.0],
        [0.0, v, u, v, 0.0, 0.0, u, 0.0],
      ];
    } else {
      textureCoords = [
        [0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0],
        [1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0],
        [1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
        [0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0],
      ];
    }

    this.f32TextureCoords = [];
    for (let i = 0; i < textureCoords.length; i++) {
      this.f32TextureCoords.push(new Float32Array(textureCoords[i]));
    }
    // Store the current combined orientation to check if it has changed
    // during the update calls and use the correct texture coordinates.
    this.combinedOrientation = combineOrientations(
      screen.orientation ? screen.orientation.angle : window.orientation,
      this.passThroughCamera.orientation
    );

    gl.bufferData(
      gl.ARRAY_BUFFER,
      this.f32TextureCoords[this.combinedOrientation],
      gl.STATIC_DRAW
    );
    this.textureCoordBuffer.itemSize = 2;
    this.textureCoordBuffer.numItems = 8;
    gl.bindBuffer(gl.ARRAY_BUFFER, null);

    this.indexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
    let indices = [0, 1, 2, 2, 1, 3];
    let ui16Indices = new Uint16Array(indices);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, ui16Indices, gl.STATIC_DRAW);
    this.indexBuffer.itemSize = 1;
    this.indexBuffer.numItems = 6;
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

    this.texture = gl.createTexture();
    gl.useProgram(null);

    // The projection matrix will be based on an identify orthographic camera
    this.projectionMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
    this.mvMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
    return this;
  }
@param {VRDisplay} vrDisplay
@param {WebGLRenderingContext} gl