animated light position

This commit is contained in:
Zack Buhman 2026-07-26 20:31:00 -05:00
parent 4526aaa2c5
commit 19ef9652d0
7 changed files with 463 additions and 409 deletions

371
cube.js Normal file
View File

@ -0,0 +1,371 @@
const cubeResponse = await fetch("obj/cube.bin");
const cubeObj = await cubeResponse.arrayBuffer();
const cubeView = new DataView(cubeObj);
const indexCount = cubeView.getUint32(0, true);
const indexBufferOffset = cubeView.getUint32(4, true);
const indexBufferSize = cubeView.getUint32(8, true);
const vertexBufferOffset = cubeView.getUint32(12, true);
const vertexBufferSize = cubeView.getUint32(16, true);
//console.log(`indexBufferOffset ${indexBufferOffset} indexBufferSize ${indexBufferSize}`);
//console.log(`vertexBufferOffset ${vertexBufferOffset} vertexBufferSize ${vertexBufferSize}`);
const cubeIndexCount = indexBufferSize / 2;
const cubeVerticesStride = 36;
const indexVertexBuffer = device.createBuffer({
label: "cell vertices",
size: indexBufferSize + vertexBufferSize,
usage: GPUBufferUsage.INDEX | GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(indexVertexBuffer, 0, cubeObj, indexBufferOffset, indexBufferSize);
device.queue.writeBuffer(indexVertexBuffer, indexBufferSize, cubeObj, vertexBufferOffset, vertexBufferSize);
//////////////////////////////////////////////////////////////////////
// instance buffer
//////////////////////////////////////////////////////////////////////
async function loadVoxModel(path)
{
const voxResponse = await fetch(path);
const vox = await voxResponse.arrayBuffer();
const voxView = new DataView(vox);
const sizeX = voxView.getUint32(0, true);
const sizeY = voxView.getUint32(4, true);
const sizeZ = voxView.getUint32(8, true);
const voxelCount = voxView.getUint32(12, true);
const instanceBuffer = device.createBuffer({
label: `instanceBuffer %{}`,
size: voxelCount * 4,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(instanceBuffer, 0, vox, 16, voxelCount * 4);
const modelConfigurationBuffer = device.createBuffer({
label: "modelConfigurationBuffer",
size: 4 * 4 + 256 * 4 * 4,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
const paletteOffset = 16 + voxelCount * 4;
const sizeArray = new Float32Array([sizeX, sizeY, sizeZ, 0]);
device.queue.writeBuffer(modelConfigurationBuffer, 0, sizeArray);
device.queue.writeBuffer(modelConfigurationBuffer, 4 * 4, vox, paletteOffset, 256 * 4 * 4);
return {
instanceBuffer: instanceBuffer,
modelConfigurationBuffer: modelConfigurationBuffer,
voxelCount: voxelCount,
path: path,
}
}
const modelPaths = [
"vox/monu1.bin",
"vox/monu7.bin",
"vox/monu9.bin",
"vox/monu16.bin",
"vox/chr_fox.bin",
"vox/ff3.bin",
"vox/dragon.bin",
];
const models = {}
for (const path of modelPaths) {
models[path] = loadVoxModel(path);
}
for (const path of modelPaths) {
models[path] = await models[path];
}
var currentModel = modelPaths[0];
//////////////////////////////////////////////////////////////////////
// vertex layout
//////////////////////////////////////////////////////////////////////
const vertexBufferLayouts = [
{
arrayStride: cubeVerticesStride,
attributes: [{
format: "float32x3",
offset: 0,
shaderLocation: 0,
}, {
format: "float32x2",
offset: 12,
shaderLocation: 1,
}, {
format: "float16x4",
offset: 20,
shaderLocation: 2,
}, {
format: "float16x4",
offset: 28,
shaderLocation: 3,
}],
stepMode: "vertex",
},
{
arrayStride: 4,
attributes: [{
format: "uint8x4",
offset: 0,
shaderLocation: 4,
}],
stepMode: "instance",
},
];
const indexWgsl = getPath("index.wgsl");
const computeWgsl = getPath("compute.wgsl");
const cellShaderModule = device.createShaderModule({
label: "cell shader",
code: await indexWgsl,
});
const bindGroupLayouts = [
device.createBindGroupLayout({
label: "bind group layout 0",
entries: [{
binding: 0,
visibility: GPUShaderStage.VERTEX | GPUShaderStage.COMPUTE,
buffer: { type: "uniform" }
}, {
binding: 1,
visibility: GPUShaderStage.VERTEX | GPUShaderStage.COMPUTE,
buffer: { type: "read-only-storage" }
}, {
binding: 2,
visibility: GPUShaderStage.COMPUTE,
buffer: { type: "storage" }
}]
}),
device.createBindGroupLayout({
label: "bind group layout 1",
entries: [{
binding: 0,
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
buffer: { type: "uniform" }
}]
}),
];
const pipelineLayout = device.createPipelineLayout({
label: "pipeline layout",
bindGroupLayouts: bindGroupLayouts,
});
const cellPipeline = device.createRenderPipeline({
label: "cell pipeline",
layout: pipelineLayout,
vertex: {
module: cellShaderModule,
entryPoint: "vertexMain",
buffers: vertexBufferLayouts,
},
fragment: {
module: cellShaderModule,
entryPoint: "fragmentMain",
targets: [{
format: canvasFormat,
}]
},
primitive: {
topology: 'triangle-list',
},
depthStencil: {
depthWriteEnabled: true,
depthCompare: 'less',
format: 'depth24plus',
},
});
const computeShaderModule = device.createShaderModule({
label: "compute shader",
code: await computeWgsl,
});
const computePipelineLayout = device.createPipelineLayout({
label: "compute pipeline layout",
bindGroupLayouts: [ bindGroupLayouts[0] ],
});
const computePipeline = device.createComputePipeline({
label: "compute pipeline",
layout: computePipelineLayout,
compute: {
module: computeShaderModule,
entryPoint: "computeMain",
},
});
const gridSize = 32;
const workGroupSize = 16;
//const uniformArray2 = new Float32Array([gridSize, gridSize, 1, 1]);
const memory = new WebAssembly.Memory({ initial: 1 });
const uniformArray = new Float32Array(memory.buffer);
uniformArray[0] = 1;
uniformArray[1] = 0;
uniformArray[2] = 0;
uniformArray[3] = 0;
const uniformArraySize = 4 * 4 + (4 * 4 * 4) * 2;
const rotate = await WebAssembly.instantiateStreaming(fetch("rotate.wasm"), {
env: { memory: memory }
});
const uniformBuffer = device.createBuffer({
label: "grid uniform",
size: uniformArraySize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(uniformBuffer, 0, uniformArray, 0, uniformArraySize / 4);
const cellStateArray = new Uint32Array(gridSize * gridSize);
const cellStateStorage = [
device.createBuffer({
label: "cell state 0",
size: cellStateArray.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
}),
device.createBuffer({
label: "cell state 1",
size: cellStateArray.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
}),
];
for (let i = 0; i < cellStateArray.length; i += 1) {
cellStateArray[i] = Math.random() > 0.6 ? 1 : 0;
}
device.queue.writeBuffer(cellStateStorage[0], 0, cellStateArray);
device.queue.writeBuffer(cellStateStorage[1], 0, cellStateArray);
const bindGroups = [
device.createBindGroup({
label: "cell bind group 0",
layout: bindGroupLayouts[0],
entries: [{
binding: 0,
resource: { buffer: uniformBuffer },
}, {
binding: 1,
resource: { buffer: cellStateStorage[0] },
}, {
binding: 2,
resource: { buffer: cellStateStorage[1] },
}],
}),
device.createBindGroup({
label: "cell bind group 1",
layout: bindGroupLayouts[0],
entries: [{
binding: 0,
resource: { buffer: uniformBuffer },
}, {
binding: 1,
resource: { buffer: cellStateStorage[1] },
}, {
binding: 2,
resource: { buffer: cellStateStorage[0] },
}],
})
];
const modelBindGroups = {};
for (const path of modelPaths) {
const bindGroup = device.createBindGroup({
label: "palette bind group",
layout: bindGroupLayouts[1],
entries: [{
binding: 0,
resource: { buffer: models[path].modelConfigurationBuffer },
}]
});
modelBindGroups[path] = bindGroup;
}
let tick = 0;
let which = 0;
function render()
{
recreateDepth();
const encoder = device.createCommandEncoder();
const computePass = encoder.beginComputePass();
computePass.setPipeline(computePipeline);
computePass.setBindGroup(0, bindGroups[which]);
const workgroupCount = Math.ceil((gridSize * gridSize) / workGroupSize);
computePass.dispatchWorkgroups(workgroupCount);
computePass.end();
const aspect = canvas.width / canvas.height;
rotate.instance.exports.rotate(tick * 0.01, aspect, 4 * 4);
tick += 1;
if ((tick % 5) == 0) {
which ^= 1;
}
if (lightingSelect.value === "diffuse") {
uniformArray[0] = 1;
} else {
uniformArray[0] = 0;
}
device.queue.writeBuffer(uniformBuffer, 0, uniformArray, 0, uniformArraySize / 4);
const renderPass = encoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),
loadOp: "clear",
clearValue: { r: 0.2, g: 0.2, b: 0.4, a: 1 },
storeOp: "store",
}],
depthStencilAttachment: {
view: depthTexture.createView(),
depthClearValue: 1.0,
depthLoadOp: 'clear',
depthStoreOp: 'store',
},
});
currentModel = modelSelect.value;
renderPass.setPipeline(cellPipeline);
const vertexOffset = indexBufferSize;
renderPass.setVertexBuffer(0, indexVertexBuffer, vertexOffset, vertexBufferSize);
renderPass.setVertexBuffer(1, models[currentModel].instanceBuffer);
renderPass.setIndexBuffer(indexVertexBuffer, "uint16", 0, indexBufferSize);
renderPass.setBindGroup(0, bindGroups[which]);
renderPass.setBindGroup(1, modelBindGroups[currentModel]);
//renderPass.draw(cubeVerticesCount, gridSize * gridSize);
//renderPass.drawIndexed(cubeIndexCount, gridSize * gridSize);
//renderPass.drawIndexed(cubeIndexCount);
renderPass.drawIndexed(cubeIndexCount, models[currentModel].voxelCount);
renderPass.end();
const commandBuffer = encoder.finish();
device.queue.submit([commandBuffer]);
requestAnimationFrame(render)
}
//requestAnimationFrame(render);
const modelSelect = document.getElementById("model-select");
for (const path of modelPaths) {
const option = document.createElement("option");
option.value = path;
option.innerHTML = path;
modelSelect.appendChild(option);
}
modelSelect.value = currentModel;
const lightingSelect = document.getElementById("lighting-select");
lightingSelect.value = "unlit";

View File

@ -1,6 +1,8 @@
struct Configuration { struct Configuration {
matrix: mat4x4f, viewProj: mat4x4f,
matrixWorld: mat4x4f, lightViewProj: mat4x4f,
lightPosition: vec4f,
eyePosition: vec4f,
}; };
@group(0) @binding(0) var<uniform> config: Configuration; @group(0) @binding(0) var<uniform> config: Configuration;
@ -27,12 +29,8 @@ struct VertexOutput {
@location(0) positionWorld: vec3f, @location(0) positionWorld: vec3f,
@location(1) normal: vec3f, @location(1) normal: vec3f,
@location(2) texture: vec2f, @location(2) texture: vec2f,
}; @location(3) lightPosition: vec3f,
@location(4) eyePosition: vec3f,
struct FragmentInput {
@location(0) positionWorld: vec3f,
@location(1) normal: vec3f,
@location(2) texture: vec2f,
}; };
@vertex @vertex
@ -42,10 +40,12 @@ fn vertexMain(input: VertexInput) -> VertexOutput
//let position = vec4f(input.position, 1); //let position = vec4f(input.position, 1);
let normal = vec4f(input.normal, 0.0f); let normal = vec4f(input.normal, 0.0f);
var output: VertexOutput; var output: VertexOutput;
output.position = config.matrix * position; output.position = config.viewProj * position;
output.positionWorld = (config.matrixWorld * position).xyz; output.positionWorld = (position).xyz;
output.normal = (config.matrixWorld * normal).xyz; output.normal = (normal).xyz;
output.texture = input.texture; output.texture = input.texture;
output.lightPosition = config.lightPosition.xyz;
output.eyePosition = config.eyePosition.xyz;
return output; return output;
} }
@ -75,11 +75,8 @@ fn blinnPhong(ndotl: f32, lightVector: vec3f, normal: vec3f, toEye: vec3f) -> ve
return specular; return specular;
} }
fn lighting(position: vec3f, normal: vec3f) -> vec3f fn lighting(position: vec3f, lightPosition: vec3f, eyePosition: vec3f, normal: vec3f) -> vec3f
{ {
let d = 0.9;
let eyePosition = vec3f(d, d / 2, d);
let lightPosition = vec3f(100, 100, 100);
let lightVector = normalize(lightPosition - position); let lightVector = normalize(lightPosition - position);
let ndotl = max(dot(lightVector, normal), 0.0); let ndotl = max(dot(lightVector, normal), 0.0);
let intensity = (ndotl + 0.3) / 1.3; let intensity = (ndotl + 0.3) / 1.3;
@ -90,9 +87,9 @@ fn lighting(position: vec3f, normal: vec3f) -> vec3f
} }
@fragment @fragment
fn fragmentMain(input: FragmentInput) -> @location(0) vec4f fn fragmentMain(input: VertexOutput) -> @location(0) vec4f
{ {
let intensity = lighting(input.positionWorld, normalize(input.normal)); let intensity = lighting(input.positionWorld, input.lightPosition, input.eyePosition, normalize(input.normal));
let color = textureSample(colorTexture, linearSampler, input.texture); let color = textureSample(colorTexture, linearSampler, input.texture);
return vec4(color.xyz * intensity, 1.0); return vec4(color.xyz * intensity, 1.0);

View File

@ -9,10 +9,10 @@
color-scheme: light dark; color-scheme: light dark;
} }
div, span { div, span {
font: 0.95em sans-serif; font: 0.8rem sans-serif;
} }
select, option { select, option {
font: 0.95em sans-serif; font: 0.8rem sans-serif;
} }
canvas { canvas {
position: absolute; position: absolute;
@ -35,6 +35,7 @@
width: 250px; width: 250px;
float: right; float: right;
margin-right: 15px; margin-right: 15px;
background: #444;
} }
.label { .label {
width: calc(40% - 0.3rem); width: calc(40% - 0.3rem);
@ -54,9 +55,22 @@
border-bottom: 1px solid #999; border-bottom: 1px solid #999;
} }
.row { .row {
background: #444;
height: 1.5rem; height: 1.5rem;
} }
code {
font: 0.95rem monospace;
padding-top: 0.1rem;
}
.float3 {
width: 30%;
display: inline-block;
text-align: right;
}
.float2 {
width: 40%;
display: inline-block;
text-align: right;
}
</style> </style>
<script src="index.js" type="module"></script> <script src="index.js" type="module"></script>
<!--<script src="index2.js" type="module"></script>--> <!--<script src="index2.js" type="module"></script>-->
@ -76,6 +90,21 @@
<option>diffuse</option> <option>diffuse</option>
</select> </select>
</div> </div>
<div class="row">
<span class="label">eye</span>
<div class="value">
<code class="float3" id="eye-value-x">0.0</code>
<code class="float3" id="eye-value-y">0.0</code>
<code class="float3" id="eye-value-z">0.0</code>
</div>
</div>
<div class="row">
<span class="label">yaw/pitch</span>
<div class="value">
<code class="float2" id="yaw">0.0</code>
<code class="float2" id="pitch">0.0</code>
</div>
</div>
<!-- <!--
<div class="row"> <div class="row">
<span class="label">test2</span> <span class="label">test2</span>

392
index.js
View File

@ -39,307 +39,13 @@ renderPass.end();
const commandBuffer = encoder.finish(); const commandBuffer = encoder.finish();
device.queue.submit([commandBuffer]); device.queue.submit([commandBuffer]);
const cubeResponse = await fetch("obj/cube.bin"); const memory = new WebAssembly.Memory({ initial: 258 * 2 });
const cubeObj = await cubeResponse.arrayBuffer();
const cubeView = new DataView(cubeObj);
const indexCount = cubeView.getUint32(0, true);
const indexBufferOffset = cubeView.getUint32(4, true);
const indexBufferSize = cubeView.getUint32(8, true);
const vertexBufferOffset = cubeView.getUint32(12, true);
const vertexBufferSize = cubeView.getUint32(16, true);
//console.log(`indexBufferOffset ${indexBufferOffset} indexBufferSize ${indexBufferSize}`);
//console.log(`vertexBufferOffset ${vertexBufferOffset} vertexBufferSize ${vertexBufferSize}`);
const cubeIndexCount = indexBufferSize / 2;
const cubeVerticesStride = 36;
const indexVertexBuffer = device.createBuffer({
label: "cell vertices",
size: indexBufferSize + vertexBufferSize,
usage: GPUBufferUsage.INDEX | GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(indexVertexBuffer, 0, cubeObj, indexBufferOffset, indexBufferSize);
device.queue.writeBuffer(indexVertexBuffer, indexBufferSize, cubeObj, vertexBufferOffset, vertexBufferSize);
//////////////////////////////////////////////////////////////////////
// instance buffer
//////////////////////////////////////////////////////////////////////
async function loadVoxModel(path)
{
const voxResponse = await fetch(path);
const vox = await voxResponse.arrayBuffer();
const voxView = new DataView(vox);
const sizeX = voxView.getUint32(0, true);
const sizeY = voxView.getUint32(4, true);
const sizeZ = voxView.getUint32(8, true);
const voxelCount = voxView.getUint32(12, true);
const instanceBuffer = device.createBuffer({
label: `instanceBuffer %{}`,
size: voxelCount * 4,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(instanceBuffer, 0, vox, 16, voxelCount * 4);
const modelConfigurationBuffer = device.createBuffer({
label: "modelConfigurationBuffer",
size: 4 * 4 + 256 * 4 * 4,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
const paletteOffset = 16 + voxelCount * 4;
const sizeArray = new Float32Array([sizeX, sizeY, sizeZ, 0]);
device.queue.writeBuffer(modelConfigurationBuffer, 0, sizeArray);
device.queue.writeBuffer(modelConfigurationBuffer, 4 * 4, vox, paletteOffset, 256 * 4 * 4);
return {
instanceBuffer: instanceBuffer,
modelConfigurationBuffer: modelConfigurationBuffer,
voxelCount: voxelCount,
path: path,
}
}
const modelPaths = [
"vox/monu1.bin",
"vox/monu7.bin",
"vox/monu9.bin",
"vox/monu16.bin",
"vox/chr_fox.bin",
"vox/ff3.bin",
"vox/dragon.bin",
];
const models = {}
for (const path of modelPaths) {
models[path] = loadVoxModel(path);
}
for (const path of modelPaths) {
models[path] = await models[path];
}
var currentModel = modelPaths[0];
//////////////////////////////////////////////////////////////////////
// vertex layout
//////////////////////////////////////////////////////////////////////
const vertexBufferLayouts = [
{
arrayStride: cubeVerticesStride,
attributes: [{
format: "float32x3",
offset: 0,
shaderLocation: 0,
}, {
format: "float32x2",
offset: 12,
shaderLocation: 1,
}, {
format: "float16x4",
offset: 20,
shaderLocation: 2,
}, {
format: "float16x4",
offset: 28,
shaderLocation: 3,
}],
stepMode: "vertex",
},
{
arrayStride: 4,
attributes: [{
format: "uint8x4",
offset: 0,
shaderLocation: 4,
}],
stepMode: "instance",
},
];
const indexWgsl = getPath("index.wgsl");
const computeWgsl = getPath("compute.wgsl");
const cellShaderModule = device.createShaderModule({
label: "cell shader",
code: await indexWgsl,
});
const bindGroupLayouts = [
device.createBindGroupLayout({
label: "bind group layout 0",
entries: [{
binding: 0,
visibility: GPUShaderStage.VERTEX | GPUShaderStage.COMPUTE,
buffer: { type: "uniform" }
}, {
binding: 1,
visibility: GPUShaderStage.VERTEX | GPUShaderStage.COMPUTE,
buffer: { type: "read-only-storage" }
}, {
binding: 2,
visibility: GPUShaderStage.COMPUTE,
buffer: { type: "storage" }
}]
}),
device.createBindGroupLayout({
label: "bind group layout 1",
entries: [{
binding: 0,
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
buffer: { type: "uniform" }
}]
}),
];
const pipelineLayout = device.createPipelineLayout({
label: "pipeline layout",
bindGroupLayouts: bindGroupLayouts,
});
const cellPipeline = device.createRenderPipeline({
label: "cell pipeline",
layout: pipelineLayout,
vertex: {
module: cellShaderModule,
entryPoint: "vertexMain",
buffers: vertexBufferLayouts,
},
fragment: {
module: cellShaderModule,
entryPoint: "fragmentMain",
targets: [{
format: canvasFormat,
}]
},
primitive: {
topology: 'triangle-list',
},
depthStencil: {
depthWriteEnabled: true,
depthCompare: 'less',
format: 'depth24plus',
},
});
const computeShaderModule = device.createShaderModule({
label: "compute shader",
code: await computeWgsl,
});
const computePipelineLayout = device.createPipelineLayout({
label: "compute pipeline layout",
bindGroupLayouts: [ bindGroupLayouts[0] ],
});
const computePipeline = device.createComputePipeline({
label: "compute pipeline",
layout: computePipelineLayout,
compute: {
module: computeShaderModule,
entryPoint: "computeMain",
},
});
const gridSize = 32;
const workGroupSize = 16;
//const uniformArray2 = new Float32Array([gridSize, gridSize, 1, 1]);
const memory = new WebAssembly.Memory({ initial: 1 });
const uniformArray = new Float32Array(memory.buffer);
uniformArray[0] = 1;
uniformArray[1] = 0;
uniformArray[2] = 0;
uniformArray[3] = 0;
const uniformArraySize = 4 * 4 + (4 * 4 * 4) * 2;
const rotate = await WebAssembly.instantiateStreaming(fetch("rotate.wasm"), {
env: { memory: memory }
});
const memory2 = new WebAssembly.Memory({ initial: 258 * 2 });
const module = await WebAssembly.instantiateStreaming(fetch("src/module.wasm"), { const module = await WebAssembly.instantiateStreaming(fetch("src/module.wasm"), {
env: { env: {
memory: memory2, memory: memory,
log: console.log, log: console.log,
} }
}); });
//document.module = module;
const uniformBuffer = device.createBuffer({
label: "grid uniform",
size: uniformArraySize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(uniformBuffer, 0, uniformArray, 0, uniformArraySize / 4);
const cellStateArray = new Uint32Array(gridSize * gridSize);
const cellStateStorage = [
device.createBuffer({
label: "cell state 0",
size: cellStateArray.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
}),
device.createBuffer({
label: "cell state 1",
size: cellStateArray.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
}),
];
for (let i = 0; i < cellStateArray.length; i += 1) {
cellStateArray[i] = Math.random() > 0.6 ? 1 : 0;
}
device.queue.writeBuffer(cellStateStorage[0], 0, cellStateArray);
device.queue.writeBuffer(cellStateStorage[1], 0, cellStateArray);
const bindGroups = [
device.createBindGroup({
label: "cell bind group 0",
layout: bindGroupLayouts[0],
entries: [{
binding: 0,
resource: { buffer: uniformBuffer },
}, {
binding: 1,
resource: { buffer: cellStateStorage[0] },
}, {
binding: 2,
resource: { buffer: cellStateStorage[1] },
}],
}),
device.createBindGroup({
label: "cell bind group 1",
layout: bindGroupLayouts[0],
entries: [{
binding: 0,
resource: { buffer: uniformBuffer },
}, {
binding: 1,
resource: { buffer: cellStateStorage[1] },
}, {
binding: 2,
resource: { buffer: cellStateStorage[0] },
}],
})
];
const modelBindGroups = {};
for (const path of modelPaths) {
const bindGroup = device.createBindGroup({
label: "palette bind group",
layout: bindGroupLayouts[1],
entries: [{
binding: 0,
resource: { buffer: models[path].modelConfigurationBuffer },
}]
});
modelBindGroups[path] = bindGroup;
}
let tick = 0;
let which = 0;
function createDepthTexture() { function createDepthTexture() {
return device.createTexture({ return device.createTexture({
@ -351,17 +57,12 @@ function createDepthTexture() {
var depthTexture = undefined; var depthTexture = undefined;
const modelSelect = document.getElementById("model-select"); const eyeValueX = document.getElementById("eye-value-x");
for (const path of modelPaths) { const eyeValueY = document.getElementById("eye-value-y");
const option = document.createElement("option"); const eyeValueZ = document.getElementById("eye-value-z");
option.value = path;
option.innerHTML = path;
modelSelect.appendChild(option);
}
modelSelect.value = currentModel;
const lightingSelect = document.getElementById("lighting-select"); const yawValue = document.getElementById("yaw");
lightingSelect.value = "unlit"; const pitchValue = document.getElementById("pitch");
function recreateDepth() function recreateDepth()
{ {
@ -376,76 +77,10 @@ function recreateDepth()
} }
} }
function render()
{
recreateDepth();
const encoder = device.createCommandEncoder();
const computePass = encoder.beginComputePass();
computePass.setPipeline(computePipeline);
computePass.setBindGroup(0, bindGroups[which]);
const workgroupCount = Math.ceil((gridSize * gridSize) / workGroupSize);
computePass.dispatchWorkgroups(workgroupCount);
computePass.end();
const aspect = canvas.width / canvas.height;
rotate.instance.exports.rotate(tick * 0.01, aspect, 4 * 4);
tick += 1;
if ((tick % 5) == 0) {
which ^= 1;
}
if (lightingSelect.value === "diffuse") {
uniformArray[0] = 1;
} else {
uniformArray[0] = 0;
}
device.queue.writeBuffer(uniformBuffer, 0, uniformArray, 0, uniformArraySize / 4);
const renderPass = encoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),
loadOp: "clear",
clearValue: { r: 0.2, g: 0.2, b: 0.4, a: 1 },
storeOp: "store",
}],
depthStencilAttachment: {
view: depthTexture.createView(),
depthClearValue: 1.0,
depthLoadOp: 'clear',
depthStoreOp: 'store',
},
});
currentModel = modelSelect.value;
renderPass.setPipeline(cellPipeline);
const vertexOffset = indexBufferSize;
renderPass.setVertexBuffer(0, indexVertexBuffer, vertexOffset, vertexBufferSize);
renderPass.setVertexBuffer(1, models[currentModel].instanceBuffer);
renderPass.setIndexBuffer(indexVertexBuffer, "uint16", 0, indexBufferSize);
renderPass.setBindGroup(0, bindGroups[which]);
renderPass.setBindGroup(1, modelBindGroups[currentModel]);
//renderPass.draw(cubeVerticesCount, gridSize * gridSize);
//renderPass.drawIndexed(cubeIndexCount, gridSize * gridSize);
//renderPass.drawIndexed(cubeIndexCount);
renderPass.drawIndexed(cubeIndexCount, models[currentModel].voxelCount);
renderPass.end();
const commandBuffer = encoder.finish();
device.queue.submit([commandBuffer]);
requestAnimationFrame(render)
}
//requestAnimationFrame(render);
const matrixSize = 4 * 4 * 4; const matrixSize = 4 * 4 * 4;
const float4Size = 4 * 4; const float4Size = 4 * 4;
const float3Size = 4 * 3; const float3Size = 4 * 3;
const viewUniformBufferSize = (matrixSize * 2) + (float4Size * 1); const viewUniformBufferSize = (matrixSize * 2) + (float4Size * 2);
const viewUniformBuffer = device.createBuffer({ const viewUniformBuffer = device.createBuffer({
label: "view uniform buffer", label: "view uniform buffer",
@ -460,7 +95,7 @@ const cameraStateAddress = module.instance.exports.mem_alloc(cameraStateSize);
module.instance.exports.camera_init(cameraStateAddress); module.instance.exports.camera_init(cameraStateAddress);
const gltfRenderer = await loadGltf(device, canvasFormat, viewUniformBuffer, memory2, module); const gltfRenderer = await loadGltf(device, canvasFormat, viewUniformBuffer, memory, module);
const lightRenderer = await loadLight(device, canvasFormat, viewUniformBuffer); const lightRenderer = await loadLight(device, canvasFormat, viewUniformBuffer);
@ -549,8 +184,15 @@ function updateView()
aspect); aspect);
device.queue.writeBuffer(viewUniformBuffer, 0, device.queue.writeBuffer(viewUniformBuffer, 0,
memory2.buffer, viewStateAddress, memory.buffer, viewStateAddress,
viewUniformBufferSize); viewUniformBufferSize);
const cameraStateF32 = new Float32Array(memory.buffer, cameraStateAddress, cameraStateSize);
eyeValueX.innerHTML = cameraStateF32[0].toFixed(1);
eyeValueY.innerHTML = cameraStateF32[1].toFixed(1);
eyeValueZ.innerHTML = cameraStateF32[2].toFixed(1);
yawValue.innerHTML = cameraStateF32[9].toFixed(2);
pitchValue.innerHTML = cameraStateF32[10].toFixed(2);
} }
function render2() function render2()

View File

@ -1,6 +1,8 @@
struct Configuration { struct Configuration {
matrix: mat4x4f, viewProj: mat4x4f,
matrixWorld: mat4x4f, lightViewProj: mat4x4f,
lightPosition: vec4f,
eyePosition: vec4f,
}; };
@group(0) @binding(0) var<uniform> config: Configuration; @group(0) @binding(0) var<uniform> config: Configuration;
@ -23,7 +25,7 @@ fn vertexMain(input: VertexInput) -> VertexOutput
let position = vec4f(input.position, 1); let position = vec4f(input.position, 1);
var output: VertexOutput; var output: VertexOutput;
output.position = config.matrix * position; output.position = config.lightViewProj * position;
output.texture = input.texture; output.texture = input.texture;
return output; return output;
} }

View File

@ -6,9 +6,9 @@ struct CameraState {
XMFLOAT3 eye; XMFLOAT3 eye;
XMFLOAT3 look; XMFLOAT3 look;
XMFLOAT3 up; XMFLOAT3 up;
float fov;
float pitch;
float yaw; float yaw;
float pitch;
float fov;
XMFLOAT3 light_position; XMFLOAT3 light_position;
}; };
@ -17,6 +17,7 @@ struct ViewState {
XMFLOAT4X4 viewProj; XMFLOAT4X4 viewProj;
XMFLOAT4X4 lightViewProj; XMFLOAT4X4 lightViewProj;
XMFLOAT4 lightPosition; XMFLOAT4 lightPosition;
XMFLOAT4 eyePosition;
}; };
static inline XMMATRIX camera_view(CameraState const * state) static inline XMMATRIX camera_view(CameraState const * state)
@ -51,13 +52,18 @@ static inline float max(float a, float b)
return a > b ? a : b; return a > b ? a : b;
} }
static inline float fract(float a)
{
return a - __builtin_floorf(a);
}
void camera_move(CameraState * state, void camera_move(CameraState * state,
int lu, int ll, int ld, int lr, int lu, int ll, int ld, int lr,
int lal, int lar, int lal, int lar,
int ru, int rl, int rd, int rr) int ru, int rl, int rd, int rr)
{ {
const float linearSpeed = 0.1; const float linearSpeed = 0.3;
const float rotationalSpeed = 0.01; const float rotationalSpeed = 0.025;
XMFLOAT3 move = {0, 0, 0}; XMFLOAT3 move = {0, 0, 0};
if (ll) if (ll)
@ -83,8 +89,10 @@ void camera_move(CameraState * state,
if (rd) if (rd)
state->pitch -= rotationalSpeed; state->pitch -= rotationalSpeed;
state->pitch = min(state->pitch, XM_PIDIV4); state->pitch = min(state->pitch, XM_PIDIV2 * 0.9);
state->pitch = max(state->pitch, -XM_PIDIV4); state->pitch = max(state->pitch, -XM_PIDIV2 * 0.9);
state->yaw = fract(state->yaw * (1.0f / XM_2PI)) * XM_2PI;
float sinyaw; float sinyaw;
float cosyaw; float cosyaw;
@ -118,21 +126,26 @@ void camera_init(CameraState * camera_state)
camera_state->look = {0, 0, -1}; camera_state->look = {0, 0, -1};
camera_state->eye = {0, 0, 5}; camera_state->eye = {0, 0, 5};
camera_state->light_position = {1, 1, 1}; camera_state->light_position = {10, 10, 10};
} }
void camera_view_projection(CameraState const * camera_state, void camera_view_projection(CameraState * camera_state,
ViewState * view_state, ViewState * view_state,
float aspect) float aspect)
{ {
XMVECTOR light_position = XMLoadFloat3(&camera_state->light_position);
light_position = XMVector3Transform(light_position, XMMatrixRotationY(0.025));
XMStoreFloat3(&camera_state->light_position, light_position);
XMMATRIX view = camera_view(camera_state); XMMATRIX view = camera_view(camera_state);
XMMATRIX projection = camera_projection(camera_state, aspect); XMMATRIX projection = camera_projection(camera_state, aspect);
XMMATRIX view_projection = view * projection; XMMATRIX view_projection = view * projection;
XMMATRIX light_world = XMMatrixTranslationFromVector(XMLoadFloat3(&camera_state->light_position)); XMMATRIX light_world = XMMatrixTranslationFromVector(light_position);
XMStoreFloat4x4(&view_state->viewProj, view_projection); XMStoreFloat4x4(&view_state->viewProj, view_projection);
XMStoreFloat4x4(&view_state->lightViewProj, light_world * view_projection); XMStoreFloat4x4(&view_state->lightViewProj, light_world * view_projection);
XMStoreFloat4(&view_state->lightPosition, XMLoadFloat3(&camera_state->light_position)); XMStoreFloat4(&view_state->lightPosition, light_position);
XMStoreFloat4(&view_state->eyePosition, XMLoadFloat3(&camera_state->eye));
} }

View File

@ -7,7 +7,7 @@ extern "C" {
int lal, int lar, int lal, int lar,
int ru, int rl, int rd, int rr); int ru, int rl, int rd, int rr);
void camera_init(CameraState * camera_state); void camera_init(CameraState * camera_state);
void camera_view_projection(CameraState const * camera_state, void camera_view_projection(CameraState * camera_state,
ViewState * view_state, ViewState * view_state,
float aspect); float aspect);
}; };