载入中...
搜索中...
未找到
StyleShaders.cpp
浏览该文件的文档.
2
4
5#include "common/Exception.h"
6#include "graphics/Graphics.h"
7#include "graphics/Shader.h"
8#include "graphics/shaders/mesh3d_toon_frag_spv.inc"
9#include "graphics/shaders/mesh3d_toon_vert_spv.inc"
11#include "stylize/shaders/anime_mesh_frag_spv.inc"
12#include "stylize/shaders/anime_mesh_wgsl.inc"
13#include "stylize/shaders/cartoon_post_frag_spv.inc"
14#include "stylize/shaders/ink_mesh_frag_spv.inc"
15#include "stylize/shaders/ink_post_frag_spv.inc"
16#include "stylize/shaders/pixel_post_frag_spv.inc"
17#include "stylize/shaders/watercolor_post_frag_spv.inc"
18#include "stylize/shaders/xray_mesh_frag_spv.inc"
19#include "stylize/shaders/ysa_mesh_frag_spv.inc"
20#include "stylize/shaders/ysa_mesh_vert_spv.inc"
21#include "stylize/shaders/ysa_mesh_wgsl.inc"
22
23#include <algorithm>
24#include <array>
25#include <cstddef>
26#include <vector>
27
28namespace eve::stylize {
29namespace {
30
31const std::array<StyleDefinition, 7> kStyles = {{
32 {"cartoon", true, true, true, false, false, graphics::PostEffectStage::BeforeTonemap, 100},
33 {"watercolor", true, false, true, false, false, graphics::PostEffectStage::AfterTonemap, 200},
34 {"ink", true, true, true, false, false, graphics::PostEffectStage::BeforeTonemap, 110},
35 {"pixel", true, false, true, false, false, graphics::PostEffectStage::AfterTonemap, 300},
36 {"xray", false, true, false, true, false, graphics::PostEffectStage::AfterOpaque, 50},
37 {"anime", false, true, false, false, false, graphics::PostEffectStage::AfterOpaque, 40},
38 {"ysa", false, true, false, false, false, graphics::PostEffectStage::AfterOpaque, 40},
39}};
40
41std::vector<uint32_t> copySpv(const uint32_t *data, size_t count) {
42 return std::vector<uint32_t>(data, data + count);
43}
44
45// WebGPU (WGSL) X-ray mesh shaders. Must match the engine's mesh3d Frame UBO
46// (group 0 binding 0, std140 = Mesh3DUBO) and shared bindings (main sampler 7,
47// scene depth 9). X-ray params are packed into texBomb/parallax/clipInfo by the
48// backend flush (see bindMeshUniforms("xray") ordering below).
49const char *kXrayMeshVertWgsl = R"wgsl(
50struct VSIn {
51 @location(0) pos: vec3f,
52 @location(1) normal: vec3f,
53 @location(2) uv: vec2f,
54};
55struct Light3D {
56 posRadius: vec4f,
57 color: vec4f,
58};
59struct Frame {
60 mvp: mat4x4f,
61 model: mat4x4f,
62 lightDir: vec4f,
63 lightColor: vec4f,
64 tint: vec4f,
65 cameraPos: vec4f,
66 ambient: vec4f,
67 lights: array<Light3D, 8>,
68 texBomb: vec4f,
69 parallax: vec4f,
70 view: mat4x4f,
71 clipInfo: vec4f,
72};
73struct VSOut {
74 @builtin(position) pos: vec4f,
75 @location(0) vNormal: vec3f,
76 @location(1) vUV: vec2f,
77 @location(2) vTint: vec4f,
78 @location(3) vWorldPos: vec3f,
79 @location(4) vCameraPos: vec3f,
80};
81@group(0) @binding(0) var<uniform> ubo: Frame;
82fn inverse3x3(m: mat3x3f) -> mat3x3f {
83 let a = m[0].x; let b = m[1].x; let c = m[2].x;
84 let d = m[0].y; let e = m[1].y; let f = m[2].y;
85 let g = m[0].z; let h = m[1].z; let i = m[2].z;
86 let det = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
87 return mat3x3f(
88 vec3f((e * i - f * h) / det, (f * g - d * i) / det, (d * h - e * g) / det),
89 vec3f((c * h - b * i) / det, (a * i - c * g) / det, (b * g - a * h) / det),
90 vec3f((b * f - c * e) / det, (c * d - a * f) / det, (a * e - b * d) / det),
91 );
92}
93@vertex
94fn vs_main(in: VSIn) -> VSOut {
95 var out: VSOut;
96 out.pos = ubo.mvp * vec4f(in.pos, 1.0);
97 let world = ubo.model * vec4f(in.pos, 1.0);
98 out.vWorldPos = world.xyz;
99 let nrm = transpose(inverse3x3(mat3x3f(ubo.model[0].xyz, ubo.model[1].xyz, ubo.model[2].xyz))) * in.normal;
100 out.vNormal = normalize(nrm);
101 out.vUV = in.uv;
102 out.vTint = ubo.tint;
103 out.vCameraPos = ubo.cameraPos.xyz;
104 return out;
105}
106)wgsl";
107
108const char *kXrayMeshFragWgsl = R"wgsl(
109struct Light3D {
110 posRadius: vec4f,
111 color: vec4f,
112};
113struct Frame {
114 mvp: mat4x4f,
115 model: mat4x4f,
116 lightDir: vec4f,
117 lightColor: vec4f,
118 tint: vec4f,
119 cameraPos: vec4f,
120 ambient: vec4f,
121 lights: array<Light3D, 8>,
122 texBomb: vec4f,
123 parallax: vec4f,
124 view: mat4x4f,
125 clipInfo: vec4f,
126};
127struct FSIn {
128 @location(0) vNormal: vec3f,
129 @location(1) vUV: vec2f,
130 @location(2) vTint: vec4f,
131 @location(3) vWorldPos: vec3f,
132 @location(4) vCameraPos: vec3f,
133};
134@group(0) @binding(0) var<uniform> ubo: Frame;
135@group(0) @binding(7) var mainSamp: sampler;
136@group(0) @binding(9) var sceneDepth: texture_depth_2d;
137
138struct FSOut {
139 @location(0) color: vec4f,
140};
141
142@fragment
143fn fs_main(in: FSIn, @builtin(position) fragPos: vec4f) -> FSOut {
144 let color = ubo.texBomb.xyz;
145 let alpha = clamp(ubo.texBomb.w, 0.0, 1.0);
146 let bias = max(ubo.parallax.x, 0.0);
147 let screenW = max(ubo.parallax.y, 1.0);
148 let screenH = max(ubo.parallax.z, 1.0);
149 let rimStrength = clamp(ubo.parallax.w, 0.0, 1.0);
150 let rimPower = max(ubo.clipInfo.z, 0.1);
151
152 let uv = fragPos.xy / vec2f(screenW, screenH);
153 let sceneZ = textureSampleLevel(sceneDepth, mainSamp, uv, 0.0);
154 let charZ = fragPos.z;
155 let occ = smoothstep(sceneZ, sceneZ + bias, charZ);
156 if (occ < 0.5) {
157 discard;
158 }
159 let N = normalize(in.vNormal);
160 let V = normalize(in.vCameraPos - in.vWorldPos);
161 let rim = pow(clamp(1.0 - max(dot(N, V), 0.0), 0.0, 1.0), rimPower);
162 let edge = mix(1.0, rim, rimStrength);
163 var out: FSOut;
164 out.color = vec4f(color, alpha * edge * occ);
165 return out;
166}
167)wgsl";
168
169} // namespace
170
171const StyleDefinition *findStyleDefinition(const std::string &style) {
172 const auto it = std::find_if(kStyles.begin(), kStyles.end(), [&](const StyleDefinition &def) {
173 return style == def.id;
174 });
175 if (it != kStyles.end()) return &*it;
176 return findEffectDefinition(style);
177}
178
179bool isKnownStyle(const std::string &style) { return findStyleDefinition(style) != nullptr; }
180
181int styleCount() { return int(kStyles.size()) + effectStyleCount(); }
182
183std::string styleIdAt(int index) {
184 if (index < 0) return {};
185 if (index < int(kStyles.size())) return kStyles[size_t(index)].id;
186 return effectStyleIdAt(index - int(kStyles.size()));
187}
188
189bool styleSupports(const std::string &style, const std::string &feature) {
190 const StyleDefinition *def = findStyleDefinition(style);
191 if (!def) return false;
192 if (feature == "post") return def->post;
193 if (feature == "mesh") return def->mesh;
194 if (feature == "cpu") return def->cpu;
195 if (feature == "depth") return def->depth;
196 if (feature == "normal") return def->normal;
197 if (feature == "gbuffer") return def->depth || def->normal;
198 return false;
199}
200
201namespace {
202// Order is the shared GLSL/WGSL parameter ABI. Seed shader uniforms from this table.
203const StyleParameterDesc kYsaParams[] = {
204 {"lightMidpoint", 0.f, -1.f, 1.f},
205 {"lightSmoothness", 0.1f, 0.00001f, 1.f},
206 {"ambientR", 0.254717f, 0.f, 1.f},
207 {"ambientG", 0.254717f, 0.f, 1.f},
208 {"ambientB", 0.254717f, 0.f, 1.f},
209 {"shadowPower", 0.5f, 0.f, 1.f},
210 {"shadowSmoothness", 1.f, 0.00001f, 1.f},
211 {"specularStrength", 0.f, 0.f, 4.f},
212 {"specularMidpoint", 0.9f, -1.f, 1.f},
213 {"specularSmoothness", 0.1f, 0.00001f, 1.f},
214 {"specularR", 1.f, 0.f, 4.f},
215 {"specularG", 1.f, 0.f, 4.f},
216 {"specularB", 1.f, 0.f, 4.f},
217 {"rimStrength", 0.f, 0.f, 4.f},
218 {"rimMidpoint", 0.6f, 0.f, 1.f},
219 {"rimSmoothness", 0.1f, 0.00001f, 1.f},
220 {"rimDynamic", 0.5f, 0.f, 1.f},
221 {"rimR", 1.f, 0.f, 4.f},
222 {"rimG", 0.f, 0.f, 4.f},
223 {"rimB", 0.f, 0.f, 4.f},
224 {"alphaClip", 0.f, 0.f, 1.f},
225 {"rimHideOnShadow", 1.f, 0.f, 1.f},
226 {"linearLighting", 0.f, 0.f, 1.f},
227};
228const StyleParameterDesc kAnimeParams[] = {
229 {"shadowThreshold", -0.08f, -1.f, 1.f},
230 {"shadowSoftness", 0.035f, 0.001f, 0.5f},
231 {"shadowStrength", 1.f, 0.f, 1.f},
232 {"rimStrength", 0.19f, 0.f, 1.f},
233 {"skin", 0.f, 0.f, 1.f},
234 {"hair", 0.f, 0.f, 1.f},
235 {"skinDetail", 1.f, 0.f, 1.f},
236 {"specularStrength", 0.22f, 0.f, 1.f},
237 {"specularPower", 32.f, 1.f, 128.f},
238 {"shadowR", 0.64f, 0.f, 1.f},
239 {"shadowG", 0.66f, 0.f, 1.f},
240 {"shadowB", 0.79f, 0.f, 1.f},
241 {"fillGradient", 0.08f, 0.f, 1.f},
242 {"unlit", 0.f, 0.f, 1.f},
243 {"surfaceSpecular", 0.f, 0.f, 1.f},
244 {"castShadowStrength", 1.f, 0.f, 1.f},
245};
246const StyleParameterDesc kCartoonParams[] = {
247 {"bands", 3.f, 1.f, 16.f}, {"outlineStrength", 1.15f, 0.f, 4.f},
248 {"outlineThreshold", 0.12f, 0.f, 1.f}, {"posterize", 5.f, 1.f, 32.f},
249 {"softEdge", 0.08f, 0.f, 1.f}, {"outlineWidth", 1.5f, 0.5f, 8.f},
250 {"shadowLift", 0.12f, 0.f, 1.f}, {"rimPower", 2.8f, 0.1f, 16.f},
251 {"rimStrength", 0.55f, 0.f, 2.f},
252};
253const StyleParameterDesc kWatercolorParams[] = {
254 {"blurAmount", 2.4f, 0.f, 12.f}, {"edgeDarken", 2.f, 0.f, 5.f},
255 {"paperStrength", 0.65f, 0.f, 1.f}, {"distortion", 0.85f, 0.f, 3.f},
256 {"bleed", 0.62f, 0.f, 1.f}, {"saturation", 0.9f, 0.f, 2.f},
257 {"granulation", 0.65f, 0.f, 1.f},
258};
259const StyleParameterDesc kInkParams[] = {
260 {"inkContrast", 1.35f, 0.f, 4.f}, {"washLevels", 5.f, 1.f, 16.f},
261 {"edgeThreshold", 0.18f, 0.f, 1.f}, {"diffusion", 3.5f, 0.f, 12.f},
262 {"paperR", 0.96f, 0.f, 1.f}, {"paperG", 0.93f, 0.f, 1.f},
263 {"paperB", 0.86f, 0.f, 1.f}, {"inkDensity", 0.75f, 0.f, 2.f},
264 {"edgeStrength", 1.1f, 0.f, 4.f}, {"contrast", 1.25f, 0.f, 4.f},
265 {"rimBoost", 0.65f, 0.f, 2.f},
266};
267const StyleParameterDesc kPixelParams[] = {
268 {"pixelSize", 5.f, 1.f, 64.f}, {"paletteSteps", 6.f, 2.f, 32.f},
269 {"ditherStrength", 0.18f, 0.f, 1.f}, {"toonBands", 3.f, 1.f, 16.f},
270 {"sharpness", 1.f, 0.f, 2.f}, {"outline", 0.9f, 0.f, 4.f},
271};
272const StyleParameterDesc kXrayParams[] = {
273 {"colorR", 1.f, 0.f, 1.f}, {"colorG", 0.62f, 0.f, 1.f},
274 {"colorB", 0.12f, 0.f, 1.f}, {"bias", 0.0002f, 0.f, 0.02f},
275 {"rimPower", 2.2f, 0.1f, 16.f}, {"rimStrength", 0.7f, 0.f, 2.f},
276 {"alpha", 0.82f, 0.f, 1.f},
277};
278
279template <size_t N>
280const StyleParameterDesc *paramAt(const StyleParameterDesc (&params)[N], int index) {
281 return index < 0 || index >= int(N) ? nullptr : &params[size_t(index)];
282}
283} // namespace
284
285int styleParamCount(const std::string &style) {
286 if (style == "ysa") return int(std::size(kYsaParams));
287 if (style == "anime") return int(std::size(kAnimeParams));
288 if (style == "cartoon") return int(sizeof(kCartoonParams) / sizeof(kCartoonParams[0]));
289 if (style == "watercolor") return int(sizeof(kWatercolorParams) / sizeof(kWatercolorParams[0]));
290 if (style == "ink") return int(sizeof(kInkParams) / sizeof(kInkParams[0]));
291 if (style == "pixel") return int(sizeof(kPixelParams) / sizeof(kPixelParams[0]));
292 if (style == "xray") return int(sizeof(kXrayParams) / sizeof(kXrayParams[0]));
293 return effectParamCount(style);
294}
295
296std::string styleParamName(const std::string &style, int index) {
298 return desc ? desc->id : std::string{};
299}
300
301const StyleParameterDesc *styleParameterAt(const std::string &style, int index) {
302 if (style == "ysa") return paramAt(kYsaParams, index);
303 if (style == "anime") return paramAt(kAnimeParams, index);
304 if (style == "cartoon") return paramAt(kCartoonParams, index);
305 if (style == "watercolor") return paramAt(kWatercolorParams, index);
306 if (style == "ink") return paramAt(kInkParams, index);
307 if (style == "pixel") return paramAt(kPixelParams, index);
308 if (style == "xray") return paramAt(kXrayParams, index);
309 return effectParameterAt(style, index);
310}
311
312const StyleParameterDesc *findStyleParameter(const std::string &style, const std::string &name) {
313 const int count = styleParamCount(style);
314 for (int i = 0; i < count; ++i) {
315 const StyleParameterDesc *desc = styleParameterAt(style, i);
316 if (desc && name == desc->id) return desc;
317 }
318 return nullptr;
319}
320
321void bindPostUniforms(graphics::Shader *shader, const std::string &style) {
322 if (!shader) throw eve::Exception("bindPostUniforms: null shader");
323
324 if (isEffectStyle(style)) {
326 return;
327 }
328
329 if (style == "cartoon") {
330 shader->declareFloat("bands");
331 shader->declareFloat("outlineStrength");
332 shader->declareFloat("outlineThreshold");
333 shader->declareFloat("posterize");
334 shader->declareFloat("texelW");
335 shader->declareFloat("texelH");
336 shader->declareFloat("time");
337 shader->declareFloat("softEdge");
338 shader->declareFloat("outlineWidth");
339 shader->declareFloat("shadowLift");
340 shader->sendFloat("bands", 3.f);
341 shader->sendFloat("outlineStrength", 1.15f);
342 shader->sendFloat("outlineThreshold", 0.12f);
343 shader->sendFloat("posterize", 5.f);
344 shader->sendFloat("texelW", 1.f / 256.f);
345 shader->sendFloat("texelH", 1.f / 256.f);
346 shader->sendFloat("time", 0.f);
347 shader->sendFloat("softEdge", 0.08f);
348 shader->sendFloat("outlineWidth", 1.5f);
349 shader->sendFloat("shadowLift", 0.12f);
350 return;
351 }
352 if (style == "watercolor") {
353 shader->declareFloat("blurAmount");
354 shader->declareFloat("edgeDarken");
355 shader->declareFloat("paperStrength");
356 shader->declareFloat("distortion");
357 shader->declareFloat("bleed");
358 shader->declareFloat("saturation");
359 shader->declareFloat("texelW");
360 shader->declareFloat("texelH");
361 shader->declareFloat("time");
362 shader->declareFloat("granulation");
363 shader->sendFloat("blurAmount", 2.4f);
364 shader->sendFloat("edgeDarken", 2.0f);
365 shader->sendFloat("paperStrength", 0.65f);
366 shader->sendFloat("distortion", 0.85f);
367 shader->sendFloat("bleed", 0.62f);
368 shader->sendFloat("saturation", 0.9f);
369 shader->sendFloat("texelW", 1.f / 256.f);
370 shader->sendFloat("texelH", 1.f / 256.f);
371 shader->sendFloat("time", 0.f);
372 shader->sendFloat("granulation", 0.65f);
373 return;
374 }
375 if (style == "ink") {
376 shader->declareFloat("inkContrast");
377 shader->declareFloat("washLevels");
378 shader->declareFloat("edgeThreshold");
379 shader->declareFloat("diffusion");
380 shader->declareFloat("paperR");
381 shader->declareFloat("paperG");
382 shader->declareFloat("paperB");
383 shader->declareFloat("inkDensity");
384 shader->declareFloat("texelW");
385 shader->declareFloat("texelH");
386 shader->declareFloat("time");
387 shader->declareFloat("edgeStrength");
388 shader->sendFloat("inkContrast", 1.35f);
389 shader->sendFloat("washLevels", 5.f);
390 shader->sendFloat("edgeThreshold", 0.18f);
391 shader->sendFloat("diffusion", 3.5f);
392 shader->sendFloat("paperR", 0.96f);
393 shader->sendFloat("paperG", 0.93f);
394 shader->sendFloat("paperB", 0.86f);
395 shader->sendFloat("inkDensity", 0.75f);
396 shader->sendFloat("texelW", 1.f / 256.f);
397 shader->sendFloat("texelH", 1.f / 256.f);
398 shader->sendFloat("time", 0.f);
399 shader->sendFloat("edgeStrength", 1.1f);
400 return;
401 }
402 if (style == "pixel") {
403 shader->declareFloat("pixelSize");
404 shader->declareFloat("paletteSteps");
405 shader->declareFloat("ditherStrength");
406 shader->declareFloat("toonBands");
407 shader->declareFloat("sharpness");
408 shader->declareFloat("texelW");
409 shader->declareFloat("texelH");
410 shader->declareFloat("time");
411 shader->declareFloat("screenW");
412 shader->declareFloat("screenH");
413 shader->declareFloat("outline");
414 shader->sendFloat("pixelSize", 5.f);
415 shader->sendFloat("paletteSteps", 6.f);
416 shader->sendFloat("ditherStrength", 0.18f);
417 shader->sendFloat("toonBands", 3.f);
418 shader->sendFloat("sharpness", 1.f);
419 shader->sendFloat("texelW", 1.f / 256.f);
420 shader->sendFloat("texelH", 1.f / 256.f);
421 shader->sendFloat("time", 0.f);
422 shader->sendFloat("screenW", 256.f);
423 shader->sendFloat("screenH", 256.f);
424 shader->sendFloat("outline", 0.9f);
425 return;
426 }
427 throw eve::Exception("bindPostUniforms: unknown style '%s'", style.c_str());
428}
429
430void bindMeshUniforms(graphics::Shader *shader, const std::string &style) {
431 if (!shader) throw eve::Exception("bindMeshUniforms: null shader");
432 if (style == "ysa") {
433 for (const auto &param : kYsaParams) {
434 shader->declareFloat(param.id);
435 shader->sendFloat(param.id, param.defaultValue);
436 }
437 return;
438 }
439 if (style == "anime") {
440 for (const auto &param : kAnimeParams) {
441 shader->declareFloat(param.id);
442 shader->sendFloat(param.id, param.defaultValue);
443 }
444 return;
445 }
446
447 if (isEffectStyle(style)) {
449 return;
450 }
451
452 if (style == "cartoon") {
453 shader->declareFloat("bands");
454 shader->declareFloat("rimPower");
455 shader->declareFloat("rimStrength");
456 shader->declareFloat("posterize");
457 shader->sendFloat("bands", 4.f);
458 shader->sendFloat("rimPower", 2.8f);
459 shader->sendFloat("rimStrength", 0.55f);
460 shader->sendFloat("posterize", 6.f);
461 return;
462 }
463 if (style == "ink") {
464 shader->declareFloat("washLevels");
465 shader->declareFloat("edgeThreshold");
466 shader->declareFloat("inkDensity");
467 shader->declareFloat("contrast");
468 shader->declareFloat("paperR");
469 shader->declareFloat("paperG");
470 shader->declareFloat("paperB");
471 shader->declareFloat("rimBoost");
472 shader->sendFloat("washLevels", 5.f);
473 shader->sendFloat("edgeThreshold", 0.35f);
474 shader->sendFloat("inkDensity", 0.9f);
475 shader->sendFloat("contrast", 1.25f);
476 shader->sendFloat("paperR", 0.93f);
477 shader->sendFloat("paperG", 0.90f);
478 shader->sendFloat("paperB", 0.82f);
479 shader->sendFloat("rimBoost", 0.65f);
480 return;
481 }
482 if (style == "xray") {
483 shader->declareFloat("colorR");
484 shader->declareFloat("colorG");
485 shader->declareFloat("colorB");
486 shader->declareFloat("bias");
487 shader->declareFloat("screenW");
488 shader->declareFloat("screenH");
489 shader->declareFloat("rimPower");
490 shader->declareFloat("rimStrength");
491 shader->declareFloat("alpha");
492 shader->sendFloat("colorR", 1.f);
493 shader->sendFloat("colorG", 0.62f);
494 shader->sendFloat("colorB", 0.12f);
495 shader->sendFloat("bias", 0.0002f);
496 shader->sendFloat("screenW", 256.f);
497 shader->sendFloat("screenH", 256.f);
498 shader->sendFloat("rimPower", 2.2f);
499 shader->sendFloat("rimStrength", 0.7f);
500 shader->sendFloat("alpha", 0.82f);
501 return;
502 }
503 throw eve::Exception("bindMeshUniforms: style '%s' has no mesh shader", style.c_str());
504}
505
506graphics::Shader *createPostShader(graphics::Graphics *gfx, const std::string &style) {
507 if (!gfx) throw eve::Exception("createPostShader: null graphics");
508 if (isEffectStyle(style)) return createEffectPostShader(gfx, style);
509 const StyleDefinition *def = findStyleDefinition(style);
510 if (!def) throw eve::Exception("createPostShader: unknown style '%s'", style.c_str());
511 if (!def->post)
512 throw eve::Exception("createPostShader: style '%s' has no post technique", style.c_str());
513
514 if (gfx->getBackendName() == "webgpu") {
515 const char *body = style == "cartoon" ? shaders::kCartoon
516 : style == "watercolor" ? shaders::kWatercolor
517 : style == "ink" ? shaders::kInk
519 graphics::Shader *sh = gfx->newShaderFromWgsl({}, std::string(shaders::kCommon) + body);
520 if (!sh || !sh->gpuHandle)
521 throw eve::Exception("createPostShader: failed to create '%s' WGSL", style.c_str());
522 bindPostUniforms(sh, style);
523 return sh;
524 }
525
526 std::vector<uint32_t> frag;
527 if (style == "cartoon")
528 frag = copySpv(cartoon_post_frag_spv, cartoon_post_frag_spv_count);
529 else if (style == "watercolor")
530 frag = copySpv(watercolor_post_frag_spv, watercolor_post_frag_spv_count);
531 else if (style == "ink")
532 frag = copySpv(ink_post_frag_spv, ink_post_frag_spv_count);
533 else
534 frag = copySpv(pixel_post_frag_spv, pixel_post_frag_spv_count);
535
536 graphics::Shader *sh = gfx->newShaderFromSpv({}, frag);
537 if (!sh || !sh->gpuHandle)
538 throw eve::Exception("createPostShader: failed to create '%s'", style.c_str());
539 bindPostUniforms(sh, style);
540 return sh;
541}
542
543graphics::Shader *createMeshShader(graphics::Graphics *gfx, const std::string &style) {
544 if (!gfx) throw eve::Exception("createMeshShader: null graphics");
545 if (isEffectStyle(style)) return createEffectMeshShader(gfx, style);
546
547 if (style == "ysa") {
548 // The Vulkan path needs its own vertex shader: mesh3d_toon.vert (shared
549 // with anime/cartoon) has no joint/weight inputs, no skin-palette
550 // transform and no vertex-color multiply, while the WebGPU path runs the
551 // backend's standard mesh vertex shader, which does all three.
553 gfx->getBackendName() == "webgpu"
554 ? gfx->newMeshShaderFromWgsl({}, std::string(shaders::kMeshCommon) + shaders::kYsaMesh)
555 : gfx->newMeshShaderFromSpv(copySpv(ysa_mesh_vert_spv, ysa_mesh_vert_spv_count),
556 copySpv(ysa_mesh_frag_spv, ysa_mesh_frag_spv_count));
557 if (!shader || !shader->gpuHandle) throw eve::Exception("createMeshShader: failed to create ysa mesh shader");
558 bindMeshUniforms(shader, style);
559 return shader;
560 }
561
562 if (style == "anime") {
564 gfx->getBackendName() == "webgpu"
565 ? gfx->newMeshShaderFromWgsl({}, std::string(shaders::kMeshCommon) + shaders::kAnimeMesh)
566 : gfx->newMeshShaderFromSpv(copySpv(mesh3d_toon_vert_spv, mesh3d_toon_vert_spv_count),
567 copySpv(anime_mesh_frag_spv, anime_mesh_frag_spv_count));
568 if (!shader || !shader->gpuHandle) throw eve::Exception("createMeshShader: failed to create anime mesh shader");
569 bindMeshUniforms(shader, style);
570 return shader;
571 }
572
573 if (style == "cartoon") {
574 if (gfx->getBackendName() == "webgpu") {
577 if (!sh || !sh->gpuHandle)
578 throw eve::Exception("createMeshShader: failed to create cartoon WGSL");
579 bindMeshUniforms(sh, style);
580 return sh;
581 }
582 auto vert = copySpv(mesh3d_toon_vert_spv, mesh3d_toon_vert_spv_count);
583 auto frag = copySpv(mesh3d_toon_frag_spv, mesh3d_toon_frag_spv_count);
585 if (!sh || !sh->gpuHandle)
586 throw eve::Exception("createMeshShader: failed to create cartoon mesh shader");
587 bindMeshUniforms(sh, style);
588 return sh;
589 }
590 if (style == "ink") {
591 if (gfx->getBackendName() == "webgpu") {
593 {}, std::string(shaders::kMeshCommon) + shaders::kInkMesh);
594 if (!sh || !sh->gpuHandle)
595 throw eve::Exception("createMeshShader: failed to create ink WGSL");
596 bindMeshUniforms(sh, style);
597 return sh;
598 }
599 auto vert = copySpv(mesh3d_toon_vert_spv, mesh3d_toon_vert_spv_count);
600 auto frag = copySpv(ink_mesh_frag_spv, ink_mesh_frag_spv_count);
602 if (!sh || !sh->gpuHandle)
603 throw eve::Exception("createMeshShader: failed to create ink mesh shader");
604 bindMeshUniforms(sh, style);
605 return sh;
606 }
607 if (style == "xray") {
608 // WebGPU has no SPIR-V mesh shaders (WGSL only); Vulkan uses SPIR-V.
609 if (gfx->getBackendName() == "webgpu") {
610 graphics::Shader *sh = gfx->newMeshShaderFromWgsl(kXrayMeshVertWgsl, kXrayMeshFragWgsl);
611 if (!sh || !sh->gpuHandle)
612 throw eve::Exception("createMeshShader: failed to create xray mesh shader (wgsl)");
613 sh->setXray(true);
614 bindMeshUniforms(sh, style);
615 return sh;
616 }
617 auto vert = copySpv(mesh3d_toon_vert_spv, mesh3d_toon_vert_spv_count);
618 auto frag = copySpv(xray_mesh_frag_spv, xray_mesh_frag_spv_count);
620 if (!sh || !sh->gpuHandle)
621 throw eve::Exception("createMeshShader: failed to create xray mesh shader");
622 sh->setXray(true);
623 bindMeshUniforms(sh, style);
624 return sh;
625 }
626 throw eve::Exception(
627 "createMeshShader: style '%s' has no mesh variant (use watercolor/pixel as post)",
628 style.c_str());
629}
630
631} // namespace eve::stylize
scene::NodeDesc desc
vk::ShaderModule vert
vk::ShaderModule frag
std::string name
Shader * shader
std::uint32_t count
std::string body
uint32_t index
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
virtual std::string getBackendName() const =0
Renderer backend id used by sibling modules (e.g. Gpgpu).
virtual Shader * newMeshShaderFromSpv(const std::vector< uint32_t > &vertSpv, const std::vector< uint32_t > &fragSpv)=0
Create a Mesh3D custom shader (MeshVertex + Frame UBO + albedo). Empty vert → default mesh3d....
virtual Shader * newShaderFromWgsl(const std::string &vertWgsl, const std::string &fragWgsl)=0
Create a 2D custom shader from WGSL source (WebGPU backend). Empty vert → default textured vertex sha...
virtual Shader * newShaderFromSpv(const std::vector< uint32_t > &vertSpv, const std::vector< uint32_t > &fragSpv)=0
Create a custom 2D shader from SPIR-V words (vert + frag). Owned by Graphics. Vertex stage may be emp...
virtual Shader * newMeshShaderFromWgsl(const std::string &vertWgsl, const std::string &fragWgsl)=0
Create a Mesh3D custom shader from WGSL source (WebGPU backend). The WGSL must declare the engine's F...
Custom GPU program.
Definition Shader.h:39
void setXray(bool x)
X-ray mesh shader. When true, drawMeshShader renders this mesh a second pass over the already-lit sce...
Definition Shader.h:72
std::vector< ParamSpec > params
constexpr const char * kPixel
Definition StylizeWgsl.h:19
constexpr const char * kWatercolor
Definition StylizeWgsl.h:13
constexpr const char * kCommon
Definition StylizeWgsl.h:4
constexpr const char * kInk
Definition StylizeWgsl.h:16
constexpr const char * kMeshCommon
Definition StylizeWgsl.h:22
constexpr const char * kCartoon
Definition StylizeWgsl.h:10
constexpr const char * kInkMesh
Definition StylizeWgsl.h:28
constexpr const char * kCartoonMesh
Definition StylizeWgsl.h:25
graphics::Shader * createMeshShader(graphics::Graphics *gfx, const std::string &style)
Creates mesh shader.
void bindEffectMeshUniforms(graphics::Shader *shader, const std::string &style)
Binds effect mesh uniforms.
graphics::Shader * createEffectMeshShader(graphics::Graphics *gfx, const std::string &style)
Creates effect mesh shader.
bool styleSupports(const std::string &style, const std::string &feature)
Style supports.
std::string styleIdAt(int index)
Style id at.
bool isEffectStyle(const std::string &style)
True when style names one of the ported effect shaders.
graphics::Shader * createPostShader(graphics::Graphics *gfx, const std::string &style)
Creates post shader.
graphics::Shader * createEffectPostShader(graphics::Graphics *gfx, const std::string &style)
Creates effect post shader.
void bindMeshUniforms(graphics::Shader *shader, const std::string &style)
Binds mesh uniforms.
int effectStyleCount()
Effect style count.
int styleCount()
Style count.
int styleParamCount(const std::string &style)
Style param count.
void bindEffectPostUniforms(graphics::Shader *shader, const std::string &style)
Binds effect post uniforms.
void bindPostUniforms(graphics::Shader *shader, const std::string &style)
Binds post uniforms.
bool isKnownStyle(const std::string &style)
True when known style.
std::string effectStyleIdAt(int index)
Effect style id at.
const StyleDefinition * findEffectDefinition(const std::string &style)
Effect-only StyleDefinition lookup, or nullptr when unknown.
const StyleParameterDesc * findStyleParameter(const std::string &style, const std::string &name)
Finds style parameter.
std::string styleParamName(const std::string &style, int index)
Style param name.
int effectParamCount(const std::string &style)
Effect param count.
const StyleParameterDesc * styleParameterAt(const std::string &style, int index)
Style parameter at.
const StyleParameterDesc * effectParameterAt(const std::string &style, int index)
Effect parameter at.
const StyleDefinition * findStyleDefinition(const std::string &style)
Return a built-in definition, or nullptr when the id is unknown.
Immutable capability metadata for one built-in stylization recipe.
Tooling metadata for one user-facing float parameter.