Browse Source

Dots pattern with fancy fadeout

ssao
A.Olokhtonov 6 months ago
parent
commit
ee42e400c4
  1. 2
      client/index.js
  2. 21
      client/webgl_draw.js
  3. 39
      client/webgl_geometry.js
  4. 30
      client/webgl_shaders.js

2
client/index.js

@ -26,6 +26,8 @@ const config = { @@ -26,6 +26,8 @@ const config = {
stroke_texture_size: 1024, // means no more than 1024^2 = 1M strokes in total (this is a LOT. HMH blackboard has like 80K)
dynamic_stroke_texture_size: 128, // means no more than 128^2 = 16K dynamic strokes at once
bvh_fullnode_depth: 5,
pattern_fadeout_min: 0.3,
pattern_fadeout_max: 0.75,
benchmark: {
zoom_level: -75,
offset: { x: 425, y: -1195 },

21
client/webgl_draw.js

@ -106,16 +106,21 @@ async function draw(state, context) { @@ -106,16 +106,21 @@ async function draw(state, context) {
gl.useProgram(context.programs['pattern'].dots);
buffers = context.buffers['pattern'];
locations = context.locations['pattern'].dots;
{
if (state.canvas.zoom >= config.pattern_fadeout_min) {
// Reused data
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_dot']);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([10, 0, 20, 0, 10, 10]), gl.STREAM_DRAW);
const one_dot = new Float32Array(geometry_gen_circle(0, 0, 1, 32));
const dot_instances = new Float32Array(geometry_gen_fullscreen_grid(state, context, 32, 32, 50, 50));
gl.bufferData(gl.ARRAY_BUFFER, one_dot, gl.STREAM_DRAW);
gl.enableVertexAttribArray(locations['a_xy']);
gl.vertexAttribPointer(locations['a_xy'], 2, gl.FLOAT, false, 2 * 4, 0);
// Per-instance data
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_instance']);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([100, 100, 150, 150, 10, 10, 200, 10]), gl.STREAM_DRAW);
gl.bufferData(gl.ARRAY_BUFFER, dot_instances, gl.STREAM_DRAW);
gl.enableVertexAttribArray(locations['a_center']);
gl.vertexAttribPointer(locations['a_center'], 2, gl.FLOAT, false, 2 * 4, 0);
gl.vertexAttribDivisor(locations['a_center'], 1);
@ -124,9 +129,17 @@ async function draw(state, context) { @@ -124,9 +129,17 @@ async function draw(state, context) {
gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom);
gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y);
gl.drawArraysInstanced(gl.TRIANGLES, 0, 3, 4);
if (config.pattern_fadeout_min <= state.canvas.zoom && state.canvas.zoom <= config.pattern_fadeout_max) {
const t = (state.canvas.zoom - config.pattern_fadeout_min) / (config.pattern_fadeout_max - config.pattern_fadeout_min);
gl.uniform1f(locations['u_fadeout'], t);
} else {
gl.uniform1f(locations['u_fadeout'], 1);
}
gl.drawArraysInstanced(gl.TRIANGLES, 0, one_dot.length / 2, dot_instances.length / 2);
}
gl.clear(gl.DEPTH_BUFFER_BIT);
gl.useProgram(context.programs['sdf'].main);
buffers = context.buffers['sdf'];
locations = context.locations['sdf'].main;

39
client/webgl_geometry.js

@ -247,3 +247,42 @@ function image_at(state, x, y) { @@ -247,3 +247,42 @@ function image_at(state, x, y) {
return null;
}
function geometry_gen_circle(cx, cy, r, n) {
const step = 2 * Math.PI / n;
const result = [];
for (let i = 0; i < n; ++i) {
const theta = i * step;
const next_theta = (i < n - 1 ? (i + 1) * step : 0);
const x = cx + r * Math.cos(theta);
const y = cy + r * Math.sin(theta);
const next_x = cx + r * Math.cos(next_theta);
const next_y = cy + r * Math.sin(next_theta);
result.push(cx, cy, x, y, next_x, next_y);
}
return result;
}
function geometry_gen_fullscreen_grid(state, context, step_x, step_y, offset_x, offset_y) {
const result = [];
const width = context.canvas.width;
const height = context.canvas.height;
const topleft = screen_to_canvas(state, {'x': 0, 'y': 0});
const bottomright = screen_to_canvas(state, {'x': width, 'y': height});
topleft.x = Math.floor(topleft.x / step_x) * step_x;
topleft.y = Math.ceil(topleft.y / step_y) * step_y;
bottomright.x = Math.floor(bottomright.x / step_x) * step_x;
bottomright.y = Math.ceil(bottomright.y / step_y) * step_y;
for (let y = topleft.y; y <= bottomright.y; y += step_y) {
for (let x = topleft.x; x <= bottomright.x; x += step_x) {
result.push(x, y);
}
}
return result;
}

30
client/webgl_shaders.js

@ -253,12 +253,29 @@ const dots_vs_src = `#version 300 es @@ -253,12 +253,29 @@ const dots_vs_src = `#version 300 es
in vec2 a_xy;
in vec2 a_center; // per-instance
out float v_dist;
out float v_fadeout;
uniform vec2 u_scale;
uniform vec2 u_res;
uniform vec2 u_translation;
uniform float u_fadeout;
void main() {
vec2 screen02 = ((a_center + a_xy) * u_scale + u_translation) / u_res * 2.0;
float apron = 2.0;
vec2 pixel = vec2(2.0) / u_res * apron;
vec2 outwards = vec2(0.0);
float rscale = apron / u_scale.x;
if (gl_VertexID % 3 == 0) {
v_dist = 0.0;
} else {
outwards = a_xy;
v_dist = 1.0 + rscale;
}
v_fadeout = u_fadeout;
vec2 screen02 = ((a_center + a_xy) * u_scale + u_translation) / u_res * 2.0 + outwards * pixel;
screen02.y = 2.0 - screen02.y;
gl_Position = vec4(screen02 - 1.0, 0.0, 1.0);
}
@ -267,12 +284,20 @@ const dots_vs_src = `#version 300 es @@ -267,12 +284,20 @@ const dots_vs_src = `#version 300 es
const dots_fs_src = `#version 300 es
precision highp float;
in float v_dist;
in float v_fadeout;
layout(location = 0) out vec4 FragColor;
void main() {
FragColor = vec4(0.0, 0.0, 0.0, 1.0);
float fade = 0.5 * length(fwidth(v_dist));
float alpha = 1.0 - smoothstep(-fade, fade, v_dist - 1.0);
alpha *= v_fadeout;
vec3 color = vec3(0.8);
FragColor = vec4(color * alpha, alpha);
}
`;
function init_webgl(state, context) {
context.canvas = document.querySelector('#c');
context.gl = context.canvas.getContext('webgl2', {
@ -363,6 +388,7 @@ function init_webgl(state, context) { @@ -363,6 +388,7 @@ function init_webgl(state, context) {
'u_res': gl.getUniformLocation(context.programs['pattern'].dots, 'u_res'),
'u_scale': gl.getUniformLocation(context.programs['pattern'].dots, 'u_scale'),
'u_translation': gl.getUniformLocation(context.programs['pattern'].dots, 'u_translation'),
'u_fadeout': gl.getUniformLocation(context.programs['pattern'].dots, 'u_fadeout'),
}
};

Loading…
Cancel
Save