Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/typegpu-docs/src/content/docs/apis/data-schemas.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,9 @@ const ArrayPartialSchema = d.arrayOf(d.f32);
const array = ArrayPartialSchema(2)([1.2, 19.29]);
// ^?
```
:::caution
The only decoration allowed on array element types is `d.location`. Decorators like `d.align` and `d.size` cannot be applied directly - wrap them in a struct instead, e.g. `d.arrayOf(d.struct({ value: d.align(16, d.u32) }), n)`.
:::

## Textures

Expand Down
8 changes: 7 additions & 1 deletion apps/typegpu-docs/src/examples/rendering/clouds/consts.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { d } from 'typegpu';

export const FOV_FACTOR = 1;
export const CLOUD_RENDER_SCALE = 0.5;
export const UPSCALE_CENTER_WEIGHT = 0.5;
export const UPSCALE_CORNER_WEIGHT = (1 - UPSCALE_CENTER_WEIGHT) / 4;

export const SUN_DIRECTION = d.vec3f(1.0, 0.0, 0.0);
export const SUN_BRIGHTNESS = 0.9;
export const LIGHT_ABSORPTION = 0.88;
export const CLOUD_EXTINCTION = 4;

export const CLOUD_COVERAGE = 0.7;
export const CLOUD_AMPLITUDE = 1.0;
Expand All @@ -24,4 +28,6 @@ export const SKY_ZENITH_TINT = d.vec3f(1.0, 0.7, 0.43);
export const SUN_GLOW = d.vec3f(1.0, 0.37, 0.17);

export const NOISE_Z_OFFSET = d.vec2f(37.0, 239.0);
export const NOISE_TEXTURE_SIZE = 256;
export const NOISE_TEXTURE_SIZE = 32;

export const DENSITY_TEXTURE_SIZE = 256;
163 changes: 139 additions & 24 deletions apps/typegpu-docs/src/examples/rendering/clouds/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { tgpu, common, d, std } from 'typegpu';
import {
CLOUD_RENDER_SCALE,
DENSITY_TEXTURE_SIZE,
FOV_FACTOR,
NOISE_TEXTURE_SIZE,
SKY_HORIZON,
SKY_ZENITH_TINT,
SUN_BRIGHTNESS,
SUN_DIRECTION,
SUN_GLOW,
UPSCALE_CENTER_WEIGHT,
UPSCALE_CORNER_WEIGHT,
WIND_SPEED,
} from './consts.ts';
import { raymarch } from './utils.ts';
import { cloudsLayout, CloudsParams } from './types.ts';
import { precomputeDensity, raymarch } from './utils.ts';
import { upscaleLayout, cloudsLayout, CloudsParams, precomputeDensityLayout } from './types.ts';
import { randf } from '@typegpu/noise';
import { defineControls } from '../../common/defineControls.ts';

Expand All @@ -31,63 +35,165 @@ for (let i = 0; i < noiseData.length; i += 1) {
noiseData[i] = Math.random() * 255;
}

const sampler = root.createSampler({
const densitySampler = root.createSampler({
magFilter: 'linear',
minFilter: 'linear',
addressModeU: 'repeat',
addressModeV: 'repeat',
addressModeW: 'repeat',
});

const upscaleSampler = root.createSampler({
magFilter: 'linear',
minFilter: 'linear',
addressModeU: 'clamp-to-edge',
addressModeV: 'clamp-to-edge',
Comment thread
cieplypolar marked this conversation as resolved.
});

const noiseTexture = root
.createTexture({
size: [NOISE_TEXTURE_SIZE, NOISE_TEXTURE_SIZE],
format: 'r8unorm',
})
.$usage('sampled', 'render');
.$usage('sampled');
noiseTexture.write(noiseData);

const bindGroup = root.createBindGroup(cloudsLayout, {
params: paramsUniform.buffer,
const densityTexture = root
.createTexture({
size: [DENSITY_TEXTURE_SIZE, DENSITY_TEXTURE_SIZE, DENSITY_TEXTURE_SIZE],
dimension: '3d',
format: 'rgba8unorm',
})
.$usage('sampled', 'storage');

const densityWriteView = densityTexture.createView(d.textureStorage3d('rgba8unorm', 'write-only'));
const densityReadView = densityTexture.createView(d.texture3d());

const precomputeDensityBindGroup = root.createBindGroup(precomputeDensityLayout, {
noiseTexture,
sampler,
sampler: densitySampler,
densityTexture: densityWriteView,
});

const pipeline = root.createRenderPipeline({
const cloudsBindGroup = root.createBindGroup(cloudsLayout, {
params: paramsUniform,
densityTexture: densityReadView,
sampler: densitySampler,
});

const precomputeDensityPipeline = root.createGuardedComputePipeline(precomputeDensity);
precomputeDensityPipeline
.with(precomputeDensityBindGroup)
.dispatchThreads(DENSITY_TEXTURE_SIZE, DENSITY_TEXTURE_SIZE, DENSITY_TEXTURE_SIZE);

Comment thread
cieplypolar marked this conversation as resolved.
const getRayDirection = (uv: d.v2f) => {
'use gpu';
const screenRes = resolutionUniform.$;
const aspect = screenRes.x / screenRes.y;
const screenPos = (uv - 0.5) * 2 * d.vec2f(std.max(aspect, 1), std.max(1 / aspect, 1));
return std.normalize(d.vec3f(screenPos.x, screenPos.y, FOV_FACTOR));
};

const cloudPipeline = root.createRenderPipeline({
targets: { format: 'rgba8unorm' },
vertex: common.fullScreenTriangle,
fragment: ({ uv }) => {
'use gpu';
randf.seed2(uv * cloudsLayout.$.params.time);
const screenRes = resolutionUniform.$;
const aspect = screenRes.x / screenRes.y;

let screenPos = (uv - 0.5) * 2;
screenPos = d.vec2f(screenPos.x * std.max(aspect, 1), screenPos.y * std.max(1 / aspect, 1));

const sunDir = std.normalize(SUN_DIRECTION);
const time = cloudsLayout.$.params.time;
randf.seed2(uv * time);
const rayOrigin = d.vec3f(
std.sin(time * 0.6) * 0.5,
std.cos(time * 0.8) * 0.5 - 1,
time * WIND_SPEED,
);
const rayDir = std.normalize(d.vec3f(screenPos.x, screenPos.y, FOV_FACTOR));
const rayDir = getRayDirection(uv);

return raymarch(rayOrigin, rayDir);
},
});

const upscalePipeline = root.createRenderPipeline({
targets: { format: presentationFormat },
vertex: common.fullScreenTriangle,
fragment: ({ uv }) => {
'use gpu';
const rayDir = getRayDirection(uv);
const sunDir = std.normalize(SUN_DIRECTION);

const sunDot = std.saturate(std.dot(rayDir, sunDir));
const sunGlow = sunDot ** (1 / SUN_BRIGHTNESS ** 3);

let skyCol = SKY_HORIZON - SKY_ZENITH_TINT * rayDir.y * 0.35;
const up = std.max(-rayDir.y, 0);
const down = std.max(rayDir.y, 0);
let skyCol = SKY_HORIZON - SKY_ZENITH_TINT * (up * 0.35 + down * 0.15);

skyCol += SUN_GLOW * sunGlow;

const cloudCol = raymarch(rayOrigin, rayDir, sunDir);
const halfTexel = 0.5 / d.vec2f(std.textureDimensions(upscaleLayout.$.cloudTexture));

let cloudCol =
std.textureSample(upscaleLayout.$.cloudTexture, upscaleLayout.$.sampler, uv) *
UPSCALE_CENTER_WEIGHT;

for (const dx of tgpu.unroll([-1, 1])) {
for (const dy of tgpu.unroll([-1, 1])) {
cloudCol +=
std.textureSample(
upscaleLayout.$.cloudTexture,
upscaleLayout.$.sampler,
uv + halfTexel * d.vec2f(dx, dy),
) * UPSCALE_CORNER_WEIGHT;
}
}

const finalCol = skyCol * (1.1 - cloudCol.a) + cloudCol.rgb;

return d.vec4f(finalCol, 1.0);
},
targets: { format: presentationFormat },
});

function getCloudTargetSize() {
return [
Math.max(1, Math.floor(canvas.width * CLOUD_RENDER_SCALE)),
Math.max(1, Math.floor(canvas.height * CLOUD_RENDER_SCALE)),
] as const;
}

function createCloudTarget(width: number, height: number) {
const texture = root
.createTexture({
size: [width, height],
format: 'rgba8unorm',
})
.$usage('render', 'sampled');

return { texture, width, height };
}

const [initialCloudWidth, initialCloudHeight] = getCloudTargetSize();
let cloudTarget = createCloudTarget(initialCloudWidth, initialCloudHeight);

function createCloudUpscaleBindGroup() {
return root.createBindGroup(upscaleLayout, {
cloudTexture: cloudTarget.texture,
sampler: upscaleSampler,
});
}

let cloudUpscaleBindGroup = createCloudUpscaleBindGroup();

const resizeObserver = new ResizeObserver(() => {
resolutionUniform.write(d.vec2f(canvas.width, canvas.height));

const [width, height] = getCloudTargetSize();
if (width === cloudTarget.width && height === cloudTarget.height) {
return;
}

const previousCloudTarget = cloudTarget;
cloudTarget = createCloudTarget(width, height);
cloudUpscaleBindGroup = createCloudUpscaleBindGroup();
previousCloudTarget.texture.destroy();
});
resizeObserver.observe(canvas);

Expand All @@ -96,13 +202,21 @@ let frameId: number;
function render(timestamp: number) {
paramsUniform.patch({ time: (timestamp / 1000) % 500 });

pipeline
.with(bindGroup)
cloudPipeline
.with(cloudsBindGroup)
.withColorAttachment({
view: cloudTarget.texture,
clearValue: [0, 0, 0, 0],
})
.draw(3);

upscalePipeline
.with(cloudUpscaleBindGroup)
.withColorAttachment({
view: context,
view: context.getCurrentTexture().createView(),
clearValue: [0, 0, 0, 1],
})
.draw(6);
.draw(3);

frameId = requestAnimationFrame(render);
}
Expand Down Expand Up @@ -145,5 +259,6 @@ export const controls = defineControls({
export function onCleanup() {
cancelAnimationFrame(frameId);
resizeObserver.disconnect();
cloudTarget.texture.destroy();
root.destroy();
}
15 changes: 14 additions & 1 deletion apps/typegpu-docs/src/examples/rendering/clouds/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,21 @@ export const CloudsParams = d.struct({
maxDistance: d.f32,
});

export const precomputeDensityLayout = tgpu.bindGroupLayout({
noiseTexture: { texture: d.texture2d() },
sampler: { sampler: 'filtering' },
Comment thread
cieplypolar marked this conversation as resolved.
densityTexture: {
storageTexture: d.textureStorage3d('rgba8unorm', 'write-only'),
},
});

export const cloudsLayout = tgpu.bindGroupLayout({
params: { uniform: CloudsParams },
noiseTexture: { texture: d.texture2d() },
densityTexture: { texture: d.texture3d() },
sampler: { sampler: 'filtering' },
});

export const upscaleLayout = tgpu.bindGroupLayout({
cloudTexture: { texture: d.texture2d() },
sampler: { sampler: 'filtering' },
});
Loading
Loading