@aethervtt/shard-render 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ablation.d.ts +74 -0
- package/dist/ablation.js +117 -0
- package/dist/assets.d.ts +3 -0
- package/dist/assets.js +43 -2
- package/dist/atmosphere-nodes.d.ts +9 -0
- package/dist/atmosphere-nodes.js +157 -30
- package/dist/atmosphere-plugin.js +8 -2
- package/dist/atmosphere-shaders.js +17 -5
- package/dist/baseline/atmosphere.d.ts +16 -0
- package/dist/baseline/atmosphere.js +301 -0
- package/dist/baseline/data-texture.d.ts +36 -0
- package/dist/baseline/data-texture.js +181 -0
- package/dist/baseline/environment.d.ts +14 -0
- package/dist/baseline/environment.js +368 -0
- package/dist/baseline/exposure.d.ts +25 -0
- package/dist/baseline/exposure.js +133 -0
- package/dist/baseline/lights.d.ts +33 -0
- package/dist/baseline/lights.js +133 -0
- package/dist/baseline/textures.d.ts +97 -0
- package/dist/baseline/textures.js +203 -0
- package/dist/camera.d.ts +2 -0
- package/dist/camera.js +4 -0
- package/dist/crowd.d.ts +21 -0
- package/dist/crowd.js +121 -0
- package/dist/culling-shaders.js +21 -2
- package/dist/culling.d.ts +11 -2
- package/dist/culling.js +44 -18
- package/dist/cutaway-plugin.d.ts +48 -0
- package/dist/cutaway-plugin.js +364 -0
- package/dist/cutaway.d.ts +27 -0
- package/dist/cutaway.js +49 -0
- package/dist/data-store.d.ts +69 -0
- package/dist/data-store.js +105 -0
- package/dist/debug-shaders.js +17 -3
- package/dist/debug-views.js +4 -2
- package/dist/decals-plugin.d.ts +5 -0
- package/dist/decals-plugin.js +54 -0
- package/dist/decals.d.ts +69 -0
- package/dist/decals.js +326 -0
- package/dist/deferred-shaders.js +2 -0
- package/dist/deferred.js +28 -4
- package/dist/deform.d.ts +5 -4
- package/dist/deform.js +4 -9
- package/dist/describe-plugin.d.ts +4 -2
- package/dist/describe-plugin.js +75 -3
- package/dist/display-nodes.js +37 -13
- package/dist/dynamic-resolution.d.ts +2 -0
- package/dist/dynamic-resolution.js +6 -1
- package/dist/environment-plugin.js +17 -2
- package/dist/environment-state.d.ts +7 -3
- package/dist/environment-state.js +20 -9
- package/dist/environment.js +7 -1
- package/dist/features.d.ts +49 -0
- package/dist/features.js +106 -0
- package/dist/foliage-budget.d.ts +64 -0
- package/dist/foliage-budget.js +108 -0
- package/dist/foliage-path.d.ts +19 -0
- package/dist/foliage-path.js +7 -0
- package/dist/foliage-shaders.d.ts +11 -0
- package/dist/foliage-shaders.js +504 -0
- package/dist/foliage.d.ts +194 -0
- package/dist/foliage.js +1027 -0
- package/dist/forward.d.ts +38 -2
- package/dist/forward.js +608 -186
- package/dist/fxaa.js +12 -2
- package/dist/gizmos-plugin.js +7 -0
- package/dist/gizmos.d.ts +3 -3
- package/dist/gizmos.js +11 -13
- package/dist/gpu-assets.d.ts +58 -4
- package/dist/gpu-assets.js +191 -8
- package/dist/graph.d.ts +45 -0
- package/dist/graph.js +169 -19
- package/dist/health.d.ts +58 -0
- package/dist/health.js +112 -0
- package/dist/index.d.ts +40 -11
- package/dist/index.js +343 -9
- package/dist/instances.d.ts +119 -8
- package/dist/instances.js +387 -78
- package/dist/interior-plugin.d.ts +121 -0
- package/dist/interior-plugin.js +465 -0
- package/dist/interior.d.ts +24 -0
- package/dist/interior.js +13 -0
- package/dist/layers.d.ts +35 -0
- package/dist/layers.js +49 -0
- package/dist/lens-plugin.d.ts +43 -0
- package/dist/lens-plugin.js +304 -0
- package/dist/lens.d.ts +41 -0
- package/dist/lens.js +99 -0
- package/dist/lights.d.ts +50 -5
- package/dist/lights.js +101 -22
- package/dist/material-pipelines.d.ts +37 -4
- package/dist/material-pipelines.js +102 -5
- package/dist/materials.d.ts +23 -0
- package/dist/materials.js +44 -1
- package/dist/outline-plugin.d.ts +48 -0
- package/dist/outline-plugin.js +529 -0
- package/dist/outline.d.ts +15 -0
- package/dist/outline.js +42 -0
- package/dist/overlay-registry.d.ts +3 -1
- package/dist/overlay-registry.js +3 -1
- package/dist/overlays.js +6 -2
- package/dist/perf-overlay.d.ts +9 -0
- package/dist/perf-overlay.js +125 -0
- package/dist/picking.d.ts +17 -0
- package/dist/picking.js +40 -11
- package/dist/pixel-perfect-plugin.js +10 -2
- package/dist/plugin.d.ts +69 -7
- package/dist/plugin.js +223 -25
- package/dist/post-common.d.ts +22 -2
- package/dist/post-common.js +82 -2
- package/dist/post-nodes.d.ts +7 -1
- package/dist/post-nodes.js +235 -111
- package/dist/post-plugin.js +5 -3
- package/dist/post-scene.d.ts +130 -0
- package/dist/post-scene.js +164 -0
- package/dist/post-shaders.js +85 -62
- package/dist/post.js +11 -1
- package/dist/projection.d.ts +24 -0
- package/dist/projection.js +87 -0
- package/dist/readback.d.ts +1 -0
- package/dist/readback.js +1 -0
- package/dist/render-scale.js +1 -0
- package/dist/ribbons-plugin.d.ts +5 -0
- package/dist/ribbons-plugin.js +59 -0
- package/dist/ribbons.d.ts +44 -0
- package/dist/ribbons.js +288 -0
- package/dist/scenario.d.ts +70 -0
- package/dist/scenario.js +125 -0
- package/dist/screen-effects.d.ts +97 -0
- package/dist/screen-effects.js +192 -0
- package/dist/shaders.d.ts +13 -0
- package/dist/shaders.js +189 -34
- package/dist/shadow-catcher.d.ts +9 -0
- package/dist/shadow-catcher.js +133 -0
- package/dist/shadows.d.ts +27 -2
- package/dist/shadows.js +100 -20
- package/dist/standard-fields.d.ts +1 -0
- package/dist/standard-fields.js +5 -0
- package/dist/standard.js +18 -0
- package/dist/stats.d.ts +45 -1
- package/dist/stats.js +91 -3
- package/dist/surface-model.d.ts +57 -0
- package/dist/surface-model.js +289 -0
- package/dist/surface-shaders.d.ts +3 -0
- package/dist/surface-shaders.js +160 -0
- package/dist/surface.d.ts +40 -0
- package/dist/surface.js +86 -0
- package/dist/target.d.ts +1 -13
- package/dist/target.js +1 -26
- package/dist/testing.d.ts +11 -0
- package/dist/testing.js +39 -1
- package/dist/tier.d.ts +13 -0
- package/dist/tier.js +14 -0
- package/dist/timer.d.ts +18 -1
- package/dist/timer.js +73 -20
- package/dist/variants.d.ts +37 -0
- package/dist/variants.js +104 -0
- package/dist/vfx-draw.d.ts +37 -0
- package/dist/vfx-draw.js +291 -0
- package/dist/vfx-state.d.ts +42 -0
- package/dist/vfx-state.js +81 -0
- package/dist/vfx.d.ts +69 -0
- package/dist/vfx.js +103 -0
- package/dist/view-visibility-plugin.d.ts +61 -0
- package/dist/view-visibility-plugin.js +267 -0
- package/dist/view-visibility.d.ts +28 -0
- package/dist/view-visibility.js +46 -0
- package/dist/view.d.ts +29 -1
- package/dist/view.js +57 -34
- package/package.json +23 -11
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { type World } from '@aethervtt/shard-core';
|
|
2
|
+
/** Each post effect's component and settings, by the name `?effects=` uses. */
|
|
3
|
+
export declare const POST_EFFECTS: {
|
|
4
|
+
readonly bloom: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
5
|
+
readonly intensity: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
6
|
+
readonly threshold: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
7
|
+
readonly knee: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
8
|
+
readonly radius: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
9
|
+
}>, {
|
|
10
|
+
readonly intensity: 0.12;
|
|
11
|
+
}];
|
|
12
|
+
readonly exposure: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
13
|
+
readonly minEv: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
14
|
+
readonly maxEv: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
15
|
+
readonly compensation: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
16
|
+
readonly speedUp: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
17
|
+
readonly speedDown: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
18
|
+
readonly metering: import("@aethervtt/shard-core").FieldType<"average" | "center" | "spot", "u8">;
|
|
19
|
+
}>, {
|
|
20
|
+
readonly compensation: 0.5;
|
|
21
|
+
}];
|
|
22
|
+
readonly dof: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
23
|
+
readonly mode: import("@aethervtt/shard-core").FieldType<"bokeh" | "gaussian", "u8">;
|
|
24
|
+
readonly focusDistance: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
25
|
+
readonly maxBlur: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
26
|
+
}>, {
|
|
27
|
+
readonly focusDistance: 9;
|
|
28
|
+
readonly maxBlur: 0.012;
|
|
29
|
+
}];
|
|
30
|
+
readonly 'motion-blur': readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
31
|
+
readonly samples: import("@aethervtt/shard-core").FieldType<number, "u32">;
|
|
32
|
+
readonly maxBlur: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
33
|
+
}>, {}];
|
|
34
|
+
readonly taa: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
35
|
+
readonly mode: import("@aethervtt/shard-core").FieldType<"fxaa" | "msaa" | "none" | "taa", "u8">;
|
|
36
|
+
}>, {
|
|
37
|
+
readonly mode: 'taa';
|
|
38
|
+
}];
|
|
39
|
+
readonly fxaa: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
40
|
+
readonly mode: import("@aethervtt/shard-core").FieldType<"fxaa" | "msaa" | "none" | "taa", "u8">;
|
|
41
|
+
}>, {
|
|
42
|
+
readonly mode: 'fxaa';
|
|
43
|
+
}];
|
|
44
|
+
readonly noaa: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
45
|
+
readonly mode: import("@aethervtt/shard-core").FieldType<"fxaa" | "msaa" | "none" | "taa", "u8">;
|
|
46
|
+
}>, {
|
|
47
|
+
readonly mode: 'none';
|
|
48
|
+
}];
|
|
49
|
+
readonly ssao: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
50
|
+
readonly radius: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
51
|
+
readonly intensity: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
52
|
+
readonly quality: import("@aethervtt/shard-core").FieldType<"high" | "low" | "medium", "u8">;
|
|
53
|
+
}>, {
|
|
54
|
+
readonly radius: 0.6;
|
|
55
|
+
}];
|
|
56
|
+
readonly fog: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
57
|
+
readonly color: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Color, "f32">;
|
|
58
|
+
readonly density: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
59
|
+
readonly heightFalloff: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
60
|
+
readonly start: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
61
|
+
readonly sunScattering: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
62
|
+
readonly mode: import("@aethervtt/shard-core").FieldType<"add" | "default", "u8">;
|
|
63
|
+
}>, {
|
|
64
|
+
readonly density: 0.015;
|
|
65
|
+
readonly heightFalloff: 0.08;
|
|
66
|
+
readonly sunScattering: 0.6;
|
|
67
|
+
}];
|
|
68
|
+
readonly grading: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
69
|
+
readonly temperature: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
70
|
+
readonly tint: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
71
|
+
readonly saturation: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
72
|
+
readonly contrast: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
73
|
+
readonly lift: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
|
|
74
|
+
readonly gamma: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
|
|
75
|
+
readonly gain: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
|
|
76
|
+
readonly lut: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Texture"> | null, "object">;
|
|
77
|
+
}>, {
|
|
78
|
+
readonly temperature: 0.1;
|
|
79
|
+
readonly saturation: 1.1;
|
|
80
|
+
readonly contrast: 1.05;
|
|
81
|
+
}];
|
|
82
|
+
readonly vignette: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
83
|
+
readonly intensity: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
84
|
+
readonly smoothness: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
85
|
+
}>, {
|
|
86
|
+
readonly intensity: 0.25;
|
|
87
|
+
}];
|
|
88
|
+
};
|
|
89
|
+
export type PostEffectName = keyof typeof POST_EFFECTS;
|
|
90
|
+
/** The full stack: every effect but the other anti-aliasing modes (TAA is on). */
|
|
91
|
+
export declare const ALL_POST_EFFECTS: readonly PostEffectName[];
|
|
92
|
+
/** The components for effect names; unknown names are skipped. */
|
|
93
|
+
export declare function postEffects(names: readonly string[]): unknown[];
|
|
94
|
+
/** A ball on a circle round the courtyard. */
|
|
95
|
+
export declare const PostSpin: import("@aethervtt/shard-core").ComponentDef<{
|
|
96
|
+
readonly radius: import("@aethervtt/shard-core").FieldType<number, "f32"> & ((options?: import("@aethervtt/shard-core").FieldOptions<number> | undefined) => import("@aethervtt/shard-core").FieldType<number, "f32">);
|
|
97
|
+
readonly speed: import("@aethervtt/shard-core").FieldType<number, "f32"> & ((options?: import("@aethervtt/shard-core").FieldOptions<number> | undefined) => import("@aethervtt/shard-core").FieldType<number, "f32">);
|
|
98
|
+
readonly height: import("@aethervtt/shard-core").FieldType<number, "f32"> & ((options?: import("@aethervtt/shard-core").FieldOptions<number> | undefined) => import("@aethervtt/shard-core").FieldType<number, "f32">);
|
|
99
|
+
}>;
|
|
100
|
+
/** Moves PostSpin balls along their circles by `Time.elapsed`. */
|
|
101
|
+
export declare const postSpin: import("@aethervtt/shard-core").SystemDef<{
|
|
102
|
+
q: import("@aethervtt/shard-core").Query;
|
|
103
|
+
}>;
|
|
104
|
+
/** The courtyard's moving parts: PostSpin and its system. Add it with the render plugins. */
|
|
105
|
+
export declare const postScenePlugin: import("@aethervtt/shard-runtime").Plugin;
|
|
106
|
+
/** Where the courtyard is looked at from: 9 m out, 2.2 m up. */
|
|
107
|
+
export declare const POST_EYE: [number, number, number];
|
|
108
|
+
export declare const POST_TARGET: [number, number, number];
|
|
109
|
+
/**
|
|
110
|
+
* The `post-stack` camera path: from POST_EYE, swinging 30° either side of the courtyard's axis
|
|
111
|
+
* once every 20 s and drifting 1.5 m in and out, always looking at POST_TARGET. `seconds` along it.
|
|
112
|
+
*/
|
|
113
|
+
export declare function postCamera(seconds: number): {
|
|
114
|
+
eye: [number, number, number];
|
|
115
|
+
target: [number, number, number];
|
|
116
|
+
};
|
|
117
|
+
/** The camera's lens: a physical exposure, so auto exposure and DoF read real values. */
|
|
118
|
+
export declare const POST_LENS: readonly [import("@aethervtt/shard-core").ComponentDef<{
|
|
119
|
+
readonly aperture: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
120
|
+
readonly shutterSpeed: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
121
|
+
readonly iso: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
122
|
+
readonly sensorHeight: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
123
|
+
readonly focalLength: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
124
|
+
}>, {
|
|
125
|
+
readonly aperture: 5.6;
|
|
126
|
+
readonly shutterSpeed: number;
|
|
127
|
+
readonly iso: 100;
|
|
128
|
+
}];
|
|
129
|
+
/** The courtyard, its sun and sky; the camera is the caller's. */
|
|
130
|
+
export declare function spawnPostScene(world: World): void;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineComponent,
|
|
3
|
+
defineSystem,
|
|
4
|
+
quat,
|
|
5
|
+
Rng,
|
|
6
|
+
t,
|
|
7
|
+
Update
|
|
8
|
+
} from "@aethervtt/shard-core";
|
|
9
|
+
import { cube, plane, sphere } from "@aethervtt/shard-mesh";
|
|
10
|
+
import { definePlugin, Time } from "@aethervtt/shard-runtime";
|
|
11
|
+
import { Transform } from "@aethervtt/shard-transform";
|
|
12
|
+
import { MaterialAsset, Materials, Meshes } from "./assets.js";
|
|
13
|
+
import { PhysicalCamera } from "./camera.js";
|
|
14
|
+
import { DefaultEnvironment } from "./environment.js";
|
|
15
|
+
import { Mesh3d, MeshMaterial } from "./instances.js";
|
|
16
|
+
import { DirectionalLight } from "./lights.js";
|
|
17
|
+
import {
|
|
18
|
+
Antialiasing,
|
|
19
|
+
AutoExposure,
|
|
20
|
+
Bloom,
|
|
21
|
+
ColorGrading,
|
|
22
|
+
DepthOfField,
|
|
23
|
+
Fog,
|
|
24
|
+
MotionBlur,
|
|
25
|
+
Ssao,
|
|
26
|
+
Vignette
|
|
27
|
+
} from "./post.js";
|
|
28
|
+
const POST_EFFECTS = {
|
|
29
|
+
bloom: [Bloom, { intensity: 0.12 }],
|
|
30
|
+
exposure: [AutoExposure, { compensation: 0.5 }],
|
|
31
|
+
dof: [DepthOfField, { focusDistance: 9, maxBlur: 0.012 }],
|
|
32
|
+
"motion-blur": [MotionBlur, {}],
|
|
33
|
+
taa: [Antialiasing, { mode: "taa" }],
|
|
34
|
+
fxaa: [Antialiasing, { mode: "fxaa" }],
|
|
35
|
+
noaa: [Antialiasing, { mode: "none" }],
|
|
36
|
+
ssao: [Ssao, { radius: 0.6 }],
|
|
37
|
+
fog: [Fog, { density: 0.015, heightFalloff: 0.08, sunScattering: 0.6 }],
|
|
38
|
+
grading: [ColorGrading, { temperature: 0.1, saturation: 1.1, contrast: 1.05 }],
|
|
39
|
+
vignette: [Vignette, { intensity: 0.25 }]
|
|
40
|
+
};
|
|
41
|
+
const ALL_POST_EFFECTS = Object.keys(POST_EFFECTS).filter((n) => n !== "fxaa" && n !== "noaa");
|
|
42
|
+
function postEffects(names) {
|
|
43
|
+
return names.map((n) => POST_EFFECTS[n]).filter((e) => e !== void 0);
|
|
44
|
+
}
|
|
45
|
+
const PostSpin = defineComponent("render/PostSpin", {
|
|
46
|
+
radius: t.f32,
|
|
47
|
+
speed: t.f32,
|
|
48
|
+
height: t.f32
|
|
49
|
+
});
|
|
50
|
+
const postSpin = defineSystem({
|
|
51
|
+
name: "render/post-spin",
|
|
52
|
+
setup: (world) => ({ q: world.query({ with: [PostSpin, Transform] }) }),
|
|
53
|
+
run: ({ q }, world) => {
|
|
54
|
+
const time = world.resource(Time).elapsed;
|
|
55
|
+
for (const table of q.tables) {
|
|
56
|
+
const r = table.column(PostSpin, "radius");
|
|
57
|
+
const s = table.column(PostSpin, "speed");
|
|
58
|
+
const h = table.column(PostSpin, "height");
|
|
59
|
+
const tr = table.column(Transform, "translation");
|
|
60
|
+
for (let i = 0; i < table.count; i++) {
|
|
61
|
+
const a = time * s[i];
|
|
62
|
+
tr[i * 3] = Math.cos(a) * r[i];
|
|
63
|
+
tr[i * 3 + 1] = h[i];
|
|
64
|
+
tr[i * 3 + 2] = Math.sin(a) * r[i] - 4;
|
|
65
|
+
}
|
|
66
|
+
table.markChanged(Transform);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
const postScenePlugin = definePlugin({
|
|
71
|
+
name: "render/post-scene",
|
|
72
|
+
dependencies: ["render"],
|
|
73
|
+
provides: [PostSpin],
|
|
74
|
+
build(app) {
|
|
75
|
+
app.addSystems(Update, postSpin);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
const POST_EYE = [0, 2.2, 9];
|
|
79
|
+
const POST_TARGET = [0, 1.2, -2];
|
|
80
|
+
function postCamera(seconds) {
|
|
81
|
+
const [tx, ty, tz] = POST_TARGET;
|
|
82
|
+
const dx = POST_EYE[0] - tx;
|
|
83
|
+
const dy = POST_EYE[1] - ty;
|
|
84
|
+
const dz = POST_EYE[2] - tz;
|
|
85
|
+
const flat = Math.sqrt(dx * dx + dz * dz);
|
|
86
|
+
const yaw = Math.atan2(dx, dz) + Math.PI / 6 * Math.sin(seconds * 2 * Math.PI / 20);
|
|
87
|
+
const distance = flat + 1.5 * Math.sin(seconds * 2 * Math.PI / 13);
|
|
88
|
+
return {
|
|
89
|
+
eye: [tx + Math.sin(yaw) * distance, ty + dy, tz + Math.cos(yaw) * distance],
|
|
90
|
+
target: [tx, ty, tz]
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const POST_LENS = [
|
|
94
|
+
PhysicalCamera,
|
|
95
|
+
{ aperture: 5.6, shutterSpeed: 1 / 1e3, iso: 100 }
|
|
96
|
+
];
|
|
97
|
+
function spawnPostScene(world) {
|
|
98
|
+
world.resource(DefaultEnvironment).sky = { turbidity: 2.5 };
|
|
99
|
+
world.spawn(
|
|
100
|
+
[DirectionalLight, { illuminance: 1e5, shadows: true }],
|
|
101
|
+
[Transform, { rotation: quat.fromEuler([0, 0, 0, 1], -0.5, 0.9, 0) }]
|
|
102
|
+
);
|
|
103
|
+
const meshes = world.resource(Meshes);
|
|
104
|
+
const materials = world.resource(Materials);
|
|
105
|
+
const mat = (v) => materials.add(new MaterialAsset(v));
|
|
106
|
+
world.spawn(
|
|
107
|
+
[Mesh3d, { mesh: meshes.add(plane({ size: 400 })) }],
|
|
108
|
+
[MeshMaterial, { material: mat({ baseColor: [0.35, 0.33, 0.3, 1], roughness: 0.9 }) }],
|
|
109
|
+
Transform
|
|
110
|
+
);
|
|
111
|
+
const block = meshes.add(cube({ size: 1 }));
|
|
112
|
+
const ball = meshes.add(sphere({ radius: 0.5, segments: 40 }));
|
|
113
|
+
const rng = new Rng(4);
|
|
114
|
+
const stone = [
|
|
115
|
+
mat({ baseColor: [0.7, 0.68, 0.64, 1], roughness: 0.7 }),
|
|
116
|
+
mat({ baseColor: [0.55, 0.3, 0.22, 1], roughness: 0.8 }),
|
|
117
|
+
mat({ baseColor: [0.9, 0.8, 0.55, 1], metallic: 1, roughness: 0.3 })
|
|
118
|
+
];
|
|
119
|
+
for (let i = 0; i < 60; i++) {
|
|
120
|
+
const size = rng.range(0.6, 2.2);
|
|
121
|
+
world.spawn(
|
|
122
|
+
[Mesh3d, { mesh: i % 3 === 2 ? ball : block }],
|
|
123
|
+
[MeshMaterial, { material: stone[i % 3] }],
|
|
124
|
+
[
|
|
125
|
+
Transform,
|
|
126
|
+
{
|
|
127
|
+
translation: [rng.range(-14, 14), size / 2, rng.range(-30, 2)],
|
|
128
|
+
rotation: quat.fromEuler([0, 0, 0, 1], 0, rng.float() * 6, 0),
|
|
129
|
+
scale: [size, size * rng.range(0.6, 2.4), size]
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
const neon = (c) => mat({ baseColor: [0, 0, 0, 1], emissive: c, emissiveLuminance: 4e5 });
|
|
135
|
+
const panels = [neon([1, 0.2, 0.6, 1]), neon([0.2, 0.8, 1, 1]), neon([1, 0.7, 0.2, 1])];
|
|
136
|
+
for (let i = 0; i < 3; i++) {
|
|
137
|
+
world.spawn(
|
|
138
|
+
[Mesh3d, { mesh: block }],
|
|
139
|
+
[MeshMaterial, { material: panels[i] }],
|
|
140
|
+
[Transform, { translation: [(i - 1) * 3, 2.5, -5], scale: [2, 0.15, 0.1] }]
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
for (let i = 0; i < 4; i++) {
|
|
144
|
+
world.spawn(
|
|
145
|
+
[Mesh3d, { mesh: ball }],
|
|
146
|
+
[MeshMaterial, { material: stone[2] }],
|
|
147
|
+
[PostSpin, { radius: 3 + i, speed: 1.2 - i * 0.2, height: 0.6 + i * 0.3 }],
|
|
148
|
+
Transform
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
ALL_POST_EFFECTS,
|
|
154
|
+
POST_EFFECTS,
|
|
155
|
+
POST_EYE,
|
|
156
|
+
POST_LENS,
|
|
157
|
+
POST_TARGET,
|
|
158
|
+
PostSpin,
|
|
159
|
+
postCamera,
|
|
160
|
+
postEffects,
|
|
161
|
+
postScenePlugin,
|
|
162
|
+
postSpin,
|
|
163
|
+
spawnPostScene
|
|
164
|
+
};
|
package/dist/post-shaders.js
CHANGED
|
@@ -69,6 +69,7 @@ fn prepass_output(in: PrepassVertex, n: vec3f) -> PrepassOutput {
|
|
|
69
69
|
import shard::prepass::common::{ PrepassVertex, PrepassOutput, prepass_vertex, prepass_output, surface_of };
|
|
70
70
|
import shard::pbr::material::pbr_input;
|
|
71
71
|
import shard::pbr::standard::material;
|
|
72
|
+
@if(CUTAWAY) import shard::cutaway::cutaway_clip;
|
|
72
73
|
|
|
73
74
|
@vertex fn vs(
|
|
74
75
|
@builtin(instance_index) instance_index: u32,
|
|
@@ -84,12 +85,20 @@ import shard::pbr::standard::material;
|
|
|
84
85
|
|
|
85
86
|
/** Depth, the shaded normal (normal maps included), and velocity. */
|
|
86
87
|
@fragment fn fs(in: PrepassVertex) -> PrepassOutput {
|
|
88
|
+
@if(CUTAWAY) cutaway_clip(in.world_position, in.clip.xy, in.flags);
|
|
87
89
|
let p = pbr_input(surface_of(in));
|
|
88
90
|
@if(MASK) if (p.alpha < material.alphaCutoff) { discard; }
|
|
89
91
|
return prepass_output(in, p.normal);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Baseline (0064): depth alone, for what reads depth when the scene is multisampled. */
|
|
95
|
+
@if(BASELINE) @fragment fn fs_depth(in: PrepassVertex) {
|
|
96
|
+
@if(CUTAWAY) cutaway_clip(in.world_position, in.clip.xy, in.flags);
|
|
97
|
+
@if(MASK) if (pbr_input(surface_of(in)).alpha < material.alphaCutoff) { discard; }
|
|
90
98
|
}`,
|
|
91
99
|
"shard::prepass::plain": `
|
|
92
100
|
import shard::prepass::common::{ PrepassVertex, PrepassOutput, prepass_vertex, prepass_output };
|
|
101
|
+
@if(CUTAWAY) import shard::cutaway::cutaway_clip;
|
|
93
102
|
|
|
94
103
|
@vertex fn vs(
|
|
95
104
|
@builtin(instance_index) instance_index: u32,
|
|
@@ -105,13 +114,19 @@ import shard::prepass::common::{ PrepassVertex, PrepassOutput, prepass_vertex, p
|
|
|
105
114
|
|
|
106
115
|
/** Materials with their own shading: the geometric normal. */
|
|
107
116
|
@fragment fn fs(in: PrepassVertex) -> PrepassOutput {
|
|
117
|
+
@if(CUTAWAY) cutaway_clip(in.world_position, in.clip.xy, in.flags);
|
|
108
118
|
return prepass_output(in, normalize(in.world_normal));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Baseline (0064): depth alone. */
|
|
122
|
+
@if(BASELINE) @fragment fn fs_depth(in: PrepassVertex) {
|
|
123
|
+
@if(CUTAWAY) cutaway_clip(in.world_position, in.clip.xy, in.flags);
|
|
109
124
|
}`,
|
|
110
|
-
"shard::post::fog": `
|
|
125
|
+
"shard::post::fog::common": `
|
|
111
126
|
import shard::view::view;
|
|
112
127
|
import shard::pbr::lights::directional;
|
|
113
128
|
import shard::pbr::environment::env_irradiance;
|
|
114
|
-
import shard::post::common::
|
|
129
|
+
import shard::post::common::world_at;
|
|
115
130
|
|
|
116
131
|
struct FogParams {
|
|
117
132
|
color: vec4f,
|
|
@@ -121,31 +136,27 @@ struct FogParams {
|
|
|
121
136
|
sun: f32,
|
|
122
137
|
}
|
|
123
138
|
|
|
124
|
-
@group(1) @binding(0) var input: texture_2d<f32>;
|
|
125
|
-
@group(1) @binding(1) var depth_texture: texture_depth_2d;
|
|
126
|
-
@group(1) @binding(2) var<uniform> fog: FogParams;
|
|
127
|
-
|
|
128
139
|
const PI: f32 = 3.14159265;
|
|
129
140
|
|
|
130
141
|
/** Optical depth of exponential height fog along the ray from t0 to t1 (meters). */
|
|
131
|
-
fn optical_depth(y0: f32, dir_y: f32, t0: f32, t1: f32) -> f32 {
|
|
142
|
+
fn optical_depth(fog: FogParams, y0: f32, dir_y: f32, t0: f32, t1: f32) -> f32 {
|
|
132
143
|
let base = fog.density * exp(-fog.falloff * y0);
|
|
133
144
|
let k = fog.falloff * dir_y;
|
|
134
145
|
if (abs(k) < 1e-5) { return base * (t1 - t0); }
|
|
135
146
|
return base * (exp(-k * t0) - exp(-k * t1)) / k;
|
|
136
147
|
}
|
|
137
148
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Height fog over color \`c\` at a pixel (\`uv\`, its depth): the fog pass, and the atmosphere's
|
|
151
|
+
* aerial perspective when a view has both (one pass for the two).
|
|
152
|
+
*/
|
|
153
|
+
fn fog_apply(c: vec4f, uv: vec2f, depth: f32, fog: FogParams) -> vec4f {
|
|
143
154
|
// The background is far away, not infinitely: a floor keeps the ray finite.
|
|
144
155
|
let world = world_at(uv, max(depth, 1e-7));
|
|
145
156
|
let to = world - view.cameraPosition;
|
|
146
157
|
let dist = length(to);
|
|
147
158
|
let dir = to / max(dist, 1e-6);
|
|
148
|
-
let tau = optical_depth(view.cameraPosition.y, dir.y, fog.start, max(dist, fog.start));
|
|
159
|
+
let tau = optical_depth(fog, view.cameraPosition.y, dir.y, fog.start, max(dist, fog.start));
|
|
149
160
|
let transmittance = exp(-tau);
|
|
150
161
|
// In-scattering: the sky (environment or ambient), plus the sun through a forward-peaked phase.
|
|
151
162
|
var sky = view.ambient;
|
|
@@ -159,7 +170,22 @@ fn optical_depth(y0: f32, dir_y: f32, t0: f32, t1: f32) -> f32 {
|
|
|
159
170
|
sun = light.color * view.sunTransmittance[0].rgb * mix(1.0 / (4.0 * PI), hg, fog.sun);
|
|
160
171
|
}
|
|
161
172
|
let inscatter = fog.color.rgb * (sky + sun) * view.exposure;
|
|
162
|
-
|
|
173
|
+
let color = c.rgb * transmittance + inscatter * (1.0 - transmittance);
|
|
174
|
+
@if(!TRANSPARENT) return vec4f(color, c.a);
|
|
175
|
+
// Premultiplied (0052): the fog is a layer of its own opacity over what's behind it.
|
|
176
|
+
@if(TRANSPARENT) return vec4f(color, c.a * transmittance + (1.0 - transmittance));
|
|
177
|
+
}`,
|
|
178
|
+
"shard::post::fog": `
|
|
179
|
+
import shard::post::common::{ uv_of, post_out };
|
|
180
|
+
import shard::post::fog::common::{ FogParams, fog_apply };
|
|
181
|
+
|
|
182
|
+
@group(1) @binding(0) var input: texture_2d<f32>;
|
|
183
|
+
@group(1) @binding(1) var depth_texture: texture_depth_2d;
|
|
184
|
+
@group(1) @binding(2) var<uniform> fog: FogParams;
|
|
185
|
+
|
|
186
|
+
@fragment fn fs(@builtin(position) frag: vec4f) -> @location(0) vec4f {
|
|
187
|
+
let px = vec2i(frag.xy);
|
|
188
|
+
return post_out(fog_apply(textureLoad(input, px, 0), uv_of(frag.xy), textureLoad(depth_texture, px, 0), fog));
|
|
163
189
|
}`,
|
|
164
190
|
"shard::post::bloom": `
|
|
165
191
|
import shard::color::luminance;
|
|
@@ -174,7 +200,6 @@ struct BloomParams {
|
|
|
174
200
|
@group(0) @binding(0) var source: texture_2d<f32>;
|
|
175
201
|
@group(0) @binding(1) var source_sampler: sampler;
|
|
176
202
|
@group(0) @binding(2) var<uniform> params: BloomParams;
|
|
177
|
-
@group(0) @binding(3) var scene: texture_2d<f32>;
|
|
178
203
|
|
|
179
204
|
fn tap(uv: vec2f, dx: f32, dy: f32) -> vec3f {
|
|
180
205
|
return textureSampleLevel(source, source_sampler, uv + vec2f(dx, dy) * params.threshold.zw, 0.0).rgb;
|
|
@@ -225,27 +250,24 @@ struct FullscreenInput { @builtin(position) clip: vec4f, @location(0) uv: vec2f
|
|
|
225
250
|
+ (tap(in.uv, -1.0, 0.0) + tap(in.uv, 1.0, 0.0) + tap(in.uv, 0.0, -1.0) + tap(in.uv, 0.0, 1.0)) * 2.0
|
|
226
251
|
+ tap(in.uv, -1.0, -1.0) + tap(in.uv, 1.0, -1.0) + tap(in.uv, -1.0, 1.0) + tap(in.uv, 1.0, 1.0);
|
|
227
252
|
return vec4f(c / 16.0, 1.0);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/** The scene plus the glow: mixed (energy-conserving) without a threshold, added with one. */
|
|
231
|
-
@fragment fn composite(in: FullscreenInput) -> @location(0) vec4f {
|
|
232
|
-
let s = textureLoad(scene, vec2i(in.clip.xy), 0);
|
|
233
|
-
let glow = textureSampleLevel(source, source_sampler, in.uv, 0.0).rgb / params.mix.y;
|
|
234
|
-
let c = select(s.rgb + glow * params.mix.x, mix(s.rgb, glow, params.mix.x), params.mix.z > 0.5);
|
|
235
|
-
return vec4f(c, s.a);
|
|
236
253
|
}`,
|
|
237
254
|
"shard::post::exposure": `
|
|
238
255
|
import shard::view::view;
|
|
239
256
|
import shard::color::luminance;
|
|
257
|
+
@if(BLOOM) import shard::post::glow::add_glow;
|
|
240
258
|
|
|
241
259
|
struct MeterParams {
|
|
242
260
|
/** EV of bin 0, bins per EV, metering mode, 0. */
|
|
243
261
|
range: vec4f,
|
|
262
|
+
/** Bloom, which the tonemap adds to the image: intensity, level count, mix (1) or add (0). */
|
|
263
|
+
glow: vec4f,
|
|
244
264
|
}
|
|
245
265
|
|
|
246
266
|
@group(0) @binding(1) var input: texture_2d<f32>;
|
|
247
267
|
@group(0) @binding(2) var<storage, read_write> histogram: array<atomic<u32>, 256>;
|
|
248
268
|
@group(0) @binding(3) var<uniform> params: MeterParams;
|
|
269
|
+
@if(BLOOM) @group(0) @binding(4) var bloom: texture_2d<f32>;
|
|
270
|
+
@if(BLOOM) @group(0) @binding(5) var bloom_sampler: sampler;
|
|
249
271
|
|
|
250
272
|
var<workgroup> local: array<atomic<u32>, 256>;
|
|
251
273
|
|
|
@@ -261,7 +283,12 @@ fn meter(@builtin(global_invocation_id) id: vec3u, @builtin(local_invocation_ind
|
|
|
261
283
|
let size = textureDimensions(input);
|
|
262
284
|
let px = id.xy * 4u + 2u;
|
|
263
285
|
if (px.x < size.x && px.y < size.y) {
|
|
264
|
-
|
|
286
|
+
var c = textureLoad(input, vec2i(px), 0).rgb;
|
|
287
|
+
// What the tonemap shows: the image with bloom's glow over it.
|
|
288
|
+
@if(BLOOM) {
|
|
289
|
+
let uv = (vec2f(px) + 0.5) / vec2f(size);
|
|
290
|
+
c = add_glow(c, textureSampleLevel(bloom, bloom_sampler, uv, 0.0).rgb, params.glow);
|
|
291
|
+
}
|
|
265
292
|
let l = luminance(c) / max(view.exposure, 1e-20);
|
|
266
293
|
let ev = log2(max(l, 1e-10) * 8.0);
|
|
267
294
|
let bin = u32(clamp((ev - params.range.x) * params.range.y, 0.0, 255.0));
|
|
@@ -278,7 +305,7 @@ fn meter(@builtin(global_invocation_id) id: vec3u, @builtin(local_invocation_ind
|
|
|
278
305
|
}`,
|
|
279
306
|
"shard::post::dof": `
|
|
280
307
|
import shard::view::view;
|
|
281
|
-
import shard::post::common::{ uv_of, world_at, view_depth };
|
|
308
|
+
import shard::post::common::{ uv_of, world_at, view_depth, post_out };
|
|
282
309
|
import shard::color::luminance;
|
|
283
310
|
|
|
284
311
|
struct DofParams {
|
|
@@ -359,11 +386,11 @@ const TAPS: i32 = 48;
|
|
|
359
386
|
let sharp = textureLoad(input, px, 0);
|
|
360
387
|
let blurred = textureSampleLevel(half_texture, linear_sampler, uv_of(frag.xy), 0.0);
|
|
361
388
|
let t = smoothstep(0.5, 1.5, abs(coc_at(px)));
|
|
362
|
-
return vec4f(mix(sharp.rgb, blurred.rgb, t), sharp.a);
|
|
389
|
+
return post_out(vec4f(mix(sharp.rgb, blurred.rgb, t), sharp.a));
|
|
363
390
|
}`,
|
|
364
391
|
"shard::post::motion_blur": `
|
|
365
392
|
import shard::view::view;
|
|
366
|
-
import shard::post::common::{ uv_of, motion };
|
|
393
|
+
import shard::post::common::{ uv_of, motion, post_out };
|
|
367
394
|
|
|
368
395
|
struct MotionParams {
|
|
369
396
|
/** Shutter fraction of a frame, max blur in pixels, samples, 0. */
|
|
@@ -385,14 +412,14 @@ struct MotionParams {
|
|
|
385
412
|
let cap = motion_params.params.y;
|
|
386
413
|
if (pixels > cap) { v *= cap / pixels; }
|
|
387
414
|
let center = textureLoad(input, px, 0);
|
|
388
|
-
if (pixels < 0.5) { return center; }
|
|
415
|
+
if (pixels < 0.5) { return post_out(center); }
|
|
389
416
|
let n = i32(motion_params.params.z);
|
|
390
417
|
var sum = vec3f(0.0);
|
|
391
418
|
for (var i = 0; i < n; i++) {
|
|
392
419
|
let t = (f32(i) + 0.5) / f32(n) - 0.5;
|
|
393
420
|
sum += textureSampleLevel(input, linear_sampler, uv + v * t, 0.0).rgb;
|
|
394
421
|
}
|
|
395
|
-
return vec4f(sum / f32(n), center.a);
|
|
422
|
+
return post_out(vec4f(sum / f32(n), center.a));
|
|
396
423
|
}`,
|
|
397
424
|
"shard::post::taa": `
|
|
398
425
|
import shard::view::view;
|
|
@@ -411,11 +438,6 @@ struct TaaParams {
|
|
|
411
438
|
@group(0) @binding(5) var linear_sampler: sampler;
|
|
412
439
|
@group(0) @binding(6) var<uniform> taa: TaaParams;
|
|
413
440
|
|
|
414
|
-
struct TaaOutput {
|
|
415
|
-
@location(0) color: vec4f,
|
|
416
|
-
@location(1) history: vec4f,
|
|
417
|
-
}
|
|
418
|
-
|
|
419
441
|
fn rgb_to_ycocg(c: vec3f) -> vec3f {
|
|
420
442
|
return vec3f(0.25 * c.r + 0.5 * c.g + 0.25 * c.b, 0.5 * c.r - 0.5 * c.b, -0.25 * c.r + 0.5 * c.g - 0.25 * c.b);
|
|
421
443
|
}
|
|
@@ -451,7 +473,7 @@ fn sample_history(uv: vec2f) -> vec3f {
|
|
|
451
473
|
return max(c / w, vec3f(0.0));
|
|
452
474
|
}
|
|
453
475
|
|
|
454
|
-
@fragment fn fs(@builtin(position) frag: vec4f) ->
|
|
476
|
+
@fragment fn fs(@builtin(position) frag: vec4f) -> @location(0) vec4f {
|
|
455
477
|
let px = vec2i(frag.xy);
|
|
456
478
|
let size = vec2i(textureDimensions(input));
|
|
457
479
|
let uv = uv_of(frag.xy);
|
|
@@ -461,12 +483,19 @@ fn sample_history(uv: vec2f) -> vec3f {
|
|
|
461
483
|
var hi = vec3f(-1e20);
|
|
462
484
|
var nearest = 0.0;
|
|
463
485
|
var nearest_px = px;
|
|
486
|
+
@if(TRANSPARENT) var alpha_lo = 1.0;
|
|
487
|
+
@if(TRANSPARENT) var alpha_hi = 0.0;
|
|
464
488
|
for (var y = -1; y <= 1; y++) {
|
|
465
489
|
for (var x = -1; x <= 1; x++) {
|
|
466
490
|
let q = clamp(px + vec2i(x, y), vec2i(0), size - 1);
|
|
467
|
-
let
|
|
491
|
+
let texel = textureLoad(input, q, 0);
|
|
492
|
+
let c = rgb_to_ycocg(compress(texel.rgb));
|
|
468
493
|
lo = min(lo, c);
|
|
469
494
|
hi = max(hi, c);
|
|
495
|
+
@if(TRANSPARENT) {
|
|
496
|
+
alpha_lo = min(alpha_lo, texel.a);
|
|
497
|
+
alpha_hi = max(alpha_hi, texel.a);
|
|
498
|
+
}
|
|
470
499
|
let d = textureLoad(depth_texture, q, 0);
|
|
471
500
|
if (d > nearest) { nearest = d; nearest_px = q; }
|
|
472
501
|
}
|
|
@@ -486,9 +515,13 @@ fn sample_history(uv: vec2f) -> vec3f {
|
|
|
486
515
|
if (m > 1.0) { h = center + offset / m; }
|
|
487
516
|
let blended = mix(ycocg_to_rgb(h), current, alpha);
|
|
488
517
|
let color = expand(max(blended, vec3f(0.0)));
|
|
489
|
-
|
|
490
|
-
out
|
|
491
|
-
|
|
518
|
+
// The output is also next frame's history.
|
|
519
|
+
var out = vec4f(color, 1.0);
|
|
520
|
+
@if(TRANSPARENT) {
|
|
521
|
+
// Coverage resolves like color: history clamped to the neighborhood, blended at the same rate.
|
|
522
|
+
let past = clamp(textureSampleLevel(history, linear_sampler, prev_uv, 0.0).a, alpha_lo, alpha_hi);
|
|
523
|
+
out.a = mix(past, textureLoad(input, px, 0).a, alpha);
|
|
524
|
+
}
|
|
492
525
|
return out;
|
|
493
526
|
}`,
|
|
494
527
|
"shard::post::ssao": `
|
|
@@ -508,7 +541,6 @@ struct SsaoParams {
|
|
|
508
541
|
@group(0) @binding(1) var depth_texture: texture_depth_2d;
|
|
509
542
|
@group(0) @binding(2) var normal_texture: texture_2d<f32>;
|
|
510
543
|
@group(0) @binding(3) var<uniform> ssao: SsaoParams;
|
|
511
|
-
@group(0) @binding(4) var half_ao: texture_2d<f32>;
|
|
512
544
|
|
|
513
545
|
const PI: f32 = 3.14159265;
|
|
514
546
|
|
|
@@ -575,26 +607,6 @@ fn view_position(px: vec2i) -> vec3f {
|
|
|
575
607
|
}
|
|
576
608
|
let ao = clamp(visibility / f32(dirs), 0.0, 1.0);
|
|
577
609
|
return vec4f(pow(ao, ssao.params.y), -p.z, 0.0, 1.0);
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
/** Back to full resolution: the four nearest half-resolution texels, weighted by depth match. */
|
|
581
|
-
@fragment fn upsample(@builtin(position) frag: vec4f) -> @location(0) vec4f {
|
|
582
|
-
let px = vec2i(frag.xy);
|
|
583
|
-
let depth = textureLoad(depth_texture, px, 0);
|
|
584
|
-
if (depth <= 0.0) { return vec4f(1.0); }
|
|
585
|
-
let z = -view_position(px).z;
|
|
586
|
-
let half_size = vec2i(textureDimensions(half_ao));
|
|
587
|
-
let base = vec2i(floor((frag.xy - 1.0) * 0.5));
|
|
588
|
-
var sum = 0.0;
|
|
589
|
-
var weight = 0.0;
|
|
590
|
-
for (var k = 0; k < 4; k++) {
|
|
591
|
-
let q = clamp(base + vec2i(k & 1, k >> 1), vec2i(0), half_size - 1);
|
|
592
|
-
let s = textureLoad(half_ao, q, 0);
|
|
593
|
-
let w = 1.0 / (1e-3 + abs(s.y - z) / max(z, 1e-3));
|
|
594
|
-
sum += s.x * w;
|
|
595
|
-
weight += w;
|
|
596
|
-
}
|
|
597
|
-
return vec4f(sum / max(weight, 1e-6), 0.0, 0.0, 1.0);
|
|
598
610
|
}`
|
|
599
611
|
};
|
|
600
612
|
const FXAA_SHADERS = {
|
|
@@ -614,6 +626,13 @@ fn at(uv: vec2f, o: vec2f, texel: vec2f) -> vec3f {
|
|
|
614
626
|
return textureSampleLevel(input, input_sampler, uv + o * texel, 0.0).rgb;
|
|
615
627
|
}
|
|
616
628
|
|
|
629
|
+
/** The output at uv: opaque views write alpha 1; premultiplied ones carry it, rgb <= alpha (0052). */
|
|
630
|
+
fn result(uv: vec2f) -> vec4f {
|
|
631
|
+
let c = textureSampleLevel(input, input_sampler, uv, 0.0);
|
|
632
|
+
@if(!TRANSPARENT) return vec4f(c.rgb, 1.0);
|
|
633
|
+
@if(TRANSPARENT) return vec4f(c.rgb, max(c.a, max(c.r, max(c.g, c.b))));
|
|
634
|
+
}
|
|
635
|
+
|
|
617
636
|
/** FXAA 3.11 (Lottes), quality preset 12: local contrast, edge direction, end search, blend. */
|
|
618
637
|
@fragment fn fs(@builtin(position) frag: vec4f) -> @location(0) vec4f {
|
|
619
638
|
let size = vec2f(textureDimensions(input));
|
|
@@ -628,7 +647,10 @@ fn at(uv: vec2f, o: vec2f, texel: vec2f) -> vec3f {
|
|
|
628
647
|
let lo = min(m, min(min(n, s), min(e, w)));
|
|
629
648
|
let hi = max(m, max(max(n, s), max(e, w)));
|
|
630
649
|
let range = hi - lo;
|
|
631
|
-
if (range < max(0.0312, hi * 0.125)) {
|
|
650
|
+
if (range < max(0.0312, hi * 0.125)) {
|
|
651
|
+
@if(!TRANSPARENT) return vec4f(center, 1.0);
|
|
652
|
+
@if(TRANSPARENT) return result(uv);
|
|
653
|
+
}
|
|
632
654
|
let nw = luma(at(uv, vec2f(-1.0, -1.0), texel));
|
|
633
655
|
let ne = luma(at(uv, vec2f(1.0, -1.0), texel));
|
|
634
656
|
let sw = luma(at(uv, vec2f(-1.0, 1.0), texel));
|
|
@@ -689,7 +711,8 @@ fn at(uv: vec2f, o: vec2f, texel: vec2f) -> vec3f {
|
|
|
689
711
|
let blend = max(edge_blend, sub_blend);
|
|
690
712
|
var final_uv = uv;
|
|
691
713
|
if (horizontal) { final_uv.y += blend * step_len; } else { final_uv.x += blend * step_len; }
|
|
692
|
-
return vec4f(textureSampleLevel(input, input_sampler, final_uv, 0.0).rgb, 1.0);
|
|
714
|
+
@if(!TRANSPARENT) return vec4f(textureSampleLevel(input, input_sampler, final_uv, 0.0).rgb, 1.0);
|
|
715
|
+
@if(TRANSPARENT) return result(final_uv);
|
|
693
716
|
}`
|
|
694
717
|
};
|
|
695
718
|
export {
|
package/dist/post.js
CHANGED
|
@@ -403,9 +403,19 @@ function postAliases(cam, base) {
|
|
|
403
403
|
let flip = false;
|
|
404
404
|
for (const [name, bit] of HDR_CHAIN) {
|
|
405
405
|
if ((effects & bit) === 0) continue;
|
|
406
|
+
if (name === "fog" && effects & PostEffect.Atmosphere) continue;
|
|
407
|
+
if (name === "bloom") {
|
|
408
|
+
out["bloom-in"] = current;
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
out[`${name}-in`] = current;
|
|
412
|
+
if (name === "taa") {
|
|
413
|
+
out["taa-out"] = "taa-history";
|
|
414
|
+
current = "taa-history";
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
406
417
|
const next = flip ? "post-b" : "post-a";
|
|
407
418
|
flip = !flip;
|
|
408
|
-
out[`${name}-in`] = current;
|
|
409
419
|
out[`${name}-out`] = next;
|
|
410
420
|
current = next;
|
|
411
421
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Entity, type World } from '@aethervtt/shard-core';
|
|
2
|
+
/**
|
|
3
|
+
* Projects a world point to CSS pixels on `camera`'s target (origin top-left). Returns false when
|
|
4
|
+
* the point is behind the camera (`out` is left unchanged), or the camera hasn't rendered yet.
|
|
5
|
+
*/
|
|
6
|
+
export declare function worldToScreen(world: World, camera: Entity, point: ArrayLike<number>, out: {
|
|
7
|
+
[index: number]: number;
|
|
8
|
+
}): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* The world ray under CSS pixel (x, y) of `camera`'s target: its origin on the near plane and its
|
|
11
|
+
* unit direction. Orthographic rays are parallel, perspective ones start at the near plane.
|
|
12
|
+
*/
|
|
13
|
+
export declare function screenToRay(world: World, camera: Entity, x: number, y: number, outOrigin: {
|
|
14
|
+
[index: number]: number;
|
|
15
|
+
}, outDir: {
|
|
16
|
+
[index: number]: number;
|
|
17
|
+
}): void;
|
|
18
|
+
/**
|
|
19
|
+
* Where the ray under CSS pixel (x, y) meets the horizontal plane `y = planeY` (a floor, a ground
|
|
20
|
+
* band). Returns false when it doesn't (parallel, or behind the camera).
|
|
21
|
+
*/
|
|
22
|
+
export declare function screenToPlane(world: World, camera: Entity, x: number, y: number, planeY: number, out: {
|
|
23
|
+
[index: number]: number;
|
|
24
|
+
}): boolean;
|