animated light position
This commit is contained in:
parent
4526aaa2c5
commit
19ef9652d0
371
cube.js
Normal file
371
cube.js
Normal 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";
|
||||||
31
gltf.wgsl
31
gltf.wgsl
@ -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);
|
||||||
|
|||||||
35
index.html
35
index.html
@ -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
392
index.js
@ -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()
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user