reze-engine 0.66.4 → 0.67.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 +75 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +182 -18
- package/dist/graph/presets/body.js +1 -1
- package/dist/graph/presets/cloth_rough.js +1 -1
- package/dist/graph/presets/cloth_smooth.js +1 -1
- package/dist/graph/presets/face.js +1 -1
- package/dist/graph/presets/metal.js +1 -1
- package/dist/graph/registry.d.ts.map +1 -1
- package/dist/graph/registry.js +84 -6
- package/dist/graph/schema.d.ts +14 -0
- package/dist/graph/schema.d.ts.map +1 -1
- 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 +176 -7
- package/dist/shaders/passes/composite.d.ts.map +1 -1
- package/dist/shaders/passes/composite.js +4 -2
- package/dist/shaders/passes/outline.d.ts +13 -0
- package/dist/shaders/passes/outline.d.ts.map +1 -1
- package/dist/shaders/passes/outline.js +82 -18
- package/package.json +1 -1
- package/src/engine.ts +199 -20
- package/src/graph/presets/body.ts +1 -1
- package/src/graph/presets/cloth_rough.ts +1 -1
- package/src/graph/presets/cloth_smooth.ts +1 -1
- package/src/graph/presets/face.ts +1 -1
- package/src/graph/presets/metal.ts +1 -1
- package/src/graph/registry.ts +85 -6
- package/src/graph/schema.ts +14 -0
- package/src/shaders/materials/nodes.ts +176 -7
- package/src/shaders/passes/composite.ts +4 -2
- package/src/shaders/passes/outline.ts +82 -18
|
@@ -97,7 +97,7 @@ override APPLY_GAMMA: bool = true;
|
|
|
97
97
|
@group(0) @binding(0) var hdrTex: texture_2d<f32>;
|
|
98
98
|
@group(0) @binding(1) var bloomTex: texture_2d<f32>; // bloomUpTexture mip 0 (full pyramid top)
|
|
99
99
|
@group(0) @binding(2) var bloomSamp: sampler;
|
|
100
|
-
@group(0) @binding(3) var<uniform> viewU: array<vec4<f32>,
|
|
100
|
+
@group(0) @binding(3) var<uniform> viewU: array<vec4<f32>, 16>;
|
|
101
101
|
// Aux mask/alpha texture. .r = bloom mask (unused here; bloom blit uses it).
|
|
102
102
|
// .g = accumulated canvas alpha (what hdr.a carried before the HDR format
|
|
103
103
|
// became rg11b10ufloat). We unpremultiply HDR by this alpha for tonemap, then
|
|
@@ -119,6 +119,8 @@ override APPLY_GAMMA: bool = true;
|
|
|
119
119
|
// viewU[9] = (grade slope.rgb, grade flags) — bit 0 the CDL grade(), bit 1 the
|
|
120
120
|
// scene's own LUT (setStageGrade) — see _rzGradeScene() below.
|
|
121
121
|
// viewU[10] = (camera world position, _) — refreshed with the basis above.
|
|
122
|
+
// viewU[11..14] = the cast's positions (effects; count in viewU[10].w).
|
|
123
|
+
// viewU[15] = (soft curve contrast, _, _, _).
|
|
122
124
|
// invGamma = 1/gamma precomputed on CPU — avoids a per-pixel divide.
|
|
123
125
|
@group(0) @binding(6) var bgEquirect: texture_2d<f32>;
|
|
124
126
|
// The scene pass's own MSAA depth buffer, bound depth-only. NOT an extra
|
|
@@ -217,7 +219,7 @@ fn srgb_encode(x: f32) -> f32 {
|
|
|
217
219
|
* The game's ACES branch is off in every scene measured.
|
|
218
220
|
*/
|
|
219
221
|
fn softTransform(c: vec3f) -> vec3f {
|
|
220
|
-
let t = pow(max(vec3f(1.0) - exp(-2.5 * max(c, vec3f(0.0))), vec3f(0.0)), vec3f(
|
|
222
|
+
let t = pow(max(vec3f(1.0) - exp(-2.5 * max(c, vec3f(0.0))), vec3f(0.0)), vec3f(viewU[15].x));
|
|
221
223
|
return vec3f(srgb_encode(t.r), srgb_encode(t.g), srgb_encode(t.b));
|
|
222
224
|
}
|
|
223
225
|
|
|
@@ -6,5 +6,18 @@
|
|
|
6
6
|
* edgeSize 16..20, so this is 20.
|
|
7
7
|
*/
|
|
8
8
|
export declare const RZ_OUTLINE_DISSOLVE_OFFSET = 20;
|
|
9
|
+
/** Where the scene-wide width multiplier sits, in bytes — the float after
|
|
10
|
+
* dissolve (Engine.setOutlineWidth). */
|
|
11
|
+
export declare const RZ_OUTLINE_WIDTH_OFFSET = 24;
|
|
12
|
+
/** Where the scene-wide colour override sits, in bytes: rgb and a weight, 0
|
|
13
|
+
* for the material's own edge colour (Engine.setOutlineColor). */
|
|
14
|
+
export declare const RZ_OUTLINE_COLOR_OFFSET = 32;
|
|
15
|
+
/** Where a style graph's outline (ShaderGraph.outline) sits, in bytes: the
|
|
16
|
+
* textured flag at 28, then its shadow colour at 48. Unset, a hull draws its
|
|
17
|
+
* PMX edge colour flat, as MMD does. */
|
|
18
|
+
export declare const RZ_OUTLINE_TEXTURED_OFFSET = 28;
|
|
19
|
+
export declare const RZ_OUTLINE_SHADOW_OFFSET = 48;
|
|
20
|
+
/** The hull's own block, in floats. */
|
|
21
|
+
export declare const RZ_OUTLINE_UNIFORM_FLOATS = 16;
|
|
9
22
|
export declare function outlineShaderWgsl(): string;
|
|
10
23
|
//# sourceMappingURL=outline.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outline.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/outline.ts"],"names":[],"mappings":"AAqBA;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"outline.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/outline.ts"],"names":[],"mappings":"AAqBA;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAA;AAC5C;yCACyC;AACzC,eAAO,MAAM,uBAAuB,KAAK,CAAA;AACzC;mEACmE;AACnE,eAAO,MAAM,uBAAuB,KAAK,CAAA;AACzC;;yCAEyC;AACzC,eAAO,MAAM,0BAA0B,KAAK,CAAA;AAC5C,eAAO,MAAM,wBAAwB,KAAK,CAAA;AAC1C,uCAAuC;AACvC,eAAO,MAAM,yBAAyB,KAAK,CAAA;AAE3C,wBAAgB,iBAAiB,IAAI,MAAM,CAgP1C"}
|
|
@@ -24,6 +24,19 @@ import { DISSOLVE_WGSL } from "../materials/common";
|
|
|
24
24
|
* edgeSize 16..20, so this is 20.
|
|
25
25
|
*/
|
|
26
26
|
export const RZ_OUTLINE_DISSOLVE_OFFSET = 20;
|
|
27
|
+
/** Where the scene-wide width multiplier sits, in bytes — the float after
|
|
28
|
+
* dissolve (Engine.setOutlineWidth). */
|
|
29
|
+
export const RZ_OUTLINE_WIDTH_OFFSET = 24;
|
|
30
|
+
/** Where the scene-wide colour override sits, in bytes: rgb and a weight, 0
|
|
31
|
+
* for the material's own edge colour (Engine.setOutlineColor). */
|
|
32
|
+
export const RZ_OUTLINE_COLOR_OFFSET = 32;
|
|
33
|
+
/** Where a style graph's outline (ShaderGraph.outline) sits, in bytes: the
|
|
34
|
+
* textured flag at 28, then its shadow colour at 48. Unset, a hull draws its
|
|
35
|
+
* PMX edge colour flat, as MMD does. */
|
|
36
|
+
export const RZ_OUTLINE_TEXTURED_OFFSET = 28;
|
|
37
|
+
export const RZ_OUTLINE_SHADOW_OFFSET = 48;
|
|
38
|
+
/** The hull's own block, in floats. */
|
|
39
|
+
export const RZ_OUTLINE_UNIFORM_FLOATS = 16;
|
|
27
40
|
export function outlineShaderWgsl() {
|
|
28
41
|
return /* wgsl */ `
|
|
29
42
|
${DISSOLVE_WGSL}
|
|
@@ -36,7 +49,7 @@ struct CameraUniforms {
|
|
|
36
49
|
viewportHeight: f32,
|
|
37
50
|
};
|
|
38
51
|
|
|
39
|
-
// THE HULL'S OWN BLOCK, not the material's.
|
|
52
|
+
// THE HULL'S OWN BLOCK, not the material's. 48 bytes of edge data, which is all
|
|
40
53
|
// this pass shades with — and dissolve, which it has to obey.
|
|
41
54
|
//
|
|
42
55
|
// It reached for the material block's own layout first, declaring the skipped
|
|
@@ -49,15 +62,34 @@ struct MaterialUniforms {
|
|
|
49
62
|
edgeColor: vec4f,
|
|
50
63
|
edgeSize: f32,
|
|
51
64
|
dissolve: f32,
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
/** The scene's width multiplier (setOutlineWidth), the same on every hull:
|
|
66
|
+
* scales the author's edgeSize rather than replacing it, so a model's thin
|
|
67
|
+
* and thick lines keep their proportion. */
|
|
68
|
+
widthScale: f32,
|
|
69
|
+
/** 1: a style graph's outline, the game's (Uber pass "Outline"): edgeSize is
|
|
70
|
+
* a world width, held between 1.2 and 3 px of a 1920-wide frame, and the
|
|
71
|
+
* colour is the picture × lerp(shadowColor, edgeColor, the key's
|
|
72
|
+
* half-Lambert). 0: the PMX edge, flat. */
|
|
73
|
+
textured: f32,
|
|
74
|
+
/** The scene's colour in place of edgeColor.rgb, by .a (0 or 1). The
|
|
75
|
+
* material's own alpha still holds — a line its author made sheer stays so. */
|
|
76
|
+
colorOverride: vec4f,
|
|
77
|
+
shadowColor: vec4f,
|
|
54
78
|
};
|
|
55
79
|
|
|
80
|
+
struct OutlineLight { direction: vec4f, color: vec4f, };
|
|
81
|
+
/** The head of the scene's light block: the key is lights[0]. */
|
|
82
|
+
struct OutlineLights { ambientColor: vec4f, lights: array<OutlineLight, 4>, };
|
|
83
|
+
|
|
56
84
|
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
57
85
|
@group(0) @binding(1) var edgeSampler: sampler;
|
|
86
|
+
@group(0) @binding(2) var<uniform> light: OutlineLights;
|
|
58
87
|
@group(1) @binding(0) var<storage, read> skinMats: array<mat4x4f>;
|
|
59
88
|
@group(2) @binding(0) var<uniform> material: MaterialUniforms;
|
|
60
89
|
@group(2) @binding(1) var diffuseTexture: texture_2d<f32>;
|
|
90
|
+
/** A look's outline tint (ShaderGraph.outline.tint): one of its images, white
|
|
91
|
+
* when it names none. */
|
|
92
|
+
@group(2) @binding(2) var tintTexture: texture_2d<f32>;
|
|
61
93
|
|
|
62
94
|
struct VertexOutput {
|
|
63
95
|
@builtin(position) position: vec4f,
|
|
@@ -72,6 +104,7 @@ struct VertexOutput {
|
|
|
72
104
|
/** How much of a pixel-wide line this one really is (1 = all of it): a line
|
|
73
105
|
* held at the minimum width fades by what it was short. */
|
|
74
106
|
@location(3) coverage: f32,
|
|
107
|
+
@location(4) normal: vec3f,
|
|
75
108
|
};
|
|
76
109
|
|
|
77
110
|
/**
|
|
@@ -94,6 +127,11 @@ const RZ_OUTLINE_FULL_FIGURE = 12.5;
|
|
|
94
127
|
* the shortfall, so far lines thin out without aliasing into dashes. */
|
|
95
128
|
const RZ_OUTLINE_MIN_PX = 1.5;
|
|
96
129
|
|
|
130
|
+
fn safe_normal2(v: vec2f) -> vec2f {
|
|
131
|
+
let l = length(v);
|
|
132
|
+
return select(vec2f(0.0), v / l, l > 1e-6);
|
|
133
|
+
}
|
|
134
|
+
|
|
97
135
|
@vertex fn vs(
|
|
98
136
|
@location(0) position: vec3f,
|
|
99
137
|
@location(1) normal: vec3f,
|
|
@@ -153,21 +191,40 @@ const RZ_OUTLINE_MIN_PX = 1.5;
|
|
|
153
191
|
// once the frame is taller than a figure. The PMX per-vertex edge scale
|
|
154
192
|
// multiplies it all, as vertex colour alpha does in the game.
|
|
155
193
|
let w = max(clipPos.w, 1e-4);
|
|
156
|
-
|
|
157
|
-
let nearNdc = viewNormal.xy * (material.edgeSize * outlineNormal.w * 4.0 / refViewport);
|
|
158
|
-
var ndc = nearNdc * (min(w, breakDepth) / w);
|
|
159
|
-
|
|
160
|
-
// Antialiased minimum (the game's _OutlineAntialias): held at the minimum
|
|
161
|
-
// width and faded by the shortfall, never thinner. The game fades by
|
|
162
|
-
// sqrt(coverage.x · coverage.y), per axis; for one width that is the ratio
|
|
163
|
-
// itself.
|
|
164
|
-
let px = length(ndc * deviceViewport * 0.5);
|
|
165
|
-
let minPx = RZ_OUTLINE_MIN_PX * max(1.0, camera.viewportHeight / 1080.0);
|
|
194
|
+
var ndc: vec2f;
|
|
166
195
|
var coverage = 1.0;
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
|
|
196
|
+
if (material.textured > 0.5) {
|
|
197
|
+
// The game's: a view-space offset of edgeSize world units along the view
|
|
198
|
+
// normal, projected, then each axis held between 1.2 px and 1.2 px ×
|
|
199
|
+
// (1 + _OutlineMaxOffsetMultiplier, 1.5) of a 1920-wide frame; a line the
|
|
200
|
+
// minimum had to push fades by how far it was pushed.
|
|
201
|
+
let off = viewNormal.xy * material.edgeSize * material.widthScale * outlineNormal.w;
|
|
202
|
+
let d1 = vec2f(off.x * camera.projection[0][0], off.y * camera.projection[1][1]) / w;
|
|
203
|
+
// the minimum along the push direction (the game's dir · 2/screen · 1.2)
|
|
204
|
+
let pxNdc = 2.0 / refViewport * 1.2 * safe_normal2(viewNormal.xy);
|
|
205
|
+
let mx = abs(pxNdc) * 2.5;
|
|
206
|
+
let o = min(mx, max(abs(d1), abs(pxNdc)));
|
|
207
|
+
// fadeOut = min(length(saturate(off − |d1|))·75, 1), on clip offsets (× w)
|
|
208
|
+
// in game units: w here is MMD units, 8 to the game's metre
|
|
209
|
+
coverage = 1.0 - min(length(saturate((o - abs(d1)) * w / 8.0)) * 75.0, 1.0);
|
|
210
|
+
ndc = o * select(vec2f(1.0), sign(d1), abs(d1) > vec2f(0.0));
|
|
211
|
+
} else {
|
|
212
|
+
let breakDepth = RZ_OUTLINE_FULL_FIGURE * camera.projection[1][1];
|
|
213
|
+
let nearNdc = viewNormal.xy * (material.edgeSize * material.widthScale * outlineNormal.w * 4.0 / refViewport);
|
|
214
|
+
ndc = nearNdc * (min(w, breakDepth) / w);
|
|
215
|
+
|
|
216
|
+
// Antialiased minimum (the game's _OutlineAntialias): held at the minimum
|
|
217
|
+
// width and faded by the shortfall, never thinner. The game fades by
|
|
218
|
+
// sqrt(coverage.x · coverage.y), per axis; for one width that is the ratio
|
|
219
|
+
// itself.
|
|
220
|
+
let px = length(ndc * deviceViewport * 0.5);
|
|
221
|
+
let minPx = RZ_OUTLINE_MIN_PX * max(1.0, camera.viewportHeight / 1080.0);
|
|
222
|
+
if (px < minPx) {
|
|
223
|
+
coverage = px / minPx;
|
|
224
|
+
ndc = select(vec2f(0.0), ndc * (minPx / px), px > 1e-6);
|
|
225
|
+
}
|
|
170
226
|
}
|
|
227
|
+
output.normal = worldNormal;
|
|
171
228
|
output.coverage = coverage;
|
|
172
229
|
output.position = vec4f(clipPos.xy + ndc * w, clipPos.z, clipPos.w);
|
|
173
230
|
output.uv = uv;
|
|
@@ -199,7 +256,8 @@ ${sceneFsOutWgsl({ name: "FSOut", aux: "mask" })}
|
|
|
199
256
|
// bind-pose position — three passes, one rule, or they disagree about which
|
|
200
257
|
// pieces are still there.
|
|
201
258
|
if (material.dissolve < 0.9995 && input.faceT > material.dissolve) { discard; }
|
|
202
|
-
let
|
|
259
|
+
let tex = textureSample(diffuseTexture, edgeSampler, input.uv);
|
|
260
|
+
let texA = tex.a;
|
|
203
261
|
if (texA < 0.05) {
|
|
204
262
|
discard;
|
|
205
263
|
}
|
|
@@ -208,7 +266,13 @@ ${sceneFsOutWgsl({ name: "FSOut", aux: "mask" })}
|
|
|
208
266
|
// alpha above), it is drawn right after the surface it traces so what lies
|
|
209
267
|
// under a faded line is already there, and 4× MSAA would quantize a fade to
|
|
210
268
|
// four steps.
|
|
211
|
-
|
|
269
|
+
var rgb = mix(material.edgeColor.rgb, material.colorOverride.rgb, material.colorOverride.a);
|
|
270
|
+
if (material.textured > 0.5) {
|
|
271
|
+
let lit = dot(-light.lights[0].direction.xyz, normalize(input.normal)) * 0.5 + 0.5;
|
|
272
|
+
let tint = textureSample(tintTexture, edgeSampler, input.uv).rgb;
|
|
273
|
+
rgb = tex.rgb * tint * mix(material.shadowColor.rgb, rgb, lit);
|
|
274
|
+
}
|
|
275
|
+
out.color = vec4f(rgb, material.edgeColor.a * texA * input.coverage);
|
|
212
276
|
out.mask = vec4f(1.0, 1.0, 0.0, out.color.a);
|
|
213
277
|
${sceneIdPadWgsl("out")} return out;
|
|
214
278
|
}
|
package/package.json
CHANGED
package/src/engine.ts
CHANGED
|
@@ -79,7 +79,14 @@ import {
|
|
|
79
79
|
} from "./shaders/lights"
|
|
80
80
|
import { buildLightGrid } from "./light-grid"
|
|
81
81
|
import { groundShaderWgsl, GROUND_NOISE_BAKE_WGSL, GROUND_NOISE_SIZE } from "./shaders/passes/ground"
|
|
82
|
-
import {
|
|
82
|
+
import {
|
|
83
|
+
outlineShaderWgsl,
|
|
84
|
+
RZ_OUTLINE_COLOR_OFFSET,
|
|
85
|
+
RZ_OUTLINE_DISSOLVE_OFFSET,
|
|
86
|
+
RZ_OUTLINE_SHADOW_OFFSET,
|
|
87
|
+
RZ_OUTLINE_TEXTURED_OFFSET,
|
|
88
|
+
RZ_OUTLINE_WIDTH_OFFSET,
|
|
89
|
+
} from "./shaders/passes/outline"
|
|
83
90
|
import { transparentDepthPrepassWgsl } from "./shaders/passes/depth-prepass"
|
|
84
91
|
import { SELECTION_MASK_SHADER_WGSL, SELECTION_EDGE_SHADER_WGSL } from "./shaders/passes/selection"
|
|
85
92
|
import { GIZMO_SHADER_WGSL } from "./shaders/passes/gizmo"
|
|
@@ -238,7 +245,7 @@ const PRESET_NAME_HINTS: Array<[MaterialPreset, string[]]> = [
|
|
|
238
245
|
"hair", "ahoge", "bang",
|
|
239
246
|
],
|
|
240
247
|
],
|
|
241
|
-
["body", ["肌", "皮肤", "skin"]],
|
|
248
|
+
["body", ["肌", "皮肤", "skin", "指甲", "nail"]],
|
|
242
249
|
["metal", ["金属", "メタル", "metal", "earring", "耳环", "耳環"]],
|
|
243
250
|
[
|
|
244
251
|
"cloth_smooth",
|
|
@@ -284,6 +291,11 @@ const PRESET_NAME_HINTS: Array<[MaterialPreset, string[]]> = [
|
|
|
284
291
|
],
|
|
285
292
|
]
|
|
286
293
|
|
|
294
|
+
// Whole names that are body parts — skin by another name. Whole, never
|
|
295
|
+
// substrings: 手 alone is a hand, inside 手套 (gloves) or 手镯 (a bracelet) it is
|
|
296
|
+
// what the hand wears; 足 is a leg, 足袋 a sock.
|
|
297
|
+
const BODY_PART_NAMES = new Set(["手", "手部", "腕", "足", "脚", "腿", "爪", "hand", "hands", "arm", "arms", "leg", "legs", "body"])
|
|
298
|
+
|
|
287
299
|
// Resolve a material name to a style category (override map first, then name hints), or
|
|
288
300
|
// null if nothing matches — a null-resolving material stays ungrouped (neutral default).
|
|
289
301
|
function resolvePreset(materialName: string, map: MaterialPresetMap | undefined): MaterialPreset | null {
|
|
@@ -293,6 +305,7 @@ function resolvePreset(materialName: string, map: MaterialPresetMap | undefined)
|
|
|
293
305
|
}
|
|
294
306
|
}
|
|
295
307
|
const lower = materialName.toLowerCase()
|
|
308
|
+
if (BODY_PART_NAMES.has(lower.replace(/[\s_.\-0-9]+$/, ""))) return "body"
|
|
296
309
|
for (const [preset, hints] of PRESET_NAME_HINTS) {
|
|
297
310
|
for (const hint of hints) {
|
|
298
311
|
if (lower.includes(hint)) return preset
|
|
@@ -641,10 +654,16 @@ export type ViewTransformOptions = {
|
|
|
641
654
|
exposure: number
|
|
642
655
|
/** After the transform, display gamma (`pow(rgb, 1/gamma)`). */
|
|
643
656
|
gamma: number
|
|
657
|
+
/**
|
|
658
|
+
* The "soft" curve's power, (1 - e^(-2.5x))^contrast. Aether Gazer sets it per
|
|
659
|
+
* scene (SceneSetting._contrast): 1.4 is its common value, the character page
|
|
660
|
+
* runs 1.7. A stage brings its own; the other transforms ignore it.
|
|
661
|
+
*/
|
|
662
|
+
contrast: number
|
|
644
663
|
/**
|
|
645
664
|
* Which display transform the frame is formed with (composite.ts, viewTransform).
|
|
646
665
|
*
|
|
647
|
-
* "soft" — Aether Gazer's own final curve, (1 - e^(-2.5x))^
|
|
666
|
+
* "soft" — Aether Gazer's own final curve, (1 - e^(-2.5x))^contrast. The default.
|
|
648
667
|
* "neutral" — Unity URP's Neutral, the Khronos PBR Neutral operator: colour
|
|
649
668
|
* passes through until the top fifth, which rolls off.
|
|
650
669
|
* "aces" — Unity URP's ACES (RRT + ODT approximation from the Core RP).
|
|
@@ -660,6 +679,7 @@ export type ViewTransformName = "soft" | "neutral" | "aces" | "none"
|
|
|
660
679
|
export const DEFAULT_VIEW_TRANSFORM: ViewTransformOptions = {
|
|
661
680
|
exposure: 0,
|
|
662
681
|
gamma: 1.0,
|
|
682
|
+
contrast: 1.4,
|
|
663
683
|
transform: "soft",
|
|
664
684
|
}
|
|
665
685
|
|
|
@@ -823,7 +843,9 @@ interface DrawCall {
|
|
|
823
843
|
/** Edge-flagged materials: interleaved inverted-hull outline drawn right after
|
|
824
844
|
* this material with the outline pipeline. Shares this call's index range;
|
|
825
845
|
* own bind group (edge uniforms + diffuse texture for the alpha test). */
|
|
826
|
-
|
|
846
|
+
/** The inverted hull: `pmx` when the PMX asked for an edge, `drawn` when it
|
|
847
|
+
* is drawn — the PMX's, or its style group's look's outline. */
|
|
848
|
+
outline?: { bindGroup: GPUBindGroup; pmx: boolean; drawn: boolean }
|
|
827
849
|
/** MODEL-SPACE AABB over this material's index range, computed at load:
|
|
828
850
|
* [minX, minY, minZ, maxX, maxY, maxZ]. Usable for culling only while the
|
|
829
851
|
* owning model is rigid (see ModelInstance.rigid) — animation moves vertices
|
|
@@ -930,6 +952,9 @@ interface ModelInstance {
|
|
|
930
952
|
* material buffers never reached it and a dissolved character kept her
|
|
931
953
|
* outline. Kept here so that write has somewhere to go. */
|
|
932
954
|
outlineUniformBuffers: GPUBuffer[]
|
|
955
|
+
/** Each outlined material's hull block and its PMX edge (colour, size), for
|
|
956
|
+
* a style graph's outline to take over and give back. */
|
|
957
|
+
outlineByMaterial: Map<string, { buffer: GPUBuffer; edge: Float32Array<ArrayBuffer>; picture: GPUTextureView }>
|
|
933
958
|
model: Model
|
|
934
959
|
/**
|
|
935
960
|
* Keep simulating this model's cloth while it is HIDDEN.
|
|
@@ -2632,7 +2657,7 @@ export class Engine {
|
|
|
2632
2657
|
// 15 × vec4f — see the viewU comment in composite.ts. The last one is the
|
|
2633
2658
|
// camera's world position, which is what lets a foreground effect turn the
|
|
2634
2659
|
// depth it is handed into a PLACE (bgWorldPos) rather than a distance.
|
|
2635
|
-
private readonly compositeUniformData = new Float32Array(
|
|
2660
|
+
private readonly compositeUniformData = new Float32Array(64)
|
|
2636
2661
|
/** Composite background (display-space sRGB 0–1) — null = transparent canvas. */
|
|
2637
2662
|
private backgroundColor: Vec3 | null = null
|
|
2638
2663
|
// 360 backdrop (equirectangular skybox, sampled by view ray in composite).
|
|
@@ -3054,6 +3079,7 @@ export class Engine {
|
|
|
3054
3079
|
return {
|
|
3055
3080
|
exposure: partial?.exposure ?? d.exposure,
|
|
3056
3081
|
gamma: partial?.gamma ?? d.gamma,
|
|
3082
|
+
contrast: partial?.contrast ?? d.contrast,
|
|
3057
3083
|
transform: partial?.transform ?? d.transform,
|
|
3058
3084
|
}
|
|
3059
3085
|
}
|
|
@@ -3072,7 +3098,7 @@ export class Engine {
|
|
|
3072
3098
|
|
|
3073
3099
|
getViewTransformOptions(): ViewTransformOptions {
|
|
3074
3100
|
const v = this.viewTransform
|
|
3075
|
-
return { exposure: v.exposure, gamma: v.gamma, transform: v.transform }
|
|
3101
|
+
return { exposure: v.exposure, gamma: v.gamma, contrast: v.contrast, transform: v.transform }
|
|
3076
3102
|
}
|
|
3077
3103
|
|
|
3078
3104
|
private colorGrading: ColorGradingOptions = {
|
|
@@ -3188,6 +3214,7 @@ export class Engine {
|
|
|
3188
3214
|
const v = this.viewTransform
|
|
3189
3215
|
if (patch.exposure !== undefined) v.exposure = patch.exposure
|
|
3190
3216
|
if (patch.gamma !== undefined) v.gamma = patch.gamma
|
|
3217
|
+
if (patch.contrast !== undefined) v.contrast = patch.contrast
|
|
3191
3218
|
if (patch.transform !== undefined) v.transform = patch.transform
|
|
3192
3219
|
if (this.device && this.compositeUniformBuffer) {
|
|
3193
3220
|
this.writeCompositeViewUniforms()
|
|
@@ -3266,6 +3293,9 @@ export class Engine {
|
|
|
3266
3293
|
u[25] = Engine.VIEW_TRANSFORM_ID[v.transform] ?? 0
|
|
3267
3294
|
u[26] = this.canvas.width
|
|
3268
3295
|
u[27] = this.canvas.height
|
|
3296
|
+
// The soft curve's power (viewU[15].x), the scene's own (SceneSetting._contrast).
|
|
3297
|
+
// Its own vec4: viewU[11..14] are the cast's positions whenever an effect runs.
|
|
3298
|
+
u[60] = v.contrast
|
|
3269
3299
|
// ── Grade (viewU[7..9]) ── The UI's three tonal COLORS map to ASC CDL here,
|
|
3270
3300
|
// on the CPU, so the shader only ever sees slope/offset/power. Mid-gray is
|
|
3271
3301
|
// neutral in all three; the signed distance from it is the amount.
|
|
@@ -3324,6 +3354,51 @@ export class Engine {
|
|
|
3324
3354
|
this.bundlesDirty = true
|
|
3325
3355
|
}
|
|
3326
3356
|
|
|
3357
|
+
/**
|
|
3358
|
+
* Every outline's width, as a multiple of what each PMX material asked for
|
|
3359
|
+
* (its edgeSize): 1 is the author's own, 0 draws none. A multiplier rather
|
|
3360
|
+
* than a width because a model's lines are not one width — the hair's rim
|
|
3361
|
+
* and a sleeve's crease differ on purpose, and scaling keeps that.
|
|
3362
|
+
*
|
|
3363
|
+
* A write, not a re-record: one float in each hull's uniform, so a slider
|
|
3364
|
+
* can drive it. Hulls built later take the current value at creation.
|
|
3365
|
+
*/
|
|
3366
|
+
private outlineWidth = 1
|
|
3367
|
+
setOutlineWidth(scale: number): void {
|
|
3368
|
+
const v = Number.isFinite(scale) ? Math.max(0, scale) : 1
|
|
3369
|
+
if (this.outlineWidth === v) return
|
|
3370
|
+
this.outlineWidth = v
|
|
3371
|
+
if (!this.device) return
|
|
3372
|
+
const one = new Float32Array([v])
|
|
3373
|
+
for (const inst of this.modelInstances.values()) {
|
|
3374
|
+
for (const buffer of inst.outlineUniformBuffers) {
|
|
3375
|
+
this.device.queue.writeBuffer(buffer, RZ_OUTLINE_WIDTH_OFFSET, one)
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
/**
|
|
3381
|
+
* One colour for every outline, in place of each PMX material's edge colour;
|
|
3382
|
+
* null hands each material its own back. Written as the edge colour is —
|
|
3383
|
+
* the values a PMX would carry — so a picked black is MMD's black. The
|
|
3384
|
+
* material's edge ALPHA is kept: only the hue is the scene's.
|
|
3385
|
+
*
|
|
3386
|
+
* A write, like setOutlineWidth: hulls built later take it at creation.
|
|
3387
|
+
*/
|
|
3388
|
+
private outlineColor: [number, number, number, number] = [0, 0, 0, 0]
|
|
3389
|
+
setOutlineColor(color: Vec3 | { x: number; y: number; z: number } | null): void {
|
|
3390
|
+
const next: [number, number, number, number] = color ? [color.x, color.y, color.z, 1] : [0, 0, 0, 0]
|
|
3391
|
+
if (next.every((v, i) => v === this.outlineColor[i])) return
|
|
3392
|
+
this.outlineColor = next
|
|
3393
|
+
if (!this.device) return
|
|
3394
|
+
const data = new Float32Array(next)
|
|
3395
|
+
for (const inst of this.modelInstances.values()) {
|
|
3396
|
+
for (const buffer of inst.outlineUniformBuffers) {
|
|
3397
|
+
this.device.queue.writeBuffer(buffer, RZ_OUTLINE_COLOR_OFFSET, data)
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3327
3402
|
/**
|
|
3328
3403
|
* Draw the id attachment instead of the scene.
|
|
3329
3404
|
*
|
|
@@ -8423,6 +8498,8 @@ export class Engine {
|
|
|
8423
8498
|
entries: [
|
|
8424
8499
|
{ binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
|
|
8425
8500
|
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: { type: "filtering" } },
|
|
8501
|
+
// the key light, for a style graph's outline colour (see outline.ts)
|
|
8502
|
+
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
|
|
8426
8503
|
],
|
|
8427
8504
|
})
|
|
8428
8505
|
// Outline per-instance reuses mainPerInstanceBindGroupLayout (same skinMats binding)
|
|
@@ -8431,6 +8508,7 @@ export class Engine {
|
|
|
8431
8508
|
entries: [
|
|
8432
8509
|
{ binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
|
|
8433
8510
|
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: "float" } },
|
|
8511
|
+
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: "float" } },
|
|
8434
8512
|
],
|
|
8435
8513
|
})
|
|
8436
8514
|
|
|
@@ -8449,6 +8527,7 @@ export class Engine {
|
|
|
8449
8527
|
entries: [
|
|
8450
8528
|
{ binding: 0, resource: { buffer: this.cameraUniformBuffer } },
|
|
8451
8529
|
{ binding: 1, resource: this.materialSampler },
|
|
8530
|
+
{ binding: 2, resource: { buffer: this.lightUniformBuffer } },
|
|
8452
8531
|
],
|
|
8453
8532
|
})
|
|
8454
8533
|
|
|
@@ -8462,6 +8541,7 @@ export class Engine {
|
|
|
8462
8541
|
entries: [
|
|
8463
8542
|
{ binding: 0, resource: { buffer: this.mirrorCameraBuffer } },
|
|
8464
8543
|
{ binding: 1, resource: this.materialSampler },
|
|
8544
|
+
{ binding: 2, resource: { buffer: this.lightUniformBuffer } },
|
|
8465
8545
|
],
|
|
8466
8546
|
})
|
|
8467
8547
|
|
|
@@ -8649,13 +8729,14 @@ export class Engine {
|
|
|
8649
8729
|
// strength belong to the combine step.
|
|
8650
8730
|
this.compositeUniformBuffer = this.device.createBuffer({
|
|
8651
8731
|
label: "composite view uniforms",
|
|
8652
|
-
//
|
|
8732
|
+
// 16 × vec4f: (exposure, invGamma, _, _) · (bloom tint, intensity) ·
|
|
8653
8733
|
// (bg rgb, mode) · camera right/up/forward basis for the 360 skybox ray ·
|
|
8654
8734
|
// (time, _, canvas width, canvas height) for user effects · three grade
|
|
8655
8735
|
// vectors (CDL offset+contrast, power+saturation, slope+flag) · camera
|
|
8656
8736
|
// world position, for an effect placing itself in the scene · four
|
|
8657
|
-
// character positions, for one that wants to respond to the cast
|
|
8658
|
-
|
|
8737
|
+
// character positions, for one that wants to respond to the cast · the
|
|
8738
|
+
// soft curve's contrast. Shaders that stop at the cast declare 15.
|
|
8739
|
+
size: 256,
|
|
8659
8740
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
8660
8741
|
})
|
|
8661
8742
|
this.dofUniformBuffer = this.device.createBuffer({
|
|
@@ -9981,6 +10062,46 @@ export class Engine {
|
|
|
9981
10062
|
if (this.cameraPoseOverride) this.camera.setVmdPose(this.cameraPoseOverride)
|
|
9982
10063
|
}
|
|
9983
10064
|
|
|
10065
|
+
/**
|
|
10066
|
+
* The host's adjustment to a camera track, added to every pose it samples.
|
|
10067
|
+
*
|
|
10068
|
+
* A downloaded camera motion is framed for whoever it was made with: a
|
|
10069
|
+
* shorter model wants it lower, a wider stage wants it further back. This
|
|
10070
|
+
* moves the shot without touching its ANGLE — the motion's rotation and roll
|
|
10071
|
+
* are its choreography, and they stay the track's.
|
|
10072
|
+
*
|
|
10073
|
+
* - `target`: world units, added to the look-at point, so the whole shot
|
|
10074
|
+
* slides with the angle unchanged.
|
|
10075
|
+
* - `distance`: positive pulls the eye further back along its own line of
|
|
10076
|
+
* sight. VMD distances are signed (negative is behind the target, the usual
|
|
10077
|
+
* case), so "further" means away from zero on whichever side it is.
|
|
10078
|
+
* - `fov`: radians, added to the track's lens, never below a sliver.
|
|
10079
|
+
*
|
|
10080
|
+
* Only a loaded track reads it: the orbit has its own sliders, and a pose
|
|
10081
|
+
* forced through setCameraPose is a statement of where the camera IS.
|
|
10082
|
+
*/
|
|
10083
|
+
private cameraTrackOffset = { target: new Vec3(0, 0, 0), distance: 0, fov: 0 }
|
|
10084
|
+
setCameraTrackOffset(offset: { target?: { x: number; y: number; z: number }; distance?: number; fov?: number } | null): void {
|
|
10085
|
+
const t = offset?.target
|
|
10086
|
+
this.cameraTrackOffset = {
|
|
10087
|
+
target: new Vec3(t?.x ?? 0, t?.y ?? 0, t?.z ?? 0),
|
|
10088
|
+
distance: offset?.distance ?? 0,
|
|
10089
|
+
fov: offset?.fov ?? 0,
|
|
10090
|
+
}
|
|
10091
|
+
}
|
|
10092
|
+
|
|
10093
|
+
/** A sampled track pose with the host's offset laid on. */
|
|
10094
|
+
private offsetTrackPose(pose: CameraPose): CameraPose {
|
|
10095
|
+
const o = this.cameraTrackOffset
|
|
10096
|
+
if (o.distance === 0 && o.fov === 0 && o.target.x === 0 && o.target.y === 0 && o.target.z === 0) return pose
|
|
10097
|
+
return {
|
|
10098
|
+
target: new Vec3(pose.target.x + o.target.x, pose.target.y + o.target.y, pose.target.z + o.target.z),
|
|
10099
|
+
rotation: pose.rotation,
|
|
10100
|
+
distance: pose.distance + Math.sign(pose.distance || -1) * o.distance,
|
|
10101
|
+
fov: Math.min(Math.PI * 0.95, Math.max(0.01, pose.fov + o.fov)),
|
|
10102
|
+
}
|
|
10103
|
+
}
|
|
10104
|
+
|
|
9984
10105
|
/** The pose currently forced from outside, or null when nothing is. */
|
|
9985
10106
|
getCameraPoseOverride(): CameraPose | null {
|
|
9986
10107
|
return this.cameraPoseOverride
|
|
@@ -10974,6 +11095,12 @@ export class Engine {
|
|
|
10974
11095
|
{ binding: 12, resource: { buffer: this.probeUniformBuffer } },
|
|
10975
11096
|
],
|
|
10976
11097
|
})
|
|
11098
|
+
// The recorded phases bind these groups by value (sceneView), so a rebuild
|
|
11099
|
+
// they never hear about leaves them drawing with the old one: the first
|
|
11100
|
+
// light cookie landed on the ground (re-bound each frame) and not on the
|
|
11101
|
+
// cast, and a swapped sky kept reflecting the one it replaced. Only init,
|
|
11102
|
+
// a world install and the first cookie get here, so re-recording is cheap.
|
|
11103
|
+
this.bundlesDirty = true
|
|
10977
11104
|
return true
|
|
10978
11105
|
}
|
|
10979
11106
|
|
|
@@ -14803,6 +14930,7 @@ export class Engine {
|
|
|
14803
14930
|
dissolve: 1,
|
|
14804
14931
|
materialUniformBuffers: [],
|
|
14805
14932
|
outlineUniformBuffers: [],
|
|
14933
|
+
outlineByMaterial: new Map(),
|
|
14806
14934
|
cullModelIndex: 0,
|
|
14807
14935
|
// Seeded false: the first skin-matrix upload decides it, and until then the
|
|
14808
14936
|
// sphere path is the safe answer (it never culls something it should not).
|
|
@@ -15444,8 +15572,12 @@ export class Engine {
|
|
|
15444
15572
|
// every material — on the heaviest mesh in the scene that doubles the
|
|
15445
15573
|
// geometry submitted per frame to draw cartoon outlines around
|
|
15446
15574
|
// architecture, which is not the look anyone is after.
|
|
15575
|
+
// Every character material gets its hull: the PMX edge flag draws it as
|
|
15576
|
+
// MMD does, and a style group whose look carries an outline draws it on
|
|
15577
|
+
// any material (assignDrawCallGroups sets `drawn`).
|
|
15447
15578
|
let outline: DrawCall["outline"]
|
|
15448
|
-
|
|
15579
|
+
const pmxEdge = (mat.edgeFlag & 0x10) !== 0 && mat.edgeSize > 0
|
|
15580
|
+
if (!inst.isStage) {
|
|
15449
15581
|
const materialUniformData = new Float32Array([
|
|
15450
15582
|
mat.edgeColor[0],
|
|
15451
15583
|
mat.edgeColor[1],
|
|
@@ -15458,21 +15590,31 @@ export class Engine {
|
|
|
15458
15590
|
// block's layout would be 32 bytes of padding per outlined material
|
|
15459
15591
|
// to carry one float. See RZ_OUTLINE_DISSOLVE_OFFSET.
|
|
15460
15592
|
1,
|
|
15593
|
+
// The scene's width multiplier, at RZ_OUTLINE_WIDTH_OFFSET.
|
|
15594
|
+
this.outlineWidth,
|
|
15595
|
+
// A style graph's outline, at RZ_OUTLINE_TEXTURED_OFFSET: none yet.
|
|
15596
|
+
0,
|
|
15597
|
+
// The scene's colour override, at RZ_OUTLINE_COLOR_OFFSET.
|
|
15598
|
+
this.outlineColor[0],
|
|
15599
|
+
this.outlineColor[1],
|
|
15600
|
+
this.outlineColor[2],
|
|
15601
|
+
this.outlineColor[3],
|
|
15602
|
+
// The graph outline's shadow colour, at RZ_OUTLINE_SHADOW_OFFSET.
|
|
15603
|
+
0,
|
|
15604
|
+
0,
|
|
15461
15605
|
0,
|
|
15462
15606
|
0,
|
|
15463
15607
|
])
|
|
15464
15608
|
const outlineUniformBuffer = this.createUniformBuffer(`${prefix}outline: ${mat.name}`, materialUniformData)
|
|
15465
15609
|
inst.gpuBuffers.push(outlineUniformBuffer)
|
|
15466
15610
|
inst.outlineUniformBuffers.push(outlineUniformBuffer)
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
|
|
15470
|
-
|
|
15471
|
-
{ binding: 0, resource: { buffer: outlineUniformBuffer } },
|
|
15472
|
-
{ binding: 1, resource: textureView },
|
|
15473
|
-
],
|
|
15611
|
+
inst.outlineByMaterial.set(mat.name, {
|
|
15612
|
+
buffer: outlineUniformBuffer,
|
|
15613
|
+
edge: materialUniformData.slice(0, 5),
|
|
15614
|
+
picture: textureView,
|
|
15474
15615
|
})
|
|
15475
|
-
|
|
15616
|
+
const outlineBindGroup = this.outlineBindGroup(`${prefix}outline: ${mat.name}`, outlineUniformBuffer, textureView, null)
|
|
15617
|
+
outline = { bindGroup: outlineBindGroup, pmx: pmxEdge, drawn: pmxEdge }
|
|
15476
15618
|
if (!inst.outlineVertexBuffer) {
|
|
15477
15619
|
const data = inst.model.getOutlineVertices()
|
|
15478
15620
|
const buf = this.device.createBuffer({
|
|
@@ -17035,7 +17177,7 @@ export class Engine {
|
|
|
17035
17177
|
} else if (this.camera.vmdDriven && this.cameraAnimation) {
|
|
17036
17178
|
// Drive the shot from the camera VMD (synced to the animated model's clock).
|
|
17037
17179
|
const pose = this.cameraAnimation.sample(this.transportTime())
|
|
17038
|
-
if (pose) this.camera.setVmdPose(pose)
|
|
17180
|
+
if (pose) this.camera.setVmdPose(this.offsetTrackPose(pose))
|
|
17039
17181
|
}
|
|
17040
17182
|
|
|
17041
17183
|
// Before the cast is written, which carries every subject's dissolve to the
|
|
@@ -18012,8 +18154,45 @@ export class Engine {
|
|
|
18012
18154
|
|
|
18013
18155
|
// Rebind each material draw call to its (successfully-installed) group's uniform buffer,
|
|
18014
18156
|
// or the zero buffer when ungrouped, then re-sort by render-class draw order.
|
|
18157
|
+
/** A hull's per-material group: its block, the picture, the look's tint. */
|
|
18158
|
+
private outlineBindGroup(label: string, buffer: GPUBuffer, picture: GPUTextureView, tint: GPUTexture | null): GPUBindGroup {
|
|
18159
|
+
return this.device.createBindGroup({
|
|
18160
|
+
label,
|
|
18161
|
+
layout: this.outlinePerMaterialBindGroupLayout,
|
|
18162
|
+
entries: [
|
|
18163
|
+
{ binding: 0, resource: { buffer } },
|
|
18164
|
+
{ binding: 1, resource: picture },
|
|
18165
|
+
{ binding: 2, resource: (tint ?? this.fallbackMaterialTexture).createView() },
|
|
18166
|
+
],
|
|
18167
|
+
})
|
|
18168
|
+
}
|
|
18169
|
+
|
|
18015
18170
|
private assignDrawCallGroups(inst: ModelInstance, claimed: Map<string, string>): void {
|
|
18016
18171
|
inst.materialToGroup.clear()
|
|
18172
|
+
// The hulls: a group whose look carries an outline (ShaderGraph.outline)
|
|
18173
|
+
// draws it in place of the PMX edge; the rest keep, or get back, their own.
|
|
18174
|
+
for (const [name, hull] of inst.outlineByMaterial) {
|
|
18175
|
+
const id = claimed.get(name)
|
|
18176
|
+
const install = id ? inst.styleGroups.get(id) : undefined
|
|
18177
|
+
const o = install?.group.graph.outline
|
|
18178
|
+
const block = o
|
|
18179
|
+
? new Float32Array([o.color[0], o.color[1], o.color[2], 1, o.width])
|
|
18180
|
+
: hull.edge
|
|
18181
|
+
this.device.queue.writeBuffer(hull.buffer, 0, block)
|
|
18182
|
+
this.device.queue.writeBuffer(hull.buffer, RZ_OUTLINE_TEXTURED_OFFSET, new Float32Array([o ? 1 : 0]))
|
|
18183
|
+
if (o) this.device.queue.writeBuffer(hull.buffer, RZ_OUTLINE_SHADOW_OFFSET, new Float32Array([...o.shadowColor, 1]))
|
|
18184
|
+
const tint = o?.tint != null ? (install?.imagesByMaterial?.[name] ?? install?.images)?.[o.tint] ?? null : null
|
|
18185
|
+
for (const dc of inst.drawCalls) {
|
|
18186
|
+
if (dc.materialName !== name || !dc.outline) continue
|
|
18187
|
+
dc.outline.bindGroup = this.outlineBindGroup(`outline: ${name}`, hull.buffer, hull.picture, tint)
|
|
18188
|
+
this.bundlesDirty = true
|
|
18189
|
+
const drawn = o ? o.width > 0 : dc.outline.pmx
|
|
18190
|
+
if (dc.outline.drawn !== drawn) {
|
|
18191
|
+
dc.outline.drawn = drawn
|
|
18192
|
+
this.bundlesDirty = true
|
|
18193
|
+
}
|
|
18194
|
+
}
|
|
18195
|
+
}
|
|
18017
18196
|
for (const dc of inst.drawCalls) {
|
|
18018
18197
|
if (!dc.baseBindGroupEntries) continue // outlines/ground are never grouped
|
|
18019
18198
|
const wantId = claimed.get(dc.materialName)
|
|
@@ -18512,7 +18691,7 @@ export class Engine {
|
|
|
18512
18691
|
}
|
|
18513
18692
|
pass.setBindGroup(2, draw.bindGroup)
|
|
18514
18693
|
this.issueDraw(pass, draw, view.args)
|
|
18515
|
-
if (draw.outline && this.outlineEnabled && view.outlines) outlined.push(draw)
|
|
18694
|
+
if (draw.outline?.drawn && this.outlineEnabled && view.outlines) outlined.push(draw)
|
|
18516
18695
|
}
|
|
18517
18696
|
// THE HULLS AFTER THE SURFACES, as MMD draws a model's edges. A hull held
|
|
18518
18697
|
// at the minimum width is faded, and it still writes depth: drawn between
|
|
@@ -24,7 +24,7 @@ export const BODY_GRAPH: ShaderGraph = {
|
|
|
24
24
|
{ id: "lit_tint", type: "hue_sat", inputs: { hue: 0.5, saturation: 1.5, value: 1.0, fac: 1.0 } },
|
|
25
25
|
{ id: "toon_color", type: "mix/blend" },
|
|
26
26
|
{ id: "bc", type: "bright_contrast", inputs: { bright: 0.1, contrast: 0.2 } },
|
|
27
|
-
{ id: "emission3", type: "vector_math/scale", inputs: { scale:
|
|
27
|
+
{ id: "emission3", type: "vector_math/scale", inputs: { scale: 3.2 } },
|
|
28
28
|
{ id: "warm_add", type: "math/add", inputs: { b: 0.5 } },
|
|
29
29
|
{ id: "warm_clamp", type: "math/clamp01" },
|
|
30
30
|
{
|
|
@@ -30,7 +30,7 @@ export const CLOTH_ROUGH_GRAPH: ShaderGraph = {
|
|
|
30
30
|
{ id: "mix_003", type: "mix/blend" },
|
|
31
31
|
{ id: "hue_004", type: "hue_sat", inputs: { hue: 0.5, saturation: 0.800000011920929, value: 2.0, fac: 1.0 } },
|
|
32
32
|
{ id: "npr_overlay", type: "mix/overlay", inputs: { fac: 1.0 } },
|
|
33
|
-
{ id: "npr_emit", type: "vector_math/scale", inputs: { scale:
|
|
33
|
+
{ id: "npr_emit", type: "vector_math/scale", inputs: { scale: 14.56 } },
|
|
34
34
|
{ id: "noise", type: "tex_noise", inputs: { scale: 17.7 } },
|
|
35
35
|
{ id: "noise_ramp", type: "ramp_linear", inputs: { pos0: 0.0, pos1: 1.0 } },
|
|
36
36
|
{ id: "bump", type: "bump", inputs: { strength: 1.0 } },
|