Comparisons

cube

The goal of this task is to render a spinning textured cube. The texture asset is stored in an image that must be loaded over the network.

regl:

const regl = require('../../regl')()
const mat4 = require('gl-mat4')

var cubePosition = [
  [-0.5, +0.5, +0.5], [+0.5, +0.5, +0.5], [+0.5, -0.5, +0.5], [-0.5, -0.5, +0.5], // positive z face.
  [+0.5, +0.5, +0.5], [+0.5, +0.5, -0.5], [+0.5, -0.5, -0.5], [+0.5, -0.5, +0.5], // positive x face
  [+0.5, +0.5, -0.5], [-0.5, +0.5, -0.5], [-0.5, -0.5, -0.5], [+0.5, -0.5, -0.5], // negative z face
  [-0.5, +0.5, -0.5], [-0.5, +0.5, +0.5], [-0.5, -0.5, +0.5], [-0.5, -0.5, -0.5], // negative x face.
  [-0.5, +0.5, -0.5], [+0.5, +0.5, -0.5], [+0.5, +0.5, +0.5], [-0.5, +0.5, +0.5], // top face
  [-0.5, -0.5, -0.5], [+0.5, -0.5, -0.5], [+0.5, -0.5, +0.5], [-0.5, -0.5, +0.5]  // bottom face
]

var cubeUv = [
  [0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], // positive z face.
  [0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], // positive x face.
  [0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], // negative z face.
  [0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], // negative x face.
  [0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], // top face
  [0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]  // bottom face
]

const cubeElements = [
  [2, 1, 0], [2, 0, 3],       // positive z face.
  [6, 5, 4], [6, 4, 7],       // positive x face.
  [10, 9, 8], [10, 8, 11],    // negative z face.
  [14, 13, 12], [14, 12, 15], // negative x face.
  [18, 17, 16], [18, 16, 19], // top face.
  [20, 21, 22], [23, 20, 22]  // bottom face
]

const drawCube = regl({
  frag: `
  precision mediump float;
  varying vec2 vUv;
  uniform sampler2D tex;
  void main () {
    gl_FragColor = texture2D(tex,vUv);
  }`,
  vert: `
  precision mediump float;
  attribute vec3 position;
  attribute vec2 uv;
  varying vec2 vUv;
  uniform mat4 projection, view;
  void main() {
    vUv = uv;
    gl_Position = projection * view * vec4(position, 1);
  }`,
  attributes: {
    position: cubePosition,
    uv: cubeUv
  },
  elements: cubeElements,
  uniforms: {
    view: ({tick}) => {
      const t = 0.01 * tick
      return mat4.lookAt([],
                         [5 * Math.cos(t), 2.5 * Math.sin(t), 5 * Math.sin(t)],
                         [0, 0.0, 0],
                         [0, 1, 0])
    },
    projection: ({viewportWidth, viewportHeight}) =>
      mat4.perspective([],
                       Math.PI / 4,
                       viewportWidth / viewportHeight,
                       0.01,
                       10),
    tex: regl.prop('texture')
  }
})

require('resl')({
  manifest: {
    texture: {
      type: 'image',
      src: 'assets/peppers.png',
      parser: (data) => regl.texture({
        data: data,
        mag: 'linear',
        min: 'linear'
      })
    }
  },
  onDone: ({texture}) => {
    regl.frame(() => {
      regl.clear({
        color: [0, 0, 0, 255],
        depth: 1
      })
      drawCube({texture})
    })
  }
})

          

webgl:

<!doctype html>
<html>
  <head>
    <title>WebGL Textured Cube Demo</title>
    <script type="text/javascript">
/* global Image, alert, requestAnimationFrame */
var canvas
var gl

var cubePositionBuffer
var cubeUvBuffer
var cubeElementsBuffers

var cubeTexture

var shaderProgram
var cubePositionAttribute
var cubeUvAttribute

var projectionUniformLocation
var viewUniformLocation
var tick

function start () {
  canvas = document.getElementById('glcanvas')
  tick = 0

  initWebGL(canvas)

  if (gl) {
    gl.clearColor(0.0, 0.0, 0.0, 1.0)
    gl.enable(gl.DEPTH_TEST)
    gl.enable(gl.CULL_FACE)

    initShaders()
    initBuffers()
    initTextures()

    // start RAF
    requestAnimationFrame(drawScene)
  }
}

function initWebGL () {
  try {
    gl = canvas.getContext('experimental-webgl')
  } catch (e) {
  }

  if (!gl) {
    alert('Unable to initialize WebGL. Your browser may not support it.')
  }
}

function initBuffers () {
  cubePositionBuffer = gl.createBuffer()
  gl.bindBuffer(gl.ARRAY_BUFFER, cubePositionBuffer)

  var cubePosition = [
    // positive z face.
      -0.5, +0.5, +0.5,
      +0.5, +0.5, +0.5,
      +0.5, -0.5, +0.5,
      -0.5, -0.5, +0.5,

    // positive x face
      +0.5, +0.5, +0.5,
      +0.5, +0.5, -0.5,
      +0.5, -0.5, -0.5,
      +0.5, -0.5, +0.5,

    // negative z face
      +0.5, +0.5, -0.5,
      -0.5, +0.5, -0.5,
      -0.5, -0.5, -0.5,
      +0.5, -0.5, -0.5,

    // negative x face.
      -0.5, +0.5, -0.5,
      -0.5, +0.5, +0.5,
      -0.5, -0.5, +0.5,
      -0.5, -0.5, -0.5,

    // top face
      -0.5, +0.5, -0.5,
      +0.5, +0.5, -0.5,
      +0.5, +0.5, +0.5,
      -0.5, +0.5, +0.5,

    // bottom face
      -0.5, -0.5, -0.5,
      +0.5, -0.5, -0.5,
      +0.5, -0.5, +0.5,
      -0.5, -0.5, +0.5
  ]
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cubePosition), gl.STATIC_DRAW)

  cubeUvBuffer = gl.createBuffer()
  gl.bindBuffer(gl.ARRAY_BUFFER, cubeUvBuffer)

  var cubeUv = [
    // positive z face.
    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,

    // positive x face.
    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,

    // negative z face.
    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,

    // negative x face.
    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,

    // top face
    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,

    // bottom face
    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0
  ]

  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cubeUv), gl.STATIC_DRAW)

  cubeElementsBuffers = gl.createBuffer()
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeElementsBuffers)

  var cubeElements = [
    // positive z face.
    2, 1, 0,
    2, 0, 3,

    // positive x face.
    6, 5, 4,
    6, 4, 7,

    // negative z face.
    10, 9, 8,
    10, 8, 11,

    // negative x face.
    14, 13, 12,
    14, 12, 15,

    // top face.
    18, 17, 16,
    18, 16, 19,

    // bottom face
    20, 21, 22,
    23, 20, 22
  ]
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeElements), gl.STATIC_DRAW)
}

function initTextures () {
  cubeTexture = gl.createTexture()
  var cubeImage = new Image()
  cubeImage.onload = function () {
    gl.bindTexture(gl.TEXTURE_2D, cubeTexture)
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, cubeImage)
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
  }
  cubeImage.src = 'assets/peppers.png'
}

function drawScene () {
  tick++
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

  // bind buffers.
  gl.bindBuffer(gl.ARRAY_BUFFER, cubePositionBuffer)
  gl.vertexAttribPointer(cubePositionAttribute, 3, gl.FLOAT, false, 0, 0)

  gl.bindBuffer(gl.ARRAY_BUFFER, cubeUvBuffer)
  gl.vertexAttribPointer(cubeUvAttribute, 2, gl.FLOAT, false, 0, 0)

  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeElementsBuffers)

  // set texture.
  gl.activeTexture(gl.TEXTURE0)
  gl.bindTexture(gl.TEXTURE_2D, cubeTexture)

  // set uniforms
  gl.uniform1i(gl.getUniformLocation(shaderProgram, 'tex'), 0)

  const t = 0.01 * tick
  var perspectiveMatrix = perspective(45, 640.0 / 480.0, 0.1, 100.0)
  gl.uniformMatrix4fv(projectionUniformLocation, false, new Float32Array(perspectiveMatrix))

  gl.uniformMatrix4fv(viewUniformLocation, false, new Float32Array(
    lookAt(
      [5 * Math.cos(t), 2.5 * Math.sin(t), 5 * Math.sin(t)],
      [0, 0.0, 0],
      [0, 1, 0])))

  gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0)

  requestAnimationFrame(drawScene)
}

function initShaders () {
  var fragmentShader = getShader(gl, 'shader-fs')
  var vertexShader = getShader(gl, 'shader-vs')

  shaderProgram = gl.createProgram()
  gl.attachShader(shaderProgram, vertexShader)
  gl.attachShader(shaderProgram, fragmentShader)
  gl.linkProgram(shaderProgram)

  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
    alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram))
  }

  gl.useProgram(shaderProgram)

  cubePositionAttribute = gl.getAttribLocation(shaderProgram, 'position')
  gl.enableVertexAttribArray(cubePositionAttribute)

  cubeUvAttribute = gl.getAttribLocation(shaderProgram, 'uv')
  gl.enableVertexAttribArray(cubeUvAttribute)

  projectionUniformLocation = gl.getUniformLocation(shaderProgram, 'projection')
  viewUniformLocation = gl.getUniformLocation(shaderProgram, 'view')
}

function getShader (gl, id) {
  var e = document.getElementById(id)
  if (!e) {
    return null
  }

  var sourceCode = e.firstChild.textContent
  var shader

  if (e.type === 'x-shader/x-fragment') {
    shader = gl.createShader(gl.FRAGMENT_SHADER)
  } else if (e.type === 'x-shader/x-vertex') {
    shader = gl.createShader(gl.VERTEX_SHADER)
  } else {
    return null
  }

  gl.shaderSource(shader, sourceCode)
  gl.compileShader(shader)

  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader))
    return null
  }

  return shader
}

// Taken from gl-mat4
// https://github.com/stackgl/gl-mat4/blob/master/lookAt.js
function lookAt (eye, center, up) {
  var x0, x1, x2, y0, y1, y2, z0, z1, z2, len
  var eyex = eye[0]
  var eyey = eye[1]
  var eyez = eye[2]
  var upx = up[0]
  var upy = up[1]
  var upz = up[2]
  var centerx = center[0]
  var centery = center[1]
  var centerz = center[2]

  z0 = eyex - centerx
  z1 = eyey - centery
  z2 = eyez - centerz

  len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2)
  z0 *= len
  z1 *= len
  z2 *= len

  x0 = upy * z2 - upz * z1
  x1 = upz * z0 - upx * z2
  x2 = upx * z1 - upy * z0
  len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2)
  if (!len) {
    x0 = 0
    x1 = 0
    x2 = 0
  } else {
    len = 1 / len
    x0 *= len
    x1 *= len
    x2 *= len
  }

  y0 = z1 * x2 - z2 * x1
  y1 = z2 * x0 - z0 * x2
  y2 = z0 * x1 - z1 * x0

  len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2)
  if (!len) {
    y0 = 0
    y1 = 0
    y2 = 0
  } else {
    len = 1 / len
    y0 *= len
    y1 *= len
    y2 *= len
  }

  return [
    x0, y0, z0, 0,
    x1, y1, z1, 0,
    x2, y2, z2, 0,
    -(x0 * eyex + x1 * eyey + x2 * eyez),
    -(y0 * eyex + y1 * eyey + y2 * eyez),
    -(z0 * eyex + z1 * eyey + z2 * eyez),
    1
  ]
}

// Taken from gl-mat4
// https://github.com/stackgl/gl-mat4/blob/master/perspective.js
function perspective (fovy, aspect, near, far) {
  var f = 1.0 / Math.tan(fovy / 2)
  var nf = 1 / (near - far)

  return [
    f / aspect, 0.0, 0.0, 0.0,
    0.0, f, 0.0, 0.0,
    0.0, 0.0, (far + near) * nf, -1.0,
    0.0, 0.0, (2 * far * near) * nf, 0.0
  ]
}
    </script>

    <script id="shader-fs" type="x-shader/x-fragment">
  precision mediump float;
  varying vec2 vUv;
  uniform sampler2D tex;
  void main () {
    gl_FragColor = texture2D(tex,vUv);
  }
    </script>

    <script id="shader-vs" type="x-shader/x-vertex">

  precision mediump float;
  attribute vec3 position;
  attribute vec2 uv;
  varying vec2 vUv;
  uniform mat4 projection, view;
  void main() {
    vUv = uv;
    gl_Position = projection * view * vec4(position, 1);
  }
    </script>
  </head>
  <body onload="start()">
    <canvas id="glcanvas" width="640" height="480">
      Your browser doesn't appear to support the <code><canvas></code> element.
    </canvas>
  </body>
</html>

          

threejs:

<!doctype html>
<html>
  <head>
    <title>threejs cube</title>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
    <meta content="utf-8" http-equiv="encoding">
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
    <script type="text/javascript">
    var scene;
    var camera;
    var renderer;
    var mesh;
    var tick;

    function start() {
      init();
      drawShape();
      render();
    }

    function drawShape() {
      var geo = new THREE.CubeGeometry(1.0, 1.0, 1.0);

      var texture = THREE.ImageUtils.loadTexture('assets/peppers.png');
      var mats = [];
      for (var i = 0; i < 6; i++) {
        mats.push(new THREE.MeshBasicMaterial({map: texture}));
      }
      var mat = new THREE.MeshFaceMaterial(mats);

      mesh = new THREE.Mesh(geo, mat);

      scene.add(mesh);

      tick = 0;
    }

    function init() {
      scene = new THREE.Scene();

      var canvas = document.getElementById('glcanvas');
      renderer = new THREE.WebGLRenderer({canvas: canvas});
      renderer.setPixelRatio( window.devicePixelRatio );
      renderer.setSize( window.innerWidth, window.innerHeight );

      camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 100 );
    }

    function rotateCube() {
      tick += 1
      var t = tick * 0.01

      camera.position.set(5 * Math.cos(t), 2.5 * Math.sin(t), 5 * Math.sin(t))
      camera.up = new THREE.Vector3(0, 1, 0)
      camera.lookAt(new THREE.Vector3(0, 0, 0))
    }

    function render() {
      requestAnimationFrame(render);
      rotateCube();
      renderer.render(scene, camera);
    }

    </script>
  </head>
  <body onload="start()">
    <canvas id="glcanvas" width="640" height="480">
      Your browser doesn't appear to support the <code><canvas></code> element.
    </canvas>
  </body>
</html>