载入中...
搜索中...
未找到
wgsl_shaders.h
浏览该文件的文档.
1#pragma once
2// WGSL shader sources for the WebGPU graphics backend.
3// WebGPU has no push constants or combined image samplers, so every pipeline
4// binds uniform buffers / textures / samplers explicitly through bind groups.
5// The memory layouts match the Vulkan GLSL sources (std140) exactly so the
6// existing C++ UBO structs (Mesh3DUBO, ShadowUBO, Lighting2DUBO) are reused.
7
8namespace eve::graphics::webgpu {
9
10// ---- 2D solid (color) -----------------------------------------------------
11inline const char *kColorVertWgsl = R"wgsl(
12struct VSIn {
13 @location(0) pos: vec2f,
14 @location(1) color: vec4f,
15};
16struct VSOut {
17 @builtin(position) pos: vec4f,
18 @location(0) color: vec4f,
19};
20@vertex
21fn vs_main(in: VSIn) -> VSOut {
22 var out: VSOut;
23 out.pos = vec4f(in.pos, 0.0, 1.0);
24 out.color = in.color;
25 return out;
26}
27)wgsl";
28
29inline const char *kColorFragWgsl = R"wgsl(
30struct FSIn {
31 @location(0) color: vec4f,
32};
33@fragment
34fn fs_main(in: FSIn) -> @location(0) vec4f {
35 return in.color;
36}
37)wgsl";
38
39// ---- 2D textured ----------------------------------------------------------
40inline const char *kTexturedVertWgsl = R"wgsl(
41struct VSIn {
42 @location(0) pos: vec2f,
43 @location(1) color: vec4f,
44 @location(2) uv: vec2f,
45};
46struct VSOut {
47 @builtin(position) pos: vec4f,
48 @location(0) color: vec4f,
49 @location(1) uv: vec2f,
50};
51@vertex
52fn vs_main(in: VSIn) -> VSOut {
53 var out: VSOut;
54 out.pos = vec4f(in.pos, 0.0, 1.0);
55 out.color = in.color;
56 out.uv = in.uv;
57 return out;
58}
59)wgsl";
60
61inline const char *kTexturedFragWgsl = R"wgsl(
62struct FSIn {
63 @location(0) color: vec4f,
64 @location(1) uv: vec2f,
65};
66@group(0) @binding(0) var mainTex: texture_2d<f32>;
67@group(0) @binding(2) var mainSamp: sampler;
68@fragment
69fn fs_main(in: FSIn) -> @location(0) vec4f {
70 return textureSample(mainTex, mainSamp, in.uv) * in.color;
71}
72)wgsl";
73
74// ---- 2D custom / post (Externals UBO: float data[32] == Shader push block) -
75inline const char *kCustom2DFragWgsl = R"wgsl(
76struct FSIn {
77 @location(0) color: vec4f,
78 @location(1) uv: vec2f,
79};
80struct Externals {
81 data: array<f32, 32>,
82};
83@group(0) @binding(0) var mainTex: texture_2d<f32>;
84@group(0) @binding(2) var mainSamp: sampler;
85@group(0) @binding(4) var<uniform> u: Externals;
86@fragment
87fn fs_main(in: FSIn) -> @location(0) vec4f {
88 return textureSample(mainTex, mainSamp, in.uv) * in.color;
89}
90)wgsl";
91
92// ---- 2D lit ---------------------------------------------------------------
93inline const char *kLit2DFragWgsl = R"wgsl(
94struct FSIn {
95 @location(0) color: vec4f,
96 @location(1) uv: vec2f,
97};
98struct Light2D {
99 posRadius: vec4f,
100 color: vec4f,
101};
102struct Lighting2D {
103 ambient: vec4f, // rgb ambient
104 lightInfo: vec4f, // x = count, y = viewW, z = viewH
105 lights: array<Light2D, 8>,
106};
107@group(0) @binding(0) var albedoTex: texture_2d<f32>;
108@group(0) @binding(1) var normalTex: texture_2d<f32>;
109@group(0) @binding(2) var mainSamp: sampler;
110@group(0) @binding(4) var<uniform> u: Lighting2D;
111@fragment
112fn fs_main(in: FSIn) -> @location(0) vec4f {
113 let base = textureSample(albedoTex, mainSamp, in.uv) * in.color;
114 var lit = base.rgb * u.ambient.rgb;
115 let count = i32(u.lightInfo.x + 0.5);
116 let view = vec2f(u.lightInfo.y, u.lightInfo.z);
117 for (var i = 0; i < 8; i = i + 1) {
118 if (i >= count) { break; }
119 let l = u.lights[i];
120 let px = in.uv * view;
121 if (l.posRadius.w <= 0.0) {
122 // Directional: light travels along posRadius.xy
123 let ndl = max(dot(vec3f(0.0, 0.0, 1.0), normalize(vec3f(l.posRadius.xy, 1.0))), 0.0);
124 lit += base.rgb * l.color.rgb * ndl;
125 } else {
126 let toL = l.posRadius.xy - px;
127 let dist = length(toL);
128 let att = clamp(1.0 - dist / max(l.posRadius.w, 1e-3), 0.0, 1.0);
129 lit += base.rgb * l.color.rgb * att * att;
130 }
131 }
132 return vec4f(lit, base.a);
133}
134)wgsl";
135
136// ---- Mesh3D (full PBR, layout matches Mesh3DUBO / ShadowUBO) --------------
137inline const char *kMesh3DVertWgsl = R"wgsl(
138struct VSIn {
139 @location(0) pos: vec3f,
140 @location(1) normal: vec3f,
141 @location(2) uv: vec2f,
142};
143struct Light3D {
144 posRadius: vec4f,
145 color: vec4f,
146};
147struct Frame {
148 mvp: mat4x4f,
149 model: mat4x4f,
150 lightDir: vec4f,
151 lightColor: vec4f,
152 tint: vec4f,
153 cameraPos: vec4f,
154 ambient: vec4f,
155 lights: array<Light3D, 8>,
156 texBomb: vec4f,
157 parallax: vec4f,
158 view: mat4x4f,
159 clipInfo: vec4f,
160 cloud: vec4f,
161 cloudWind: vec4f,
162};
163
164struct VSOut {
165 @builtin(position) pos: vec4f,
166 @location(0) vNormal: vec3f,
167 @location(1) vUV: vec2f,
168 @location(2) vTint: vec4f,
169 @location(3) vWorldPos: vec3f,
170 @location(4) vCameraPos: vec3f,
171 @location(5) vViewPos: vec3f,
172};
173@group(0) @binding(0) var<uniform> ubo: Frame;
174// WGSL has no stdlib inverse(); implement 3x3 inverse for the normal matrix.
175fn inverse3x3(m: mat3x3f) -> mat3x3f {
176 let a = m[0].x; let b = m[1].x; let c = m[2].x;
177 let d = m[0].y; let e = m[1].y; let f = m[2].y;
178 let g = m[0].z; let h = m[1].z; let i = m[2].z;
179 let det = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
180 return mat3x3f(
181 vec3f((e * i - f * h) / det, (f * g - d * i) / det, (d * h - e * g) / det),
182 vec3f((c * h - b * i) / det, (a * i - c * g) / det, (b * g - a * h) / det),
183 vec3f((b * f - c * e) / det, (c * d - a * f) / det, (a * e - b * d) / det),
184 );
185}
186@vertex
187fn vs_main(in: VSIn) -> VSOut {
188 var out: VSOut;
189 out.pos = ubo.mvp * vec4f(in.pos, 1.0);
190 let world = ubo.model * vec4f(in.pos, 1.0);
191 out.vWorldPos = world.xyz;
192 out.vViewPos = (ubo.view * world).xyz;
193 let nrm = transpose(inverse3x3(mat3x3f(ubo.model[0].xyz, ubo.model[1].xyz, ubo.model[2].xyz))) * in.normal;
194 out.vNormal = normalize(nrm);
195 out.vUV = in.uv;
196 out.vTint = ubo.tint;
197 out.vCameraPos = ubo.cameraPos.xyz;
198 return out;
199}
200)wgsl";
201
202inline const char *kMesh3DFragWgsl = R"wgsl(
203struct Light3D {
204 posRadius: vec4f,
205 color: vec4f,
206};
207struct Frame {
208 mvp: mat4x4f,
209 model: mat4x4f,
210 lightDir: vec4f,
211 lightColor: vec4f,
212 tint: vec4f,
213 cameraPos: vec4f,
214 ambient: vec4f,
215 lights: array<Light3D, 8>,
216 texBomb: vec4f,
217 parallax: vec4f,
218 view: mat4x4f,
219 clipInfo: vec4f,
220 cloud: vec4f,
221 cloudWind: vec4f,
222};
223struct ShadowFrame {
224 lightVP: array<mat4x4f, 3>,
225 splits: vec4f,
226 bias: vec4f,
227 cascadeBias: vec4f,
228 cascadeTexel: vec4f,
229};
230struct FSIn {
231 @location(0) vNormal: vec3f,
232 @location(1) vUV: vec2f,
233 @location(2) vTint: vec4f,
234 @location(3) vWorldPos: vec3f,
235 @location(4) vCameraPos: vec3f,
236 @location(5) vViewPos: vec3f,
237};
238@group(0) @binding(0) var<uniform> ubo: Frame;
239@group(0) @binding(1) var albedoSampler: texture_2d<f32>;
240@group(0) @binding(2) var normalSampler: texture_2d<f32>;
241@group(0) @binding(3) var envSampler: texture_cube<f32>;
242@group(0) @binding(4) var<uniform> shadow: ShadowFrame;
243@group(0) @binding(5) var shadowMap: texture_depth_2d_array;
244@group(0) @binding(6) var heightSampler: texture_2d<f32>;
245@group(0) @binding(7) var mainSamp: sampler;
246@group(0) @binding(8) var shadowSamp: sampler_comparison;
247
248const PI: f32 = 3.14159265359;
249
250fn distGGX(n: vec3f, h: vec3f, rough: f32) -> f32 {
251 let a = max(rough * rough, 0.002);
252 let a2 = a * a;
253 let ndh = max(dot(n, h), 0.0);
254 let denom = (ndh * ndh * (a2 - 1.0) + 1.0);
255 return a2 / max(PI * denom * denom, 1e-4);
256}
257fn geomSchlick(ndv: f32, rough: f32) -> f32 {
258 let r = rough + 1.0;
259 let k = (r * r) / 8.0;
260 return ndv / max(ndv * (1.0 - k) + k, 1e-4);
261}
262fn geomSmith(n: vec3f, v: vec3f, l: vec3f, rough: f32) -> f32 {
263 return geomSchlick(max(dot(n, v), 0.0), rough) * geomSchlick(max(dot(n, l), 0.0), rough);
264}
265fn fresnelSchlick(cosT: f32, f0: vec3f) -> vec3f {
266 return f0 + (1.0 - f0) * pow(clamp(1.0 - cosT, 0.0, 1.0), 5.0);
267}
268fn shadeLight(n: vec3f, v: vec3f, albedo: vec3f, metallic: f32, rough: f32, l: vec3f, rad: vec3f) -> vec3f {
269 let ndl = max(dot(n, l), 0.0);
270 let diffuse = mix(ndl, ndl * 0.5 + 0.5, 0.25);
271 let h = normalize(v + l);
272 let f0 = mix(vec3f(0.04), albedo, metallic);
273 let ndf = distGGX(n, h, rough);
274 let g = geomSmith(n, v, l, rough);
275 let f = fresnelSchlick(max(dot(h, v), 0.0), f0);
276 let spec = (ndf * g * f) / max(4.0 * max(dot(n, v), 0.0) * max(ndl, 0.001), 1e-3);
277 let kd = (vec3f(1.0) - f) * (1.0 - metallic);
278 return (kd * albedo * diffuse + spec * ndl) * rad;
279}
280fn cloudHash(p: vec2f) -> f32 {
281 let ip = ivec2(floor(mod(p, 64.0)));
282 var h = (u32(ip.x) * 374761393u) ^ (u32(ip.y) * 668265263u);
283 h = (h ^ (h >> 13)) * 1274126177u;
284 h = h ^ (h >> 16);
285 return f32(h & 0x00FFFFFFu) / f32(0x00FFFFFFu);
286}
287fn cloudNoise(p: vec2f) -> f32 {
288 let i = floor(p);
289 var f = fract(p);
290 f = f * f * (3.0 - 2.0 * f);
291 let a = cloudHash(i);
292 let b = cloudHash(i + vec2f(1.0, 0.0));
293 let c = cloudHash(i + vec2f(0.0, 1.0));
294 let d = cloudHash(i + vec2f(1.0, 1.0));
295 return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
296}
297fn cloudFbm(p: vec2f) -> f32 {
298 var sum = 0.0;
299 var amp = 0.5;
300 var freq = 1.0;
301 for (var i = 0; i < 4; i = i + 1) {
302 sum += amp * cloudNoise(p * freq);
303 amp *= 0.5;
304 freq *= 2.0;
305 }
306 return sum * 2.0;
307}
308fn cloudShadowFactor(worldPos: vec3f) -> f32 {
309 if (ubo.cloud.x < 1e-4) { return 1.0; }
310 let cell = 1.0 / max(ubo.cloud.y, 1e-4);
311 let drift = (ubo.cloudWind.xy * cell) * ubo.cloud.z;
312 let p = worldPos.xz * cell - drift;
313 let f = cloudFbm(p);
314 let c = smoothstep(ubo.cloudWind.z - 0.1, ubo.cloudWind.z + 0.1, f);
315 let d = cloudNoise(p * 3.0 + vec2f(11.7, 5.3));
316 let covered = mix(c, c * d, ubo.cloudWind.w);
317 return 1.0 - clamp(covered, 0.0, 1.0) * clamp(ubo.cloud.x, 0.0, 1.0);
318}
319fn sampleShadowCascade(worldPos: vec3f, cascade: i32, bias: f32) -> f32 { let lightClip = shadow.lightVP[cascade] * vec4f(worldPos, 1.0);
320 let ndc = lightClip.xyz / max(lightClip.w, 1e-6);
321 let uv = ndc.xy * 0.5 + 0.5;
322 let depth = ndc.z;
323 if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0 || depth < 0.0 || depth > 1.0) {
324 return 1.0;
325 }
326 let texel = 1.0 / vec2f(textureDimensions(shadowMap));
327 var sum: f32 = 0.0;
328 let zref = depth - bias;
329 for (var i = 0; i < 9; i = i + 1) {
330 var s: vec2f;
331 if (i == 0) { s = uv + vec2f(-0.5, -0.5) * texel; }
332 else if (i == 1) { s = uv + vec2f(0.0, -0.6) * texel; }
333 else if (i == 2) { s = uv + vec2f(0.5, -0.5) * texel; }
334 else if (i == 3) { s = uv + vec2f(-0.6, 0.0) * texel; }
335 else if (i == 4) { s = uv; }
336 else if (i == 5) { s = uv + vec2f(0.6, 0.0) * texel; }
337 else if (i == 6) { s = uv + vec2f(-0.5, 0.5) * texel; }
338 else if (i == 7) { s = uv + vec2f(0.0, 0.6) * texel; }
339 else { s = uv + vec2f(0.5, 0.5) * texel; }
340 if (s.x >= 0.0 && s.x <= 1.0 && s.y >= 0.0 && s.y <= 1.0) {
341 sum += textureSampleCompareLevel(shadowMap, shadowSamp, s, cascade, zref);
342 }
343 }
344 return sum / 9.0;
345}
346fn sampleShadowPCF(worldPos: vec3f, n: vec3f, viewDepth: f32, ndl: f32) -> f32 {
347 if (shadow.bias.y < 0.5 || shadow.bias.z < 0.5 || shadow.splits.w < 1e-4) {
348 return 1.0;
349 }
350 var cascade: i32 = 2;
351 if (viewDepth < shadow.splits.x) { cascade = 0; }
352 else if (viewDepth < shadow.splits.y) { cascade = 1; }
353 var tw: f32;
354 if (cascade == 0) { tw = shadow.cascadeTexel.x; }
355 else if (cascade == 1) { tw = shadow.cascadeTexel.y; }
356 else { tw = shadow.cascadeTexel.z; }
357 var cb: f32;
358 if (cascade == 0) { cb = shadow.cascadeBias.x; }
359 else if (cascade == 1) { cb = shadow.cascadeBias.y; }
360 else { cb = shadow.cascadeBias.z; }
361 if (cb < 1e-8) { cb = shadow.bias.x; }
362 let bias = cb * mix(0.75, 1.0, clamp(ndl, 0.0, 1.0));
363 let p = worldPos + n * ((2.0 * max(tw, 1e-6)) / max(ndl, 0.2));
364 let vis = sampleShadowCascade(p, cascade, bias);
365 return mix(0.04, 1.0, vis);
366}
367@fragment
368fn fs_main(in: FSIn) -> @location(0) vec4f {
369 var nGeom = normalize(in.vNormal);
370 let v = normalize(in.vCameraPos - in.vWorldPos);
371 if (dot(nGeom, v) < 0.0) { nGeom = -nGeom; }
372 let base = textureSample(albedoSampler, mainSamp, in.vUV) * in.vTint;
373 if (base.a < 0.5) { discard; }
374 let albedo = base.rgb;
375 let metallic = clamp(ubo.ambient.w, 0.0, 1.0);
376 let rough = clamp(ubo.cameraPos.w, 0.04, 1.0);
377 let count = i32(ubo.lightDir.w + 0.5);
378 let nSmp = textureSample(normalSampler, mainSamp, in.vUV).xyz;
379 var n = nGeom;
380 if (length(nSmp - vec3f(0.5, 0.5, 1.0)) > 0.04) {
381 let mapN = nSmp * 2.0 - 1.0;
382 n = normalize(mapN);
383 }
384 var lo = vec3f(0.0);
385 let viewDepth = max(-in.vViewPos.z, 0.0);
386 let primaryL = normalize(ubo.lightDir.xyz);
387 let shadowVis = sampleShadowPCF(in.vWorldPos, n, viewDepth, max(dot(n, primaryL), 0.0));
388 if (length(ubo.lightColor.rgb) > 1e-6) {
389 lo += shadeLight(n, v, albedo, metallic, rough, primaryL, ubo.lightColor.rgb) * shadowVis *
390 cloudShadowFactor(in.vWorldPos);
391 }
392 for (var i = 0; i < 8; i = i + 1) {
393 if (i >= count) { break; }
394 let lgt = ubo.lights[i];
395 var rad = lgt.color.rgb;
396 var l: vec3f;
397 if (lgt.posRadius.w <= 0.0) {
398 l = normalize(lgt.posRadius.xyz);
399 if (length(l - primaryL) < 1e-3) { continue; }
400 } else {
401 let toL = lgt.posRadius.xyz - in.vWorldPos;
402 let dist = length(toL);
403 l = toL / max(dist, 1e-4);
404 let atten = clamp(1.0 - dist / max(lgt.posRadius.w, 1e-3), 0.0, 1.0);
405 rad *= atten * atten;
406 }
407 lo += shadeLight(n, v, albedo, metallic, rough, l, rad);
408 }
409 let hemi = clamp(n.y * 0.5 + 0.5, 0.0, 1.0);
410 let skyIrr = ubo.ambient.rgb * 1.1 + ubo.lightColor.rgb * 0.12;
411 let gndIrr = ubo.ambient.rgb * vec3f(0.72, 0.62, 0.52);
412 let irr = mix(gndIrr, skyIrr, hemi);
413 var color = albedo * irr * (1.0 - metallic) + lo;
414 let envIntensity = ubo.lightColor.w;
415 if (envIntensity > 1e-4) {
416 let r = reflect(-v, n);
417 let envSpec = textureSampleLevel(envSampler, mainSamp, r, rough * 5.0).rgb * envIntensity;
418 let f0 = mix(vec3f(0.04), albedo, metallic);
419 let f = fresnelSchlick(max(dot(n, v), 0.0), f0);
420 color += envSpec * f;
421 let irr2 = textureSampleLevel(envSampler, mainSamp, n, 5.0).rgb * envIntensity;
422 color += albedo * irr2 * (1.0 - metallic) * (1.0 - f) * 0.45;
423 }
424 color = color / (color + vec3f(1.0));
425 // The scene texture's alpha would otherwise carry linear depth, which the
426 // swapchain compositor blends as transparency (making the mesh nearly
427 // invisible). Output opaque.
428 return vec4f(color, 1.0);
429}
430)wgsl";
431
432// ---- Mesh3D depth-only shadow --------------------------------------------------
433inline const char *kMesh3DShadowVertWgsl = R"wgsl(
434struct VSIn {
435 @location(0) pos: vec3f,
436 @location(1) normal: vec3f,
437 @location(2) uv: vec2f,
438};
439struct Push {
440 mvp: mat4x4f,
441};
442@group(0) @binding(0) var<uniform> pc: Push;
443@vertex
444fn vs_main(in: VSIn) -> @builtin(position) vec4f {
445 return pc.mvp * vec4f(in.pos, 1.0);
446}
447)wgsl";
448
449// ---- GBuffer pass (normal / linear-depth / albedo) -------------------------
450inline const char *kMesh3DGbufferVertWgsl = R"wgsl(
451struct VSIn {
452 @location(0) pos: vec3f,
453 @location(1) normal: vec3f,
454 @location(2) uv: vec2f,
455};
456struct Push {
457 mvp: mat4x4f,
458 modelR0: vec4f,
459 modelR1: vec4f,
460 modelR2: vec4f,
461 clip: vec4f,
462};
463struct VSOut {
464 @builtin(position) pos: vec4f,
465 @location(0) vNormal: vec3f,
466 @location(1) vNdcZ: f32,
467 @location(2) vUV: vec2f,
468};
469@group(0) @binding(0) var<uniform> pc: Push;
470@vertex
471fn vs_main(in: VSIn) -> VSOut {
472 var out: VSOut;
473 out.pos = pc.mvp * vec4f(in.pos, 1.0);
474 let nrm = mat3x3f(pc.modelR0.xyz, pc.modelR1.xyz, pc.modelR2.xyz) * in.normal;
475 out.vNormal = normalize(nrm);
476 out.vNdcZ = out.pos.z;
477 out.vUV = in.uv;
478 return out;
479}
480)wgsl";
481
482inline const char *kMesh3DGbufferFragWgsl = R"wgsl(
483struct FSIn {
484 @location(0) vNormal: vec3f,
485 @location(1) vNdcZ: f32,
486 @location(2) vUV: vec2f,
487};
488struct Push {
489 mvp: mat4x4f,
490 modelR0: vec4f,
491 modelR1: vec4f,
492 modelR2: vec4f,
493 clip: vec4f,
494};
495struct GBufOut {
496 @location(0) normal: vec4f,
497 @location(1) depthColor: vec4f,
498 @location(2) albedo: vec4f,
499};
500@group(0) @binding(0) var<uniform> pc: Push;
501@group(0) @binding(1) var albedoSampler: texture_2d<f32>;
502@group(0) @binding(2) var mainSamp: sampler;
503@fragment
504fn fs_main(in: FSIn) -> GBufOut {
505 var out: GBufOut;
506 out.normal = vec4f(in.vNormal * 0.5 + 0.5, 1.0);
507 let nearZ = max(pc.clip.x, 1e-4);
508 let farZ = max(pc.clip.y, nearZ + 1e-3);
509 // NDC z -> linear [0,1] over the clip range (matches scene color A).
510 let ndc = clamp(in.vNdcZ, 0.0, 1.0);
511 let linear = nearZ * farZ / max(farZ - ndc * (farZ - nearZ), 1e-6);
512 out.depthColor = vec4f(linear, linear, linear, 1.0);
513 out.albedo = textureSample(albedoSampler, mainSamp, in.vUV);
514 return out;
515}
516)wgsl";
517
518// ---- Voxel rect --------------------------------------------------------------
519inline const char *kVoxelRectVertWgsl = R"wgsl(
520struct VSIn {
521 @location(0) corner: vec2f,
522 @location(1) packed: u32,
523};
524struct PC {
525 viewProj: mat4x4f,
526 chunkOrigin: vec4f,
527 atlasInfo: vec4f,
528 tint: vec4f,
529};
530struct VSOut {
531 @builtin(position) pos: vec4f,
532 @location(0) uv: vec2f,
533 @location(1) tint: vec4f,
534};
535@group(0) @binding(0) var<uniform> pc: PC;
536@vertex
537fn vs_main(in: VSIn) -> VSOut {
538 var out: VSOut;
539 // packed: x(5) y(5) z(5) w(5) h(5) tex(7)
540 let px = f32(in.packed & 31u);
541 let py = f32((in.packed >> 5u) & 31u);
542 let pz = f32((in.packed >> 10u) & 31u);
543 let w = f32((in.packed >> 15u) & 31u);
544 let h = f32((in.packed >> 20u) & 31u);
545 let tex = f32((in.packed >> 25u) & 127u);
546 var world: vec3f = pc.chunkOrigin.xyz + vec3f(px, py, pz);
547 let face = i32(pc.chunkOrigin.w + 0.5);
548 var u: f32 = in.corner.x;
549 var v: f32 = in.corner.y;
550 if (face == 0) { world += vec3f(0.0, v * h, u * w); }
551 else if (face == 1) { world += vec3f(0.0, v * h, w - u * w); }
552 else if (face == 2) { world += vec3f(u * w, 0.0, v * h); }
553 else if (face == 3) { world += vec3f(w - u * w, 0.0, v * h); }
554 else if (face == 4) { world += vec3f(u * w, v * h, 0.0); }
555 else { world += vec3f(w - u * w, v * h, 0.0); }
556 out.pos = pc.viewProj * vec4f(world, 1.0);
557 let tiles = max(pc.atlasInfo.x, 1.0);
558 let tw = 1.0 / tiles;
559 let col = floor(tex / tiles);
560 let row = tex - col * tiles;
561 out.uv = vec2f((u + row) * tw, (v + col) * tw);
562 out.tint = pc.tint;
563 return out;
564}
565)wgsl";
566
567inline const char *kVoxelRectFragWgsl = R"wgsl(
568struct FSIn {
569 @location(0) uv: vec2f,
570 @location(1) tint: vec4f,
571};
572@group(0) @binding(1) var atlas: texture_2d<f32>;
573@group(0) @binding(2) var atlasSamp: sampler;
574@fragment
575fn fs_main(in: FSIn) -> @location(0) vec4f {
576 return textureSample(atlas, atlasSamp, in.uv) * in.tint;
577}
578)wgsl";
579
580} // namespace eve::graphics::webgpu
const char * kColorVertWgsl
const char * kMesh3DFragWgsl
const char * kColorFragWgsl
const char * kTexturedVertWgsl
const char * kMesh3DVertWgsl
const char * kMesh3DGbufferFragWgsl
const char * kMesh3DShadowVertWgsl
const char * kTexturedFragWgsl
const char * kLit2DFragWgsl
const char * kVoxelRectVertWgsl
const char * kVoxelRectFragWgsl
const char * kMesh3DGbufferVertWgsl
const char * kCustom2DFragWgsl