|
|
|
const vertex_shader_source = `
|
|
|
|
attribute vec2 a_pos;
|
|
|
|
attribute vec3 a_color;
|
|
|
|
|
|
|
|
uniform vec2 u_scale;
|
|
|
|
uniform vec2 u_res;
|
|
|
|
uniform vec2 u_translation;
|
|
|
|
uniform int u_layer;
|
|
|
|
|
|
|
|
varying vec3 v_color;
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
vec2 screen01 = (a_pos * u_scale + u_translation) / u_res;
|
|
|
|
vec2 screen02 = screen01 * 2.0;
|
|
|
|
screen02.y = 2.0 - screen02.y;
|
|
|
|
vec2 screen11 = screen02 - 1.0;
|
|
|
|
v_color = a_color;
|
|
|
|
gl_Position = vec4(screen11, u_layer, 1);
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
const fragment_shader_source = `
|
|
|
|
precision mediump float;
|
|
|
|
|
|
|
|
varying vec3 v_color;
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
gl_FragColor = vec4(v_color, 1.0);
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
function init_webgl(state, context) {
|
|
|
|
context.canvas = document.querySelector('#c');
|
|
|
|
context.gl = context.canvas.getContext('webgl', {
|
|
|
|
'preserveDrawingBuffer': true,
|
|
|
|
'desynchronized': true,
|
|
|
|
'antialias': true,
|
|
|
|
});
|
|
|
|
|
|
|
|
context.gl.enable(context.gl.BLEND);
|
|
|
|
context.gl.blendFunc(context.gl.ONE, context.gl.ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
const vertex_shader = create_shader(context.gl, context.gl.VERTEX_SHADER, vertex_shader_source);
|
|
|
|
const fragment_shader = create_shader(context.gl, context.gl.FRAGMENT_SHADER, fragment_shader_source);
|
|
|
|
const program = create_program(context.gl, vertex_shader, fragment_shader)
|
|
|
|
|
|
|
|
context.program = program;
|
|
|
|
|
|
|
|
context.locations['a_pos'] = context.gl.getAttribLocation(program, 'a_pos');
|
|
|
|
context.locations['a_color'] = context.gl.getAttribLocation(program, 'a_color');
|
|
|
|
|
|
|
|
context.locations['u_res'] = context.gl.getUniformLocation(program, 'u_res');
|
|
|
|
context.locations['u_scale'] = context.gl.getUniformLocation(program, 'u_scale');
|
|
|
|
context.locations['u_translation'] = context.gl.getUniformLocation(program, 'u_translation');
|
|
|
|
context.locations['u_layer'] = context.gl.getUniformLocation(program, 'u_layer');
|
|
|
|
|
|
|
|
context.buffers['b_pos'] = context.gl.createBuffer();
|
|
|
|
context.buffers['b_color'] = context.gl.createBuffer();
|
|
|
|
|
|
|
|
const resize_canvas = (entries) => {
|
|
|
|
// https://www.khronos.org/webgl/wiki/HandlingHighDPI
|
|
|
|
const entry = entries[0];
|
|
|
|
|
|
|
|
let width;
|
|
|
|
let height;
|
|
|
|
|
|
|
|
if (entry.devicePixelContentBoxSize) {
|
|
|
|
width = entry.devicePixelContentBoxSize[0].inlineSize;
|
|
|
|
height = entry.devicePixelContentBoxSize[0].blockSize;
|
|
|
|
} else if (entry.contentBoxSize) {
|
|
|
|
// fallback for Safari that will not always be correct
|
|
|
|
width = Math.round(entry.contentBoxSize[0].inlineSize * devicePixelRatio);
|
|
|
|
height = Math.round(entry.contentBoxSize[0].blockSize * devicePixelRatio);
|
|
|
|
}
|
|
|
|
|
|
|
|
context.canvas.width = width;
|
|
|
|
context.canvas.height = height;
|
|
|
|
|
|
|
|
window.requestAnimationFrame(() => draw(state, context));
|
|
|
|
}
|
|
|
|
|
|
|
|
const resize_observer = new ResizeObserver(resize_canvas);
|
|
|
|
resize_observer.observe(context.canvas);
|
|
|
|
}
|
|
|
|
|
|
|
|
function create_shader(gl, type, source) {
|
|
|
|
const shader = gl.createShader(type);
|
|
|
|
|
|
|
|
gl.shaderSource(shader, source);
|
|
|
|
gl.compileShader(shader);
|
|
|
|
|
|
|
|
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
|
|
return shader;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.error(type, ':', gl.getShaderInfoLog(shader));
|
|
|
|
|
|
|
|
gl.deleteShader(shader);
|
|
|
|
}
|
|
|
|
|
|
|
|
function create_program(gl, vs, fs) {
|
|
|
|
const program = gl.createProgram();
|
|
|
|
|
|
|
|
gl.attachShader(program, vs);
|
|
|
|
gl.attachShader(program, fs);
|
|
|
|
gl.linkProgram(program);
|
|
|
|
|
|
|
|
if (gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
|
|
return program;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.error('link:', gl.getProgramInfoLog(program));
|
|
|
|
|
|
|
|
gl.deleteProgram(program);
|
|
|
|
}
|