Skip to content

Commit 09e689b

Browse files
committed
split height field compute from rendering, fix grid main axis color
1 parent dee8569 commit 09e689b

File tree

9 files changed

+261
-63
lines changed

9 files changed

+261
-63
lines changed

frontend/src/ts/dynamicObject/heightField/HeightFieldPipeline.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,58 @@ import { resolveShader } from "../../rendering/Shader";
55
import Renderer from "../../rendering/Renderer";
66
import { Project } from "../../project/Project";
77

8+
export const heightFieldDataLayout = gpuDevice.createBindGroupLayout(
9+
{
10+
label: "height field data bind group layout",
11+
entries:
12+
[
13+
{
14+
binding: 0,
15+
visibility: GPUShaderStage.VERTEX,
16+
buffer:
17+
{
18+
type: "uniform"
19+
}
20+
},
21+
{
22+
binding: 1,
23+
visibility: GPUShaderStage.VERTEX,
24+
texture:
25+
{
26+
multisampled: false,
27+
sampleType: "unfilterable-float",
28+
viewDimension: "2d"
29+
}
30+
}
31+
]
32+
});
33+
34+
export const heightFieldComputeBindGroupLayout = gpuDevice.createBindGroupLayout(
35+
{
36+
label: "height field compute bind group layout",
37+
entries:
38+
[
39+
{
40+
binding: 0,
41+
visibility: GPUShaderStage.COMPUTE,
42+
buffer:
43+
{
44+
type: "uniform"
45+
}
46+
},
47+
{
48+
binding: 1,
49+
visibility: GPUShaderStage.COMPUTE,
50+
storageTexture:
51+
{
52+
access: "write-only",
53+
format: "rgba32float",
54+
viewDimension: "2d"
55+
}
56+
}
57+
]
58+
});
59+
860
export class HeightFieldPipeline extends AbstractPipeline {
961
vertexEntryPoint = vertexEntryPoint;
1062
fragmentEntryPoint = fragmentEntryPoint;
@@ -56,7 +108,7 @@ export class HeightFieldPipeline extends AbstractPipeline {
56108
const renderer = this.project.renderer;
57109
return gpuDevice.createPipelineLayout({
58110
label: "Height field pipeline layout",
59-
bindGroupLayouts: [renderer.bindGroup0Layout, renderer.bindGroupR3Layout],
111+
bindGroupLayouts: [renderer.bindGroup0Layout, renderer.bindGroupR3Layout, heightFieldDataLayout],
60112
});
61113
}
62114

Lines changed: 106 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,135 @@
1-
import { HeightFieldPipeline } from "./HeightFieldPipeline";
1+
import { heightFieldComputeBindGroupLayout, heightFieldDataLayout, HeightFieldPipeline } from "./HeightFieldPipeline";
22
import { Project } from "../../project/Project";
33
import R3Object from "../../project/R3Object";
44
import { HeightFieldSelectionPipeline } from "./HeightFieldSelectionPipeline";
5+
import { gpuDevice } from "../../GPUX";
6+
import { resolveShader } from "../../rendering/Shader";
7+
import heightFieldComputeCode from "./heightFieldCompute.wgsl?raw";
58

69
export default class HeightFieldR3 extends R3Object {
710
pipeline: HeightFieldPipeline;
811
selectionPipeline: HeightFieldSelectionPipeline;
9-
xVerts: number;
10-
yVerts: number;
11-
constructor(project: Project, xVerts: number = 1000, yVerts: number = 1000) {
12+
computePipeline: GPUComputePipeline;
13+
private _xVerts: number;
14+
private _yVerts: number;
15+
cacheTexture: GPUTexture;
16+
dataBuffer: GPUBuffer;
17+
dataBindGroup: GPUBindGroup;
18+
computeBindGroup: GPUBindGroup;
19+
constructor(project: Project, heightFunction: string, xVerts: number = 1000, yVerts: number = 1000) {
1220
super(project);
13-
this.xVerts = xVerts;
14-
this.yVerts = yVerts;
21+
this._xVerts = xVerts;
22+
this._yVerts = yVerts;
23+
this.cacheTexture = gpuDevice.createTexture({
24+
label: "Cache Texture for height field",
25+
format: "rgba32float",
26+
dimension: "2d",
27+
size: [xVerts, yVerts, 1],
28+
usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING
29+
});
1530
this.pipeline = new HeightFieldPipeline(project, "Height Field Pipeline");
1631
this.selectionPipeline = new HeightFieldSelectionPipeline(project, "Height Field Selection Pieline");
32+
this.dataBuffer = gpuDevice.createBuffer({
33+
label: "height field data buffer",
34+
size: 10 * 4,
35+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
36+
});
37+
this.floatData[0] = -10;
38+
this.floatData[1] = -10;
39+
this.floatData[2] = 20;
40+
this.floatData[3] = 20;
41+
this.floatData[4] = -10;
42+
this.floatData[5] = -10;
43+
this.floatData[6] = 20;
44+
this.floatData[7] = 20;
45+
this.uintData[0] = xVerts;
46+
this.uintData[1] = yVerts;
47+
gpuDevice.queue.writeBuffer(this.dataBuffer, 0, this.floatData);
48+
gpuDevice.queue.writeBuffer(this.dataBuffer, this.floatData.byteLength, this.uintData);
49+
this.dataBindGroup = gpuDevice.createBindGroup(
50+
{
51+
label: "height field data bind group",
52+
layout: heightFieldDataLayout,
53+
entries:
54+
[
55+
{
56+
binding: 0,
57+
resource:
58+
{
59+
label: "height field data buffer binding",
60+
buffer: this.dataBuffer
61+
}
62+
},
63+
{
64+
binding: 1,
65+
resource: this.cacheTexture.createView()
66+
}
67+
]
68+
});
69+
this.computeBindGroup = gpuDevice.createBindGroup(
70+
{
71+
label: "height field compute bind group",
72+
layout: heightFieldComputeBindGroupLayout,
73+
entries:
74+
[
75+
{
76+
binding: 0,
77+
resource:
78+
{
79+
label: "height field data buffer binding",
80+
buffer: this.dataBuffer
81+
}
82+
},
83+
{
84+
binding: 1,
85+
resource: this.cacheTexture.createView()
86+
}
87+
]
88+
});
89+
const computeModule = gpuDevice.createShaderModule({
90+
label: "height field compute shader module",
91+
code: resolveShader(heightFieldComputeCode, { heightCode: heightFunction }),
92+
compilationHints: [{ entryPoint: "computeHeight" }],
93+
})
94+
const computePipelineLayout = gpuDevice.createPipelineLayout({
95+
label: "height field compute pipeline layout",
96+
bindGroupLayouts: [project.renderer.bindGroup0Layout, heightFieldComputeBindGroupLayout],
97+
})
98+
this.computePipeline = gpuDevice.createComputePipeline(
99+
{
100+
label: "Height field compute pipeline",
101+
layout: computePipelineLayout,
102+
compute: {
103+
module: computeModule
104+
}
105+
});
17106
}
18107

19108
getVerts(): number {
20-
return this.xVerts * this.yVerts + this.xVerts * (this.yVerts - 2) + 2 * this.yVerts - 2;
109+
return this._xVerts * this._yVerts + this._xVerts * (this._yVerts - 2) + 2 * this._yVerts - 2;
21110
}
22111

23112
render(renderPassEncoder: GPURenderPassEncoder): void {
24113
renderPassEncoder.setPipeline(this.pipeline.gpuPipeline);
25114
this.updateUniforms();
26115
renderPassEncoder.setBindGroup(1, this.defaultBindGroup);
116+
renderPassEncoder.setBindGroup(2, this.dataBindGroup);
27117
renderPassEncoder.draw(this.getVerts());
28118
}
29119

30120
renderSelection(renderPassEncoder: GPURenderPassEncoder): void {
31121
renderPassEncoder.setPipeline(this.selectionPipeline.gpuPipeline);
32122
this.updateUniforms();
33123
renderPassEncoder.setBindGroup(1, this.defaultBindGroup);
124+
renderPassEncoder.setBindGroup(2, this.dataBindGroup);
34125
renderPassEncoder.draw(this.getVerts());
35126
}
127+
128+
floatData = new Float32Array(8);
129+
uintData = new Uint32Array(2);
130+
compute(computePassEncoder: GPUComputePassEncoder) {
131+
computePassEncoder.setPipeline(this.computePipeline);
132+
computePassEncoder.setBindGroup(1, this.computeBindGroup);
133+
computePassEncoder.dispatchWorkgroups(Math.ceil(this._xVerts / 8), Math.ceil(this._xVerts / 8), 1);
134+
}
36135
}

frontend/src/ts/dynamicObject/heightField/HeightFieldSelectionPipeline.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import staticShaderCode from "./heightField.wgsl?raw";
44
import { resolveShader } from "../../rendering/Shader";
55
import Renderer from "../../rendering/Renderer";
66
import { Project } from "../../project/Project";
7+
import { heightFieldDataLayout } from "./HeightFieldPipeline";
78

89
export class HeightFieldSelectionPipeline extends AbstractPipeline {
910
vertexEntryPoint = vertexGeometryEntryPoint;
@@ -55,8 +56,8 @@ export class HeightFieldSelectionPipeline extends AbstractPipeline {
5556
createPipelineLayout(): GPUPipelineLayout | "auto" {
5657
const renderer = this.project.renderer;
5758
return gpuDevice.createPipelineLayout({
58-
label: "Height field pipeline layout",
59-
bindGroupLayouts: [renderer.bindGroup0Layout, renderer.bindGroupR3Layout],
59+
label: "Height field geometry pipeline layout",
60+
bindGroupLayouts: [renderer.bindGroup0Layout, renderer.bindGroupR3Layout, heightFieldDataLayout],
6061
});
6162
}
6263

frontend/src/ts/dynamicObject/heightField/heightField.wgsl

Lines changed: 38 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
struct VertexIn {
2-
@builtin(vertex_index) index: u32,
3-
}
4-
51
struct VertexOut {
62
@builtin(position) position: vec4f,
73
@location(0) ls_pos: vec3f,
@@ -11,83 +7,79 @@ struct VertexOut {
117
#include <utility/frame>
128
#include <utility/r3>
139

14-
const xVerts = 1000u;
15-
const yVerts = 1000u;
16-
const vertsPerRow = xVerts * 2u;
17-
const vertsPerRowWithNaN = vertsPerRow + 2u;
18-
19-
const geometryMin = vec2f(-10);
20-
const geometrySize = vec2f(20);
21-
const domainMin = vec2f(-10);
22-
const domainSize = vec2f(20);
23-
const epsilon = 0.0001;
24-
25-
fn hf(p: vec2f) -> f32 {
26-
return sin(p.x + 0.1 * f32(view.frame.x)) * sin(p.y);
10+
struct HeightField {
11+
geometryMin: vec2f,
12+
geometrySize: vec2f,
13+
domainMin: vec2f,
14+
domainSize: vec2f,
15+
xVerts: u32,
16+
yVerts: u32,
2717
}
18+
@group(2) @binding(0) var<uniform> heightField: HeightField;
19+
@group(2) @binding(1) var heightCache: texture_2d<f32>;
2820

29-
fn pos(p: vec2f) -> vec3f {
30-
return vec3f(p, hf(p));
21+
fn mapToLocal(p: vec2f) -> vec2f {
22+
return p * heightField.geometrySize + heightField.geometryMin;
3123
}
3224

33-
fn mapToDomain(x: f32, y: f32) -> vec2f {
34-
return vec2f(x, y) * domainSize + domainMin;
25+
struct Coords {
26+
norm: vec2f,
27+
id: vec2u
3528
}
3629

37-
fn mapToLocal(x: f32, y: f32) -> vec2f {
38-
return vec2f(x, y) * geometrySize + geometryMin;
39-
}
30+
fn getNormalized(index: u32) -> Coords {
31+
let vertsPerRow = heightField.xVerts * 2u;
32+
let vertsPerRowWithNaN = vertsPerRow + 2u;
4033

41-
fn getNormalized(input: VertexIn) -> vec2f {
42-
let nanVertices = 2 * (input.index / vertsPerRowWithNaN);
43-
var vIndex: u32 = input.index - nanVertices;
34+
let nanVertices: u32 = 2 * (index / vertsPerRowWithNaN);
35+
var vIndex: u32 = index - nanVertices;
4436

45-
let indexInRowWithNaN = input.index % vertsPerRowWithNaN;
37+
let indexInRowWithNaN: u32 = index % vertsPerRowWithNaN;
4638
if(indexInRowWithNaN >= vertsPerRow) {
4739
vIndex -= 1;
4840
}
4941

5042
let row: u32 = vIndex / vertsPerRow;
5143
let indexInRow: u32 = vIndex % vertsPerRow;
5244
let xInRow: u32 = indexInRow / 2u;
53-
let upper: u32 = indexInRow % 2u;
45+
let upper: u32 = 1u - indexInRow % 2u;
46+
let yInColumn = row + upper;
5447

55-
let xNorm = f32(xInRow) / f32(xVerts - 1);
56-
let yNorm = f32(row + upper) / f32(yVerts - 1);
57-
return vec2f(xNorm, yNorm);
48+
let xNorm = f32(xInRow) / f32(heightField.xVerts - 1);
49+
let yNorm = f32(yInColumn) / f32(heightField.yVerts - 1);
50+
return Coords(vec2f(xNorm, yNorm), vec2u(xInRow, yInColumn));
5851
}
5952

6053
@vertex
61-
fn vertex_main(input: VertexIn) -> VertexOut {
54+
fn vertex_main(@builtin(vertex_index) index: u32) -> VertexOut {
6255
var output: VertexOut;
63-
64-
let norm = getNormalized(input);
6556

66-
let domP0 = pos(mapToDomain(norm.x, norm.y));
67-
let domP1 = pos(mapToDomain(norm.x + epsilon, norm.y));
68-
let domP2 = pos(mapToDomain(norm.x, norm.y + epsilon));
69-
let ls_pos = vec3f(mapToLocal(norm.x, norm.y), domP0.z);
57+
let coords = getNormalized(index);
58+
let data = textureLoad(heightCache, coords.id, 0);
59+
60+
let ls_pos = vec3f(mapToLocal(coords.norm), data.w);
7061
let ws_pos = (vertexUniform.transformation * vec4f(ls_pos, 1)).xyz;
7162

7263
output.position = view.projectionMat * view.viewMat * vec4f(ws_pos, 1);
7364
output.ls_pos = ls_pos;
74-
output.normal = normalize(cross(domP2 - domP0, domP1 - domP0));
7565

7666
let normMat: mat3x3<f32> = mat3x3<f32>(
7767
vertexUniform.transformation[0].xyz,
7868
vertexUniform.transformation[1].xyz,
7969
vertexUniform.transformation[2].xyz
8070
);
81-
output.normal = normMat * output.normal;
71+
output.normal = normMat * data.xyz;
8272
return output;
8373
}
8474

8575
@vertex
86-
fn vertex_geometry(input: VertexIn) -> @builtin(position) vec4f {
87-
let norm = getNormalized(input);
88-
let domP0 = pos(mapToDomain(norm.x, norm.y));
89-
let ls_pos = vec3f(mapToLocal(norm.x, norm.y), domP0.z);
76+
fn vertex_geometry(@builtin(vertex_index) index: u32) -> @builtin(position) vec4f {
77+
let coords = getNormalized(index);
78+
let data = textureLoad(heightCache, coords.id, 0);
79+
80+
let ls_pos = vec3f(mapToLocal(coords.norm), data.w);
9081
let ws_pos = (vertexUniform.transformation * vec4f(ls_pos, 1)).xyz;
82+
9183
return view.projectionMat * view.viewMat * vec4f(ws_pos, 1);;
9284
}
9385

@@ -100,7 +92,7 @@ fn steps(v: vec3f, stepSize: f32) -> vec3f {
10092
@fragment
10193
fn fragment_main(@builtin(front_facing) front_facing: bool, vertexData: VertexOut) -> R3FragmentOutput {
10294
let n = normalize(vertexData.normal) * select(-1.0, 1.0, front_facing);
103-
let color = vec3(clamp(dot(n, vec3(1,0,0)), 0.0, 1.0));
95+
let color = vec3(clamp(dot(n, normalize(vec3(1,0,0))), 0.0, 1.0));
10496
let outColor = vec4f(createOutputFragment(color), 1);
10597
return R3FragmentOutput(outColor, fragmentUniform.id);
10698
}

0 commit comments

Comments
 (0)