271 lines
8.3 KiB
JavaScript
271 lines
8.3 KiB
JavaScript
import { getPath } from "./common.js";
|
|
|
|
function test(position)
|
|
{
|
|
const x = position[0] * 4 * Math.PI;
|
|
const y = position[1] * 4 * Math.PI;
|
|
return Math.cos(y - 3 + Math.sin(x)) * 0.5 + 0.5;
|
|
}
|
|
|
|
function length(vector)
|
|
{
|
|
return Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]);
|
|
}
|
|
|
|
function sub(a, b)
|
|
{
|
|
return [
|
|
a[0] - b[0],
|
|
a[1] - b[1],
|
|
];
|
|
}
|
|
|
|
function smoothing(radius, distance)
|
|
{
|
|
const volume = Math.PI * Math.pow(radius, 8) / 4;
|
|
const value = Math.max(0, radius * radius - distance * distance);
|
|
return value * value * value / volume;
|
|
}
|
|
|
|
class FlatRenderer {
|
|
createParticles(device)
|
|
{
|
|
this.maxDim = 32;
|
|
this.maxParticles = this.maxDim * this.maxDim;
|
|
this.particleStride = 4 * 2; // in elements
|
|
this.particles = new Float32Array(this.maxParticles * this.particleStride);
|
|
this.particleBuffers = [];
|
|
this.particleBindGroups = [];
|
|
|
|
this.particleConfiguration = new Float32Array(3 * 4);
|
|
this.particleConfigurationBuffers = [];
|
|
|
|
for (let i = 0; i < 2; i++) {
|
|
const buffer = device.createBuffer({
|
|
label: `particle buffer ${i}`,
|
|
size: this.particles.byteLength,
|
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
});
|
|
this.particleBuffers.push(buffer);
|
|
|
|
const configBuffer = device.createBuffer({
|
|
label: `particle configuration buffer ${i}`,
|
|
size: this.particleConfiguration.byteLength,
|
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
});
|
|
this.particleConfigurationBuffers.push(configBuffer);
|
|
|
|
const bindGroup = device.createBindGroup({
|
|
label: `particle buffer bind group ${i}`,
|
|
layout: this.bindGroupLayout.particle,
|
|
entries: [{
|
|
binding: 0,
|
|
resource: { buffer: configBuffer },
|
|
}, {
|
|
binding: 1,
|
|
resource: { buffer: buffer },
|
|
}],
|
|
});
|
|
this.particleBindGroups.push(bindGroup);
|
|
}
|
|
|
|
for (let y = 0; y < this.maxDim; y++) {
|
|
for (let x = 0; x < this.maxDim; x++) {
|
|
const index = y * this.maxDim + x;
|
|
//this.particles[index * this.particleStride + 0] = (x / this.maxDim) + 0.5 / this.maxDim;
|
|
//this.particles[index * this.particleStride + 1] = (y / this.maxDim) + 0.5 / this.maxDim;
|
|
// position
|
|
const position = [Math.random(), Math.random()];
|
|
this.particles[index * this.particleStride + 0] = position[0];
|
|
this.particles[index * this.particleStride + 1] = position[1];
|
|
this.particles[index * this.particleStride + 2] = 0.0;
|
|
this.particles[index * this.particleStride + 3] = 1.0;
|
|
// value.x
|
|
this.particles[index * this.particleStride + 4] = test(position);
|
|
}
|
|
}
|
|
}
|
|
|
|
constructor(device, canvasFormat, viewUniformBuffer, shaderModule)
|
|
{
|
|
const label = "flat";
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// buffer
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
const array = new Uint16Array(6);
|
|
array[0] = 0;
|
|
array[1] = 1;
|
|
array[2] = 2;
|
|
array[3] = 0;
|
|
array[4] = 3;
|
|
array[5] = 1;
|
|
|
|
this.buffer = device.createBuffer({
|
|
label: `${label} renderer buffer`,
|
|
size: array.byteLength,
|
|
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
});
|
|
|
|
device.queue.writeBuffer(this.buffer, 0, array, 0, array.length);
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// pipeline
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
this.bindGroupLayout = {
|
|
view: device.createBindGroupLayout({
|
|
label: `${label} bind group layout view`,
|
|
entries: [{
|
|
binding: 0,
|
|
visibility: GPUShaderStage.VERTEX,
|
|
buffer: { type: "uniform" }
|
|
}]
|
|
}),
|
|
particle: device.createBindGroupLayout({
|
|
label: `${label} bind group layout particle`,
|
|
entries: [{
|
|
binding: 0,
|
|
visibility: GPUShaderStage.FRAGMENT,
|
|
buffer: { type: "uniform" }
|
|
}, {
|
|
binding: 1,
|
|
visibility: GPUShaderStage.FRAGMENT,
|
|
buffer: { type: "read-only-storage" }
|
|
}]
|
|
}),
|
|
};
|
|
|
|
const pipelineLayout = device.createPipelineLayout({
|
|
label: `${label} pipeline layout`,
|
|
bindGroupLayouts: [
|
|
this.bindGroupLayout.view,
|
|
this.bindGroupLayout.particle
|
|
],
|
|
});
|
|
|
|
this.renderPipeline = device.createRenderPipeline({
|
|
label: `${label} pipeline`,
|
|
layout: pipelineLayout,
|
|
vertex: {
|
|
module: shaderModule,
|
|
entryPoint: "vertexMain",
|
|
},
|
|
fragment: {
|
|
module: shaderModule,
|
|
entryPoint: "fragmentMain",
|
|
targets: [{
|
|
format: canvasFormat,
|
|
}]
|
|
},
|
|
primitive: {
|
|
topology: "triangle-list",
|
|
},
|
|
depthStencil: {
|
|
depthWriteEnabled: true,
|
|
depthCompare: "less",
|
|
format: "depth24plus",
|
|
},
|
|
});
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// particles
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
this.createParticles(device);
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// bind group
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
this.bindGroups = [
|
|
device.createBindGroup({
|
|
label: `${label} bind group`,
|
|
layout: this.bindGroupLayout.view,
|
|
entries: [{
|
|
binding: 0,
|
|
resource: { buffer: viewUniformBuffer },
|
|
}],
|
|
}),
|
|
];
|
|
}
|
|
|
|
positionDensity(position, circleRadius, particleCount)
|
|
{
|
|
var density = 0.0;
|
|
for (var i = 0; i < particleCount; i++) {
|
|
const particlePosition = [this.particles[i * this.particleStride + 0],
|
|
this.particles[i * this.particleStride + 1]];
|
|
const distance = length(sub(position, particlePosition));
|
|
const influence = smoothing(circleRadius, distance);
|
|
density += influence;
|
|
}
|
|
return density;
|
|
}
|
|
|
|
updateParticles(device, frameNumber, configuration)
|
|
{
|
|
for (var i = 0; i < configuration.particleCount; i++) {
|
|
const position = [this.particles[i * this.particleStride + 0],
|
|
this.particles[i * this.particleStride + 1]];
|
|
const density = this.positionDensity(position,
|
|
configuration.circleRadius,
|
|
configuration.particleCount);
|
|
// value.y
|
|
this.particles[i * this.particleStride + 5] = density;
|
|
}
|
|
|
|
device.queue.writeBuffer(this.particleBuffers[frameNumber], 0,
|
|
this.particles, 0, this.particles.length);
|
|
|
|
const PARTICLE = 0;
|
|
const CIRCLE = 1;
|
|
const MOUSE = 2;
|
|
|
|
const config = this.particleConfiguration;
|
|
config[PARTICLE * 4 + 0] = configuration.particleCount;
|
|
config[PARTICLE * 4 + 1] = configuration.particleSize;
|
|
config[PARTICLE * 4 + 2] = configuration.particleMass1;
|
|
config[PARTICLE * 4 + 3] = configuration.particleMass2;
|
|
|
|
config[CIRCLE * 4 + 0] = configuration.circleRadius;
|
|
config[CIRCLE * 4 + 1] = configuration.circleThickness;
|
|
config[CIRCLE * 4 + 2] = configuration.testIntensity;
|
|
config[CIRCLE * 4 + 3] = configuration.mass12Mix;
|
|
|
|
config[MOUSE * 4 + 0] = configuration.mousePosition[0];
|
|
config[MOUSE * 4 + 1] = configuration.mousePosition[1];
|
|
config[MOUSE * 4 + 2] = configuration.mousePosition[2];
|
|
config[MOUSE * 4 + 3] = configuration.mousePosition[3];
|
|
|
|
device.queue.writeBuffer(this.particleConfigurationBuffers[frameNumber], 0,
|
|
this.particleConfiguration, 0, this.particleConfiguration.length)
|
|
}
|
|
|
|
render(device, renderPass, frameNumber, configuration)
|
|
{
|
|
this.updateParticles(device, frameNumber, configuration);
|
|
|
|
renderPass.setPipeline(this.renderPipeline);
|
|
renderPass.setIndexBuffer(this.buffer, "uint16");
|
|
renderPass.setBindGroup(0, this.bindGroups[0]);
|
|
renderPass.setBindGroup(1, this.particleBindGroups[frameNumber]);
|
|
renderPass.drawIndexed(6);
|
|
}
|
|
}
|
|
|
|
async function loadFlat(device, canvasFormat, viewUniformBuffer)
|
|
{
|
|
const flatWgsl = await getPath("flat.wgsl");
|
|
const shaderModule = device.createShaderModule({
|
|
label: "flag shader",
|
|
code: flatWgsl,
|
|
});
|
|
|
|
const renderer = new FlatRenderer(device, canvasFormat, viewUniformBuffer, shaderModule);
|
|
return renderer;
|
|
}
|
|
|
|
export { loadFlat };
|