载入中...
搜索中...
未找到
FluidSsfKernels.h
浏览该文件的文档.
1#pragma once
2
34namespace eve::fluids {
35
37inline const char* kSsfClear = R"GLSL(
38#version 450
40layout(local_size_x = 64) in;
42layout(set = 0, binding = 1) buffer DepthA { uint d[]; } depthA;
44layout(set = 0, binding = 2) buffer DepthB { uint d[]; } depthB;
46layout(set = 0, binding = 3) buffer Thick { uint t[]; } thick;
48layout(set = 0, binding = 4) buffer Normal { vec4 n[]; } normal;
50layout(set = 0, binding = 5) buffer Color { uint c[]; } color;
52layout(push_constant) uniform PC { float d[32]; } pc;
54void main() {
55 uint i = gl_GlobalInvocationID.x;
56 uint total = uint(pc.d[23]) * uint(pc.d[24]);
57 if (i >= total) return;
58 depthA.d[i] = 0xFFFFFFFFu;
59 depthB.d[i] = 0xFFFFFFFFu;
60 thick.t[i] = 0u;
61 normal.n[i] = vec4(0.0);
62 color.c[i] = 0u;
63}
64)GLSL";
65
67inline const char* kSsfSplat = R"GLSL(
68#version 450
70layout(local_size_x = 64) in;
72layout(set = 0, binding = 0) buffer Particles { vec4 p[]; } parts;
74layout(set = 0, binding = 1) buffer DepthA { uint d[]; } depthA;
76layout(set = 0, binding = 3) buffer Thick { uint t[]; } thick;
78layout(push_constant) uniform PC { float d[32]; } pc;
79
81mat4 loadVP() {
83 return mat4(pc.d[0], pc.d[1], pc.d[2], pc.d[3],
84 pc.d[4], pc.d[5], pc.d[6], pc.d[7],
85 pc.d[8], pc.d[9], pc.d[10], pc.d[11],
86 pc.d[12], pc.d[13], pc.d[14], pc.d[15]);
87}
88
90void main() {
91 uint i = gl_GlobalInvocationID.x;
92 uint n = uint(pc.d[16]);
93 if (i >= n) return;
94 vec4 clip = loadVP() * vec4(parts.p[i].xyz, 1.0);
95 bool orthographic = pc.d[17] > 0.5;
96 if (!orthographic && clip.w <= 1e-4) return;
97 vec3 ndc = clip.xyz / clip.w;
98 if (any(lessThan(ndc, vec3(-1.0))) || any(greaterThan(ndc, vec3(1.0)))) return;
99 float W = pc.d[23];
100 float H = pc.d[24];
101 float sx = (ndc.x * 0.5 + 0.5) * W;
102 float sy = (0.5 - ndc.y * 0.5) * H;
103 float nearZ = max(pc.d[19], 1e-4);
104 float farZ = max(pc.d[20], nearZ + 1e-3);
105 float tanHalf = max(pc.d[21], 1e-4);
106 float depth = orthographic ? nearZ + (ndc.z * 0.5 + 0.5) * (farZ - nearZ) : clip.w;
107 float radiusPx = (pc.d[25] * (H * 0.5) / tanHalf) /
108 (orthographic ? 1.0 : max(depth, 1e-4));
109 if (radiusPx < 0.5) return;
110 int x0 = int(max(floor(sx - radiusPx), 0.0));
111 int x1 = int(min(ceil(sx + radiusPx), W - 1.0));
112 int y0 = int(max(floor(sy - radiusPx), 0.0));
113 int y1 = int(min(ceil(sy + radiusPx), H - 1.0));
114 float r2 = radiusPx * radiusPx;
115 float scale = pc.d[27];
116 for (int yy = y0; yy <= y1; ++yy) {
117 for (int xx = x0; xx <= x1; ++xx) {
118 float ddx = float(xx) + 0.5 - sx;
119 float ddy = float(yy) + 0.5 - sy;
120 float q = (ddx * ddx + ddy * ddy) / r2;
121 if (q >= 1.0) continue;
122 uint idx = uint(yy) * uint(W) + uint(xx);
123 // Spherical cap in the projected particle disc, not a flat billboard.
124 float cap = pc.d[25] * sqrt(1.0 - q);
125 float t = clamp((depth - cap - nearZ) / (farZ - nearZ), 0.0, 1.0);
126 uint key = uint(t * 16777215.0);
128 atomicMin(depthA.d[idx], key);
129 uint contribution = uint(clamp(2.0 * cap * scale * 256.0, 0.0, 16777215.0));
131 atomicAdd(thick.t[idx], contribution);
132 }
133 }
134}
135)GLSL";
136
142inline const char* kSsfColorClear = R"GLSL(
143#version 450
145layout(local_size_x = 64) in;
147layout(set = 0, binding = 7) buffer Accum { uint v[]; } accum;
149layout(push_constant) uniform PC { float d[32]; } pc;
151void main() {
152 uint i = gl_GlobalInvocationID.x;
153 if (i >= uint(pc.d[23]) * uint(pc.d[24])) return;
154 uint at = i * 5u;
155 for (uint channel = 0u; channel < 5u; ++channel) accum.v[at + channel] = 0u;
156}
157)GLSL";
158
164inline const char* kSsfColorSplat = R"GLSL(
165#version 450
167layout(local_size_x = 64) in;
169layout(set = 0, binding = 0) buffer Particles { vec4 p[]; } parts;
171layout(set = 0, binding = 6) buffer ParticleColors { vec4 c[]; } particleColors;
173layout(set = 0, binding = 7) buffer Accum { uint v[]; } accum;
175layout(push_constant) uniform PC { float d[32]; } pc;
177mat4 loadVP() {
179 return mat4(pc.d[0], pc.d[1], pc.d[2], pc.d[3], pc.d[4], pc.d[5], pc.d[6], pc.d[7],
180 pc.d[8], pc.d[9], pc.d[10], pc.d[11], pc.d[12], pc.d[13], pc.d[14], pc.d[15]);
181}
183void main() {
184 uint i = gl_GlobalInvocationID.x;
185 if (i >= uint(pc.d[16])) return;
186 vec4 clip = loadVP() * vec4(parts.p[i].xyz, 1.0);
187 bool orthographic = pc.d[17] > 0.5;
188 if (!orthographic && clip.w <= 1e-4) return;
189 vec3 ndc = clip.xyz / clip.w;
190 if (any(lessThan(ndc, vec3(-1.0))) || any(greaterThan(ndc, vec3(1.0)))) return;
191 float W = pc.d[23], H = pc.d[24];
192 float sx = (ndc.x * 0.5 + 0.5) * W;
193 float sy = (0.5 - ndc.y * 0.5) * H;
194 float depth = orthographic ? pc.d[19] + (ndc.z * 0.5 + 0.5) * (pc.d[20] - pc.d[19]) : clip.w;
195 float radiusPx = (pc.d[25] * (H * 0.5) / max(pc.d[21], 1e-4)) /
196 (orthographic ? 1.0 : max(depth, 1e-4));
197 if (radiusPx < 0.5) return;
198 int x0 = int(max(floor(sx - radiusPx), 0.0)), x1 = int(min(ceil(sx + radiusPx), W - 1.0));
199 int y0 = int(max(floor(sy - radiusPx), 0.0)), y1 = int(min(ceil(sy + radiusPx), H - 1.0));
200 vec4 tint = clamp(particleColors.c[i], 0.0, 1.0);
201 float radiusSquared = radiusPx * radiusPx;
202 for (int y = y0; y <= y1; ++y) for (int x = x0; x <= x1; ++x) {
203 vec2 delta = vec2(float(x) + 0.5 - sx, float(y) + 0.5 - sy);
204 float q = 1.0 - dot(delta, delta) / radiusSquared;
205 if (q <= 0.0) continue;
206 uint scaled = max(1u, uint(q * 4095.0));
207 uint at = (uint(y) * uint(W) + uint(x)) * 5u;
209 atomicAdd(accum.v[at + 0u], scaled);
211 atomicAdd(accum.v[at + 1u], uint(float(scaled) * tint.r));
213 atomicAdd(accum.v[at + 2u], uint(float(scaled) * tint.g));
215 atomicAdd(accum.v[at + 3u], uint(float(scaled) * tint.b));
217 atomicAdd(accum.v[at + 4u], uint(float(scaled) * tint.a));
218 }
219}
220)GLSL";
221
222// Splat preprojected oriented ellipsoids without expanding push constants.
223inline constexpr auto kSsfAnisotropicSplat = R"GLSL(
224#version 450
226layout(local_size_x = 64) in;
228layout(set = 0, binding = 0) buffer Ellipsoids { vec4 e[]; } ellipsoids;
230layout(set = 0, binding = 1) buffer DepthA { uint d[]; } depthA;
232layout(set = 0, binding = 3) buffer Thick { uint t[]; } thick;
234layout(push_constant) uniform PC { float d[32]; } pc;
235
237void main() {
238 uint i = gl_GlobalInvocationID.x;
239 if (i >= uint(pc.d[16])) return;
240 vec4 projected = ellipsoids.e[i * 3u + 0u];
241 vec4 inverseAndRadius = ellipsoids.e[i * 3u + 1u];
242 vec4 depthSlope = ellipsoids.e[i * 3u + 2u];
243 if (projected.w <= 0.0) return;
244 float W = pc.d[23], H = pc.d[24];
245 int x0 = int(max(floor(projected.x - projected.w), 0.0));
246 int x1 = int(min(ceil(projected.x + projected.w), W - 1.0));
247 int y0 = int(max(floor(projected.y - projected.w), 0.0));
248 int y1 = int(min(ceil(projected.y + projected.w), H - 1.0));
249 float nearZ = max(pc.d[19], 1e-4);
250 float farZ = max(pc.d[20], nearZ + 1e-3);
251 for (int yy = y0; yy <= y1; ++yy) for (int xx = x0; xx <= x1; ++xx) {
252 vec2 delta = vec2(float(xx) + 0.5 - projected.x, float(yy) + 0.5 - projected.y);
253 float q = inverseAndRadius.x * delta.x * delta.x +
254 2.0 * inverseAndRadius.y * delta.x * delta.y +
255 inverseAndRadius.z * delta.y * delta.y;
256 if (q >= 1.0) continue;
257 float cap = inverseAndRadius.w * sqrt(max(0.0, 1.0 - q));
258 float centerDepth = projected.z + dot(depthSlope.xy, delta);
259 float t = clamp((centerDepth - cap - nearZ) / (farZ - nearZ), 0.0, 1.0);
260 uint idx = uint(yy) * uint(W) + uint(xx);
262 atomicMin(depthA.d[idx], uint(t * 16777215.0));
263 uint contribution = uint(clamp(2.0 * cap * pc.d[27] * 256.0, 0.0, 16777215.0));
265 atomicAdd(thick.t[idx], contribution);
266 }
267}
268)GLSL";
269
271inline const char* kSsfSmooth = R"GLSL(
272#version 450
274layout(local_size_x = 64) in;
276layout(set = 0, binding = 1) buffer DepthA { uint d[]; } depthA;
278layout(set = 0, binding = 2) buffer DepthB { uint d[]; } depthB;
280layout(push_constant) uniform PC { float d[32]; } pc;
281
283float decode(uint key) {
284 float nearZ = max(pc.d[19], 1e-4);
285 float farZ = max(pc.d[20], nearZ + 1e-3);
286 return nearZ + (float(key) / 16777215.0) * (farZ - nearZ);
287}
288
290void main() {
291 uint i = gl_GlobalInvocationID.x;
292 uint W = uint(pc.d[23]);
293 uint H = uint(pc.d[24]);
294 if (i >= W * H) return;
295 uint cx = i % W;
296 uint cy = i / W;
297 uint centerKey = depthA.d[i];
298 if (centerKey == 0xFFFFFFFFu) {
299 depthB.d[i] = centerKey;
300 return;
301 }
302 float center = decode(centerKey);
303 float falloff = max(pc.d[28], 1e-4); // depth edge falloff
304 float projectedBlur = pc.d[29] < 0.0 ? 2.0 :
305 pc.d[29] * float(H) / (2.0 * max(pc.d[21], 1e-4) * (pc.d[17] > 0.5 ? 1.0 : center));
306 int kernelRadius = clamp(int(ceil(projectedBlur)), 0, 4);
307 float sigma = max(0.5, projectedBlur * 0.5);
308 float sum = center;
309 float wsum = 1.0;
310 for (int oy = -kernelRadius; oy <= kernelRadius; ++oy) {
311 for (int ox = -kernelRadius; ox <= kernelRadius; ++ox) {
312 if (ox == 0 && oy == 0) continue;
313 int xx = int(cx) + ox;
314 int yy = int(cy) + oy;
315 if (xx < 0 || yy < 0 || xx >= int(W) || yy >= int(H)) continue;
316 uint idx = uint(yy) * W + uint(xx);
317 uint nk = depthA.d[idx];
318 if (nk == 0xFFFFFFFFu) continue;
319 float nd = decode(nk);
320 float spatial = exp(-float(ox * ox + oy * oy) / (2.0 * sigma * sigma));
321 float wDepth = exp(-abs(nd - center) / falloff);
322 float w = spatial * wDepth;
323 sum += nd * w;
324 wsum += w;
325 }
326 }
327 float outD = sum / max(wsum, 1e-5);
328 float nearZ = max(pc.d[19], 1e-4);
329 float farZ = max(pc.d[20], nearZ + 1e-3);
330 float t = clamp((outD - nearZ) / (farZ - nearZ), 0.0, 1.0);
331 depthB.d[i] = uint(t * 16777215.0);
332}
333)GLSL";
334
336inline const char* kSsfNormal = R"GLSL(
337#version 450
339layout(local_size_x = 64) in;
341layout(set = 0, binding = 1) buffer DepthA { uint d[]; } depthA;
343layout(set = 0, binding = 4) buffer Normal { vec4 n[]; } normal;
345layout(push_constant) uniform PC { float d[32]; } pc;
346
348float decode(uint key) {
349 float nearZ = max(pc.d[19], 1e-4);
350 float farZ = max(pc.d[20], nearZ + 1e-3);
351 return nearZ + (float(key) / 16777215.0) * (farZ - nearZ);
352}
353
355float sampleDepth(ivec2 off, uint W, uint H, uint cx, uint cy) {
356 int xx = int(cx) + off.x;
357 int yy = int(cy) + off.y;
358 if (xx < 0 || yy < 0 || xx >= int(W) || yy >= int(H)) return 1e30;
359 uint k = depthA.d[uint(yy) * W + uint(xx)];
360 return k == 0xFFFFFFFFu ? 1e30 : decode(k);
361}
362
364vec3 surfaceDerivative(vec3 forward, vec3 backward, bool forwardValid, bool backwardValid) {
365 if (!forwardValid && backwardValid) return backward;
366 if (!backwardValid && forwardValid) return forward;
367 // Preserve symmetry when differences are indistinguishable at depth-key precision.
368 float tolerance = 2.0 * (pc.d[20] - pc.d[19]) / 16777215.0;
369 if ((!forwardValid && !backwardValid) || abs(abs(forward.z) - abs(backward.z)) <= tolerance)
370 return (forward + backward) * 0.5;
372 return abs(forward.z) < abs(backward.z) ? forward : backward;
373}
374
376vec3 viewPos(vec2 uv, float depth) {
377 float tanHalf = max(pc.d[21], 1e-4);
378 float aspect = max(pc.d[22], 1e-4);
379 float scale = pc.d[17] > 0.5 ? 1.0 : depth;
380 float x = (uv.x * 2.0 - 1.0) * aspect * tanHalf * scale;
381 float y = (1.0 - uv.y * 2.0) * tanHalf * scale;
383 return vec3(x, y, -depth);
384}
385
387void main() {
388 uint i = gl_GlobalInvocationID.x;
389 uint W = uint(pc.d[23]);
390 uint H = uint(pc.d[24]);
391 if (i >= W * H) return;
392 uint cx = i % W;
393 uint cy = i / W;
394 uint ck = depthA.d[i];
395 if (ck == 0xFFFFFFFFu) {
396 normal.n[i] = vec4(0.0);
397 return;
398 }
399 float cd = decode(ck);
400 vec2 uv = vec2((float(cx) + 0.5) / float(W), (float(cy) + 0.5) / float(H));
401 float texelW = 1.0 / float(W);
402 float texelH = 1.0 / float(H);
403 float dl = sampleDepth(ivec2(-1, 0), W, H, cx, cy);
404 float dr = sampleDepth(ivec2(1, 0), W, H, cx, cy);
405 float dt = sampleDepth(ivec2(0, -1), W, H, cx, cy);
406 float db = sampleDepth(ivec2(0, 1), W, H, cx, cy);
407 vec3 center = viewPos(uv, cd);
408 vec3 pL = viewPos(uv - vec2(texelW, 0.0), dl < 1e29 ? dl : cd);
409 vec3 pR = viewPos(uv + vec2(texelW, 0.0), dr < 1e29 ? dr : cd);
410 vec3 pT = viewPos(uv - vec2(0.0, texelH), dt < 1e29 ? dt : cd);
411 vec3 pB = viewPos(uv + vec2(0.0, texelH), db < 1e29 ? db : cd);
412 vec3 dpx = surfaceDerivative(pR - center, center - pL, dr < 1e29, dl < 1e29);
413 vec3 dpy = surfaceDerivative(pB - center, center - pT, db < 1e29, dt < 1e29);
414 vec3 n = normalize(cross(dpx, dpy));
415 if (n.z < 0.0) n = -n;
416 normal.n[i] = vec4(n, 0.0);
417}
418)GLSL";
419
421inline const char* kSsfShade = R"GLSL(
422#version 450
424layout(local_size_x = 64) in;
426layout(set = 0, binding = 1) buffer DepthA { uint d[]; } depthA;
428layout(set = 0, binding = 3) buffer Thick { uint t[]; } thick;
430layout(set = 0, binding = 4) buffer Normal { vec4 n[]; } normal;
432layout(set = 0, binding = 5) buffer Color { uint c[]; } color;
434layout(set = 0, binding = 7) buffer Accum { uint v[]; } accum;
436layout(push_constant) uniform PC { float d[32]; } pc;
437
439void main() {
440 uint i = gl_GlobalInvocationID.x;
441 uint W = uint(pc.d[23]);
442 uint H = uint(pc.d[24]);
443 if (i >= W * H) return;
444 if (depthA.d[i] == 0xFFFFFFFFu) {
445 color.c[i] = 0u;
446 return;
447 }
448 vec3 n = normal.n[i].xyz;
449 float thickness = float(thick.t[i]) / 256.0;
450 vec3 L = normalize(vec3(0.35, 0.65, 0.55));
451 vec3 V = vec3(0.0, 0.0, 1.0);
452 float diff = max(dot(n, L), 0.0);
453 int mode = int(pc.d[26]);
454 bool particleTint = mode >= 20;
455 if (particleTint) mode -= 20;
456 bool uniformTint = !particleTint && mode >= 10;
457 if (uniformTint) mode -= 10;
458 vec3 outC;
459 float alpha;
460 if (uniformTint || particleTint) {
461 uint accumulatedAt = i * 5u;
462 uint accumulatedWeight = particleTint ? accum.v[accumulatedAt] : 1u;
463 if (accumulatedWeight == 0u) {
464 color.c[i] = 0u;
465 return;
466 }
467 vec4 tint = particleTint
468 ? vec4(accum.v[accumulatedAt + 1u], accum.v[accumulatedAt + 2u],
469 accum.v[accumulatedAt + 3u], accum.v[accumulatedAt + 4u]) / float(accumulatedWeight)
471 : vec4(pc.d[0], pc.d[1], pc.d[2], pc.d[3]);
472 vec3 reflectionColor = vec3(pc.d[4], pc.d[5], pc.d[6]);
473 bool lighting = pc.d[7] > 0.5;
474 float smoothness = pc.d[8];
475 float metalness = pc.d[9];
476 float ambient = pc.d[10];
477 float reflection = pc.d[11];
478 float opacity = pc.d[12];
479 float cutoff = pc.d[13];
480 if (thickness * 10.0 < cutoff) {
481 color.c[i] = 0u;
482 return;
483 }
484 float light = lighting ?
486 clamp(ambient + (1.0 - min(ambient, 1.0)) * diff, 0.0, 6.0) : 1.0;
487 float exponent = 4.0 + 124.0 * smoothness;
488 float spec = lighting ? pow(max(n.z, 0.0), exponent) * smoothness * 0.56 : 0.0;
489 float fresnel = 0.04 + 0.96 * pow(1.0 - max(n.z, 0.0), 5.0);
490 vec3 reflected = mix(reflectionColor, tint.rgb, metalness);
491 outC = tint.rgb * light + reflected * fresnel * reflection + vec3(spec);
492 alpha = clamp(thickness * opacity, 0.0, 1.0) * tint.a;
493 } else if (mode == 1) {
494 // Mud: diffuse brown, darker with thickness, rough specular.
495 vec3 base = vec3(0.36, 0.23, 0.12) * (0.45 + 0.55 * diff);
496 float attenuation = exp(-thickness * 1.8);
497 vec3 Hv = normalize(L + V);
498 float spec = pow(max(dot(n, Hv), 0.0), 8.0) * 0.12;
499 outC = base * attenuation + vec3(spec);
500 alpha = clamp(thickness * 0.6, 0.0, 1.0);
501 } else {
502 vec3 base = vec3(0.05, 0.32, 0.72) * (0.55 + 0.45 * diff);
503 float fresnel = 0.04 + 0.96 * pow(1.0 - max(dot(n, V), 0.0), 5.0);
504 vec3 Hv = normalize(L + V);
505 float spec = pow(max(dot(n, Hv), 0.0), 64.0) * 0.45;
506 outC = base + vec3(0.55, 0.72, 1.0) * fresnel * 0.75 + vec3(spec);
507 alpha = clamp(thickness * 0.35, 0.0, 1.0);
508 }
509 // Match the CPU output's clamp and truncation (packUnorm4x8 rounds instead).
510 uvec4 rgba = uvec4(clamp(vec4(outC, alpha), 0.0, 1.0) * 255.0);
511 color.c[i] = rgba.x | (rgba.y << 8u) | (rgba.z << 16u) | (rgba.w << 24u);
512}
513)GLSL";
514
515} // namespace eve::fluids
GLSL compute kernels for the GPU surface-flow solver.
Definition FluidTarget.h:12
const char * kSsfColorSplat
Accumulate isotropic particle colors using the CPU tint pass' disc weights. @lifetime The returned po...
const char * kSsfSmooth
One bilateral smoothing pass (read depthA, write depthB).
const char * kSsfShade
Water/mud shading from depth + normal + thickness.
const char * kSsfClear
Reset depth (0xFFFFFFFF), thickness, normal and color buffers.
const char * kSsfSplat
Splat particles into depth + thickness (atomic min / add).
const char * kSsfNormal
Reconstruct view-space normals from the smoothed depth.
const char * kSsfColorClear
Clear lazily-created fixed-point multicolor accumulation buffers. @lifetime The returned pointer rema...
constexpr auto kSsfAnisotropicSplat