You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
5.8 KiB
193 lines
5.8 KiB
const stroke_vs_src = ` |
|
attribute float a_type; |
|
attribute vec2 a_pos; |
|
attribute vec2 a_texcoord; |
|
attribute vec3 a_color; |
|
|
|
uniform vec2 u_scale; |
|
uniform vec2 u_res; |
|
uniform vec2 u_translation; |
|
|
|
varying vec3 v_color; |
|
varying vec2 v_texcoord; |
|
varying float v_type; |
|
|
|
void main() { |
|
vec2 screen01 = (a_pos * u_scale + u_translation) / u_res; |
|
vec2 screen02 = screen01 * 2.0; |
|
|
|
screen02.y = 2.0 - screen02.y; |
|
|
|
v_color = a_color; |
|
v_texcoord = a_texcoord; |
|
v_type = a_type; |
|
|
|
gl_Position = vec4(screen02 - 1.0, 0, 1); |
|
} |
|
`; |
|
|
|
const stroke_fs_src = ` |
|
#extension GL_OES_standard_derivatives : enable |
|
|
|
precision mediump float; |
|
|
|
varying vec3 v_color; |
|
varying vec2 v_texcoord; |
|
varying float v_type; |
|
|
|
void main() { |
|
vec2 uv = v_texcoord * 2.0 - 1.0; |
|
float sdf = 1.0 - mix(abs(uv.y), length(uv), v_type); |
|
float pd = fwidth(sdf); |
|
float alpha = 1.0 - smoothstep(pd, 0.0, sdf); |
|
|
|
gl_FragColor = vec4(v_color * alpha, alpha); |
|
} |
|
`; |
|
|
|
const tquad_vs_src = ` |
|
attribute vec2 a_pos; |
|
attribute vec2 a_texcoord; |
|
|
|
uniform vec2 u_scale; |
|
uniform vec2 u_res; |
|
uniform vec2 u_translation; |
|
|
|
varying vec2 v_texcoord; |
|
|
|
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_texcoord = a_texcoord; |
|
gl_Position = vec4(screen11, 0, 1); |
|
} |
|
`; |
|
|
|
const tquad_fs_src = ` |
|
precision mediump float; |
|
|
|
varying vec2 v_texcoord; |
|
|
|
uniform sampler2D u_texture; |
|
uniform bool u_outline; |
|
|
|
void main() { |
|
if (!u_outline) { |
|
gl_FragColor = texture2D(u_texture, v_texcoord); |
|
} else { |
|
gl_FragColor = mix(texture2D(u_texture, v_texcoord), vec4(0.7, 0.7, 0.95, 1), 0.5); |
|
} |
|
} |
|
`; |
|
|
|
function init_webgl(state, context) { |
|
context.canvas = document.querySelector('#c'); |
|
context.gl = context.canvas.getContext('webgl', { |
|
'preserveDrawingBuffer': true, |
|
'desynchronized': true, |
|
'antialias': false, |
|
}); |
|
|
|
const gl = context.gl; |
|
|
|
gl.enable(gl.BLEND); |
|
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); |
|
gl.getExtension('OES_standard_derivatives'); |
|
|
|
const stroke_vs = create_shader(gl, gl.VERTEX_SHADER, stroke_vs_src); |
|
const stroke_fs = create_shader(gl, gl.FRAGMENT_SHADER, stroke_fs_src); |
|
|
|
const quad_vs = create_shader(gl, gl.VERTEX_SHADER, tquad_vs_src); |
|
const quad_fs = create_shader(gl, gl.FRAGMENT_SHADER, tquad_fs_src); |
|
|
|
context.programs['stroke'] = create_program(gl, stroke_vs, stroke_fs); |
|
context.programs['quad'] = create_program(gl, quad_vs, quad_fs); |
|
|
|
context.locations['stroke'] = { |
|
'a_type': gl.getAttribLocation(context.programs['stroke'], 'a_type'), |
|
'a_pos': gl.getAttribLocation(context.programs['stroke'], 'a_pos'), |
|
'a_texcoord': gl.getAttribLocation(context.programs['stroke'], 'a_texcoord'), |
|
'a_color': gl.getAttribLocation(context.programs['stroke'], 'a_color'), |
|
|
|
'u_res': gl.getUniformLocation(context.programs['stroke'], 'u_res'), |
|
'u_scale': gl.getUniformLocation(context.programs['stroke'], 'u_scale'), |
|
'u_translation': gl.getUniformLocation(context.programs['stroke'], 'u_translation'), |
|
}; |
|
|
|
context.locations['quad'] = { |
|
'a_pos': gl.getAttribLocation(context.programs['quad'], 'a_pos'), |
|
'a_texcoord': gl.getAttribLocation(context.programs['quad'], 'a_texcoord'), |
|
'u_res': gl.getUniformLocation(context.programs['quad'], 'u_res'), |
|
'u_scale': gl.getUniformLocation(context.programs['quad'], 'u_scale'), |
|
'u_translation': gl.getUniformLocation(context.programs['quad'], 'u_translation'), |
|
'u_outline': gl.getUniformLocation(context.programs['quad'], 'u_outline'), |
|
'u_texture': gl.getUniformLocation(context.programs['quad'], 'u_texture'), |
|
}; |
|
|
|
context.buffers['stroke'] = { |
|
'b_packed': context.gl.createBuffer(), |
|
}; |
|
|
|
context.buffers['quad'] = { |
|
'b_pos': context.gl.createBuffer(), |
|
'b_texcoord': 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; |
|
|
|
schedule_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); |
|
}
|
|
|