Files
graphfx/src/nodes/WebGL/WebGl.js

243 lines
7.2 KiB
JavaScript

import Node from '../Node';
import {createCanvas, mediaSize, paintToCanvas} from '../canvas';
export default class WebGL extends Node {
constructor(name, inputs={}) {
super(name, Object.assign({
image: {
type: 'Image',
},
}, inputs), {
image: {
type: 'Image',
},
width: {
type: 'Number',
},
height: {
type: 'Number',
}
});
}
get vert() {
return `
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
`;
}
get frag() {
return `
precision mediump float;
// our texture
uniform sampler2D u_image;
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(u_image, v_texCoord).bgra;
}
`
}
fragShader(gl) {
var shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, this.frag);
gl.compileShader(shader);
if ( !gl.getShaderParameter(shader, gl.COMPILE_STATUS) ) {
var info = gl.getShaderInfoLog(shader);
throw 'Could not compile WebGL program. \n\n' + info;
}
return shader;
}
vertShader(gl) {
var shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, this.vert);
gl.compileShader(shader);
if ( !gl.getShaderParameter(shader, gl.COMPILE_STATUS) ) {
var info = gl.getShaderInfoLog(shader);
throw 'Could not compile WebGL program. \n\n' + info;
}
return shader;
}
/**
*
* @param {WebGLRenderingContext} gl
*/
compileProgram(gl) {
const program = gl.createProgram();
gl.attachShader(program, this.vertShader(gl));
gl.attachShader(program, this.fragShader(gl));
gl.linkProgram(program);
if ( !gl.getProgramParameter( program, gl.LINK_STATUS) ) {
var info = gl.getProgramInfoLog(program);
throw 'Could not compile WebGL program. \n\n' + info;
}
return program;
}
get image() {
return this.in.image.value;
}
createTexture(gl) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Set the parameters so we can render any size image.
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.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
return texture;
}
_setParams(gl, program) {
}
setup() {
if (!this.canvas) {
this.program = null;
this.canvas = createCanvas(0, 0);
}
const gl = this.canvas.getContext('webgl');
if (!this.program) {
this.program = this.compileProgram(gl);
gl.useProgram(this.program);
this.createTexture(gl);
}
}
async _update() {
const image = this.in.image.value;
if (!image) return;
const {width, height} = mediaSize(image);
this.setup();
const canvas = this.canvas;
canvas.width = width;
canvas.height = height;
const gl = canvas.getContext('webgl');
const program = this.program;
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
const positionLocation = gl.getAttribLocation(program, "a_position");
const texcoordLocation = gl.getAttribLocation(program, "a_texCoord");
const resolutionLocation = gl.getUniformLocation(program, "u_resolution");
if (this.image) {
// Upload the image into the texture.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.image);
}
const texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
this.setRectangle(gl, 0, 0, width, height);
// Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionLocation, size, type, normalize, stride, offset);
// Turn on the teccord attribute
gl.enableVertexAttribArray(texcoordLocation);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
// Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
texcoordLocation, size, type, normalize, stride, offset);
// set the resolution
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
this._setParams(gl, program);
// Draw the rectangle.
gl.drawArrays(gl.TRIANGLES, 0, 6);
const result = await createImageBitmap(canvas);
this.out.image.value = result;
this.out.width.value = this.out.image.value.width;
this.out.height.value = this.out.image.value.height;
}
setRectangle(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var y2 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2,
]), gl.STATIC_DRAW);
}
}