Refactor & improve
This commit is contained in:
parent
b51cba28ad
commit
b3d9229af5
22 changed files with 713 additions and 334 deletions
28
src/utils/catmull-rom-interpolation.ts
Normal file
28
src/utils/catmull-rom-interpolation.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export const catmullRomInterpolation = (
|
||||
p0: vec2,
|
||||
p1: vec2,
|
||||
p2: vec2,
|
||||
p3: vec2,
|
||||
t: number
|
||||
): vec2 => {
|
||||
const t2 = t * t;
|
||||
const t3 = t2 * t;
|
||||
|
||||
const x =
|
||||
0.5 *
|
||||
(2 * p1[0] +
|
||||
(-p0[0] + p2[0]) * t +
|
||||
(2 * p0[0] - 5 * p1[0] + 4 * p2[0] - p3[0]) * t2 +
|
||||
(-p0[0] + 3 * p1[0] - 3 * p2[0] + p3[0]) * t3);
|
||||
|
||||
const y =
|
||||
0.5 *
|
||||
(2 * p1[1] +
|
||||
(-p0[1] + p2[1]) * t +
|
||||
(2 * p0[1] - 5 * p1[1] + 4 * p2[1] - p3[1]) * t2 +
|
||||
(-p0[1] + 3 * p1[1] - 3 * p2[1] + p3[1]) * t3);
|
||||
|
||||
return [x, y];
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
export class DeltaTimeCalculator {
|
||||
private previousTime: DOMHighResTimeStamp | null = null;
|
||||
|
||||
constructor() {
|
||||
constructor(private readonly maxDeltaTimeInSeconds: number = 1 / 30) {
|
||||
document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));
|
||||
}
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ export class DeltaTimeCalculator {
|
|||
|
||||
const delta = currentTime - this.previousTime;
|
||||
this.previousTime = currentTime;
|
||||
return delta / 1000;
|
||||
return Math.min(delta / 1000, this.maxDeltaTimeInSeconds);
|
||||
}
|
||||
|
||||
private handleVisibilityChange() {
|
||||
|
|
|
|||
93
src/utils/graphics/fbm-noise/fbm-noise.ts
Normal file
93
src/utils/graphics/fbm-noise/fbm-noise.ts
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import { Random } from '../../random';
|
||||
import { setUpFullScreenQuad } from '../full-screen-quad/full-screen-quad';
|
||||
import random from '../random.wgsl';
|
||||
import { smartCompile } from '../smart-compile';
|
||||
import noise from './fbm-noise.wgsl';
|
||||
|
||||
const textureCache = new Map<string, GPUTexture>();
|
||||
|
||||
export const generateFbmNoise = ({
|
||||
device,
|
||||
width = 1024,
|
||||
height = 1024,
|
||||
octaves = 8,
|
||||
lacunarity = 2,
|
||||
amplitude = 0.5,
|
||||
gain = 0.5,
|
||||
}: {
|
||||
device: GPUDevice;
|
||||
width?: number;
|
||||
height?: number;
|
||||
octaves?: number;
|
||||
lacunarity?: number;
|
||||
amplitude?: number;
|
||||
gain?: number;
|
||||
}): GPUTextureView => {
|
||||
const constants = {
|
||||
octaves,
|
||||
lacunarity,
|
||||
amplitude,
|
||||
gain,
|
||||
seedR: Random.getRandom(),
|
||||
seedG: Random.getRandom(),
|
||||
seedB: Random.getRandom(),
|
||||
seedA: Random.getRandom(),
|
||||
};
|
||||
const cacheKey = `${width}x${height}x${JSON.stringify(constants)}`;
|
||||
if (!textureCache.has(cacheKey)) {
|
||||
const { buffer, vertex } = setUpFullScreenQuad(device);
|
||||
const quadVertexBuffer = buffer;
|
||||
|
||||
const pipeline = device.createRenderPipeline({
|
||||
layout: 'auto',
|
||||
vertex,
|
||||
fragment: {
|
||||
module: smartCompile(device, random, noise),
|
||||
entryPoint: 'fragment',
|
||||
constants,
|
||||
targets: [
|
||||
{
|
||||
format: 'rgba16float',
|
||||
},
|
||||
],
|
||||
},
|
||||
primitive: {
|
||||
topology: 'triangle-strip',
|
||||
},
|
||||
});
|
||||
|
||||
const colorTexture = device.createTexture({
|
||||
size: {
|
||||
width,
|
||||
height,
|
||||
depthOrArrayLayers: 1,
|
||||
},
|
||||
format: 'rgba16float',
|
||||
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
|
||||
});
|
||||
|
||||
const renderPassDescriptor: GPURenderPassDescriptor = {
|
||||
colorAttachments: [
|
||||
{
|
||||
view: colorTexture.createView(),
|
||||
clearValue: { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
|
||||
loadOp: 'clear',
|
||||
storeOp: 'store',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const commandEncoder = device.createCommandEncoder();
|
||||
|
||||
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
|
||||
passEncoder.setPipeline(pipeline);
|
||||
passEncoder.setVertexBuffer(0, quadVertexBuffer);
|
||||
passEncoder.draw(4, 1);
|
||||
passEncoder.end();
|
||||
|
||||
device.queue.submit([commandEncoder.finish()]);
|
||||
textureCache.set(cacheKey, colorTexture);
|
||||
}
|
||||
|
||||
return textureCache.get(cacheKey).createView();
|
||||
};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { ErrorHandler, Severity } from '../error-handler';
|
||||
|
||||
export const initializeGPU = async (): Promise<GPUDevice> => {
|
||||
export const initializeGpu = async (): Promise<GPUDevice> => {
|
||||
const gpu = navigator.gpu;
|
||||
if (!gpu) {
|
||||
throw new Error('WebGPU is not supported in your browser');
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { Random } from '../../random';
|
||||
import { setUpFullScreenQuad } from '../full-screen-quad/full-screen-quad';
|
||||
import random from '../random.wgsl';
|
||||
import { smartCompile } from '../smart-compile';
|
||||
import noise from './noise.wgsl';
|
||||
|
||||
const textureCache = new Map<string, GPUTexture>();
|
||||
|
||||
|
|
@ -10,20 +8,12 @@ export const generateNoise = ({
|
|||
device,
|
||||
width = 1024,
|
||||
height = 1024,
|
||||
octaves = 8,
|
||||
lacunarity = 2,
|
||||
amplitude = 0.5,
|
||||
gain = 0.5,
|
||||
}: {
|
||||
device: GPUDevice;
|
||||
width?: number;
|
||||
height?: number;
|
||||
octaves?: number;
|
||||
lacunarity?: number;
|
||||
amplitude?: number;
|
||||
gain?: number;
|
||||
}): GPUTextureView => {
|
||||
const cacheKey = `${width}x${height}x${octaves}x${lacunarity}x${amplitude}x${gain}`;
|
||||
const cacheKey = `${width}x${height}`;
|
||||
if (!textureCache.has(cacheKey)) {
|
||||
const { buffer, vertex } = setUpFullScreenQuad(device);
|
||||
const quadVertexBuffer = buffer;
|
||||
|
|
@ -32,18 +22,21 @@ export const generateNoise = ({
|
|||
layout: 'auto',
|
||||
vertex,
|
||||
fragment: {
|
||||
module: smartCompile(device, random, noise),
|
||||
module: smartCompile(
|
||||
device,
|
||||
random,
|
||||
/* wgsl */ `
|
||||
@fragment
|
||||
fn fragment(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
|
||||
return vec4(
|
||||
random_with_seed(uv, 1),
|
||||
random_with_seed(uv, 2),
|
||||
random_with_seed(uv, 3),
|
||||
random_with_seed(uv, 4),
|
||||
);
|
||||
}`
|
||||
),
|
||||
entryPoint: 'fragment',
|
||||
constants: {
|
||||
octaves,
|
||||
lacunarity,
|
||||
amplitude,
|
||||
gain,
|
||||
seedR: Random.getRandom(),
|
||||
seedG: Random.getRandom(),
|
||||
seedB: Random.getRandom(),
|
||||
seedA: Random.getRandom(),
|
||||
},
|
||||
targets: [
|
||||
{
|
||||
format: 'rgba16float',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { ErrorHandler, Severity } from '../error-handler';
|
||||
|
||||
export const smartCompile = (device: GPUDevice, ...code: Array<string>) => {
|
||||
export const smartCompile = (
|
||||
device: GPUDevice,
|
||||
...code: Array<string>
|
||||
): GPUShaderModule => {
|
||||
const concatenated = code.join('\n\n');
|
||||
|
||||
const module = device.createShaderModule({
|
||||
|
|
@ -15,7 +18,9 @@ export const smartCompile = (device: GPUDevice, ...code: Array<string>) => {
|
|||
warning: Severity.WARNING,
|
||||
error: Severity.ERROR,
|
||||
}[message.type],
|
||||
`${message.message}\n${concatenated.split('\n')[message.lineNum - 1]}`
|
||||
`${message.message}\n${
|
||||
concatenated.split('\n')[message.lineNum - 1]
|
||||
}\n\nCode:\n${concatenated}\n`
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue