From 31a0b0113a7e7fde71b2676c96725191087c7f07 Mon Sep 17 00:00:00 2001 From: "A.Olokhtonov" Date: Thu, 21 Dec 2023 01:45:09 +0300 Subject: [PATCH] Prototype: separate per-quad data using instanced rendeding and per-stroke data using a texture --- client/webgl_draw.js | 70 +++++++++++++++++++++++++++++++++++- client/webgl_shaders.js | 80 +++++++++++++++++++++++++---------------- 2 files changed, 118 insertions(+), 32 deletions(-) diff --git a/client/webgl_draw.js b/client/webgl_draw.js index 41e0a66..b79daa4 100644 --- a/client/webgl_draw.js +++ b/client/webgl_draw.js @@ -57,6 +57,74 @@ function draw(state, context) { gl.clearDepth(0.0); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + gl.bindBuffer(gl.ARRAY_BUFFER, lod.data_buffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, lod.index_buffer); + + const quad_data = serializer_create(1024); + + ser_f32(quad_data, 0); + ser_f32(quad_data, 0); + ser_f32(quad_data, 200); + ser_f32(quad_data, 100); + ser_u32(quad_data, 0); + + ser_f32(quad_data, 200); + ser_f32(quad_data, 100); + ser_f32(quad_data, 255); + ser_f32(quad_data, 500); + ser_u32(quad_data, 0); + + ser_f32(quad_data, 100); + ser_f32(quad_data, 300); + ser_f32(quad_data, 125); + ser_f32(quad_data, 854); + ser_u32(quad_data, 1); + + + gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(quad_data.buffer, 0, quad_data.offset), gl.STATIC_DRAW); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array([0, 1, 2, 3, 2, 1, 4, 5, 6, 7, 6, 5, 8, 9, 10, 11, 10, 9]), gl.STATIC_DRAW); + + locations = context.locations['sdf'].main; + + gl.useProgram(context.programs['sdf'].main); + + gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height); + 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.uniform1i(locations['u_stroke_count'], 2); + gl.uniform1i(locations['u_debug_mode'], state.debug.red); + gl.uniform1i(locations['u_stroke_data'], 0); + + gl.enableVertexAttribArray(locations['a_ab']); + gl.enableVertexAttribArray(locations['a_stroke_id']); + + gl.vertexAttribPointer(locations['a_ab'], 4, gl.FLOAT, false, 5 * 4, 0); + gl.vertexAttribIPointer(locations['a_stroke_id'], 1, gl.INT, 5 * 4, 4 * 4); + + gl.vertexAttribDivisor(locations['a_ab'], 1); + gl.vertexAttribDivisor(locations['a_stroke_id'], 1); + + const stroke_data = serializer_create(1024); + + ser_u8(stroke_data, 255); + ser_u8(stroke_data, 0); + ser_u8(stroke_data, 0); + ser_u8(stroke_data, 8); + + ser_u8(stroke_data, 0); + ser_u8(stroke_data, 0); + ser_u8(stroke_data, 255); + ser_u8(stroke_data, 1); + + gl.bindTexture(gl.TEXTURE_2D, context.textures['stroke_data']); + gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 2, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 16, 0, 0, 255, 2])); + gl.activeTexture(gl.TEXTURE0); + + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0, 3); + + + /* const before_clip = performance.now(); const index_count = bvh_clip(state, context, lod_level); const after_clip = performance.now(); @@ -124,7 +192,7 @@ function draw(state, context) { //index_buffer.reverse(); gl.drawElements(gl.TRIANGLES, index_count, gl.UNSIGNED_INT, 0); } - +*/ /* // Dynamic data (stroke previews that are currently in progress) const dynamic_points = context.dynamic_serializer.offset / config.bytes_per_point; diff --git a/client/webgl_shaders.js b/client/webgl_shaders.js index c451d09..ca0e408 100644 --- a/client/webgl_shaders.js +++ b/client/webgl_shaders.js @@ -102,18 +102,17 @@ const nop_fs_src = `#version 300 es `; const sdf_vs_src = `#version 300 es - in vec3 a_pos; // .z is radius - in vec4 a_line; - in vec3 a_color; - + in vec4 a_ab; // original points + in float a_radius; in int a_stroke_id; uniform vec2 u_scale; uniform vec2 u_res; uniform vec2 u_translation; - uniform int u_stroke_count; + uniform highp sampler2D u_stroke_data; + out vec4 v_line; out vec2 v_texcoord; out vec3 v_color; @@ -121,40 +120,52 @@ const sdf_vs_src = `#version 300 es flat out float v_thickness; void main() { - vec2 screen01 = (a_pos.xy * u_scale + u_translation) / u_res; - vec2 screen02 = screen01 * 2.0; - - float apron = 2.0; - vec2 line_dir = normalize(a_line.zw - a_line.xy); + vec2 screen02; + float apron = 1.0; // google "futanari inflation rule 34" + + vec4 stroke_data = texelFetch(u_stroke_data, ivec2(a_stroke_id, 0), 0); + float radius = stroke_data.w * 255.0; + + vec2 a = a_ab.xy; + vec2 b = a_ab.zw; + + vec2 line_dir = normalize(b - a); vec2 up_dir = vec2(line_dir.y, -line_dir.x); vec2 pixel = vec2(2.0) / u_res * apron; float rscale = apron / u_scale.x; - int vertex_index = gl_VertexID & 0x3; + int vertex_index = gl_VertexID % 6; + + vec2 outwards; + vec2 origin; if (vertex_index == 0) { // "top left" aka "p1" - screen02 += up_dir * pixel - line_dir * pixel; - v_texcoord = a_pos.xy + up_dir * rscale - line_dir * rscale; - } else if (vertex_index == 1) { + origin = a; + outwards = up_dir - line_dir; + } else if (vertex_index == 1 || vertex_index == 5) { // "top right" aka "p2" - screen02 += up_dir * pixel + line_dir * pixel; - v_texcoord = a_pos.xy + up_dir * rscale + line_dir * rscale; - } else if (vertex_index == 2) { + origin = b; + outwards = up_dir + line_dir; + } else if (vertex_index == 2 || vertex_index == 4) { // "bottom left" aka "p3" - screen02 += -up_dir * pixel - line_dir * pixel; - v_texcoord = a_pos.xy - up_dir * rscale - line_dir * rscale; + origin = a; + outwards = -up_dir - line_dir; } else { // "bottom right" aka "p4" - screen02 += -up_dir * pixel + line_dir * pixel; - v_texcoord = a_pos.xy - up_dir * rscale + line_dir * rscale; + origin = b; + outwards = -up_dir + line_dir; } - + + vec2 pos = origin + normalize(outwards) * radius; + screen02 = (pos.xy * u_scale + u_translation) / u_res + outwards * pixel; + v_texcoord = pos.xy + outwards * rscale; + screen02.y = 2.0 - screen02.y; - v_line = a_line; - v_color = a_color; - v_thickness = a_pos.z; + v_line = vec4(a, b); + v_thickness = radius; + v_color = stroke_data.xyz; gl_Position = vec4(screen02 - 1.0, (float(a_stroke_id) / float(u_stroke_count)) * 2.0 - 1.0, 1); } @@ -182,8 +193,8 @@ const sdf_fs_src = `#version 300 es float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); float dist = length(pa - ba * h) - v_thickness / 2.0; - float fade = length(fwidth(v_texcoord)); - float alpha = 1.0 - smoothstep(0.0, fade, dist); + float fade = 0.5 * length(fwidth(v_texcoord)); + float alpha = 1.0 - smoothstep(-fade, fade, dist); FragColor = vec4(v_color * alpha, alpha); } else { @@ -292,17 +303,15 @@ function init_webgl(state, context) { }, 'main': { - 'a_pos': gl.getAttribLocation(context.programs['sdf'].main, 'a_pos'), - 'a_line': gl.getAttribLocation(context.programs['sdf'].main, 'a_line'), - 'a_color': gl.getAttribLocation(context.programs['sdf'].main, 'a_color'), + 'a_ab': gl.getAttribLocation(context.programs['sdf'].main, 'a_ab'), 'a_stroke_id': gl.getAttribLocation(context.programs['sdf'].main, 'a_stroke_id'), 'u_res': gl.getUniformLocation(context.programs['sdf'].main, 'u_res'), 'u_scale': gl.getUniformLocation(context.programs['sdf'].main, 'u_scale'), 'u_translation': gl.getUniformLocation(context.programs['sdf'].main, 'u_translation'), 'u_debug_mode': gl.getUniformLocation(context.programs['sdf'].main, 'u_debug_mode'), - 'u_tile_size': gl.getUniformLocation(context.programs['sdf'].main, 'u_tile_size'), 'u_stroke_count': gl.getUniformLocation(context.programs['sdf'].main, 'u_stroke_count'), + 'u_stroke_data': gl.getUniformLocation(context.programs['sdf'].main, 'u_stroke_data'), } }; @@ -321,6 +330,15 @@ function init_webgl(state, context) { 'b_packed_dynamic_index': gl.createBuffer(), }; + context.textures = { + 'stroke_data': gl.createTexture(), + }; + + gl.bindTexture(gl.TEXTURE_2D, context.textures['stroke_data']); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + const resize_canvas = (entries) => { // https://www.khronos.org/webgl/wiki/HandlingHighDPI const entry = entries[0];