shader perf improvements
This commit is contained in:
parent
1424fb6cac
commit
bf1ca91e8b
|
|
@ -24,13 +24,9 @@
|
||||||
border: 2px solid rgba(0, 0, 0, 0.5);
|
border: 2px solid rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
distance-field canvas {
|
distance-field {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
inset: 0;
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,16 @@ import { WebGLUtils } from './utils/webgl.ts';
|
||||||
export class DistanceField extends HTMLElement {
|
export class DistanceField extends HTMLElement {
|
||||||
static tagName = 'distance-field';
|
static tagName = 'distance-field';
|
||||||
|
|
||||||
private geometries: NodeListOf<Element>;
|
|
||||||
private textures: WebGLTexture[] = [];
|
private textures: WebGLTexture[] = [];
|
||||||
private pingPongIndex: number = 0;
|
private pingPongIndex: number = 0;
|
||||||
|
|
||||||
|
private shapes!: NodeListOf<Element>;
|
||||||
private canvas!: HTMLCanvasElement;
|
private canvas!: HTMLCanvasElement;
|
||||||
private glContext!: WebGL2RenderingContext;
|
private glContext!: WebGL2RenderingContext;
|
||||||
private framebuffer!: WebGLFramebuffer;
|
private framebuffer!: WebGLFramebuffer;
|
||||||
private fullscreenQuadVAO!: WebGLVertexArrayObject;
|
private fullscreenQuadVAO!: WebGLVertexArrayObject;
|
||||||
private shapeVAO!: WebGLVertexArrayObject;
|
private shapeVAO!: WebGLVertexArrayObject;
|
||||||
|
private offsetCache: Map<number, Float32Array> = new Map();
|
||||||
|
|
||||||
private jfaProgram!: WebGLProgram; // Shader program for the Jump Flooding Algorithm
|
private jfaProgram!: WebGLProgram; // Shader program for the Jump Flooding Algorithm
|
||||||
private renderProgram!: WebGLProgram; // Shader program for final rendering
|
private renderProgram!: WebGLProgram; // Shader program for final rendering
|
||||||
|
|
@ -25,11 +26,13 @@ export class DistanceField extends HTMLElement {
|
||||||
|
|
||||||
private static readonly MAX_DISTANCE = 99999.0;
|
private static readonly MAX_DISTANCE = 99999.0;
|
||||||
|
|
||||||
constructor() {
|
static define() {
|
||||||
super();
|
customElements.define(this.tagName, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
// Collect all geometry elements to process
|
// Collect all geometry elements to process
|
||||||
this.geometries = document.querySelectorAll('fc-geometry');
|
this.shapes = document.querySelectorAll('fc-geometry');
|
||||||
|
|
||||||
// Initialize WebGL context and canvas
|
// Initialize WebGL context and canvas
|
||||||
const { gl, canvas } = WebGLUtils.createWebGLCanvas(this.clientWidth, this.clientHeight, this);
|
const { gl, canvas } = WebGLUtils.createWebGLCanvas(this.clientWidth, this.clientHeight, this);
|
||||||
|
|
@ -53,15 +56,9 @@ export class DistanceField extends HTMLElement {
|
||||||
|
|
||||||
// Start the Jump Flooding Algorithm
|
// Start the Jump Flooding Algorithm
|
||||||
this.runJFA();
|
this.runJFA();
|
||||||
}
|
|
||||||
|
|
||||||
static define() {
|
|
||||||
customElements.define(this.tagName, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
window.addEventListener('resize', this.handleResize);
|
window.addEventListener('resize', this.handleResize);
|
||||||
this.geometries.forEach((geometry) => {
|
this.shapes.forEach((geometry) => {
|
||||||
geometry.addEventListener('move', this.handleGeometryUpdate);
|
geometry.addEventListener('move', this.handleGeometryUpdate);
|
||||||
geometry.addEventListener('resize', this.handleGeometryUpdate);
|
geometry.addEventListener('resize', this.handleGeometryUpdate);
|
||||||
});
|
});
|
||||||
|
|
@ -69,7 +66,7 @@ export class DistanceField extends HTMLElement {
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
window.removeEventListener('resize', this.handleResize);
|
window.removeEventListener('resize', this.handleResize);
|
||||||
this.geometries.forEach((geometry) => {
|
this.shapes.forEach((geometry) => {
|
||||||
geometry.removeEventListener('move', this.handleGeometryUpdate);
|
geometry.removeEventListener('move', this.handleGeometryUpdate);
|
||||||
geometry.removeEventListener('resize', this.handleGeometryUpdate);
|
geometry.removeEventListener('resize', this.handleGeometryUpdate);
|
||||||
});
|
});
|
||||||
|
|
@ -146,6 +143,13 @@ export class DistanceField extends HTMLElement {
|
||||||
if (!this.framebuffer) {
|
if (!this.framebuffer) {
|
||||||
this.framebuffer = gl.createFramebuffer()!;
|
this.framebuffer = gl.createFramebuffer()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if framebuffer is complete
|
||||||
|
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
|
||||||
|
if (status !== gl.FRAMEBUFFER_COMPLETE) {
|
||||||
|
console.error('Framebuffer is not complete:', status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -163,7 +167,7 @@ export class DistanceField extends HTMLElement {
|
||||||
|
|
||||||
// Collect positions and assign unique IDs to all shapes
|
// Collect positions and assign unique IDs to all shapes
|
||||||
const positions: number[] = [];
|
const positions: number[] = [];
|
||||||
this.geometries.forEach((geometry, index) => {
|
this.shapes.forEach((geometry, index) => {
|
||||||
const rect = geometry.getBoundingClientRect();
|
const rect = geometry.getBoundingClientRect();
|
||||||
|
|
||||||
// Convert DOM coordinates to Normalized Device Coordinates (NDC)
|
// Convert DOM coordinates to Normalized Device Coordinates (NDC)
|
||||||
|
|
@ -239,7 +243,7 @@ export class DistanceField extends HTMLElement {
|
||||||
|
|
||||||
// Bind VAO and draw shapes
|
// Bind VAO and draw shapes
|
||||||
gl.bindVertexArray(this.shapeVAO);
|
gl.bindVertexArray(this.shapeVAO);
|
||||||
gl.drawArrays(gl.TRIANGLES, 0, this.geometries.length * 6);
|
gl.drawArrays(gl.TRIANGLES, 0, this.shapes.length * 6);
|
||||||
|
|
||||||
// Unbind VAO and framebuffer
|
// Unbind VAO and framebuffer
|
||||||
gl.bindVertexArray(null);
|
gl.bindVertexArray(null);
|
||||||
|
|
@ -251,15 +255,11 @@ export class DistanceField extends HTMLElement {
|
||||||
* It progressively reduces step sizes to refine the distance calculations.
|
* It progressively reduces step sizes to refine the distance calculations.
|
||||||
*/
|
*/
|
||||||
private runJFA() {
|
private runJFA() {
|
||||||
const maxDimension = Math.max(this.canvas.width, this.canvas.height);
|
let stepSize = 1 << Math.floor(Math.log2(Math.max(this.canvas.width, this.canvas.height)));
|
||||||
let stepSize = Math.pow(2, Math.floor(Math.log2(maxDimension)));
|
|
||||||
|
|
||||||
const minStepSize = 1;
|
|
||||||
|
|
||||||
// Perform passes with decreasing step sizes
|
// Perform passes with decreasing step sizes
|
||||||
while (stepSize >= minStepSize) {
|
for (; stepSize >= 1; stepSize >>= 1) {
|
||||||
this.renderPass(stepSize);
|
this.renderPass(stepSize);
|
||||||
stepSize = Math.floor(stepSize / 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the final result to the screen
|
// Render the final result to the screen
|
||||||
|
|
@ -403,13 +403,19 @@ export class DistanceField extends HTMLElement {
|
||||||
* @returns A Float32Array of offsets.
|
* @returns A Float32Array of offsets.
|
||||||
*/
|
*/
|
||||||
private computeOffsets(stepSize: number): Float32Array {
|
private computeOffsets(stepSize: number): Float32Array {
|
||||||
const offsets: number[] = [];
|
if (this.offsetCache.has(stepSize)) {
|
||||||
|
return this.offsetCache.get(stepSize)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offsets = [];
|
||||||
for (let y = -1; y <= 1; y++) {
|
for (let y = -1; y <= 1; y++) {
|
||||||
for (let x = -1; x <= 1; x++) {
|
for (let x = -1; x <= 1; x++) {
|
||||||
offsets.push((x * stepSize) / this.canvas.width, (y * stepSize) / this.canvas.height);
|
offsets.push((x * stepSize) / this.canvas.width, (y * stepSize) / this.canvas.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new Float32Array(offsets);
|
const offsetArray = new Float32Array(offsets);
|
||||||
|
this.offsetCache.set(stepSize, offsetArray);
|
||||||
|
return offsetArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -448,7 +454,7 @@ export class DistanceField extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear other references
|
// Clear other references
|
||||||
this.geometries = null!;
|
this.shapes = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -526,6 +532,12 @@ out vec4 outColor;
|
||||||
|
|
||||||
uniform sampler2D u_texture;
|
uniform sampler2D u_texture;
|
||||||
|
|
||||||
|
vec3 hsv2rgb(vec3 c) {
|
||||||
|
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||||
|
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||||
|
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 texel = texture(u_texture, v_texCoord);
|
vec4 texel = texture(u_texture, v_texCoord);
|
||||||
|
|
||||||
|
|
@ -533,12 +545,9 @@ void main() {
|
||||||
float shapeID = texel.z;
|
float shapeID = texel.z;
|
||||||
float distance = texel.a;
|
float distance = texel.a;
|
||||||
|
|
||||||
// Generate a hash-based color for each shape
|
float hue = fract(shapeID * 0.61803398875); // Golden ratio conjugate
|
||||||
vec3 shapeColor = vec3(
|
vec3 shapeColor = hsv2rgb(vec3(hue, 0.5, 0.95));
|
||||||
fract(sin(shapeID * 12.9898) * 43758.5453),
|
|
||||||
fract(sin(shapeID * 78.233) * 43758.5453),
|
|
||||||
fract(sin(shapeID * 93.433) * 43758.5453)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Visualize distance as intensity
|
// Visualize distance as intensity
|
||||||
float intensity = exp(-distance * 10.0);
|
float intensity = exp(-distance * 10.0);
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,7 @@ export class WebGLUtils {
|
||||||
|
|
||||||
// Set canvas styles
|
// Set canvas styles
|
||||||
canvas.style.position = 'absolute';
|
canvas.style.position = 'absolute';
|
||||||
canvas.style.top = '0';
|
canvas.style.inset = '0';
|
||||||
canvas.style.left = '0';
|
|
||||||
canvas.style.width = '100%';
|
|
||||||
canvas.style.height = '100%';
|
|
||||||
canvas.style.zIndex = '-1';
|
canvas.style.zIndex = '-1';
|
||||||
|
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue