载入中...
搜索中...
未找到
StyleShaders.cpp
浏览该文件的文档.
2
3#include "common/Exception.h"
4#include "graphics/Graphics.h"
5#include "graphics/Shader.h"
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"
14
15#include <algorithm>
16#include <array>
17#include <cstddef>
18#include <vector>
19
20namespace eve::stylize {
21namespace {
22
23const std::array<const char *, 5> kStyles = {"cartoon", "watercolor", "ink", "pixel", "xray"};
24
25std::vector<uint32_t> copySpv(const uint32_t *data, size_t count) {
26 return std::vector<uint32_t>(data, data + count);
27}
28
29// WebGPU (WGSL) X-ray mesh shaders. Must match the engine's mesh3d Frame UBO
30// (group 0 binding 0, std140 = Mesh3DUBO) and shared bindings (main sampler 7,
31// scene depth 9). X-ray params are packed into texBomb/parallax/clipInfo by the
32// backend flush (see bindMeshUniforms("xray") ordering below).
33const char *kXrayMeshVertWgsl = R"wgsl(
34struct VSIn {
35 @location(0) pos: vec3f,
36 @location(1) normal: vec3f,
37 @location(2) uv: vec2f,
38};
39struct Light3D {
40 posRadius: vec4f,
41 color: vec4f,
42};
43struct Frame {
44 mvp: mat4x4f,
45 model: mat4x4f,
46 lightDir: vec4f,
47 lightColor: vec4f,
48 tint: vec4f,
49 cameraPos: vec4f,
50 ambient: vec4f,
51 lights: array<Light3D, 8>,
52 texBomb: vec4f,
53 parallax: vec4f,
54 view: mat4x4f,
55 clipInfo: vec4f,
56};
57struct VSOut {
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,
64};
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);
71 return mat3x3f(
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),
75 );
76}
77@vertex
78fn vs_main(in: VSIn) -> VSOut {
79 var out: 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);
85 out.vUV = in.uv;
86 out.vTint = ubo.tint;
87 out.vCameraPos = ubo.cameraPos.xyz;
88 return out;
89}
90)wgsl";
91
92const char *kXrayMeshFragWgsl = R"wgsl(
93struct Light3D {
94 posRadius: vec4f,
95 color: vec4f,
96};
97struct Frame {
98 mvp: mat4x4f,
99 model: mat4x4f,
100 lightDir: vec4f,
101 lightColor: vec4f,
102 tint: vec4f,
103 cameraPos: vec4f,
104 ambient: vec4f,
105 lights: array<Light3D, 8>,
106 texBomb: vec4f,
107 parallax: vec4f,
108 view: mat4x4f,
109 clipInfo: vec4f,
110};
111struct FSIn {
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,
117};
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;
121
122struct FSOut {
123 @location(0) color: vec4f,
124};
125
126@fragment
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);
135
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);
140 if (occ < 0.5) {
141 discard;
142 }
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);
147 var out: FSOut;
148 out.color = vec4f(color, alpha * edge * occ);
149 return out;
150}
151)wgsl";
152
153} // namespace
154
155bool isKnownStyle(const std::string &style) {
156 return std::find(kStyles.begin(), kStyles.end(), style) != kStyles.end();
157}
158
159int styleCount() { return int(kStyles.size()); }
160
161std::string styleIdAt(int index) {
162 if (index < 0 || index >= int(kStyles.size())) return {};
163 return kStyles[size_t(index)];
164}
165
166bool styleSupports(const std::string &style, const std::string &feature) {
167 if (!isKnownStyle(style)) return false;
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; // depth/normal available via graphics.RenderControl
171 return false;
172}
173
174namespace {
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"};
189} // namespace
190
191int styleParamCount(const std::string &style) {
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]));
197 return 0;
198}
199
200std::string styleParamName(const std::string &style, int index) {
201 if (index < 0 || index >= styleParamCount(style)) return {};
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];
207 return {};
208}
209
210void bindPostUniforms(graphics::Shader *shader, const std::string &style) {
211 if (!shader) throw eve::Exception("bindPostUniforms: null shader");
212
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);
234 return;
235 }
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);
257 return;
258 }
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);
284 return;
285 }
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);
309 return;
310 }
311 throw eve::Exception("bindPostUniforms: unknown style '%s'", style.c_str());
312}
313
314void bindMeshUniforms(graphics::Shader *shader, const std::string &style) {
315 if (!shader) throw eve::Exception("bindMeshUniforms: null shader");
316
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);
326 return;
327 }
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);
345 return;
346 }
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);
366 return;
367 }
368 throw eve::Exception("bindMeshUniforms: style '%s' has no mesh shader", style.c_str());
369}
370
371graphics::Shader *createPostShader(graphics::Graphics *gfx, const std::string &style) {
372 if (!gfx) throw eve::Exception("createPostShader: null graphics");
373 if (!isKnownStyle(style))
374 throw eve::Exception("createPostShader: unknown style '%s'", style.c_str());
375
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);
383 else
384 frag = copySpv(pixel_post_frag_spv, pixel_post_frag_spv_count);
385
386 graphics::Shader *sh = gfx->newShaderFromSpv({}, frag);
387 if (!sh || !sh->gpuHandle)
388 throw eve::Exception("createPostShader: failed to create '%s'", style.c_str());
389 bindPostUniforms(sh, style);
390 return sh;
391}
392
393graphics::Shader *createMeshShader(graphics::Graphics *gfx, const std::string &style) {
394 if (!gfx) throw eve::Exception("createMeshShader: null graphics");
395
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);
400 if (!sh || !sh->gpuHandle)
401 throw eve::Exception("createMeshShader: failed to create cartoon mesh shader");
402 bindMeshUniforms(sh, style);
403 return sh;
404 }
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);
409 if (!sh || !sh->gpuHandle)
410 throw eve::Exception("createMeshShader: failed to create ink mesh shader");
411 bindMeshUniforms(sh, style);
412 return sh;
413 }
414 if (style == "xray") {
415 // WebGPU has no SPIR-V mesh shaders (WGSL only); Vulkan uses SPIR-V.
416 if (gfx->getBackendName() == "webgpu") {
417 graphics::Shader *sh = gfx->newMeshShaderFromWgsl(kXrayMeshVertWgsl, kXrayMeshFragWgsl);
418 if (!sh || !sh->gpuHandle)
419 throw eve::Exception("createMeshShader: failed to create xray mesh shader (wgsl)");
420 sh->setXray(true);
421 bindMeshUniforms(sh, style);
422 return sh;
423 }
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);
427 if (!sh || !sh->gpuHandle)
428 throw eve::Exception("createMeshShader: failed to create xray mesh shader");
429 sh->setXray(true);
430 bindMeshUniforms(sh, style);
431 return sh;
432 }
433 throw eve::Exception(
434 "createMeshShader: style '%s' has no mesh variant (use watercolor/pixel as post)",
435 style.c_str());
436}
437
438} // namespace eve::stylize
vk::ShaderModule vert
vk::ShaderModule frag
Shader * shader
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...
Custom GPU program.
Definition Shader.h:30
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:61
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)