6#include "graphics/shaders/mesh3d_toon_frag_spv.inc"
7#include "graphics/shaders/mesh3d_toon_vert_spv.inc"
8#include "stylize/shaders/cartoon_post_frag_spv.inc"
9#include "stylize/shaders/ink_mesh_frag_spv.inc"
10#include "stylize/shaders/ink_post_frag_spv.inc"
11#include "stylize/shaders/pixel_post_frag_spv.inc"
12#include "stylize/shaders/watercolor_post_frag_spv.inc"
13#include "stylize/shaders/xray_mesh_frag_spv.inc"
23const std::array<const char *, 5> kStyles = {
"cartoon",
"watercolor",
"ink",
"pixel",
"xray"};
25std::vector<uint32_t> copySpv(
const uint32_t *data,
size_t count) {
26 return std::vector<uint32_t>(data, data + count);
33const char *kXrayMeshVertWgsl = R
"wgsl(
35 @location(0) pos: vec3f,
36 @location(1) normal: vec3f,
37 @location(2) uv: vec2f,
51 lights: array<Light3D, 8>,
58 @builtin(position) pos: vec4f,
59 @location(0) vNormal: vec3f,
60 @location(1) vUV: vec2f,
61 @location(2) vTint: vec4f,
62 @location(3) vWorldPos: vec3f,
63 @location(4) vCameraPos: vec3f,
65@group(0) @binding(0) var<uniform> ubo: Frame;
66fn inverse3x3(m: mat3x3f) -> mat3x3f {
67 let a = m[0].x; let b = m[1].x; let c = m[2].x;
68 let d = m[0].y; let e = m[1].y; let f = m[2].y;
69 let g = m[0].z; let h = m[1].z; let i = m[2].z;
70 let det = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
72 vec3f((e * i - f * h) / det, (f * g - d * i) / det, (d * h - e * g) / det),
73 vec3f((c * h - b * i) / det, (a * i - c * g) / det, (b * g - a * h) / det),
74 vec3f((b * f - c * e) / det, (c * d - a * f) / det, (a * e - b * d) / det),
78fn vs_main(in: VSIn) -> VSOut {
80 out.pos = ubo.mvp * vec4f(in.pos, 1.0);
81 let world = ubo.model * vec4f(in.pos, 1.0);
82 out.vWorldPos = world.xyz;
83 let nrm = transpose(inverse3x3(mat3x3f(ubo.model[0].xyz, ubo.model[1].xyz, ubo.model[2].xyz))) * in.normal;
84 out.vNormal = normalize(nrm);
87 out.vCameraPos = ubo.cameraPos.xyz;
92const char *kXrayMeshFragWgsl = R
"wgsl(
105 lights: array<Light3D, 8>,
112 @location(0) vNormal: vec3f,
113 @location(1) vUV: vec2f,
114 @location(2) vTint: vec4f,
115 @location(3) vWorldPos: vec3f,
116 @location(4) vCameraPos: vec3f,
118@group(0) @binding(0) var<uniform> ubo: Frame;
119@group(0) @binding(7) var mainSamp: sampler;
120@group(0) @binding(9) var sceneDepth: texture_depth_2d;
123 @location(0) color: vec4f,
127fn fs_main(in: FSIn, @builtin(position) fragPos: vec4f) -> FSOut {
128 let color = ubo.texBomb.xyz;
129 let alpha = clamp(ubo.texBomb.w, 0.0, 1.0);
130 let bias = max(ubo.parallax.x, 0.0);
131 let screenW = max(ubo.parallax.y, 1.0);
132 let screenH = max(ubo.parallax.z, 1.0);
133 let rimStrength = clamp(ubo.parallax.w, 0.0, 1.0);
134 let rimPower = max(ubo.clipInfo.z, 0.1);
136 let uv = fragPos.xy / vec2f(screenW, screenH);
137 let sceneZ = textureSampleLevel(sceneDepth, mainSamp, uv, 0.0);
138 let charZ = fragPos.z;
139 let occ = smoothstep(sceneZ, sceneZ + bias, charZ);
143 let N = normalize(in.vNormal);
144 let V = normalize(in.vCameraPos - in.vWorldPos);
145 let rim = pow(clamp(1.0 - max(dot(N, V), 0.0), 0.0, 1.0), rimPower);
146 let edge = mix(1.0, rim, rimStrength);
148 out.color = vec4f(color, alpha * edge * occ);
156 return std::find(kStyles.begin(), kStyles.end(), style) != kStyles.end();
162 if (index < 0 || index >=
int(kStyles.size()))
return {};
163 return kStyles[size_t(index)];
168 if (feature ==
"post" || feature ==
"cpu")
return true;
169 if (feature ==
"mesh")
return style ==
"cartoon" || style ==
"ink" || style ==
"xray";
170 if (feature ==
"gbuffer")
return true;
175const char *kCartoonParams[] = {
"bands",
"outlineStrength",
"outlineThreshold",
"posterize",
176 "texelW",
"texelH",
"time",
"softEdge",
177 "outlineWidth",
"shadowLift"};
178const char *kWatercolorParams[] = {
"blurAmount",
"edgeDarken",
"paperStrength",
"distortion",
179 "bleed",
"saturation",
"texelW",
"texelH",
180 "time",
"granulation"};
181const char *kInkParams[] = {
"inkContrast",
"washLevels",
"edgeThreshold",
"diffusion",
182 "paperR",
"paperG",
"paperB",
"inkDensity",
183 "texelW",
"texelH",
"time",
"edgeStrength"};
184const char *kPixelParams[] = {
"pixelSize",
"paletteSteps",
"ditherStrength",
"toonBands",
185 "sharpness",
"texelW",
"texelH",
"time",
186 "screenW",
"screenH",
"outline"};
187const char *kXrayParams[] = {
"colorR",
"colorG",
"colorB",
"bias",
188 "screenW",
"screenH",
"rimPower",
"rimStrength",
"alpha"};
192 if (style ==
"cartoon")
return int(
sizeof(kCartoonParams) /
sizeof(kCartoonParams[0]));
193 if (style ==
"watercolor")
return int(
sizeof(kWatercolorParams) /
sizeof(kWatercolorParams[0]));
194 if (style ==
"ink")
return int(
sizeof(kInkParams) /
sizeof(kInkParams[0]));
195 if (style ==
"pixel")
return int(
sizeof(kPixelParams) /
sizeof(kPixelParams[0]));
196 if (style ==
"xray")
return int(
sizeof(kXrayParams) /
sizeof(kXrayParams[0]));
202 if (style ==
"cartoon")
return kCartoonParams[index];
203 if (style ==
"watercolor")
return kWatercolorParams[index];
204 if (style ==
"ink")
return kInkParams[index];
205 if (style ==
"pixel")
return kPixelParams[index];
206 if (style ==
"xray")
return kXrayParams[index];
213 if (style ==
"cartoon") {
214 shader->declareFloat(
"bands");
215 shader->declareFloat(
"outlineStrength");
216 shader->declareFloat(
"outlineThreshold");
217 shader->declareFloat(
"posterize");
218 shader->declareFloat(
"texelW");
219 shader->declareFloat(
"texelH");
220 shader->declareFloat(
"time");
221 shader->declareFloat(
"softEdge");
222 shader->declareFloat(
"outlineWidth");
223 shader->declareFloat(
"shadowLift");
224 shader->sendFloat(
"bands", 3.f);
225 shader->sendFloat(
"outlineStrength", 1.15f);
226 shader->sendFloat(
"outlineThreshold", 0.12f);
227 shader->sendFloat(
"posterize", 5.f);
228 shader->sendFloat(
"texelW", 1.f / 256.f);
229 shader->sendFloat(
"texelH", 1.f / 256.f);
230 shader->sendFloat(
"time", 0.f);
231 shader->sendFloat(
"softEdge", 0.08f);
232 shader->sendFloat(
"outlineWidth", 1.5f);
233 shader->sendFloat(
"shadowLift", 0.12f);
236 if (style ==
"watercolor") {
237 shader->declareFloat(
"blurAmount");
238 shader->declareFloat(
"edgeDarken");
239 shader->declareFloat(
"paperStrength");
240 shader->declareFloat(
"distortion");
241 shader->declareFloat(
"bleed");
242 shader->declareFloat(
"saturation");
243 shader->declareFloat(
"texelW");
244 shader->declareFloat(
"texelH");
245 shader->declareFloat(
"time");
246 shader->declareFloat(
"granulation");
247 shader->sendFloat(
"blurAmount", 2.4f);
248 shader->sendFloat(
"edgeDarken", 2.0f);
249 shader->sendFloat(
"paperStrength", 0.65f);
250 shader->sendFloat(
"distortion", 0.85f);
251 shader->sendFloat(
"bleed", 0.62f);
252 shader->sendFloat(
"saturation", 0.9f);
253 shader->sendFloat(
"texelW", 1.f / 256.f);
254 shader->sendFloat(
"texelH", 1.f / 256.f);
255 shader->sendFloat(
"time", 0.f);
256 shader->sendFloat(
"granulation", 0.65f);
259 if (style ==
"ink") {
260 shader->declareFloat(
"inkContrast");
261 shader->declareFloat(
"washLevels");
262 shader->declareFloat(
"edgeThreshold");
263 shader->declareFloat(
"diffusion");
264 shader->declareFloat(
"paperR");
265 shader->declareFloat(
"paperG");
266 shader->declareFloat(
"paperB");
267 shader->declareFloat(
"inkDensity");
268 shader->declareFloat(
"texelW");
269 shader->declareFloat(
"texelH");
270 shader->declareFloat(
"time");
271 shader->declareFloat(
"edgeStrength");
272 shader->sendFloat(
"inkContrast", 1.35f);
273 shader->sendFloat(
"washLevels", 5.f);
274 shader->sendFloat(
"edgeThreshold", 0.18f);
275 shader->sendFloat(
"diffusion", 3.5f);
276 shader->sendFloat(
"paperR", 0.96f);
277 shader->sendFloat(
"paperG", 0.93f);
278 shader->sendFloat(
"paperB", 0.86f);
279 shader->sendFloat(
"inkDensity", 0.75f);
280 shader->sendFloat(
"texelW", 1.f / 256.f);
281 shader->sendFloat(
"texelH", 1.f / 256.f);
282 shader->sendFloat(
"time", 0.f);
283 shader->sendFloat(
"edgeStrength", 1.1f);
286 if (style ==
"pixel") {
287 shader->declareFloat(
"pixelSize");
288 shader->declareFloat(
"paletteSteps");
289 shader->declareFloat(
"ditherStrength");
290 shader->declareFloat(
"toonBands");
291 shader->declareFloat(
"sharpness");
292 shader->declareFloat(
"texelW");
293 shader->declareFloat(
"texelH");
294 shader->declareFloat(
"time");
295 shader->declareFloat(
"screenW");
296 shader->declareFloat(
"screenH");
297 shader->declareFloat(
"outline");
298 shader->sendFloat(
"pixelSize", 5.f);
299 shader->sendFloat(
"paletteSteps", 6.f);
300 shader->sendFloat(
"ditherStrength", 0.18f);
301 shader->sendFloat(
"toonBands", 3.f);
302 shader->sendFloat(
"sharpness", 1.f);
303 shader->sendFloat(
"texelW", 1.f / 256.f);
304 shader->sendFloat(
"texelH", 1.f / 256.f);
305 shader->sendFloat(
"time", 0.f);
306 shader->sendFloat(
"screenW", 256.f);
307 shader->sendFloat(
"screenH", 256.f);
308 shader->sendFloat(
"outline", 0.9f);
311 throw eve::Exception(
"bindPostUniforms: unknown style '%s'", style.c_str());
317 if (style ==
"cartoon") {
318 shader->declareFloat(
"bands");
319 shader->declareFloat(
"rimPower");
320 shader->declareFloat(
"rimStrength");
321 shader->declareFloat(
"posterize");
322 shader->sendFloat(
"bands", 4.f);
323 shader->sendFloat(
"rimPower", 2.8f);
324 shader->sendFloat(
"rimStrength", 0.55f);
325 shader->sendFloat(
"posterize", 6.f);
328 if (style ==
"ink") {
329 shader->declareFloat(
"washLevels");
330 shader->declareFloat(
"edgeThreshold");
331 shader->declareFloat(
"inkDensity");
332 shader->declareFloat(
"contrast");
333 shader->declareFloat(
"paperR");
334 shader->declareFloat(
"paperG");
335 shader->declareFloat(
"paperB");
336 shader->declareFloat(
"rimBoost");
337 shader->sendFloat(
"washLevels", 5.f);
338 shader->sendFloat(
"edgeThreshold", 0.35f);
339 shader->sendFloat(
"inkDensity", 0.9f);
340 shader->sendFloat(
"contrast", 1.25f);
341 shader->sendFloat(
"paperR", 0.93f);
342 shader->sendFloat(
"paperG", 0.90f);
343 shader->sendFloat(
"paperB", 0.82f);
344 shader->sendFloat(
"rimBoost", 0.65f);
347 if (style ==
"xray") {
348 shader->declareFloat(
"colorR");
349 shader->declareFloat(
"colorG");
350 shader->declareFloat(
"colorB");
351 shader->declareFloat(
"bias");
352 shader->declareFloat(
"screenW");
353 shader->declareFloat(
"screenH");
354 shader->declareFloat(
"rimPower");
355 shader->declareFloat(
"rimStrength");
356 shader->declareFloat(
"alpha");
357 shader->sendFloat(
"colorR", 1.f);
358 shader->sendFloat(
"colorG", 0.62f);
359 shader->sendFloat(
"colorB", 0.12f);
360 shader->sendFloat(
"bias", 0.0002f);
361 shader->sendFloat(
"screenW", 256.f);
362 shader->sendFloat(
"screenH", 256.f);
363 shader->sendFloat(
"rimPower", 2.2f);
364 shader->sendFloat(
"rimStrength", 0.7f);
365 shader->sendFloat(
"alpha", 0.82f);
368 throw eve::Exception(
"bindMeshUniforms: style '%s' has no mesh shader", style.c_str());
372 if (!gfx)
throw eve::Exception(
"createPostShader: null graphics");
374 throw eve::Exception(
"createPostShader: unknown style '%s'", style.c_str());
376 std::vector<uint32_t>
frag;
377 if (style ==
"cartoon")
378 frag = copySpv(cartoon_post_frag_spv, cartoon_post_frag_spv_count);
379 else if (style ==
"watercolor")
380 frag = copySpv(watercolor_post_frag_spv, watercolor_post_frag_spv_count);
381 else if (style ==
"ink")
382 frag = copySpv(ink_post_frag_spv, ink_post_frag_spv_count);
384 frag = copySpv(pixel_post_frag_spv, pixel_post_frag_spv_count);
388 throw eve::Exception(
"createPostShader: failed to create '%s'", style.c_str());
394 if (!gfx)
throw eve::Exception(
"createMeshShader: null graphics");
396 if (style ==
"cartoon") {
397 auto vert = copySpv(mesh3d_toon_vert_spv, mesh3d_toon_vert_spv_count);
398 auto frag = copySpv(mesh3d_toon_frag_spv, mesh3d_toon_frag_spv_count);
401 throw eve::Exception(
"createMeshShader: failed to create cartoon mesh shader");
405 if (style ==
"ink") {
406 auto vert = copySpv(mesh3d_toon_vert_spv, mesh3d_toon_vert_spv_count);
407 auto frag = copySpv(ink_mesh_frag_spv, ink_mesh_frag_spv_count);
410 throw eve::Exception(
"createMeshShader: failed to create ink mesh shader");
414 if (style ==
"xray") {
419 throw eve::Exception(
"createMeshShader: failed to create xray mesh shader (wgsl)");
424 auto vert = copySpv(mesh3d_toon_vert_spv, mesh3d_toon_vert_spv_count);
425 auto frag = copySpv(xray_mesh_frag_spv, xray_mesh_frag_spv_count);
428 throw eve::Exception(
"createMeshShader: failed to create xray mesh shader");
434 "createMeshShader: style '%s' has no mesh variant (use watercolor/pixel as post)",
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 * 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...
void setXray(bool x)
X-ray mesh shader. When true, drawMeshShader renders this mesh a second pass over the already-lit sce...
std::string styleIdAt(int index)
graphics::Shader * createPostShader(graphics::Graphics *gfx, const std::string &style)
graphics::Shader * createMeshShader(graphics::Graphics *gfx, const std::string &style)
void bindPostUniforms(graphics::Shader *shader, const std::string &style)
std::string styleParamName(const std::string &style, int index)
void bindMeshUniforms(graphics::Shader *shader, const std::string &style)
bool isKnownStyle(const std::string &style)
bool styleSupports(const std::string &style, const std::string &feature)
int styleParamCount(const std::string &style)