reze-engine 0.63.0 → 0.65.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/engine.d.ts +226 -96
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1187 -732
- package/dist/graph/compile.d.ts.map +1 -1
- package/dist/graph/compile.js +4 -1
- package/dist/graph/registry.d.ts +3 -0
- package/dist/graph/registry.d.ts.map +1 -1
- package/dist/graph/registry.js +133 -0
- package/dist/graph/schema.d.ts +12 -0
- package/dist/graph/schema.d.ts.map +1 -1
- package/dist/graph/slots.d.ts.map +1 -1
- package/dist/graph/slots.js +15 -18
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/model.d.ts +10 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +15 -1
- package/dist/outline-normals.d.ts +12 -0
- package/dist/outline-normals.d.ts.map +1 -0
- package/dist/outline-normals.js +100 -0
- package/dist/pmx-loader.d.ts +3 -0
- package/dist/pmx-loader.d.ts.map +1 -1
- package/dist/pmx-loader.js +7 -2
- package/dist/shaders/lights.d.ts +3 -2
- package/dist/shaders/lights.d.ts.map +1 -1
- package/dist/shaders/lights.js +16 -3
- package/dist/shaders/materials/common.d.ts.map +1 -1
- package/dist/shaders/materials/common.js +65 -56
- package/dist/shaders/materials/nodes.d.ts +1 -1
- package/dist/shaders/materials/nodes.d.ts.map +1 -1
- package/dist/shaders/materials/nodes.js +210 -0
- package/dist/shaders/passes/bloom.d.ts +4 -3
- package/dist/shaders/passes/bloom.d.ts.map +1 -1
- package/dist/shaders/passes/bloom.js +58 -87
- package/dist/shaders/passes/composite.d.ts.map +1 -1
- package/dist/shaders/passes/composite.js +122 -64
- package/dist/shaders/passes/cull.d.ts +1 -1
- package/dist/shaders/passes/cull.d.ts.map +1 -1
- package/dist/shaders/passes/cull.js +7 -3
- package/dist/shaders/passes/ground.d.ts.map +1 -1
- package/dist/shaders/passes/ground.js +32 -55
- package/dist/shaders/passes/mirror.d.ts +18 -12
- package/dist/shaders/passes/mirror.d.ts.map +1 -1
- package/dist/shaders/passes/mirror.js +59 -13
- package/dist/shaders/passes/outline.d.ts.map +1 -1
- package/dist/shaders/passes/outline.js +71 -14
- package/dist/shaders/passes/particles.d.ts +1 -1
- package/dist/shaders/passes/particles.js +1 -1
- package/dist/shaders/scene-light-api.d.ts +18 -10
- package/dist/shaders/scene-light-api.d.ts.map +1 -1
- package/dist/shaders/scene-light-api.js +58 -46
- package/dist/shadow-cascades.d.ts +47 -20
- package/dist/shadow-cascades.d.ts.map +1 -1
- package/dist/shadow-cascades.js +36 -13
- package/dist/unity/character.d.ts +59 -0
- package/dist/unity/character.d.ts.map +1 -0
- package/dist/unity/character.js +120 -0
- package/dist/unity/globals.d.ts +93 -0
- package/dist/unity/globals.d.ts.map +1 -0
- package/dist/unity/globals.js +340 -0
- package/dist/unity/host.d.ts +152 -0
- package/dist/unity/host.d.ts.map +1 -0
- package/dist/unity/host.js +529 -0
- package/dist/unity/looks.d.ts +93 -0
- package/dist/unity/looks.d.ts.map +1 -0
- package/dist/unity/looks.js +312 -0
- package/dist/unity/skin.d.ts +9 -0
- package/dist/unity/skin.d.ts.map +1 -0
- package/dist/unity/skin.js +120 -0
- package/dist/unity/stage.d.ts +155 -0
- package/dist/unity/stage.d.ts.map +1 -0
- package/dist/unity/stage.js +410 -0
- package/dist/unity/state.d.ts +22 -0
- package/dist/unity/state.d.ts.map +1 -0
- package/dist/unity/state.js +170 -0
- package/package.json +1 -1
- package/src/engine.ts +1535 -964
- package/src/graph/compile.ts +3 -1
- package/src/graph/registry.ts +142 -0
- package/src/graph/schema.ts +9 -0
- package/src/graph/slots.ts +15 -18
- package/src/index.ts +12 -0
- package/src/model.ts +18 -1
- package/src/outline-normals.ts +110 -0
- package/src/pmx-loader.ts +8 -2
- package/src/shaders/lights.ts +16 -3
- package/src/shaders/materials/common.ts +65 -57
- package/src/shaders/materials/nodes.ts +210 -0
- package/src/shaders/passes/bloom.ts +59 -87
- package/src/shaders/passes/composite.ts +122 -64
- package/src/shaders/passes/cull.ts +7 -3
- package/src/shaders/passes/ground.ts +32 -55
- package/src/shaders/passes/mirror.ts +60 -13
- package/src/shaders/passes/outline.ts +71 -14
- package/src/shaders/passes/particles.ts +1 -1
- package/src/shaders/scene-light-api.ts +59 -46
- package/src/shadow-cascades.ts +61 -32
- package/src/unity/character.ts +169 -0
- package/src/unity/globals.ts +443 -0
- package/src/unity/host.ts +630 -0
- package/src/unity/looks.ts +391 -0
- package/src/unity/skin.ts +131 -0
- package/src/unity/stage.ts +604 -0
- package/src/unity/state.ts +172 -0
- package/src/shaders/agx-lut.ts +0 -30
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { sceneFsOutWgsl } from "../passes/scene-contract"
|
|
2
2
|
import { lightsApi } from "../lights"
|
|
3
3
|
import { SHADOW_CASCADES } from "../../shadow-cascades"
|
|
4
|
-
import {
|
|
4
|
+
import { sunShadowWgsl } from "../scene-light-api"
|
|
5
5
|
import { WORLD_AMBIENT_WGSL } from "../lights"
|
|
6
6
|
|
|
7
7
|
// Shared WGSL blocks concatenated by every material shader.
|
|
@@ -61,6 +61,9 @@ struct LightUniforms {
|
|
|
61
61
|
* amount 0 is off. Then the view-projection its map was drawn with. */
|
|
62
62
|
castShadow: vec4f,
|
|
63
63
|
castVP: mat4x4f,
|
|
64
|
+
/** The rendering layers each directional light reaches, slot by slot — a
|
|
65
|
+
* draw is lit by the first whose bits it shares (rzMainLight). */
|
|
66
|
+
dirLayers: vec4u,
|
|
64
67
|
};
|
|
65
68
|
|
|
66
69
|
// Per-material uniforms. Every material binds this layout even if it ignores fields;
|
|
@@ -148,13 +151,10 @@ struct LightVP { viewProj: array<mat4x4f, ${SHADOW_CASCADES.length}>, };
|
|
|
148
151
|
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
149
152
|
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
150
153
|
@group(0) @binding(2) var diffuseSampler: sampler;
|
|
151
|
-
|
|
154
|
+
// The sun's shadow atlas: every cascade in its own tile — see shadow-cascades.ts.
|
|
155
|
+
@group(0) @binding(3) var shadowAtlas: texture_depth_2d;
|
|
152
156
|
@group(0) @binding(4) var shadowSampler: sampler_comparison;
|
|
153
157
|
@group(0) @binding(5) var<uniform> lightVP: LightVP;
|
|
154
|
-
// The far cascade's map — coarser texels over a much wider box, so the stage
|
|
155
|
-
// keeps its shadows when the crisp near volume ends. Binding 7: 6 is the
|
|
156
|
-
// positional lights, 9 the BRDF LUT.
|
|
157
|
-
@group(0) @binding(7) var shadowMapFar: texture_depth_2d;
|
|
158
158
|
// The world's own sky, for a surface that REFLECTS it rather than merely being
|
|
159
159
|
// lit by it. Equirect, in the same layout and the same linear radiance the
|
|
160
160
|
// composite draws the dome from, and mipped — a rough surface reads a coarser
|
|
@@ -188,14 +188,39 @@ fn _rzCastShadow(wp: vec3f) -> f32 {
|
|
|
188
188
|
}
|
|
189
189
|
// binding(9) brdfLut is declared inside NODES_WGSL (nodes.ts).
|
|
190
190
|
@group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
191
|
+
// What this object's lighting is, apart from the scene's, as the game's
|
|
192
|
+
// pipeline gives each renderer its own:
|
|
193
|
+
// layers.x its rendering-layer bits, which decide the lights that reach it —
|
|
194
|
+
// a stage's daylight and the cast's key are two lights on two
|
|
195
|
+
// layers, as the game lights them;
|
|
196
|
+
// ambient.x 1 while it has an ambient of its own (Engine.setModelAmbient),
|
|
197
|
+
// 0 for the world's;
|
|
198
|
+
// sh that ambient's irradiance, folded as the world's (ibl.ts);
|
|
199
|
+
// fill.rgb its fill (Engine.setModelFill), added after its graph times its
|
|
200
|
+
// surface colour — zero for a model that has none.
|
|
201
|
+
struct ObjectLight { layers: vec4u, ambient: vec4f, sh: array<vec4f, 9>, fill: vec4f }
|
|
202
|
+
@group(1) @binding(1) var<uniform> objectLight: ObjectLight;
|
|
203
|
+
|
|
204
|
+
/** The light arriving at a surface of this object facing n: its own ambient
|
|
205
|
+
* where loaded data gave it one (a stage's probes, a character's), else the
|
|
206
|
+
* world's. The same polynomial rzWorldAmbient evaluates. */
|
|
207
|
+
fn rzObjectAmbient(n: vec3f) -> vec3f {
|
|
208
|
+
if (objectLight.ambient.x < 0.5) { return rzWorldAmbient(n); }
|
|
209
|
+
let s = objectLight.sh;
|
|
210
|
+
let c = s[0].xyz + s[1].xyz * n.y + s[2].xyz * n.z + s[3].xyz * n.x
|
|
211
|
+
+ s[4].xyz * (n.x * n.y) + s[5].xyz * (n.y * n.z) + s[6].xyz * (3.0 * n.z * n.z - 1.0)
|
|
212
|
+
+ s[7].xyz * (n.x * n.z) + s[8].xyz * (n.x * n.x - n.y * n.y);
|
|
213
|
+
return max(c, vec3f(0.0));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** The directional light slot that lights this draw: the first whose layers
|
|
217
|
+
* it shares, else the sun's. */
|
|
218
|
+
fn rzMainLight() -> u32 {
|
|
219
|
+
for (var k = 0u; k < 4u; k++) {
|
|
220
|
+
if ((light.dirLayers[k] & objectLight.layers.x) != 0u) { return k; }
|
|
221
|
+
}
|
|
222
|
+
return 0u;
|
|
223
|
+
}
|
|
199
224
|
@group(2) @binding(0) var diffuseTexture: texture_2d<f32>;
|
|
200
225
|
@group(2) @binding(1) var<uniform> material: MaterialUniforms;
|
|
201
226
|
// Reserved for future sphere/toon graph nodes; graphs that don't read them get the
|
|
@@ -226,58 +251,41 @@ fn safe_normal(nIn: vec3f) -> vec3f {
|
|
|
226
251
|
`;
|
|
227
252
|
|
|
228
253
|
// ─── Shadow sampler (3×3 PCF, cascade-selected) ─────────────────────
|
|
229
|
-
// Normal-bias 0.08, depth-bias 0.001 NDC; the
|
|
230
|
-
// shared with
|
|
231
|
-
// from SHADOW_CASCADES so they cannot go stale the way the hardcoded 1/2048
|
|
232
|
-
// once did (PCF taps landed TWO texels apart after the map grew to 4096,
|
|
233
|
-
// quantizing self-shadow edges into crawling gray squares).
|
|
254
|
+
// Normal-bias 0.08, depth-bias 0.001 NDC; the lookup is sunShadowWgsl in
|
|
255
|
+
// scene-light-api.ts, shared with the ground and effects.
|
|
234
256
|
//
|
|
235
|
-
// Selection: the
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
// shadowing any stage deeper than the box.
|
|
241
|
-
|
|
257
|
+
// Selection: the first cascade whose box covers the point — with a margin that
|
|
258
|
+
// keeps the whole PCF kernel inside its tile — else LIT. Lit is a fix, not
|
|
259
|
+
// merely a default: past every cascade there is no occlusion information, and
|
|
260
|
+
// comparing against unrelated depths would silently shadow any stage deeper
|
|
261
|
+
// than the boxes.
|
|
242
262
|
|
|
243
263
|
const SAMPLE_SHADOW_WGSL = /* wgsl */ `
|
|
264
|
+
${sunShadowWgsl("_rzSun", "shadowAtlas", "shadowSampler", "lightVP.viewProj")}
|
|
244
265
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
266
|
+
/** The sun's cast shadow alone, without sampleShadow's facing test: what a
|
|
267
|
+
* character's ramp is min'd with (its own N·L carries the terminator). */
|
|
268
|
+
fn rzSunOcclusion(worldPos: vec3f, n: vec3f) -> f32 {
|
|
269
|
+
let castAmt = light.lights[0].direction.w;
|
|
270
|
+
if (castAmt <= 0.0) { return 1.0; }
|
|
271
|
+
let a = _rzSunLocate(worldPos + n * 0.08);
|
|
272
|
+
if (a.w < 0.0) { return 1.0; }
|
|
273
|
+
return mix(1.0, _rzSunTaps(a), castAmt);
|
|
251
274
|
}
|
|
252
275
|
|
|
253
276
|
fn sampleShadow(worldPos: vec3f, n: vec3f) -> f32 {
|
|
254
277
|
// THE SCENE'S ONE SHADOW SWITCH, and it lives on the sun because the sun is
|
|
255
|
-
// the only thing that casts.
|
|
256
|
-
//
|
|
257
|
-
// is, on the ground and on the cast alike. Gated here rather than by not
|
|
258
|
-
// rendering the map: the map is also what the ground catcher reads, and a host
|
|
259
|
-
// that wants a figure lit with no shadow still wants everything else about the
|
|
260
|
-
// frame unchanged.
|
|
261
|
-
//
|
|
262
|
-
// Lit is the answer when it is off. Returning 0 would be "fully shadowed",
|
|
263
|
-
// which is the opposite of what a disabled shadow means.
|
|
278
|
+
// the only thing that casts. Lit is the answer when it is off: returning 0
|
|
279
|
+
// would be "fully shadowed", the opposite of what a disabled shadow means.
|
|
264
280
|
let castAmt = light.lights[0].direction.w;
|
|
265
281
|
if (castAmt <= 0.0) { return 1.0; }
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
let c1 = lightVP.viewProj[1] * vec4f(biasedPos, 1.0);
|
|
274
|
-
let n1 = c1.xyz / max(c1.w, 1e-6);
|
|
275
|
-
if (all(abs(n1.xy) < vec2f(0.98)) && n1.z > 0.0 && n1.z < 1.0) {
|
|
276
|
-
return mix(1.0, sampleShadowFar(n1), castAmt);
|
|
277
|
-
}
|
|
278
|
-
// Outside every cascade there is no occlusion information; lit is the only
|
|
279
|
-
// honest answer.
|
|
280
|
-
return 1.0;
|
|
282
|
+
// A surface turned from the sun is in its own shade — when the sun is what
|
|
283
|
+
// keys it. Under a key of its own layer the atlas is only the occluders'
|
|
284
|
+
// shadow, and the key's N·L decides its terminator.
|
|
285
|
+
if (rzMainLight() == 0u && dot(n, -light.lights[0].direction.xyz) <= 0.0) { return 0.0; }
|
|
286
|
+
let a = _rzSunLocate(worldPos + n * 0.08);
|
|
287
|
+
if (a.w < 0.0) { return 1.0; }
|
|
288
|
+
return mix(1.0, _rzSunTaps(a), castAmt);
|
|
281
289
|
}
|
|
282
290
|
|
|
283
291
|
`;
|
|
@@ -499,4 +507,4 @@ fn rz_dissolve_threshold(restPos: vec3f) -> f32 {
|
|
|
499
507
|
// The FSOut struct is NOT in here any more — see commonFsOutWgsl above. Every
|
|
500
508
|
// consumer of this constant appends it immediately, which is where it was.
|
|
501
509
|
export const COMMON_MATERIAL_PRELUDE_WGSL =
|
|
502
|
-
COMMON_BINDINGS_WGSL + WORLD_AMBIENT_WGSL + lightsApi(0, 6) + SAMPLE_SHADOW_WGSL + COMMON_VS_WGSL
|
|
510
|
+
COMMON_BINDINGS_WGSL + WORLD_AMBIENT_WGSL + lightsApi(0, 6, "objectLight.layers.x") + SAMPLE_SHADOW_WGSL + COMMON_VS_WGSL
|
|
@@ -348,6 +348,215 @@ fn rzWorldSpecularLod(dir: vec3f, roughness: f32, unity: f32) -> vec3f {
|
|
|
348
348
|
return textureSampleLevel(worldEnvTexture, diffuseSampler, vec2f(u, v), lod).rgb * light.ambientColor.w;
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
+
// ── Aether Gazer's character shading, step by step ──
|
|
352
|
+
// From the decompiled ForwardBase pass of SimPipeline/Character/Debug. Its
|
|
353
|
+
// spaces are the game's: a view space looking down -Z (Unity's camera space),
|
|
354
|
+
// which the engine's +Z-forward view is turned into by negating z.
|
|
355
|
+
|
|
356
|
+
/** CharacterEffect's key light, in sim_TowardMatrix's frame: the camera's
|
|
357
|
+
* backward direction flattened to the ground (d), up, and up × d. The
|
|
358
|
+
* direction points toward the light. */
|
|
359
|
+
fn ag_key_light(inclination: f32, azimuth: f32) -> vec3f {
|
|
360
|
+
let f = vec3f(camera.view[0].z, camera.view[1].z, camera.view[2].z);
|
|
361
|
+
let flat = vec3f(-f.x, 0.0, -f.z);
|
|
362
|
+
let len = length(flat);
|
|
363
|
+
let d = select(vec3f(0.0, 0.0, 1.0), flat / max(len, 1e-5), len > 1e-5);
|
|
364
|
+
let incl = radians(inclination);
|
|
365
|
+
let az = radians(-azimuth);
|
|
366
|
+
let c = cos(incl);
|
|
367
|
+
let k = vec3f(sin(az) * -c, sin(incl), cos(az) * c);
|
|
368
|
+
return normalize(vec3f(d.z, 0.0, -d.x) * k.x + vec3f(0.0, 1.0, 0.0) * k.y + d * k.z);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/** u on the shadow ramp: N·L, darkened by the mask's occlusion
|
|
372
|
+
* (1 - min((1 - ao) * scale, 1)), and min'd with the sun's cast shadow. */
|
|
373
|
+
fn ag_shade_term(n: vec3f, l: vec3f, ao: f32, aoScale: f32, receive: f32, wp: vec3f) -> f32 {
|
|
374
|
+
let ndl = max(dot(n, l), 0.0);
|
|
375
|
+
var t = (1.0 - min((1.0 - ao) * aoScale, 1.0)) * ndl;
|
|
376
|
+
if (receive > 0.5) { t = min(t, rzSunOcclusion(wp, n)); }
|
|
377
|
+
return t;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** A ramp image's uv, kept off the image's edge texels so a repeating sampler
|
|
381
|
+
* does not blend the far end in. The row is Unity's v, which runs up from the
|
|
382
|
+
* image's bottom row; the image is uploaded top row first. */
|
|
383
|
+
fn ag_ramp_uv(value: f32, row: f32) -> vec3f {
|
|
384
|
+
return vec3f(clamp(value, 0.002, 0.998), clamp(1.0 - row, 0.002, 0.998), 0.0);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** The view-space reflection, as the game builds it: the view ray mirrored on
|
|
388
|
+
* the normal, into camera space, halved and pushed half a unit toward the
|
|
389
|
+
* eye, normalised. xy*0.5+0.5 is the sphere image's uv (xyz here, z unused),
|
|
390
|
+
* and z how much it faces the eye (w). */
|
|
391
|
+
fn ag_view_reflection(n: vec3f, v: vec3f) -> vec4f {
|
|
392
|
+
let r = reflect(-v, n);
|
|
393
|
+
let rv = vec3f(
|
|
394
|
+
camera.view[0].x * r.x + camera.view[1].x * r.y + camera.view[2].x * r.z,
|
|
395
|
+
camera.view[0].y * r.x + camera.view[1].y * r.y + camera.view[2].y * r.z,
|
|
396
|
+
-(camera.view[0].z * r.x + camera.view[1].z * r.y + camera.view[2].z * r.z),
|
|
397
|
+
);
|
|
398
|
+
let m = normalize(rv * 0.5 + vec3f(0.0, 0.0, 0.5));
|
|
399
|
+
// A sphere image is addressed as Unity reads it, v up: flipped for the
|
|
400
|
+
// top-row-first upload.
|
|
401
|
+
return vec4f(m.x * 0.5 + 0.5, 0.5 - m.y * 0.5, 0.0, m.z);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/** The matcap's lift: max(mask * (matcap * strength - 1) + 1, 1). */
|
|
405
|
+
fn ag_matcap(color: vec3f, matcap: vec3f, strength: f32, mask: f32) -> vec3f {
|
|
406
|
+
return color * max(mask * (matcap * strength - vec3f(1.0)) + vec3f(1.0), vec3f(1.0));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** The rim: smoothstep'd to the silhouette by facing, limited to the two rim
|
|
410
|
+
* directions (CharacterEffect: (cos a·s, sin a·s, z), a = azimuth·π,
|
|
411
|
+
* s = sqrt(1 - z²)), laid on as a colour dodge: min(max(c, 2), c / (1 - 2·rim)). */
|
|
412
|
+
fn ag_rim(color: vec3f, facing: f32, uvw: vec3f, rimColor: vec3f, threshold: f32, fade: f32, range: f32,
|
|
413
|
+
inclination: f32, azimuth1: f32, azimuth2: f32, mask: f32) -> vec3f {
|
|
414
|
+
let m = vec3f(uvw.x * 2.0 - 1.0, 1.0 - uvw.y * 2.0, facing);
|
|
415
|
+
let t = saturate((facing - (threshold + fade)) / (threshold - (threshold + fade)));
|
|
416
|
+
let edge = t * t * (3.0 - 2.0 * t);
|
|
417
|
+
let s = sqrt(max(1.0 - inclination * inclination, 0.0));
|
|
418
|
+
let a1 = azimuth1 * 3.1415927;
|
|
419
|
+
let a2 = azimuth2 * 3.1415927;
|
|
420
|
+
let d1 = vec3f(cos(a1) * s, sin(a1) * s, inclination);
|
|
421
|
+
let d2 = vec3f(cos(a2) * s, sin(a2) * s, inclination);
|
|
422
|
+
let along = saturate(max(dot(m, d1), dot(m, d2)) + range);
|
|
423
|
+
let rim = min(edge, along) * rimColor;
|
|
424
|
+
let f = min(rim * mask * 2.0, vec3f(0.99));
|
|
425
|
+
return min(max(color, vec3f(2.0)), color / (vec3f(1.0) - f));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** The fill: inner to outer by pow(1 - facing², softness + 2), laid over by
|
|
429
|
+
* its amount. */
|
|
430
|
+
fn ag_fill(color: vec3f, facing: f32, inner: vec3f, outer: vec3f, amount: f32, softness: f32) -> vec3f {
|
|
431
|
+
let k = pow(max(1.0 - facing * facing, 0.0), softness + 2.0);
|
|
432
|
+
return mix(color, mix(inner, outer, k), amount);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// ── Aether Gazer's PBR character shading (SimPipeline/Character/PBR/Uber) ──
|
|
436
|
+
// The ForwardBase pass of the game's current character shader, decompiled and
|
|
437
|
+
// written back out step by step. Its ramp image carries the whole tone of the
|
|
438
|
+
// look in rows: the diffuse ramp in the row the material picks, the specular
|
|
439
|
+
// ramp at 0.95, the environment's floor at 0.85.
|
|
440
|
+
|
|
441
|
+
/** A ramp image on a group slot, read at a computed coordinate — mip 0 only,
|
|
442
|
+
* as the game reads it, and so legal wherever the shading branches. */
|
|
443
|
+
fn _ag_ramp(slot: u32, uv: vec2f) -> vec4f {
|
|
444
|
+
// Unity's v runs up from the bottom row; the image is uploaded top row first.
|
|
445
|
+
let c = clamp(vec2f(uv.x, 1.0 - uv.y), vec2f(0.002), vec2f(0.998));
|
|
446
|
+
switch slot {
|
|
447
|
+
case 1u: { return textureSampleLevel(groupTexture1, diffuseSampler, c, 0.0); }
|
|
448
|
+
case 2u: { return textureSampleLevel(groupTexture2, diffuseSampler, c, 0.0); }
|
|
449
|
+
case 3u: { return textureSampleLevel(groupTexture3, diffuseSampler, c, 0.0); }
|
|
450
|
+
default: { return textureSampleLevel(groupTexture0, diffuseSampler, c, 0.0); }
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* The whole lighting of one Uber material under the character's key light:
|
|
456
|
+
* diffuse = base·(1−metal) · ramp(min(shadow, occlusion·N·L), row)
|
|
457
|
+
* specular = GGX through the ramp's 0.95 row, × F0, plus the rim, × the
|
|
458
|
+
* shade term
|
|
459
|
+
* ambient = the object's SH at n × diffuse
|
|
460
|
+
* reflection = the scene's environment along the reflected view, at a
|
|
461
|
+
* roughness mip, × the mobile environment-BRDF fit
|
|
462
|
+
* (F0·A + ramp(N·L·B, 0.85))
|
|
463
|
+
* Lamps and fog are the engine's, laid on after.
|
|
464
|
+
*/
|
|
465
|
+
fn ag_uber(slot: u32, base: vec3f, metallic: f32, roughness: f32, occlusion: f32, n: vec3f, l: vec3f,
|
|
466
|
+
row: f32, receive: f32, rimMask: f32, rimMid: f32, rimWidth: f32, rimTint: vec3f,
|
|
467
|
+
rimIntensity: f32, rimAlbedo: f32, rimInLight: f32, emissive: vec3f, reflection: f32,
|
|
468
|
+
shade: f32, face: f32, wp: vec3f, v: vec3f) -> vec3f {
|
|
469
|
+
let diffuse = base * (1.0 - metallic);
|
|
470
|
+
let f0 = mix(vec3f(0.04), base, metallic);
|
|
471
|
+
let pr = clamp(roughness, 0.0, 1.0);
|
|
472
|
+
let r = max(pr * pr, 0.0078125);
|
|
473
|
+
let a2 = r * r;
|
|
474
|
+
let h = normalize(v + l);
|
|
475
|
+
let ndl = saturate(dot(n, l));
|
|
476
|
+
let ndv = saturate(dot(v, n));
|
|
477
|
+
let ndh = saturate(dot(n, h));
|
|
478
|
+
let vdh = saturate(dot(v, h));
|
|
479
|
+
let ldh = saturate(dot(l, h));
|
|
480
|
+
|
|
481
|
+
// where on the ramp: N·L under occlusion, and the sun's cast shadow
|
|
482
|
+
var shadow = 1.0;
|
|
483
|
+
if (receive > 0.5) { shadow = rzSunOcclusion(wp, n); }
|
|
484
|
+
// A face replaces N·L with its SDF shade; anything else reads N·L.
|
|
485
|
+
let u = min(shadow, occlusion * select(ndl, shade, shade >= 0.0));
|
|
486
|
+
let ramp = _ag_ramp(slot, vec2f(u, row)).rgb;
|
|
487
|
+
|
|
488
|
+
// GGX, as the pass writes it, then through the ramp's specular row
|
|
489
|
+
let dd = ndh * ndh * (a2 - 1.0) + 1.0;
|
|
490
|
+
let D = select(a2 / (dd * dd), 1.0, dd * dd == a2);
|
|
491
|
+
let vis = min(0.5 / (ndl * (ndv * (1.0 - r) + r) + ndv * (ndl * (1.0 - r) + r)), 1.0);
|
|
492
|
+
let vis2 = min(0.5 / (ldh * (vdh * (1.0 - r) + r) + vdh * (ldh * (1.0 - r) + r)), 1.0);
|
|
493
|
+
let x = saturate(D * vis * a2 / vis2);
|
|
494
|
+
let specRamp = _ag_ramp(slot, vec2f(x, 0.95)).rgb;
|
|
495
|
+
let spec = clamp(vis * specRamp * min(1.0 / a2, 2048.0), vec3f(0.0), vec3f(10.0));
|
|
496
|
+
|
|
497
|
+
// the rim: toward the silhouette on the lit side, tinted toward the albedo
|
|
498
|
+
let lit = select(0.0, 1.0, u > 0.001);
|
|
499
|
+
let t = saturate(((ndl * 0.25 + 0.75) * (1.0 - ndv) * lit - (rimMid - rimWidth)) / max(2.0 * rimWidth, 1e-4));
|
|
500
|
+
let edge = t * t * (3.0 - 2.0 * t);
|
|
501
|
+
let tint = min(rimTint * rimIntensity * mix(vec3f(1.0), base, rimAlbedo) * 2.0 * rimMask, vec3f(0.99));
|
|
502
|
+
let rim = tint * edge * (1.0 + rimInLight * (u - 1.0));
|
|
503
|
+
|
|
504
|
+
// A face (FACE_MODE) takes no GGX: its light is the ramp, the rim and its
|
|
505
|
+
// optional SDF highlight (off unless _FaceSpecular).
|
|
506
|
+
let isFace = face > 0.5;
|
|
507
|
+
let direct = (select(spec * f0, vec3f(0.0), isFace) + rim) * u + diffuse * ramp;
|
|
508
|
+
|
|
509
|
+
// the environment, with the game's fit of the split-sum
|
|
510
|
+
let a004 = min((1.0 - pr) * (1.0 - pr), exp2(-9.28 * ndv)) * (1.0 - pr) + (0.0425 - 0.0275 * pr);
|
|
511
|
+
let A = -a004 * 1.04 + (1.04 - 0.572 * pr);
|
|
512
|
+
let ns = saturate(dot(n, mix(reflect(-l, n), h, 0.8)));
|
|
513
|
+
let B = _ag_ramp(slot, vec2f(ns * (a004 * 1.04 + (0.022 * pr - 0.04)), 0.85)).r;
|
|
514
|
+
let env = rzWorldSpecularLod(reflect(-v, n), pr, 1.0) * reflection;
|
|
515
|
+
|
|
516
|
+
// ... and the environment at a flat 0.0157 rather than through the BRDF fit.
|
|
517
|
+
let envTerm = select((f0 * A + B) * env, env * 0.0157, isFace);
|
|
518
|
+
return emissive + rzObjectAmbient(n) * diffuse + envTerm + direct;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* The face's shade from its SDF image (Uber FACE_MODE, _SDFType 0), in place of
|
|
524
|
+
* N·L: the key light taken into the face's frame and flattened, the field read
|
|
525
|
+
* at the uv and at its mirror, the side the light is on choosing which, both
|
|
526
|
+
* compared against the light's front-to-back angle with _BlendSmoothness, then
|
|
527
|
+
* softened by how level the light is and a half-Lambert. The face frame is the
|
|
528
|
+
* head bone's, turned to the game's convention (x to the face's right, z out of
|
|
529
|
+
* it). An SDF is painted in the face's uvs, so it belongs to models that share
|
|
530
|
+
* the game's face layout; a white slot reads as a face lit evenly until the
|
|
531
|
+
* light goes behind it.
|
|
532
|
+
*/
|
|
533
|
+
fn ag_face_sdf(slot: u32, uv: vec2f, l: vec3f, n: vec3f, smoothness: f32, invert: f32) -> f32 {
|
|
534
|
+
let hb = skinMats[u32(max(material.headBoneIndex, 0.0))];
|
|
535
|
+
let fx = -normalize(hb[0].xyz);
|
|
536
|
+
let fy = normalize(hb[1].xyz);
|
|
537
|
+
let fz = -normalize(hb[2].xyz);
|
|
538
|
+
let lf = normalize(vec3f(dot(l, fx), dot(l, fy), dot(l, fz)));
|
|
539
|
+
let fl = lf.xz * inverseSqrt(max(dot(lf.xz, lf.xz), 1.1754944e-38));
|
|
540
|
+
let u0 = mix(uv.x, 1.0 - uv.x, invert);
|
|
541
|
+
let s1 = _ag_ramp(slot, vec2f(u0, 1.0 - uv.y));
|
|
542
|
+
let s2 = _ag_ramp(slot, vec2f(1.0 - u0, 1.0 - uv.y));
|
|
543
|
+
let left = fl.x < 0.0;
|
|
544
|
+
let a = select(s2.x, s1.x, left);
|
|
545
|
+
let b = select(s1.x, s2.x, left);
|
|
546
|
+
let flag = select(0.0, 1.0, s1.w > 0.5);
|
|
547
|
+
let k = max(smoothness, 0.001);
|
|
548
|
+
let th = fl.y * -0.5 + 0.5;
|
|
549
|
+
let w = 1.0 - th;
|
|
550
|
+
var m1 = max(saturate((a - th) / k + 0.5), saturate((b - w - 1.0) / k + 0.5));
|
|
551
|
+
m1 = min(m1, min(saturate((2.0 - a - w) / k + 0.5), saturate((2.0 - b - w) / k + 0.5)));
|
|
552
|
+
var m2 = max(saturate((b - th - 1.0) / k + 0.5), saturate((a - w) / k + 0.5));
|
|
553
|
+
m2 = min(m2, min(saturate((a + w) / k + 0.5), saturate((b + w) / k + 0.5)));
|
|
554
|
+
var r = flag * (m1 - m2) + m2;
|
|
555
|
+
r *= (1.0 - abs(lf.z)) * 0.5 + 0.5;
|
|
556
|
+
r *= saturate(dot(n, l)) * 0.5 + 0.5;
|
|
557
|
+
return r;
|
|
558
|
+
}
|
|
559
|
+
|
|
351
560
|
fn group_tex0(uv: vec2f) -> vec4f { return textureSample(groupTexture0, diffuseSampler, uv); }
|
|
352
561
|
fn group_tex1(uv: vec2f) -> vec4f { return textureSample(groupTexture1, diffuseSampler, uv); }
|
|
353
562
|
fn group_tex2(uv: vec2f) -> vec4f { return textureSample(groupTexture2, diffuseSampler, uv); }
|
|
@@ -1056,6 +1265,7 @@ struct _RzLampPair { d: vec3f, s: vec3f };
|
|
|
1056
1265
|
|
|
1057
1266
|
fn _rzLampOne(i: u32, p: vec3f, n: vec3f, v: vec3f, ndv: f32, roughness: f32, unity: f32) -> _RzLampPair {
|
|
1058
1267
|
var out = _RzLampPair(vec3f(0.0), vec3f(0.0));
|
|
1268
|
+
if (!_rzLightReaches(i)) { return out; }
|
|
1059
1269
|
let pr = _rzLightVec(i, 0u);
|
|
1060
1270
|
let d = pr.xyz - p;
|
|
1061
1271
|
let dist = length(d);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// Bloom — Aether Gazer's own chain (AGSimPostFX.Bloom, URP-style): a soft-knee
|
|
2
|
+
// prefilter to half size, a separable Gaussian down to a few pixels, then back
|
|
3
|
+
// up level by level, each blend leaning toward the coarser level by `scatter`.
|
|
4
|
+
// The composite adds the result × tint × intensity before the view transform.
|
|
5
5
|
|
|
6
6
|
const FULLSCREEN_VS = /* wgsl */ `
|
|
7
7
|
@vertex fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
|
|
@@ -11,114 +11,86 @@ const FULLSCREEN_VS = /* wgsl */ `
|
|
|
11
11
|
}
|
|
12
12
|
`
|
|
13
13
|
|
|
14
|
-
// Full-res HDR → half-res.
|
|
15
|
-
|
|
14
|
+
// Full-res HDR → half-res. The game takes ONE bilinear sample at the half-res
|
|
15
|
+
// pixel's centre, which is the 2×2 box of the block under it; here the four
|
|
16
|
+
// texels are loaded so each can be un-premultiplied (the scene target is
|
|
17
|
+
// premultiplied; its alpha lives in the aux target's .g) and gated by the bloom
|
|
18
|
+
// mask (.r) first.
|
|
19
|
+
export const BLOOM_PREFILTER_SHADER_WGSL = `${FULLSCREEN_VS}
|
|
16
20
|
@group(0) @binding(0) var hdrTex: texture_2d<f32>;
|
|
17
|
-
@group(0) @binding(1) var<uniform>
|
|
21
|
+
@group(0) @binding(1) var<uniform> params: vec4<f32>; // threshold, knee, clamp, scatter
|
|
18
22
|
@group(0) @binding(2) var maskTex: texture_2d<f32>;
|
|
19
23
|
|
|
20
|
-
fn
|
|
21
|
-
return dot(max(c, vec3f(0.0)), vec3f(0.2126, 0.7152, 0.0722));
|
|
22
|
-
}
|
|
23
|
-
fn fetch(c: vec2<i32>, clampV: f32) -> vec3f {
|
|
24
|
+
fn fetch(c: vec2<i32>) -> vec3f {
|
|
24
25
|
let d = vec2<i32>(textureDimensions(hdrTex));
|
|
25
26
|
let cc = clamp(c, vec2<i32>(0), d - vec2<i32>(1));
|
|
26
|
-
let s = textureLoad(hdrTex, cc, 0);
|
|
27
|
-
// hdrTex is rg11b10ufloat (no alpha channel). Alpha lives in maskTex.g, written
|
|
28
|
-
// alongside the bloom mask in .r by the scene pass. Scene uses src-alpha blend
|
|
29
|
-
// with clear alpha 0 → hdr rgb is premultiplied; divide by the aux alpha to
|
|
30
|
-
// recover straight color before the Karis luminance weighting.
|
|
31
27
|
let aux = textureLoad(maskTex, cc, 0);
|
|
32
|
-
|
|
33
|
-
let rgb = max(s.rgb / max(aux.g, 1e-6), vec3f(0.0));
|
|
34
|
-
let masked = rgb * mask;
|
|
35
|
-
// Blender clamps each tap BEFORE Karis average (eevee_bloom: color = min(clampIntensity, color)).
|
|
36
|
-
return select(masked, min(masked, vec3f(clampV)), clampV > 0.0);
|
|
28
|
+
return max(textureLoad(hdrTex, cc, 0).rgb / max(aux.g, 1e-6), vec3f(0.0)) * aux.r;
|
|
37
29
|
}
|
|
38
30
|
|
|
39
31
|
@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f {
|
|
40
|
-
let
|
|
41
|
-
let
|
|
42
|
-
let
|
|
43
|
-
|
|
44
|
-
let
|
|
45
|
-
let
|
|
46
|
-
let
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
let wb = 1.0 / (1.0 + luminance(b));
|
|
50
|
-
let wc = 1.0 / (1.0 + luminance(c));
|
|
51
|
-
let wd = 1.0 / (1.0 + luminance(d));
|
|
52
|
-
let avg = (a * wa + b * wb + c * wc + d * wd) / max(wa + wb + wc + wd, 1e-6);
|
|
53
|
-
// EEVEE quadratic threshold (brightness = max-channel, then soft-knee curve).
|
|
54
|
-
let bright = max(avg.r, max(avg.g, avg.b));
|
|
55
|
-
let soft = clamp(bright - prefilter.x + prefilter.y, 0.0, 2.0 * prefilter.y);
|
|
56
|
-
let q = (soft * soft) / (4.0 * max(prefilter.y, 1e-4) + 1e-6);
|
|
57
|
-
let contrib = max(q, bright - prefilter.x) / max(bright, 1e-4);
|
|
58
|
-
return vec4f(max(avg * contrib, vec3f(0.0)), 1.0);
|
|
32
|
+
let base = vec2<i32>(p.xy - vec2f(0.5)) * 2;
|
|
33
|
+
let avg = (fetch(base) + fetch(base + vec2<i32>(1, 0)) + fetch(base + vec2<i32>(0, 1)) + fetch(base + vec2<i32>(1, 1))) * 0.25;
|
|
34
|
+
let c = min(avg, vec3f(params.z));
|
|
35
|
+
// URP's quadratic soft knee on the brightest channel.
|
|
36
|
+
let bright = max(c.r, max(c.g, c.b));
|
|
37
|
+
let knee = params.y;
|
|
38
|
+
let soft = clamp(bright - params.x + knee, 0.0, 2.0 * knee);
|
|
39
|
+
let curve = soft * soft / (4.0 * knee + 1e-4);
|
|
40
|
+
return vec4f(c * (max(curve, bright - params.x) / max(bright, 1e-4)), 1.0);
|
|
59
41
|
}
|
|
60
42
|
`
|
|
61
43
|
|
|
62
|
-
//
|
|
63
|
-
|
|
44
|
+
// Horizontal 9-tap Gaussian (σ ≈ 2 at the destination's scale), its taps two
|
|
45
|
+
// SOURCE texels apart: the destination is half the source, so this both halves
|
|
46
|
+
// and blurs. Each tap lands on a texel boundary, so bilinear averages pairs.
|
|
47
|
+
export const BLOOM_BLUR_H_SHADER_WGSL = `${FULLSCREEN_VS}
|
|
64
48
|
@group(0) @binding(0) var srcTex: texture_2d<f32>;
|
|
65
49
|
@group(0) @binding(1) var srcSamp: sampler;
|
|
66
50
|
|
|
67
|
-
fn
|
|
68
|
-
|
|
51
|
+
@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f {
|
|
52
|
+
let srcDims = vec2f(textureDimensions(srcTex));
|
|
53
|
+
let dstDims = max(floor(srcDims * 0.5), vec2f(1.0));
|
|
54
|
+
let uv = p.xy / dstDims;
|
|
55
|
+
let dx = vec2f(2.0 / srcDims.x, 0.0);
|
|
56
|
+
var w = array<f32, 5>(0.22702703, 0.19459459, 0.12162162, 0.05405405, 0.01621622);
|
|
57
|
+
var o = textureSampleLevel(srcTex, srcSamp, uv, 0.0).rgb * w[0];
|
|
58
|
+
for (var i = 1; i < 5; i++) {
|
|
59
|
+
let off = dx * f32(i);
|
|
60
|
+
o += (textureSampleLevel(srcTex, srcSamp, uv - off, 0.0).rgb + textureSampleLevel(srcTex, srcSamp, uv + off, 0.0).rgb) * w[i];
|
|
61
|
+
}
|
|
62
|
+
return vec4f(o, 1.0);
|
|
69
63
|
}
|
|
64
|
+
`
|
|
65
|
+
|
|
66
|
+
// Vertical pass at the same size: the same 9-tap Gaussian as five bilinear taps.
|
|
67
|
+
export const BLOOM_BLUR_V_SHADER_WGSL = `${FULLSCREEN_VS}
|
|
68
|
+
@group(0) @binding(0) var srcTex: texture_2d<f32>;
|
|
69
|
+
@group(0) @binding(1) var srcSamp: sampler;
|
|
70
70
|
|
|
71
71
|
@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f {
|
|
72
|
-
let
|
|
73
|
-
let
|
|
74
|
-
|
|
75
|
-
let
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
let C = samp(uv, t * vec2f( 2.0, -2.0));
|
|
80
|
-
let D = samp(uv, t * vec2f(-1.0, -1.0));
|
|
81
|
-
let E = samp(uv, t * vec2f( 1.0, -1.0));
|
|
82
|
-
let F = samp(uv, t * vec2f(-2.0, 0.0));
|
|
83
|
-
let G = samp(uv, t * vec2f( 0.0, 0.0));
|
|
84
|
-
let H = samp(uv, t * vec2f( 2.0, 0.0));
|
|
85
|
-
let I = samp(uv, t * vec2f(-1.0, 1.0));
|
|
86
|
-
let J = samp(uv, t * vec2f( 1.0, 1.0));
|
|
87
|
-
let K = samp(uv, t * vec2f(-2.0, 2.0));
|
|
88
|
-
let L = samp(uv, t * vec2f( 0.0, 2.0));
|
|
89
|
-
let M = samp(uv, t * vec2f( 2.0, 2.0));
|
|
90
|
-
var o = (D + E + I + J) * (0.5 / 4.0);
|
|
91
|
-
o = o + (A + B + G + F) * (0.125 / 4.0);
|
|
92
|
-
o = o + (B + C + H + G) * (0.125 / 4.0);
|
|
93
|
-
o = o + (F + G + L + K) * (0.125 / 4.0);
|
|
94
|
-
o = o + (G + H + M + L) * (0.125 / 4.0);
|
|
72
|
+
let dims = vec2f(textureDimensions(srcTex));
|
|
73
|
+
let uv = p.xy / dims;
|
|
74
|
+
let near = vec2f(0.0, 1.3846154 / dims.y);
|
|
75
|
+
let far = vec2f(0.0, 3.2307692 / dims.y);
|
|
76
|
+
var o = textureSampleLevel(srcTex, srcSamp, uv, 0.0).rgb * 0.22702703;
|
|
77
|
+
o += (textureSampleLevel(srcTex, srcSamp, uv - near, 0.0).rgb + textureSampleLevel(srcTex, srcSamp, uv + near, 0.0).rgb) * 0.31621623;
|
|
78
|
+
o += (textureSampleLevel(srcTex, srcSamp, uv - far, 0.0).rgb + textureSampleLevel(srcTex, srcSamp, uv + far, 0.0).rgb) * 0.07027027;
|
|
95
79
|
return vec4f(o, 1.0);
|
|
96
80
|
}
|
|
97
81
|
`
|
|
98
82
|
|
|
99
|
-
//
|
|
83
|
+
// One level up: this level's blur leaned toward the coarser one by scatter.
|
|
100
84
|
export const BLOOM_UPSAMPLE_SHADER_WGSL = `${FULLSCREEN_VS}
|
|
101
|
-
@group(0) @binding(0) var
|
|
102
|
-
@group(0) @binding(1) var
|
|
85
|
+
@group(0) @binding(0) var highTex: texture_2d<f32>; // down[i]
|
|
86
|
+
@group(0) @binding(1) var lowTex: texture_2d<f32>; // coarser: down[i+1] at the top, up[i+1] after
|
|
103
87
|
@group(0) @binding(2) var srcSamp: sampler;
|
|
104
|
-
@group(0) @binding(3) var<uniform>
|
|
88
|
+
@group(0) @binding(3) var<uniform> params: vec4<f32>; // threshold, knee, clamp, scatter
|
|
105
89
|
|
|
106
90
|
@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f {
|
|
107
|
-
let
|
|
108
|
-
let
|
|
109
|
-
let
|
|
110
|
-
|
|
111
|
-
var o = textureSampleLevel(srcTex, srcSamp, uv + t * vec2f(-1.0, -1.0), 0.0).rgb * 1.0;
|
|
112
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 0.0, -1.0), 0.0).rgb * 2.0;
|
|
113
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 1.0, -1.0), 0.0).rgb * 1.0;
|
|
114
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f(-1.0, 0.0), 0.0).rgb * 2.0;
|
|
115
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 0.0, 0.0), 0.0).rgb * 4.0;
|
|
116
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 1.0, 0.0), 0.0).rgb * 2.0;
|
|
117
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f(-1.0, 1.0), 0.0).rgb * 1.0;
|
|
118
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 0.0, 1.0), 0.0).rgb * 2.0;
|
|
119
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 1.0, 1.0), 0.0).rgb * 1.0;
|
|
120
|
-
o = o * (1.0 / 16.0);
|
|
121
|
-
let base = textureSampleLevel(baseTex, srcSamp, uv, 0.0).rgb;
|
|
122
|
-
return vec4f(o + base, 1.0);
|
|
91
|
+
let uv = p.xy / vec2f(textureDimensions(highTex));
|
|
92
|
+
let high = textureSampleLevel(highTex, srcSamp, uv, 0.0).rgb;
|
|
93
|
+
let low = textureSampleLevel(lowTex, srcSamp, uv, 0.0).rgb;
|
|
94
|
+
return vec4f(mix(high, low, params.w), 1.0);
|
|
123
95
|
}
|
|
124
96
|
`
|