Published
Edited
Jun 5, 2020
1 fork
Importers
4 stars
Insert cell
Insert cell
Insert cell
fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, `
// Fragment shader provides colors (gl_FragColor)

precision mediump float;

uniform float u_time;
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform sampler2D u_texture;

void main() {
vec2 st = (gl_FragCoord.xy - u_resolution/2.) / u_resolution.y;
vec2 m = (u_mouse - u_resolution/2.) / u_resolution.y;

vec2 pos = mod(st * 5.0, 1.0);
vec3 c = texture2D(u_texture, pos).xyz;
c = mix(vec3(st, sin(u_time) / 2.0 + 0.5), c, step(0.1, distance(st, m)));
gl_FragColor = vec4(c, 1.0);
}
`)
Insert cell
vertexShader = createShader(gl, gl.VERTEX_SHADER, `
// Vertex shader provides clipspace coordinates (gl_Position)

precision mediump float;

attribute vec2 a_position;
uniform vec2 u_resolution;

void main() {
// Attribute a_position is in screen space coordinates so we need to convert them into clip space,
// clip space values belong to [-1, 1].
gl_Position = vec4((a_position / u_resolution * 2. - 1.) * vec2(1, -1), 0., 1.);
}
`)
Insert cell
main = (async () => {
// create a shader program (= vertex shader + fragment shader)
const program = createProgram(gl, vertexShader, fragmentShader)

// create a buffer to hold vertex positions
const vertexBuffer = createVertexBuffer(gl)

// store uniforms and attributes locations
const a_positionLoc = gl.getAttribLocation(program, 'a_position')
const u_timeLoc = gl.getUniformLocation(program, 'u_time')
const u_resolutionLoc = gl.getUniformLocation(program, 'u_resolution')
const u_mouseLoc = gl.getUniformLocation(program, 'u_mouse')
gl.canvas.addEventListener('mousemove', e => gl.uniform2f(u_mouseLoc, e.offsetX * devicePixelRatio, gl.canvas.height - e.offsetY * devicePixelRatio))
const texture = await loadTexture(gl, 'https://gist.githubusercontent.com/mbostock/9511ae067889eefa5537eedcbbf87dab/raw/944b6e5fe8dd535d6381b93d88bf4a854dac53d4/mona-lisa.jpg')

// set viewport before drawing
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)

// define what program for drawing(rendering)
gl.useProgram(program)

// start rendering loop
function* rendering(){
const startTime = Date.now()
while(true) {
// update u_time and u_resolution uniforms
const time = (Date.now() - startTime) / 1000
gl.uniform1f(u_timeLoc, time)
gl.uniform2f(u_resolutionLoc, gl.canvas.width, gl.canvas.height)

gl.bindTexture(gl.TEXTURE_2D, texture)

// clear the canvas before we start drawing on it.
gl.clearColor(1.0, 1.0, Math.sin(time) / 2.0 + 0.5, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

// set vertexBuffer to 'a_position' attribute
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) // bind vertexBuffer to ARRAY_BUFFER
gl.enableVertexAttribArray(a_positionLoc) // enable individual attributes
const numComponents = 2 // pull out 2 values from vertexBuffer per iteration (=per vertex)
const type = gl.FLOAT // the data in the buffer is 32bit floats
const normalize = false // don't normalize
const stride = 0 // how many bytes to get from one set of values to the next
const offset = 0 // how many bytes inside the buffer to start from
gl.vertexAttribPointer(a_positionLoc, numComponents, type, normalize, stride, offset) // Bind the buffer currently bound to gl.ARRAY_BUFFER to a generic vertex attribute

// make a draw call
const primitiveType = gl.TRIANGLE_STRIP // set how the vertices should connect
const count = 4 // specify the number of indices (vertices) to be rendered
gl.drawArrays(primitiveType, offset, count); // Render primitives from array data

yield time
}
}
return rendering()
})()
Insert cell
loadTexture = (gl, url) => new Promise((resolve, reject) => {
const image = new Image
image.crossOrigin = "anonymous"
image.onerror = reject
image.onload = () => {
const size = 2048
const context = DOM.context2d(size, size, 1)
context.canvas.style = "height: 60px; display: block;"
context.scale(1, -1)
context.drawImage(image, 0, 0, image.naturalWidth, image.naturalWidth, 0, -size, size, size)

const texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, context.canvas)
resolve(texture)
}
image.src = url
})
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more