Skip to content

Instantly share code, notes, and snippets.

@Inclushe
Last active April 1, 2022 18:16
Show Gist options
  • Save Inclushe/e34cc292187a4c3a0a78c995762a503d to your computer and use it in GitHub Desktop.
Save Inclushe/e34cc292187a4c3a0a78c995762a503d to your computer and use it in GitHub Desktop.

WebGL Notes

Useful Links

Initializing

  1. Set up context with canvas element
const canvas = document.querySelector('canvas')
const gl = canvas.getContext('webgl')
  1. Create Program
const program = gl.createProgram()
  1. Create, compile and attach shaders to program
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)

const vShaderSource = `
attribute vec2 position;

void main() {
  gl_PointSize = 20.0;
  gl_Position = vec4(position / 2.0, 0, 1);
}
`

const fShaderSource = `
precision mediump float;

void main() {
  gl_FragColor = vec4(1, 0, 0, 1);
}
`

function compileShader(shader, source) {
  gl.shaderSource(shader, source)
  gl.compileShader(shader)

  const log = gl.getShaderInfoLog(shader)
  if (log) throw new Error(log)
}

compileShader(vertexShader, vShaderSource)
compileShader(fragmentShader, fShaderSource)

gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
  1. Link and use program
gl.linkProgram(program)
gl.useProgram(program)
  1. For each attribute passed from Javascript

    1. Get attrib location/pointer from shader
    const positionPointer = gl.getAttribLocation(program, 'position')
    1. Create data (ex. Float32)
    const positionData = new Float32Array([
      -1.0,  1.0,
       1.0,  1.0,
       1.0, -1.0,
      -1.0, -1.0 
    ])
    1. Create buffer
    const positionBuffer = gl.createBuffer(gl.ARRAY_BUFFER)
    1. Bind buffer
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
    1. Buffer data
    gl.bufferData(gl.ARRAY_BUFFER, positionData, gl.STATIC_DRAW)
    1. Enable vertex attrib array
    gl.enableVertexAttribArray(positionPointer)
    1. Vertex attrib pointer
    const attributeSize = 2
    const type = gl.FLOAT
    const normalized = false
    const stride = 0
    const offset = 0
    
    gl.vertexAttribPointer(positionPointer, attributeSize, type, normalized, stride, offset)
  2. For each uniform passed from Javascript

    1. Get uniform location/pointer from shader
    const resolutionUniformLocation = gl.getUniformLocation(program, 'resolution')
    1. Set value of pointer
    gl.uniform2fv(resolutionUniformLocation, [canvas.width, canvas.height])
  3. For each index buffer passed from Javascript

    1. Create data
    const indexData = new Uint8Array([
      0, 1, 2,
      1, 2, 3
    ])
    1. Create buffer
    const indexBuffer = gl.createBuffer(gl.ARRAY_BUFFER)
    1. Bind buffer
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
    1. Buffer data
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW)
  4. For each texture passed from Javascript

    1. Use a vertex shader like this
    attribute vec2 position;
    
    void main() {
        gl_Position = vec4(position, 0, 1);
    }
    1. Use a fragment shader like this
    precision mediump float;
    
    uniform sampler2D texture;
    uniform vec2 resolution;
    
    void main() {
      vec2 texCoord = gl_FragCoord.xy / resolution;
      gl_FragColor = texture2D(texture, texCoord);
    }
    1. Draw a rectangle
    const vertexPositionData = new Float32Array([
      -1.0,  1.0,
       1.0,  1.0,
      -1.0, -1.0,
       1.0, -1.0,
    ])
    const vertexPositionBuffer = gl.createBuffer()
    
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer)
    gl.bufferData(gl.ARRAY_BUFFER, vertexPosition, gl.STATIC_DRAW)
    const attributeLocations = {
      position: gl.getAttribLocation(program, 'position'),
    }
    
    const uniformLocations = {
      texture: gl.getUniformLocation(program, 'texture'),
      resolution: gl.getUniformLocation(program, 'resolution')
    }
    
    gl.enableVertexAttribArray(attributeLocations.position)
    gl.vertexAttribPointer(attributeLocations.position, 2, gl.FLOAT, false, 0, 0)
    
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexPositionData.length / 2)
    1. Load image
    export async function loadImage(src) {
      const img = new Image()
    
      let _resolve
      const p = new Promise((resolve) => _resolve = resolve)
    
      img.onload = () => {
          _resolve(img)
      }
    
      img.src = src
    
      return p
    }
    
    import textureImageSrc from '../assets/images/texture.jpg'
    
    loadImage(textureImageSrc).then((textureImg) => {
      const texture = gl.createTexture()
      gl.bindTexture(gl.TEXTURE_2D, texture)
      gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1)
      gl.texImage2D(
        gl.TEXTURE_2D,
        0,
        gl.RGBA,
        gl.RGBA,
        gl.UNSIGNED_BYTE,
        textureImg
      )
    
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
    
      gl.activeTexture(gl.TEXTURE0)
      gl.uniform1i(uniformLocations.texture, 0)
      gl.uniform2fv(uniformLocations.resolution, [canvas.width, canvas.height])
    
      gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0)
    })
  5. Draw to context

  • If using index buffer
gl.drawElements(gl.TRIANGLES, indexData.length, gl.UNSIGNED_BYTE, 0)
  • Else
gl.drawArrays(gl.TRIANGLES, 0, positionData.length / 2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment