From 99bef468cf5675f64ae965634a9e1cabac079689 Mon Sep 17 00:00:00 2001 From: Szymon Szulc <103948576+cieplypolar@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:26:29 +0100 Subject: [PATCH 1/9] chore: Jelly-slider wgsl resolution test (#1884) --- .../examples/individual/jelly-slider.test.ts | 769 ++++++++++++++++++ .../tests/examples/utils/commonMocks.ts | 8 + 2 files changed, 777 insertions(+) create mode 100644 packages/typegpu/tests/examples/individual/jelly-slider.test.ts diff --git a/packages/typegpu/tests/examples/individual/jelly-slider.test.ts b/packages/typegpu/tests/examples/individual/jelly-slider.test.ts new file mode 100644 index 0000000000..a4fd60a826 --- /dev/null +++ b/packages/typegpu/tests/examples/individual/jelly-slider.test.ts @@ -0,0 +1,769 @@ +/** + * @vitest-environment jsdom + */ + +import { describe, expect } from 'vitest'; +import { it } from '../../utils/extendedIt.ts'; +import { runExampleTest, setupCommonMocks } from '../utils/baseTest.ts'; +import { + mockFonts, + mockImageLoading, + mockResizeObserver, +} from '../utils/commonMocks.ts'; + +describe('jelly-slider example', () => { + setupCommonMocks(); + + it('should produce valid code', async ({ device }) => { + const shaderCodes = await runExampleTest({ + category: 'rendering', + name: 'jelly-slider', + setupMocks: () => { + mockFonts(); + mockImageLoading(); + mockResizeObserver(); + }, + expectedCalls: 6, + }, device); + + expect(shaderCodes).toMatchInlineSnapshot(` + " + struct VertexOutput { + @builtin(position) pos: vec4f, + @location(0) uv: vec2f, + } + + @vertex + fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput { + let pos = array(vec2f(-1, -1), vec2f(3, -1), vec2f(-1, 3)); + let uv = array(vec2f(0, 1), vec2f(2, 1), vec2f(0, -1)); + + var output: VertexOutput; + output.pos = vec4f(pos[vertexIndex], 0, 1); + output.uv = uv[vertexIndex]; + return output; + } + + + + @group(0) @binding(0) var inputTexture: texture_2d; + @group(0) @binding(1) var inputSampler: sampler; + + @fragment + fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f { + return textureSample(inputTexture, inputSampler, uv); + } + + + struct fullScreenTriangle_Input_1 { + @builtin(vertex_index) vertexIndex: u32, + } + + struct fullScreenTriangle_Output_2 { + @builtin(position) pos: vec4f, + @location(0) uv: vec2f, + } + + @vertex fn fullScreenTriangle_0(in: fullScreenTriangle_Input_1) -> fullScreenTriangle_Output_2 { + const pos = array(vec2f(-1, -1), vec2f(3, -1), vec2f(-1, 3)); + const uv = array(vec2f(0, 1), vec2f(2, 1), vec2f(0, -1)); + + return fullScreenTriangle_Output_2(vec4f(pos[in.vertexIndex], 0, 1), uv[in.vertexIndex]); + } + + @group(0) @binding(0) var randomUniform_4: vec2f; + + var seed_7: vec2f; + + fn seed2_6(value: vec2f) { + seed_7 = value; + } + + fn randSeed2_5(seed: vec2f) { + seed2_6(seed); + } + + struct Camera_10 { + view: mat4x4f, + proj: mat4x4f, + viewInv: mat4x4f, + projInv: mat4x4f, + } + + @group(0) @binding(1) var cameraUniform_9: Camera_10; + + struct Ray_11 { + origin: vec3f, + direction: vec3f, + } + + fn getRay_8(ndc: vec2f) -> Ray_11 { + var clipPos = vec4f(ndc.x, ndc.y, -1, 1f); + var invView = cameraUniform_9.viewInv; + var invProj = cameraUniform_9.projInv; + var viewPos = (invProj * clipPos); + var viewPosNormalized = vec4f((viewPos.xyz / viewPos.w), 1f); + var worldPos = (invView * viewPosNormalized); + var rayOrigin = invView[3].xyz; + var rayDir = normalize((worldPos.xyz - rayOrigin)); + return Ray_11(rayOrigin, rayDir); + } + + fn sdRoundedBox2d_15(p: vec2f, size: vec2f, cornerRadius: f32) -> f32 { + var d = ((abs(p) - size) + vec2f(cornerRadius)); + return ((length(max(d, vec2f())) + min(max(d.x, d.y), 0f)) - cornerRadius); + } + + fn rectangleCutoutDist_14(position: vec2f) -> f32 { + var groundRoundness = 0.02; + return sdRoundedBox2d_15(position, vec2f((1f + groundRoundness), (0.2f + groundRoundness)), (0.2f + groundRoundness)); + } + + fn opExtrudeY_16(p: vec3f, dd: f32, h: f32) -> f32 { + var w = vec2f(dd, (abs(p.y) - h)); + return (min(max(w.x, w.y), 0f) + length(max(w, vec2f()))); + } + + fn opUnion_17(d1: f32, d2: f32) -> f32 { + return min(d1, d2); + } + + fn sdPlane_18(p: vec3f, n: vec3f, h: f32) -> f32 { + return (dot(p, n) + h); + } + + fn getMainSceneDist_13(position: vec3f) -> f32 { + var groundThickness = 0.03; + var groundRoundness = 0.02; + return opUnion_17(sdPlane_18(position, vec3f(0, 1, 0), 0.06f), (opExtrudeY_16(position, -rectangleCutoutDist_14(position.xz), (groundThickness - groundRoundness)) - groundRoundness)); + } + + @group(0) @binding(2) var item_20: vec4f; + + @group(0) @binding(3) var digitsTextureView_22: texture_2d_array; + + @group(0) @binding(4) var filteringSampler_23: sampler; + + fn renderPercentageOnGround_21(hitPosition: vec3f, center: vec3f, percentage: u32) -> vec4f { + var textWidth = 0.38; + var textHeight = 0.33; + if (((abs((hitPosition.x - center.x)) > (textWidth * 0.5f)) || (abs((hitPosition.z - center.z)) > (textHeight * 0.5f)))) { + return vec4f(); + } + var localX = (hitPosition.x - center.x); + var localZ = (hitPosition.z - center.z); + var uvX = ((localX + (textWidth * 0.5f)) / textWidth); + var uvZ = ((localZ + (textHeight * 0.5f)) / textHeight); + if (((((uvX < 0f) || (uvX > 1f)) || (uvZ < 0f)) || (uvZ > 1f))) { + return vec4f(); + } + return textureSampleLevel(digitsTextureView_22, filteringSampler_23, vec2f(uvX, uvZ), percentage, 0); + } + + struct DirectionalLight_25 { + direction: vec3f, + color: vec3f, + } + + @group(0) @binding(5) var lightUniform_24: DirectionalLight_25; + + @group(0) @binding(6) var bezierTexture_26: texture_2d; + + fn item_28() -> f32 { + var a = dot(seed_7, vec2f(23.140779495239258, 232.6168975830078)); + var b = dot(seed_7, vec2f(54.47856521606445, 345.8415222167969)); + seed_7.x = fract((cos(a) * 136.8168f)); + seed_7.y = fract((cos(b) * 534.7645f)); + return seed_7.y; + } + + fn randFloat01_27() -> f32 { + return item_28(); + } + + fn getNormalFromSdf_30(position: vec3f, epsilon: f32) -> vec3f { + var k = vec3f(1f, -1, 0f); + var offset1 = (k.xyy * epsilon); + var offset2 = (k.yyx * epsilon); + var offset3 = (k.yxy * epsilon); + var offset4 = (k.xxx * epsilon); + var sample1 = (offset1 * getMainSceneDist_13((position + offset1))); + var sample2 = (offset2 * getMainSceneDist_13((position + offset2))); + var sample3 = (offset3 * getMainSceneDist_13((position + offset3))); + var sample4 = (offset4 * getMainSceneDist_13((position + offset4))); + var gradient = (((sample1 + sample2) + sample3) + sample4); + return normalize(gradient); + } + + fn getNormalMain_29(position: vec3f) -> vec3f { + if (((abs(position.z) > 0.22f) || (abs(position.x) > 1.02f))) { + return vec3f(0, 1, 0); + } + return getNormalFromSdf_30(position, 1e-4f); + } + + @group(0) @binding(7) var jellyColorUniform_31: vec4f; + + fn sqLength_32(a: vec3f) -> f32 { + return dot(a, a); + } + + fn getFakeShadow_34(position: vec3f, lightDir: vec3f) -> vec3f { + var jellyColor = jellyColorUniform_31; + var endCapX = item_20.x; + if ((position.y < -0.03)) { + var fadeSharpness = 30; + var inset = 0.02; + var cutout = (rectangleCutoutDist_14(position.xz) + inset); + var edgeDarkening = saturate((1f - (cutout * f32(fadeSharpness)))); + var lightGradient = saturate((((-position.z * 4f) * lightDir.z) + 1f)); + return ((vec3f(1) * edgeDarkening) * (lightGradient * 0.5f)); + } + else { + var finalUV = vec2f((((position.x - ((position.z * lightDir.x) * sign(lightDir.z))) * 0.5f) + 0.5f), ((1f - ((-position.z / lightDir.z) * 0.5f)) - 0.2f)); + var data = textureSampleLevel(bezierTexture_26, filteringSampler_23, finalUV, 0); + var jellySaturation = mix(0, data.y, saturate(((position.x * 1.5f) + 1.1f))); + var shadowColor = mix(vec3f(), jellyColor.xyz, jellySaturation); + var contrast = ((20f * saturate(finalUV.y)) * (0.8f + (endCapX * 0.2f))); + var shadowOffset = -0.3; + var featherSharpness = 10; + var uvEdgeFeather = (((saturate((finalUV.x * f32(featherSharpness))) * saturate(((1f - finalUV.x) * f32(featherSharpness)))) * saturate(((1f - finalUV.y) * f32(featherSharpness)))) * saturate(finalUV.y)); + var influence = (saturate(((1f - lightDir.y) * 2f)) * uvEdgeFeather); + return mix(vec3f(1), mix(shadowColor, vec3f(1), saturate(((data.x * contrast) + shadowOffset))), influence); + } + } + + fn calculateLighting_33(hitPosition: vec3f, normal: vec3f, rayOrigin: vec3f) -> vec3f { + var lightDir = -(lightUniform_24.direction); + var fakeShadow = getFakeShadow_34(hitPosition, lightDir); + var diffuse = max(dot(normal, lightDir), 0f); + var viewDir = normalize((rayOrigin - hitPosition)); + var reflectDir = reflect(-(lightDir), normal); + var specularFactor = pow(max(dot(viewDir, reflectDir), 0f), 10f); + var specular = (lightUniform_24.color * (specularFactor * 0.6f)); + var baseColor = vec3f(0.8999999761581421); + var directionalLight = (((baseColor * lightUniform_24.color) * diffuse) * fakeShadow); + var ambientLight = ((baseColor * vec3f(0.6000000238418579)) * 0.6); + var finalSpecular = (specular * fakeShadow); + return saturate(((directionalLight + ambientLight) + finalSpecular)); + } + + struct SdfBbox_40 { + left: f32, + right: f32, + bottom: f32, + top: f32, + } + + fn getSliderBbox_39() -> SdfBbox_40 { + return SdfBbox_40(-1.0190000534057617f, 1.0899999141693115f, -0.30000001192092896f, 0.6499999761581421f); + } + + struct LineInfo_42 { + t: f32, + distance: f32, + normal: vec2f, + } + + fn sdInflatedPolyline2D_41(p: vec2f) -> LineInfo_42 { + var bbox = getSliderBbox_39(); + var uv = vec2f(((p.x - bbox.left) / (bbox.right - bbox.left)), ((bbox.top - p.y) / (bbox.top - bbox.bottom))); + var clampedUV = saturate(uv); + var sampledColor = textureSampleLevel(bezierTexture_26, filteringSampler_23, clampedUV, 0); + var segUnsigned = sampledColor.x; + var progress = sampledColor.y; + var normal = sampledColor.zw; + return LineInfo_42(progress, segUnsigned, normal); + } + + fn opExtrudeZ_43(p: vec3f, dd: f32, h: f32) -> f32 { + var w = vec2f(dd, (abs(p.z) - h)); + return (min(max(w.x, w.y), 0f) + length(max(w, vec2f()))); + } + + fn sliderApproxDist_38(position: vec3f) -> f32 { + var bbox = getSliderBbox_39(); + var p = position.xy; + if (((((p.x < bbox.left) || (p.x > bbox.right)) || (p.y < bbox.bottom)) || (p.y > bbox.top))) { + return 1000000000; + } + var poly2D = sdInflatedPolyline2D_41(p); + var dist3D = (opExtrudeZ_43(position, poly2D.distance, 0.17f) - 0.024f); + return dist3D; + } + + fn getSceneDistForAO_37(position: vec3f) -> f32 { + var mainScene = getMainSceneDist_13(position); + var sliderApprox = sliderApproxDist_38(position); + return min(mainScene, sliderApprox); + } + + fn calculateAO_36(position: vec3f, normal: vec3f) -> f32 { + var totalOcclusion = 0f; + var sampleWeight = 1f; + var stepDistance = 0.03333333333333333; + for (var i = 1; (i <= 3i); i++) { + var sampleHeight = (stepDistance * f32(i)); + var samplePosition = (position + (normal * sampleHeight)); + var distanceToSurface = (getSceneDistForAO_37(samplePosition) - 5e-3f); + var occlusionContribution = max(0f, (sampleHeight - distanceToSurface)); + totalOcclusion += (occlusionContribution * sampleWeight); + sampleWeight *= 0.5f; + if ((totalOcclusion > 0.2f)) { + break; + } + } + var rawAO = (1f - ((0.5f * totalOcclusion) / 0.1f)); + return saturate(rawAO); + } + + fn applyAO_35(litColor: vec3f, hitPosition: vec3f, normal: vec3f) -> vec4f { + var ao = calculateAO_36(hitPosition, normal); + var finalColor = (litColor * ao); + return vec4f(finalColor, 1f); + } + + fn renderBackground_19(rayOrigin: vec3f, rayDirection: vec3f, backgroundHitDist: f32, offset: f32) -> vec4f { + var hitPosition = (rayOrigin + (rayDirection * backgroundHitDist)); + var percentageSample = renderPercentageOnGround_21(hitPosition, vec3f(0.7200000286102295, 0, 0), u32(((item_20.x + 0.43f) * 84f))); + var highlights = 0f; + var highlightWidth = 1f; + var highlightHeight = 0.2; + var offsetX = 0f; + var offsetZ = 0.05000000074505806f; + var lightDir = lightUniform_24.direction; + var causticScale = 0.2; + offsetX -= (lightDir.x * causticScale); + offsetZ += (lightDir.z * causticScale); + var endCapX = item_20.x; + var sliderStretch = ((endCapX + 1f) * 0.5f); + if (((abs((hitPosition.x + offsetX)) < highlightWidth) && (abs((hitPosition.z + offsetZ)) < highlightHeight))) { + var uvX_orig = ((((hitPosition.x + offsetX) + (highlightWidth * 2f)) / highlightWidth) * 0.5f); + var uvZ_orig = ((((hitPosition.z + offsetZ) + (highlightHeight * 2f)) / highlightHeight) * 0.5f); + var centeredUV = vec2f((uvX_orig - 0.5f), (uvZ_orig - 0.5f)); + var finalUV = vec2f(centeredUV.x, (1f - (pow((abs((centeredUV.y - 0.5f)) * 2f), 2f) * 0.3f))); + var density = max(0f, ((textureSampleLevel(bezierTexture_26, filteringSampler_23, finalUV, 0).x - 0.25f) * 8f)); + var fadeX = smoothstep(0, -0.2, (hitPosition.x - endCapX)); + var fadeZ = (1f - pow((abs((centeredUV.y - 0.5f)) * 2f), 3f)); + var fadeStretch = saturate((1f - sliderStretch)); + var edgeFade = ((saturate(fadeX) * saturate(fadeZ)) * fadeStretch); + highlights = ((((pow(density, 3f) * edgeFade) * 3f) * (1f + lightDir.z)) / 1.5f); + } + var originYBound = saturate((rayOrigin.y + 0.01f)); + var posOffset = (hitPosition + (vec3f(0, 1, 0) * ((offset * (originYBound / (1f + originYBound))) * (1f + (randFloat01_27() / 2f))))); + var newNormal = getNormalMain_29(posOffset); + var jellyColor = jellyColorUniform_31; + var sqDist = sqLength_32((hitPosition - vec3f(endCapX, 0f, 0f))); + var bounceLight = (jellyColor.xyz * ((1f / ((sqDist * 15f) + 1f)) * 0.4f)); + var sideBounceLight = ((jellyColor.xyz * ((1f / ((sqDist * 40f) + 1f)) * 0.3f)) * abs(newNormal.z)); + var litColor = calculateLighting_33(posOffset, newNormal, rayOrigin); + var backgroundColor = ((applyAO_35((vec3f(1) * litColor), posOffset, newNormal) + vec4f(bounceLight, 0f)) + vec4f(sideBounceLight, 0f)); + var textColor = saturate((backgroundColor.xyz * vec3f(0.5))); + return vec4f((mix(backgroundColor.xyz, textColor, percentageSample.x) * (1f + highlights)), 1f); + } + + struct BoxIntersection_45 { + hit: bool, + tMin: f32, + tMax: f32, + } + + fn intersectBox_44(rayOrigin: vec3f, rayDirection: vec3f, boxMin: vec3f, boxMax: vec3f) -> BoxIntersection_45 { + var invDir = (vec3f(1) / rayDirection); + var t1 = ((boxMin - rayOrigin) * invDir); + var t2 = ((boxMax - rayOrigin) * invDir); + var tMinVec = min(t1, t2); + var tMaxVec = max(t1, t2); + var tMin = max(max(tMinVec.x, tMinVec.y), tMinVec.z); + var tMax = min(min(tMaxVec.x, tMaxVec.y), tMaxVec.z); + var result = BoxIntersection_45(); + result.hit = ((tMax >= tMin) && (tMax >= 0f)); + result.tMin = tMin; + result.tMax = tMax; + return result; + } + + fn sdPie_49(p: vec2f, c: vec2f, r: f32) -> f32 { + var p_w = p; + p_w.x = abs(p.x); + var l = (length(p_w) - r); + var m = length((p_w - (c * clamp(dot(p_w, c), 0f, r)))); + return max(l, (m * sign(((c.y * p_w.x) - (c.x * p_w.y))))); + } + + fn cap3D_48(position: vec3f) -> f32 { + var endCap = item_20; + var secondLastPoint = vec2f(endCap.x, endCap.y); + var lastPoint = vec2f(endCap.z, endCap.w); + var angle = atan2((lastPoint.y - secondLastPoint.y), (lastPoint.x - secondLastPoint.x)); + var rot = mat2x2f(cos(angle), -sin(angle), sin(angle), cos(angle)); + var pieP = (position - vec3f(secondLastPoint, 0f)); + pieP = vec3f((rot * pieP.xy), pieP.z); + var hmm = sdPie_49(pieP.zx, vec2f(1, 0), 0.17f); + var extrudeEnd = (opExtrudeY_16(pieP, hmm, 1e-3f) - 0.024f); + return extrudeEnd; + } + + fn sliderSdf3D_47(position: vec3f) -> LineInfo_42 { + var poly2D = sdInflatedPolyline2D_41(position.xy); + var finalDist = 0f; + if ((poly2D.t > 0.94f)) { + finalDist = cap3D_48(position); + } + else { + var body = (opExtrudeZ_43(position, poly2D.distance, 0.17f) - 0.024f); + finalDist = body; + } + return LineInfo_42(poly2D.t, finalDist, poly2D.normal); + } + + struct HitInfo_50 { + distance: f32, + objectType: i32, + t: f32, + } + + fn getSceneDist_46(position: vec3f) -> HitInfo_50 { + var mainScene = getMainSceneDist_13(position); + var poly3D = sliderSdf3D_47(position); + var hitInfo = HitInfo_50(); + if ((poly3D.distance < mainScene)) { + hitInfo.distance = poly3D.distance; + hitInfo.objectType = 1i; + hitInfo.t = poly3D.t; + } + else { + hitInfo.distance = mainScene; + hitInfo.objectType = 2i; + } + return hitInfo; + } + + fn getNormalFromSdf_54(position: vec3f, epsilon: f32) -> vec3f { + var k = vec3f(1f, -1, 0f); + var offset1 = (k.xyy * epsilon); + var offset2 = (k.yyx * epsilon); + var offset3 = (k.yxy * epsilon); + var offset4 = (k.xxx * epsilon); + var sample1 = (offset1 * cap3D_48((position + offset1))); + var sample2 = (offset2 * cap3D_48((position + offset2))); + var sample3 = (offset3 * cap3D_48((position + offset3))); + var sample4 = (offset4 * cap3D_48((position + offset4))); + var gradient = (((sample1 + sample2) + sample3) + sample4); + return normalize(gradient); + } + + fn getNormalCap_53(pos: vec3f) -> vec3f { + return getNormalFromSdf_54(pos, 0.01f); + } + + fn getSliderNormal_52(position: vec3f, hitInfo: HitInfo_50) -> vec3f { + var poly2D = sdInflatedPolyline2D_41(position.xy); + var gradient2D = poly2D.normal; + var threshold = 0.14450000000000002; + var absZ = abs(position.z); + var zDistance = max(0f, (((absZ - threshold) * 0.17f) / (0.17f - threshold))); + var edgeDistance = (0.024f - poly2D.distance); + var edgeContrib = 0.9; + var zContrib = (1f - edgeContrib); + var zDirection = sign(position.z); + var zAxisVector = vec3f(0f, 0f, zDirection); + var edgeBlendDistance = ((edgeContrib * 0.024f) + (zContrib * 0.17f)); + var blendFactor = smoothstep(edgeBlendDistance, 0, ((zDistance * zContrib) + (edgeDistance * edgeContrib))); + var normal2D = vec3f(gradient2D.xy, 0f); + var blendedNormal = mix(zAxisVector, normal2D, ((blendFactor * 0.5f) + 0.5f)); + var normal = normalize(blendedNormal); + if ((hitInfo.t > 0.94f)) { + var ratio = ((hitInfo.t - 0.94f) / 0.02f); + var fullNormal = getNormalCap_53(position); + normal = normalize(mix(normal, fullNormal, ratio)); + } + return normal; + } + + fn getNormal_51(position: vec3f, hitInfo: HitInfo_50) -> vec3f { + if (((hitInfo.objectType == 1i) && (hitInfo.t < 0.96f))) { + return getSliderNormal_52(position, hitInfo); + } + return select(getNormalCap_53(position), getNormalMain_29(position), (hitInfo.objectType == 2i)); + } + + fn fresnelSchlick_55(cosTheta: f32, ior1: f32, ior2: f32) -> f32 { + var r0 = pow(((ior1 - ior2) / (ior1 + ior2)), 2f); + return (r0 + ((1f - r0) * pow((1f - cosTheta), 5f))); + } + + @group(0) @binding(8) var blurEnabledUniform_57: u32; + + fn rayMarchNoJelly_56(rayOrigin: vec3f, rayDirection: vec3f) -> vec3f { + var distanceFromOrigin = 0f; + var hit = 0f; + for (var i = 0; (i < 6i); i++) { + var p = (rayOrigin + (rayDirection * distanceFromOrigin)); + hit = getMainSceneDist_13(p); + distanceFromOrigin += hit; + if (((distanceFromOrigin > 10f) || (hit < 0.01f))) { + break; + } + } + if ((distanceFromOrigin < 10f)) { + return renderBackground_19(rayOrigin, rayDirection, distanceFromOrigin, select(0f, 0.87f, (blurEnabledUniform_57 == 1u))).xyz; + } + return vec3f(); + } + + fn beerLambert_58(sigma: vec3f, dist: f32) -> vec3f { + return exp((sigma * -dist)); + } + + fn rayMarch_12(rayOrigin: vec3f, rayDirection: vec3f, uv: vec2f) -> vec4f { + var totalSteps = 0u; + var backgroundDist = 0f; + for (var i = 0; (i < 64i); i++) { + var p = (rayOrigin + (rayDirection * backgroundDist)); + var hit = getMainSceneDist_13(p); + backgroundDist += hit; + if ((hit < 1e-3f)) { + break; + } + } + var background = renderBackground_19(rayOrigin, rayDirection, backgroundDist, 0f); + var bbox = getSliderBbox_39(); + var zDepth = 0.25f; + var sliderMin = vec3f(bbox.left, bbox.bottom, -zDepth); + var sliderMax = vec3f(bbox.right, bbox.top, zDepth); + var intersection = intersectBox_44(rayOrigin, rayDirection, sliderMin, sliderMax); + if (!intersection.hit) { + return background; + } + var distanceFromOrigin = max(0f, intersection.tMin); + for (var i = 0; (i < 64i); i++) { + if ((totalSteps >= 64u)) { + break; + } + var currentPosition = (rayOrigin + (rayDirection * distanceFromOrigin)); + var hitInfo = getSceneDist_46(currentPosition); + distanceFromOrigin += hitInfo.distance; + totalSteps++; + if ((hitInfo.distance < 1e-3f)) { + var hitPosition = (rayOrigin + (rayDirection * distanceFromOrigin)); + if (!(hitInfo.objectType == 1i)) { + break; + } + var N = getNormal_51(hitPosition, hitInfo); + var I = rayDirection; + var cosi = min(1f, max(0f, dot(-(I), N))); + var F = fresnelSchlick_55(cosi, 1f, 1.4199999570846558f); + var reflection = saturate(vec3f((hitPosition.y + 0.2f))); + var eta = 0.7042253521126761; + var k = (1f - ((eta * eta) * (1f - (cosi * cosi)))); + var refractedColor = vec3f(); + if ((k > 0f)) { + var refrDir = normalize(((I * eta) + (N * ((eta * cosi) - sqrt(k))))); + var p = (hitPosition + (refrDir * 2e-3)); + var exitPos = (p + (refrDir * 2e-3)); + var env = rayMarchNoJelly_56(exitPos, refrDir); + var progress = hitInfo.t; + var jellyColor = jellyColorUniform_31; + var scatterTint = (jellyColor.xyz * 1.5); + var density = 20f; + var absorb = ((vec3f(1) - jellyColor.xyz) * density); + var T = beerLambert_58((absorb * pow(progress, 2f)), 0.08); + var lightDir = -(lightUniform_24.direction); + var forward = max(0f, dot(lightDir, refrDir)); + var scatter = (scatterTint * ((3f * forward) * pow(progress, 3f))); + refractedColor = ((env * T) + scatter); + } + var jelly = ((reflection * F) + (refractedColor * (1f - F))); + return vec4f(jelly, 1f); + } + if ((distanceFromOrigin > backgroundDist)) { + break; + } + } + return background; + } + + struct raymarchFn_Input_59 { + @location(0) uv: vec2f, + } + + @fragment fn raymarchFn_3(_arg_0: raymarchFn_Input_59) -> @location(0) vec4f { + randSeed2_5((randomUniform_4 * _arg_0.uv)); + var ndc = vec2f(((_arg_0.uv.x * 2f) - 1f), -((_arg_0.uv.y * 2f) - 1f)); + var ray = getRay_8(ndc); + var color = rayMarch_12(ray.origin, ray.direction, _arg_0.uv); + return vec4f(tanh((color.xyz * 1.3)), 1f); + } + + @group(0) @binding(0) var currentTexture_1: texture_2d; + + @group(0) @binding(1) var historyTexture_2: texture_2d; + + @group(0) @binding(2) var outputTexture_3: texture_storage_2d; + + struct taaResolveFn_Input_4 { + @builtin(global_invocation_id) gid: vec3u, + } + + @compute @workgroup_size(16, 16) fn taaResolveFn_0(_arg_0: taaResolveFn_Input_4) { + var currentColor = textureLoad(currentTexture_1, vec2u(_arg_0.gid.xy), 0); + var historyColor = textureLoad(historyTexture_2, vec2u(_arg_0.gid.xy), 0); + var minColor = vec3f(9999); + var maxColor = vec3f(-9999); + var dimensions = textureDimensions(currentTexture_1); + for (var x = -1; (x <= 1i); x++) { + for (var y = -1; (y <= 1i); y++) { + var sampleCoord = (vec2i(_arg_0.gid.xy) + vec2i(x, y)); + var clampedCoord = clamp(sampleCoord, vec2i(), (vec2i(dimensions.xy) - vec2i(1))); + var neighborColor = textureLoad(currentTexture_1, clampedCoord, 0); + minColor = min(minColor, neighborColor.xyz); + maxColor = max(maxColor, neighborColor.xyz); + } + } + var historyColorClamped = clamp(historyColor.xyz, minColor, maxColor); + var uv = (vec2f(_arg_0.gid.xy) / vec2f(dimensions.xy)); + var textRegionMinX = 0.7099999785423279f; + var textRegionMaxX = 0.8500000238418579f; + var textRegionMinY = 0.4699999988079071f; + var textRegionMaxY = 0.550000011920929f; + var borderSize = 0.019999999552965164f; + var fadeInX = smoothstep((textRegionMinX - borderSize), (textRegionMinX + borderSize), uv.x); + var fadeOutX = (1f - smoothstep((textRegionMaxX - borderSize), (textRegionMaxX + borderSize), uv.x)); + var fadeInY = smoothstep((textRegionMinY - borderSize), (textRegionMinY + borderSize), uv.y); + var fadeOutY = (1f - smoothstep((textRegionMaxY - borderSize), (textRegionMaxY + borderSize), uv.y)); + var inTextRegion = (((fadeInX * fadeOutX) * fadeInY) * fadeOutY); + var blendFactor = mix(0.8999999761581421f, 0.699999988079071f, inTextRegion); + var resolvedColor = vec4f(mix(currentColor.xyz, historyColorClamped, blendFactor), 1f); + textureStore(outputTexture_3, vec2u(_arg_0.gid.x, _arg_0.gid.y), resolvedColor); + } + + struct fullScreenTriangle_Input_1 { + @builtin(vertex_index) vertexIndex: u32, + } + + struct fullScreenTriangle_Output_2 { + @builtin(position) pos: vec4f, + @location(0) uv: vec2f, + } + + @vertex fn fullScreenTriangle_0(in: fullScreenTriangle_Input_1) -> fullScreenTriangle_Output_2 { + const pos = array(vec2f(-1, -1), vec2f(3, -1), vec2f(-1, 3)); + const uv = array(vec2f(0, 1), vec2f(2, 1), vec2f(0, -1)); + + return fullScreenTriangle_Output_2(vec4f(pos[in.vertexIndex], 0, 1), uv[in.vertexIndex]); + } + + @group(1) @binding(0) var currentTexture_4: texture_2d; + + @group(0) @binding(0) var filteringSampler_5: sampler; + + struct fragmentMain_Input_6 { + @location(0) uv: vec2f, + } + + @fragment fn fragmentMain_3(input: fragmentMain_Input_6) -> @location(0) vec4f { + return textureSample(currentTexture_4, filteringSampler_5, input.uv); + } + + @group(0) @binding(0) var sizeUniform_1: vec3u; + + @group(0) @binding(1) var bezierWriteView_3: texture_storage_2d; + + @group(0) @binding(2) var pointsView_4: array; + + @group(0) @binding(3) var controlPointsView_5: array; + + fn dot2_7(v: vec2f) -> f32 { + return dot(v, v); + } + + fn sdBezier_6(pos: vec2f, A: vec2f, B: vec2f, C: vec2f) -> f32 { + var a = (B - A); + var b = ((A - (B * 2)) + C); + var c = (a * 2f); + var d = (A - pos); + var dotB = max(dot(b, b), 1e-4f); + var kk = (1f / dotB); + var kx = (kk * dot(a, b)); + var ky = ((kk * ((2f * dot(a, a)) + dot(d, b))) / 3f); + var kz = (kk * dot(d, a)); + var res = 0f; + var p = (ky - (kx * kx)); + var p3 = ((p * p) * p); + var q = ((kx * (((2f * kx) * kx) - (3f * ky))) + kz); + var h = ((q * q) + (4f * p3)); + if ((h >= 0f)) { + h = sqrt(h); + var x = ((vec2f(h, -h) - q) * 0.5); + var uv = (sign(x) * pow(abs(x), vec2f(0.3333333432674408))); + var t = clamp(((uv.x + uv.y) - kx), 0f, 1f); + res = dot2_7((d + ((c + (b * t)) * t))); + } + else { + var z = sqrt(-p); + var v = (acos((q / ((p * z) * 2f))) / 3f); + var m = cos(v); + var n = (sin(v) * 1.732050808f); + var t = saturate(((vec3f((m + m), (-n - m), (n - m)) * z) - kx)); + res = min(dot2_7((d + ((c + (b * t.x)) * t.x))), dot2_7((d + ((c + (b * t.y)) * t.y)))); + } + return sqrt(res); + } + + fn wrappedCallback_2(x: u32, y: u32, _arg_2: u32) { + var size = textureDimensions(bezierWriteView_3); + var pixelUV = ((vec2f(f32(x), f32(y)) + 0.5) / vec2f(size)); + var sliderPos = vec2f((-1.0189999997615815f + (pixelUV.x * 2.108999973535538f)), (0.65f - (pixelUV.y * 0.95f))); + var minDist = 1e+10f; + var closestSegment = 0i; + var closestT = 0f; + var epsilon = 0.029999999329447746f; + var xOffset = vec2f(epsilon, 0f); + var yOffset2 = vec2f(0f, epsilon); + var xPlusDist = 1e+10f; + var xMinusDist = 1e+10f; + var yPlusDist = 1e+10f; + var yMinusDist = 1e+10f; + for (var i = 0; (i < (17 - 1)); i++) { + var A = pointsView_4[i]; + var B = pointsView_4[(i + 1i)]; + var C = controlPointsView_5[i]; + var dist = sdBezier_6(sliderPos, A, C, B); + if ((dist < minDist)) { + minDist = dist; + closestSegment = i; + var AB = (B - A); + var AP = (sliderPos - A); + var ABLength = length(AB); + if ((ABLength > 0f)) { + closestT = clamp((dot(AP, AB) / (ABLength * ABLength)), 0f, 1f); + } + else { + closestT = 0f; + } + } + xPlusDist = min(xPlusDist, sdBezier_6((sliderPos + xOffset), A, C, B)); + xMinusDist = min(xMinusDist, sdBezier_6((sliderPos - xOffset), A, C, B)); + yPlusDist = min(yPlusDist, sdBezier_6((sliderPos + yOffset2), A, C, B)); + yMinusDist = min(yMinusDist, sdBezier_6((sliderPos - yOffset2), A, C, B)); + } + var overallProgress = ((f32(closestSegment) + closestT) / f32((17 - 1))); + var normalX = ((xPlusDist - xMinusDist) / (2f * epsilon)); + var normalY = ((yPlusDist - yMinusDist) / (2f * epsilon)); + textureStore(bezierWriteView_3, vec2u(x, y), vec4f(minDist, overallProgress, normalX, normalY)); + } + + struct mainCompute_Input_8 { + @builtin(global_invocation_id) id: vec3u, + } + + @compute @workgroup_size(16, 16, 1) fn mainCompute_0(in: mainCompute_Input_8) { + if (any(in.id >= sizeUniform_1)) { + return; + } + wrappedCallback_2(in.id.x, in.id.y, in.id.z); + }" + `); + }); +}); diff --git a/packages/typegpu/tests/examples/utils/commonMocks.ts b/packages/typegpu/tests/examples/utils/commonMocks.ts index d8914e2f2c..816c904a10 100644 --- a/packages/typegpu/tests/examples/utils/commonMocks.ts +++ b/packages/typegpu/tests/examples/utils/commonMocks.ts @@ -46,6 +46,14 @@ export function setupCommonMocks() { }); } +export function mockFonts() { + Object.defineProperty(document, 'fonts', { + value: { + load: vi.fn().mockResolvedValue([{}, {}]), + }, + }); +} + export function mockResizeObserver() { vi.stubGlobal( 'ResizeObserver', From dae310b891db77659b95f0ca7836fbed8b87187e Mon Sep 17 00:00:00 2001 From: Konrad Reczko Date: Fri, 7 Nov 2025 17:59:17 +0100 Subject: [PATCH 2/9] better blur and adaptive steps + jitter --- .../simulation/slime-mold-3d/index.ts | 106 +++++++++++------- 1 file changed, 64 insertions(+), 42 deletions(-) diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts index 2a6e2cc004..2abe8aeca0 100644 --- a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts +++ b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts @@ -7,7 +7,7 @@ import * as m from 'wgpu-matrix'; const root = await tgpu.init({ device: { - optionalFeatures: ['float32-filterable'], + optionalFeatures: ['float32-filterable', 'timestamp-query'], }, }); const canFilter = root.enabledFeatures.has('float32-filterable'); @@ -32,8 +32,8 @@ const CAMERA_FOV_DEGREES = 60; const CAMERA_DISTANCE_MULTIPLIER = 1.5; const CAMERA_INITIAL_ANGLE = Math.PI / 4; -const RAYMARCH_STEPS = 128; -const DENSITY_MULTIPLIER = 0.05; +const RAYMARCH_STEPS = 48; +const DENSITY_MULTIPLIER = 0.1; const RANDOM_DIRECTION_WEIGHT = 0.3; const CENTER_BIAS_WEIGHT = 0.7; @@ -140,6 +140,12 @@ const computeLayout = tgpu.bindGroupLayout({ newState: { storageTexture: d.textureStorage3d('r32float', 'write-only') }, }); +const blurLayout = tgpu.bindGroupLayout({ + oldState: { texture: d.texture3d() }, + newState: { storageTexture: d.textureStorage3d('r32float', 'write-only') }, + sampler: { sampler: 'filtering' }, +}); + const renderLayout = tgpu.bindGroupLayout({ state: { texture: d.texture3d(), @@ -306,52 +312,48 @@ const updateAgents = tgpu['~unstable'].computeFn({ ); }); +const sampler = root['~unstable'].createSampler({ + magFilter: canFilter ? 'linear' : 'nearest', + minFilter: canFilter ? 'linear' : 'nearest', +}); + const blur = tgpu['~unstable'].computeFn({ in: { gid: d.builtin.globalInvocationId }, workgroupSize: BLUR_WORKGROUP_SIZE, })(({ gid }) => { - const dims = std.textureDimensions(computeLayout.$.oldState); + const dims = std.textureDimensions(blurLayout.$.oldState); if (gid.x >= dims.x || gid.y >= dims.y || gid.z >= dims.z) return; + const uv = d.vec3f(gid).add(0.5).div(d.vec3f(dims)); + const offset = d.vec3f(1).div(d.vec3f(dims)); + let sum = d.f32(); - let count = d.f32(); - - for (let offsetZ = -1; offsetZ <= 1; offsetZ++) { - for (let offsetY = -1; offsetY <= 1; offsetY++) { - for (let offsetX = -1; offsetX <= 1; offsetX++) { - const samplePos = d.vec3i(gid.xyz).add( - d.vec3i(offsetX, offsetY, offsetZ), - ); - const dimsi = d.vec3i(dims); - - if ( - samplePos.x >= 0 && samplePos.x < dimsi.x && - samplePos.y >= 0 && samplePos.y < dimsi.y && - samplePos.z >= 0 && samplePos.z < dimsi.z - ) { - const value = - std.textureLoad(computeLayout.$.oldState, d.vec3u(samplePos)).x; - sum = sum + value; - count = count + 1; - } - } + + for (let axis = 0; axis < 3; axis++) { + for (let sign = -1; sign <= 1; sign += 2) { + const offsetVec = d.vec3f( + std.select(0, offset.x * sign, axis === 0), + std.select(0, offset.y * sign, axis === 1), + std.select(0, offset.z * sign, axis === 2), + ); + sum = sum + std.textureSampleLevel( + blurLayout.$.oldState, + blurLayout.$.sampler, + uv.add(offsetVec), + 0, + ).x; } } - const blurred = sum / count; + const blurred = sum / 6.0; const newValue = std.saturate(blurred - params.$.evaporationRate); std.textureStore( - computeLayout.$.newState, + blurLayout.$.newState, gid.xyz, d.vec4f(newValue, 0, 0, 1), ); }); -const sampler = root['~unstable'].createSampler({ - magFilter: canFilter ? 'linear' : 'nearest', - minFilter: canFilter ? 'linear' : 'nearest', -}); - // Ray-box intersection const rayBoxIntersection = ( rayOrigin: d.v3f, @@ -375,6 +377,7 @@ const fragmentShader = tgpu['~unstable'].fragmentFn({ in: { uv: d.vec2f }, out: d.vec4f, })(({ uv }) => { + randf.seed2(uv); const ndc = d.vec2f(uv.x * 2 - 1, 1 - uv.y * 2); const ndcNear = d.vec4f(ndc, -1, 1); const ndcFar = d.vec4f(ndc, 1, 1); @@ -393,11 +396,23 @@ const fragmentShader = tgpu['~unstable'].fragmentFn({ return d.vec4f(); } - // March params - const tStart = std.max(isect.tNear, 0); + const jitter = randf.sample() * 20; + const tStart = std.max(isect.tNear + jitter, jitter); const tEnd = isect.tFar; - const numSteps = RAYMARCH_STEPS; - const stepSize = (tEnd - tStart) / numSteps; + + const intersectionLength = tEnd - tStart; + const baseStepsPerUnit = d.f32(0.3); + const minSteps = d.i32(8); + const maxSteps = d.i32(RAYMARCH_STEPS); + + const adaptiveSteps = std.clamp( + d.i32(intersectionLength * baseStepsPerUnit), + minSteps, + maxSteps, + ); + + const numSteps = adaptiveSteps; + const stepSize = intersectionLength / d.f32(numSteps); const thresholdLo = d.f32(0.06); const thresholdHi = d.f32(0.25); @@ -411,11 +426,8 @@ const fragmentShader = tgpu['~unstable'].fragmentFn({ const TMin = d.f32(1e-3); - for (let i = 0; i < numSteps; i++) { - if (transmittance <= TMin) { - break; - } - + let i = d.i32(0); + while (i < numSteps && transmittance > TMin) { const t = tStart + (d.f32(i) + 0.5) * stepSize; const pos = rayOrigin.add(rayDir.mul(t)); const texCoord = pos.div(resolution); @@ -433,6 +445,8 @@ const fragmentShader = tgpu['~unstable'].fragmentFn({ accum = accum.add(contrib.mul(transmittance)); transmittance = transmittance * (1 - alphaSrc); + + i += 1; } const alpha = 1 - transmittance; @@ -459,6 +473,14 @@ const bindGroups = [0, 1].map((i) => }) ); +const blurBindGroups = [0, 1].map((i) => + root.createBindGroup(blurLayout, { + oldState: textures[i], + newState: textures[1 - i], + sampler: sampler, + }) +); + const renderBindGroups = [0, 1].map((i) => root.createBindGroup(renderLayout, { state: textures[i], @@ -476,7 +498,7 @@ function frame() { params.writePartial({ deltaTime }); blurPipeline - .with(bindGroups[currentTexture]) + .with(blurBindGroups[currentTexture]) .dispatchWorkgroups( Math.ceil(resolution.x / BLUR_WORKGROUP_SIZE[0]), Math.ceil(resolution.y / BLUR_WORKGROUP_SIZE[1]), From 27cd85cc1038bc0dfa2ceb62b7cdd4e4d0eae9ec Mon Sep 17 00:00:00 2001 From: Konrad Reczko Date: Fri, 7 Nov 2025 18:05:04 +0100 Subject: [PATCH 3/9] revert some random files --- .../examples/individual/jelly-slider.test.ts | 769 ------------------ .../tests/examples/utils/commonMocks.ts | 8 - 2 files changed, 777 deletions(-) delete mode 100644 packages/typegpu/tests/examples/individual/jelly-slider.test.ts diff --git a/packages/typegpu/tests/examples/individual/jelly-slider.test.ts b/packages/typegpu/tests/examples/individual/jelly-slider.test.ts deleted file mode 100644 index a4fd60a826..0000000000 --- a/packages/typegpu/tests/examples/individual/jelly-slider.test.ts +++ /dev/null @@ -1,769 +0,0 @@ -/** - * @vitest-environment jsdom - */ - -import { describe, expect } from 'vitest'; -import { it } from '../../utils/extendedIt.ts'; -import { runExampleTest, setupCommonMocks } from '../utils/baseTest.ts'; -import { - mockFonts, - mockImageLoading, - mockResizeObserver, -} from '../utils/commonMocks.ts'; - -describe('jelly-slider example', () => { - setupCommonMocks(); - - it('should produce valid code', async ({ device }) => { - const shaderCodes = await runExampleTest({ - category: 'rendering', - name: 'jelly-slider', - setupMocks: () => { - mockFonts(); - mockImageLoading(); - mockResizeObserver(); - }, - expectedCalls: 6, - }, device); - - expect(shaderCodes).toMatchInlineSnapshot(` - " - struct VertexOutput { - @builtin(position) pos: vec4f, - @location(0) uv: vec2f, - } - - @vertex - fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput { - let pos = array(vec2f(-1, -1), vec2f(3, -1), vec2f(-1, 3)); - let uv = array(vec2f(0, 1), vec2f(2, 1), vec2f(0, -1)); - - var output: VertexOutput; - output.pos = vec4f(pos[vertexIndex], 0, 1); - output.uv = uv[vertexIndex]; - return output; - } - - - - @group(0) @binding(0) var inputTexture: texture_2d; - @group(0) @binding(1) var inputSampler: sampler; - - @fragment - fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f { - return textureSample(inputTexture, inputSampler, uv); - } - - - struct fullScreenTriangle_Input_1 { - @builtin(vertex_index) vertexIndex: u32, - } - - struct fullScreenTriangle_Output_2 { - @builtin(position) pos: vec4f, - @location(0) uv: vec2f, - } - - @vertex fn fullScreenTriangle_0(in: fullScreenTriangle_Input_1) -> fullScreenTriangle_Output_2 { - const pos = array(vec2f(-1, -1), vec2f(3, -1), vec2f(-1, 3)); - const uv = array(vec2f(0, 1), vec2f(2, 1), vec2f(0, -1)); - - return fullScreenTriangle_Output_2(vec4f(pos[in.vertexIndex], 0, 1), uv[in.vertexIndex]); - } - - @group(0) @binding(0) var randomUniform_4: vec2f; - - var seed_7: vec2f; - - fn seed2_6(value: vec2f) { - seed_7 = value; - } - - fn randSeed2_5(seed: vec2f) { - seed2_6(seed); - } - - struct Camera_10 { - view: mat4x4f, - proj: mat4x4f, - viewInv: mat4x4f, - projInv: mat4x4f, - } - - @group(0) @binding(1) var cameraUniform_9: Camera_10; - - struct Ray_11 { - origin: vec3f, - direction: vec3f, - } - - fn getRay_8(ndc: vec2f) -> Ray_11 { - var clipPos = vec4f(ndc.x, ndc.y, -1, 1f); - var invView = cameraUniform_9.viewInv; - var invProj = cameraUniform_9.projInv; - var viewPos = (invProj * clipPos); - var viewPosNormalized = vec4f((viewPos.xyz / viewPos.w), 1f); - var worldPos = (invView * viewPosNormalized); - var rayOrigin = invView[3].xyz; - var rayDir = normalize((worldPos.xyz - rayOrigin)); - return Ray_11(rayOrigin, rayDir); - } - - fn sdRoundedBox2d_15(p: vec2f, size: vec2f, cornerRadius: f32) -> f32 { - var d = ((abs(p) - size) + vec2f(cornerRadius)); - return ((length(max(d, vec2f())) + min(max(d.x, d.y), 0f)) - cornerRadius); - } - - fn rectangleCutoutDist_14(position: vec2f) -> f32 { - var groundRoundness = 0.02; - return sdRoundedBox2d_15(position, vec2f((1f + groundRoundness), (0.2f + groundRoundness)), (0.2f + groundRoundness)); - } - - fn opExtrudeY_16(p: vec3f, dd: f32, h: f32) -> f32 { - var w = vec2f(dd, (abs(p.y) - h)); - return (min(max(w.x, w.y), 0f) + length(max(w, vec2f()))); - } - - fn opUnion_17(d1: f32, d2: f32) -> f32 { - return min(d1, d2); - } - - fn sdPlane_18(p: vec3f, n: vec3f, h: f32) -> f32 { - return (dot(p, n) + h); - } - - fn getMainSceneDist_13(position: vec3f) -> f32 { - var groundThickness = 0.03; - var groundRoundness = 0.02; - return opUnion_17(sdPlane_18(position, vec3f(0, 1, 0), 0.06f), (opExtrudeY_16(position, -rectangleCutoutDist_14(position.xz), (groundThickness - groundRoundness)) - groundRoundness)); - } - - @group(0) @binding(2) var item_20: vec4f; - - @group(0) @binding(3) var digitsTextureView_22: texture_2d_array; - - @group(0) @binding(4) var filteringSampler_23: sampler; - - fn renderPercentageOnGround_21(hitPosition: vec3f, center: vec3f, percentage: u32) -> vec4f { - var textWidth = 0.38; - var textHeight = 0.33; - if (((abs((hitPosition.x - center.x)) > (textWidth * 0.5f)) || (abs((hitPosition.z - center.z)) > (textHeight * 0.5f)))) { - return vec4f(); - } - var localX = (hitPosition.x - center.x); - var localZ = (hitPosition.z - center.z); - var uvX = ((localX + (textWidth * 0.5f)) / textWidth); - var uvZ = ((localZ + (textHeight * 0.5f)) / textHeight); - if (((((uvX < 0f) || (uvX > 1f)) || (uvZ < 0f)) || (uvZ > 1f))) { - return vec4f(); - } - return textureSampleLevel(digitsTextureView_22, filteringSampler_23, vec2f(uvX, uvZ), percentage, 0); - } - - struct DirectionalLight_25 { - direction: vec3f, - color: vec3f, - } - - @group(0) @binding(5) var lightUniform_24: DirectionalLight_25; - - @group(0) @binding(6) var bezierTexture_26: texture_2d; - - fn item_28() -> f32 { - var a = dot(seed_7, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_7, vec2f(54.47856521606445, 345.8415222167969)); - seed_7.x = fract((cos(a) * 136.8168f)); - seed_7.y = fract((cos(b) * 534.7645f)); - return seed_7.y; - } - - fn randFloat01_27() -> f32 { - return item_28(); - } - - fn getNormalFromSdf_30(position: vec3f, epsilon: f32) -> vec3f { - var k = vec3f(1f, -1, 0f); - var offset1 = (k.xyy * epsilon); - var offset2 = (k.yyx * epsilon); - var offset3 = (k.yxy * epsilon); - var offset4 = (k.xxx * epsilon); - var sample1 = (offset1 * getMainSceneDist_13((position + offset1))); - var sample2 = (offset2 * getMainSceneDist_13((position + offset2))); - var sample3 = (offset3 * getMainSceneDist_13((position + offset3))); - var sample4 = (offset4 * getMainSceneDist_13((position + offset4))); - var gradient = (((sample1 + sample2) + sample3) + sample4); - return normalize(gradient); - } - - fn getNormalMain_29(position: vec3f) -> vec3f { - if (((abs(position.z) > 0.22f) || (abs(position.x) > 1.02f))) { - return vec3f(0, 1, 0); - } - return getNormalFromSdf_30(position, 1e-4f); - } - - @group(0) @binding(7) var jellyColorUniform_31: vec4f; - - fn sqLength_32(a: vec3f) -> f32 { - return dot(a, a); - } - - fn getFakeShadow_34(position: vec3f, lightDir: vec3f) -> vec3f { - var jellyColor = jellyColorUniform_31; - var endCapX = item_20.x; - if ((position.y < -0.03)) { - var fadeSharpness = 30; - var inset = 0.02; - var cutout = (rectangleCutoutDist_14(position.xz) + inset); - var edgeDarkening = saturate((1f - (cutout * f32(fadeSharpness)))); - var lightGradient = saturate((((-position.z * 4f) * lightDir.z) + 1f)); - return ((vec3f(1) * edgeDarkening) * (lightGradient * 0.5f)); - } - else { - var finalUV = vec2f((((position.x - ((position.z * lightDir.x) * sign(lightDir.z))) * 0.5f) + 0.5f), ((1f - ((-position.z / lightDir.z) * 0.5f)) - 0.2f)); - var data = textureSampleLevel(bezierTexture_26, filteringSampler_23, finalUV, 0); - var jellySaturation = mix(0, data.y, saturate(((position.x * 1.5f) + 1.1f))); - var shadowColor = mix(vec3f(), jellyColor.xyz, jellySaturation); - var contrast = ((20f * saturate(finalUV.y)) * (0.8f + (endCapX * 0.2f))); - var shadowOffset = -0.3; - var featherSharpness = 10; - var uvEdgeFeather = (((saturate((finalUV.x * f32(featherSharpness))) * saturate(((1f - finalUV.x) * f32(featherSharpness)))) * saturate(((1f - finalUV.y) * f32(featherSharpness)))) * saturate(finalUV.y)); - var influence = (saturate(((1f - lightDir.y) * 2f)) * uvEdgeFeather); - return mix(vec3f(1), mix(shadowColor, vec3f(1), saturate(((data.x * contrast) + shadowOffset))), influence); - } - } - - fn calculateLighting_33(hitPosition: vec3f, normal: vec3f, rayOrigin: vec3f) -> vec3f { - var lightDir = -(lightUniform_24.direction); - var fakeShadow = getFakeShadow_34(hitPosition, lightDir); - var diffuse = max(dot(normal, lightDir), 0f); - var viewDir = normalize((rayOrigin - hitPosition)); - var reflectDir = reflect(-(lightDir), normal); - var specularFactor = pow(max(dot(viewDir, reflectDir), 0f), 10f); - var specular = (lightUniform_24.color * (specularFactor * 0.6f)); - var baseColor = vec3f(0.8999999761581421); - var directionalLight = (((baseColor * lightUniform_24.color) * diffuse) * fakeShadow); - var ambientLight = ((baseColor * vec3f(0.6000000238418579)) * 0.6); - var finalSpecular = (specular * fakeShadow); - return saturate(((directionalLight + ambientLight) + finalSpecular)); - } - - struct SdfBbox_40 { - left: f32, - right: f32, - bottom: f32, - top: f32, - } - - fn getSliderBbox_39() -> SdfBbox_40 { - return SdfBbox_40(-1.0190000534057617f, 1.0899999141693115f, -0.30000001192092896f, 0.6499999761581421f); - } - - struct LineInfo_42 { - t: f32, - distance: f32, - normal: vec2f, - } - - fn sdInflatedPolyline2D_41(p: vec2f) -> LineInfo_42 { - var bbox = getSliderBbox_39(); - var uv = vec2f(((p.x - bbox.left) / (bbox.right - bbox.left)), ((bbox.top - p.y) / (bbox.top - bbox.bottom))); - var clampedUV = saturate(uv); - var sampledColor = textureSampleLevel(bezierTexture_26, filteringSampler_23, clampedUV, 0); - var segUnsigned = sampledColor.x; - var progress = sampledColor.y; - var normal = sampledColor.zw; - return LineInfo_42(progress, segUnsigned, normal); - } - - fn opExtrudeZ_43(p: vec3f, dd: f32, h: f32) -> f32 { - var w = vec2f(dd, (abs(p.z) - h)); - return (min(max(w.x, w.y), 0f) + length(max(w, vec2f()))); - } - - fn sliderApproxDist_38(position: vec3f) -> f32 { - var bbox = getSliderBbox_39(); - var p = position.xy; - if (((((p.x < bbox.left) || (p.x > bbox.right)) || (p.y < bbox.bottom)) || (p.y > bbox.top))) { - return 1000000000; - } - var poly2D = sdInflatedPolyline2D_41(p); - var dist3D = (opExtrudeZ_43(position, poly2D.distance, 0.17f) - 0.024f); - return dist3D; - } - - fn getSceneDistForAO_37(position: vec3f) -> f32 { - var mainScene = getMainSceneDist_13(position); - var sliderApprox = sliderApproxDist_38(position); - return min(mainScene, sliderApprox); - } - - fn calculateAO_36(position: vec3f, normal: vec3f) -> f32 { - var totalOcclusion = 0f; - var sampleWeight = 1f; - var stepDistance = 0.03333333333333333; - for (var i = 1; (i <= 3i); i++) { - var sampleHeight = (stepDistance * f32(i)); - var samplePosition = (position + (normal * sampleHeight)); - var distanceToSurface = (getSceneDistForAO_37(samplePosition) - 5e-3f); - var occlusionContribution = max(0f, (sampleHeight - distanceToSurface)); - totalOcclusion += (occlusionContribution * sampleWeight); - sampleWeight *= 0.5f; - if ((totalOcclusion > 0.2f)) { - break; - } - } - var rawAO = (1f - ((0.5f * totalOcclusion) / 0.1f)); - return saturate(rawAO); - } - - fn applyAO_35(litColor: vec3f, hitPosition: vec3f, normal: vec3f) -> vec4f { - var ao = calculateAO_36(hitPosition, normal); - var finalColor = (litColor * ao); - return vec4f(finalColor, 1f); - } - - fn renderBackground_19(rayOrigin: vec3f, rayDirection: vec3f, backgroundHitDist: f32, offset: f32) -> vec4f { - var hitPosition = (rayOrigin + (rayDirection * backgroundHitDist)); - var percentageSample = renderPercentageOnGround_21(hitPosition, vec3f(0.7200000286102295, 0, 0), u32(((item_20.x + 0.43f) * 84f))); - var highlights = 0f; - var highlightWidth = 1f; - var highlightHeight = 0.2; - var offsetX = 0f; - var offsetZ = 0.05000000074505806f; - var lightDir = lightUniform_24.direction; - var causticScale = 0.2; - offsetX -= (lightDir.x * causticScale); - offsetZ += (lightDir.z * causticScale); - var endCapX = item_20.x; - var sliderStretch = ((endCapX + 1f) * 0.5f); - if (((abs((hitPosition.x + offsetX)) < highlightWidth) && (abs((hitPosition.z + offsetZ)) < highlightHeight))) { - var uvX_orig = ((((hitPosition.x + offsetX) + (highlightWidth * 2f)) / highlightWidth) * 0.5f); - var uvZ_orig = ((((hitPosition.z + offsetZ) + (highlightHeight * 2f)) / highlightHeight) * 0.5f); - var centeredUV = vec2f((uvX_orig - 0.5f), (uvZ_orig - 0.5f)); - var finalUV = vec2f(centeredUV.x, (1f - (pow((abs((centeredUV.y - 0.5f)) * 2f), 2f) * 0.3f))); - var density = max(0f, ((textureSampleLevel(bezierTexture_26, filteringSampler_23, finalUV, 0).x - 0.25f) * 8f)); - var fadeX = smoothstep(0, -0.2, (hitPosition.x - endCapX)); - var fadeZ = (1f - pow((abs((centeredUV.y - 0.5f)) * 2f), 3f)); - var fadeStretch = saturate((1f - sliderStretch)); - var edgeFade = ((saturate(fadeX) * saturate(fadeZ)) * fadeStretch); - highlights = ((((pow(density, 3f) * edgeFade) * 3f) * (1f + lightDir.z)) / 1.5f); - } - var originYBound = saturate((rayOrigin.y + 0.01f)); - var posOffset = (hitPosition + (vec3f(0, 1, 0) * ((offset * (originYBound / (1f + originYBound))) * (1f + (randFloat01_27() / 2f))))); - var newNormal = getNormalMain_29(posOffset); - var jellyColor = jellyColorUniform_31; - var sqDist = sqLength_32((hitPosition - vec3f(endCapX, 0f, 0f))); - var bounceLight = (jellyColor.xyz * ((1f / ((sqDist * 15f) + 1f)) * 0.4f)); - var sideBounceLight = ((jellyColor.xyz * ((1f / ((sqDist * 40f) + 1f)) * 0.3f)) * abs(newNormal.z)); - var litColor = calculateLighting_33(posOffset, newNormal, rayOrigin); - var backgroundColor = ((applyAO_35((vec3f(1) * litColor), posOffset, newNormal) + vec4f(bounceLight, 0f)) + vec4f(sideBounceLight, 0f)); - var textColor = saturate((backgroundColor.xyz * vec3f(0.5))); - return vec4f((mix(backgroundColor.xyz, textColor, percentageSample.x) * (1f + highlights)), 1f); - } - - struct BoxIntersection_45 { - hit: bool, - tMin: f32, - tMax: f32, - } - - fn intersectBox_44(rayOrigin: vec3f, rayDirection: vec3f, boxMin: vec3f, boxMax: vec3f) -> BoxIntersection_45 { - var invDir = (vec3f(1) / rayDirection); - var t1 = ((boxMin - rayOrigin) * invDir); - var t2 = ((boxMax - rayOrigin) * invDir); - var tMinVec = min(t1, t2); - var tMaxVec = max(t1, t2); - var tMin = max(max(tMinVec.x, tMinVec.y), tMinVec.z); - var tMax = min(min(tMaxVec.x, tMaxVec.y), tMaxVec.z); - var result = BoxIntersection_45(); - result.hit = ((tMax >= tMin) && (tMax >= 0f)); - result.tMin = tMin; - result.tMax = tMax; - return result; - } - - fn sdPie_49(p: vec2f, c: vec2f, r: f32) -> f32 { - var p_w = p; - p_w.x = abs(p.x); - var l = (length(p_w) - r); - var m = length((p_w - (c * clamp(dot(p_w, c), 0f, r)))); - return max(l, (m * sign(((c.y * p_w.x) - (c.x * p_w.y))))); - } - - fn cap3D_48(position: vec3f) -> f32 { - var endCap = item_20; - var secondLastPoint = vec2f(endCap.x, endCap.y); - var lastPoint = vec2f(endCap.z, endCap.w); - var angle = atan2((lastPoint.y - secondLastPoint.y), (lastPoint.x - secondLastPoint.x)); - var rot = mat2x2f(cos(angle), -sin(angle), sin(angle), cos(angle)); - var pieP = (position - vec3f(secondLastPoint, 0f)); - pieP = vec3f((rot * pieP.xy), pieP.z); - var hmm = sdPie_49(pieP.zx, vec2f(1, 0), 0.17f); - var extrudeEnd = (opExtrudeY_16(pieP, hmm, 1e-3f) - 0.024f); - return extrudeEnd; - } - - fn sliderSdf3D_47(position: vec3f) -> LineInfo_42 { - var poly2D = sdInflatedPolyline2D_41(position.xy); - var finalDist = 0f; - if ((poly2D.t > 0.94f)) { - finalDist = cap3D_48(position); - } - else { - var body = (opExtrudeZ_43(position, poly2D.distance, 0.17f) - 0.024f); - finalDist = body; - } - return LineInfo_42(poly2D.t, finalDist, poly2D.normal); - } - - struct HitInfo_50 { - distance: f32, - objectType: i32, - t: f32, - } - - fn getSceneDist_46(position: vec3f) -> HitInfo_50 { - var mainScene = getMainSceneDist_13(position); - var poly3D = sliderSdf3D_47(position); - var hitInfo = HitInfo_50(); - if ((poly3D.distance < mainScene)) { - hitInfo.distance = poly3D.distance; - hitInfo.objectType = 1i; - hitInfo.t = poly3D.t; - } - else { - hitInfo.distance = mainScene; - hitInfo.objectType = 2i; - } - return hitInfo; - } - - fn getNormalFromSdf_54(position: vec3f, epsilon: f32) -> vec3f { - var k = vec3f(1f, -1, 0f); - var offset1 = (k.xyy * epsilon); - var offset2 = (k.yyx * epsilon); - var offset3 = (k.yxy * epsilon); - var offset4 = (k.xxx * epsilon); - var sample1 = (offset1 * cap3D_48((position + offset1))); - var sample2 = (offset2 * cap3D_48((position + offset2))); - var sample3 = (offset3 * cap3D_48((position + offset3))); - var sample4 = (offset4 * cap3D_48((position + offset4))); - var gradient = (((sample1 + sample2) + sample3) + sample4); - return normalize(gradient); - } - - fn getNormalCap_53(pos: vec3f) -> vec3f { - return getNormalFromSdf_54(pos, 0.01f); - } - - fn getSliderNormal_52(position: vec3f, hitInfo: HitInfo_50) -> vec3f { - var poly2D = sdInflatedPolyline2D_41(position.xy); - var gradient2D = poly2D.normal; - var threshold = 0.14450000000000002; - var absZ = abs(position.z); - var zDistance = max(0f, (((absZ - threshold) * 0.17f) / (0.17f - threshold))); - var edgeDistance = (0.024f - poly2D.distance); - var edgeContrib = 0.9; - var zContrib = (1f - edgeContrib); - var zDirection = sign(position.z); - var zAxisVector = vec3f(0f, 0f, zDirection); - var edgeBlendDistance = ((edgeContrib * 0.024f) + (zContrib * 0.17f)); - var blendFactor = smoothstep(edgeBlendDistance, 0, ((zDistance * zContrib) + (edgeDistance * edgeContrib))); - var normal2D = vec3f(gradient2D.xy, 0f); - var blendedNormal = mix(zAxisVector, normal2D, ((blendFactor * 0.5f) + 0.5f)); - var normal = normalize(blendedNormal); - if ((hitInfo.t > 0.94f)) { - var ratio = ((hitInfo.t - 0.94f) / 0.02f); - var fullNormal = getNormalCap_53(position); - normal = normalize(mix(normal, fullNormal, ratio)); - } - return normal; - } - - fn getNormal_51(position: vec3f, hitInfo: HitInfo_50) -> vec3f { - if (((hitInfo.objectType == 1i) && (hitInfo.t < 0.96f))) { - return getSliderNormal_52(position, hitInfo); - } - return select(getNormalCap_53(position), getNormalMain_29(position), (hitInfo.objectType == 2i)); - } - - fn fresnelSchlick_55(cosTheta: f32, ior1: f32, ior2: f32) -> f32 { - var r0 = pow(((ior1 - ior2) / (ior1 + ior2)), 2f); - return (r0 + ((1f - r0) * pow((1f - cosTheta), 5f))); - } - - @group(0) @binding(8) var blurEnabledUniform_57: u32; - - fn rayMarchNoJelly_56(rayOrigin: vec3f, rayDirection: vec3f) -> vec3f { - var distanceFromOrigin = 0f; - var hit = 0f; - for (var i = 0; (i < 6i); i++) { - var p = (rayOrigin + (rayDirection * distanceFromOrigin)); - hit = getMainSceneDist_13(p); - distanceFromOrigin += hit; - if (((distanceFromOrigin > 10f) || (hit < 0.01f))) { - break; - } - } - if ((distanceFromOrigin < 10f)) { - return renderBackground_19(rayOrigin, rayDirection, distanceFromOrigin, select(0f, 0.87f, (blurEnabledUniform_57 == 1u))).xyz; - } - return vec3f(); - } - - fn beerLambert_58(sigma: vec3f, dist: f32) -> vec3f { - return exp((sigma * -dist)); - } - - fn rayMarch_12(rayOrigin: vec3f, rayDirection: vec3f, uv: vec2f) -> vec4f { - var totalSteps = 0u; - var backgroundDist = 0f; - for (var i = 0; (i < 64i); i++) { - var p = (rayOrigin + (rayDirection * backgroundDist)); - var hit = getMainSceneDist_13(p); - backgroundDist += hit; - if ((hit < 1e-3f)) { - break; - } - } - var background = renderBackground_19(rayOrigin, rayDirection, backgroundDist, 0f); - var bbox = getSliderBbox_39(); - var zDepth = 0.25f; - var sliderMin = vec3f(bbox.left, bbox.bottom, -zDepth); - var sliderMax = vec3f(bbox.right, bbox.top, zDepth); - var intersection = intersectBox_44(rayOrigin, rayDirection, sliderMin, sliderMax); - if (!intersection.hit) { - return background; - } - var distanceFromOrigin = max(0f, intersection.tMin); - for (var i = 0; (i < 64i); i++) { - if ((totalSteps >= 64u)) { - break; - } - var currentPosition = (rayOrigin + (rayDirection * distanceFromOrigin)); - var hitInfo = getSceneDist_46(currentPosition); - distanceFromOrigin += hitInfo.distance; - totalSteps++; - if ((hitInfo.distance < 1e-3f)) { - var hitPosition = (rayOrigin + (rayDirection * distanceFromOrigin)); - if (!(hitInfo.objectType == 1i)) { - break; - } - var N = getNormal_51(hitPosition, hitInfo); - var I = rayDirection; - var cosi = min(1f, max(0f, dot(-(I), N))); - var F = fresnelSchlick_55(cosi, 1f, 1.4199999570846558f); - var reflection = saturate(vec3f((hitPosition.y + 0.2f))); - var eta = 0.7042253521126761; - var k = (1f - ((eta * eta) * (1f - (cosi * cosi)))); - var refractedColor = vec3f(); - if ((k > 0f)) { - var refrDir = normalize(((I * eta) + (N * ((eta * cosi) - sqrt(k))))); - var p = (hitPosition + (refrDir * 2e-3)); - var exitPos = (p + (refrDir * 2e-3)); - var env = rayMarchNoJelly_56(exitPos, refrDir); - var progress = hitInfo.t; - var jellyColor = jellyColorUniform_31; - var scatterTint = (jellyColor.xyz * 1.5); - var density = 20f; - var absorb = ((vec3f(1) - jellyColor.xyz) * density); - var T = beerLambert_58((absorb * pow(progress, 2f)), 0.08); - var lightDir = -(lightUniform_24.direction); - var forward = max(0f, dot(lightDir, refrDir)); - var scatter = (scatterTint * ((3f * forward) * pow(progress, 3f))); - refractedColor = ((env * T) + scatter); - } - var jelly = ((reflection * F) + (refractedColor * (1f - F))); - return vec4f(jelly, 1f); - } - if ((distanceFromOrigin > backgroundDist)) { - break; - } - } - return background; - } - - struct raymarchFn_Input_59 { - @location(0) uv: vec2f, - } - - @fragment fn raymarchFn_3(_arg_0: raymarchFn_Input_59) -> @location(0) vec4f { - randSeed2_5((randomUniform_4 * _arg_0.uv)); - var ndc = vec2f(((_arg_0.uv.x * 2f) - 1f), -((_arg_0.uv.y * 2f) - 1f)); - var ray = getRay_8(ndc); - var color = rayMarch_12(ray.origin, ray.direction, _arg_0.uv); - return vec4f(tanh((color.xyz * 1.3)), 1f); - } - - @group(0) @binding(0) var currentTexture_1: texture_2d; - - @group(0) @binding(1) var historyTexture_2: texture_2d; - - @group(0) @binding(2) var outputTexture_3: texture_storage_2d; - - struct taaResolveFn_Input_4 { - @builtin(global_invocation_id) gid: vec3u, - } - - @compute @workgroup_size(16, 16) fn taaResolveFn_0(_arg_0: taaResolveFn_Input_4) { - var currentColor = textureLoad(currentTexture_1, vec2u(_arg_0.gid.xy), 0); - var historyColor = textureLoad(historyTexture_2, vec2u(_arg_0.gid.xy), 0); - var minColor = vec3f(9999); - var maxColor = vec3f(-9999); - var dimensions = textureDimensions(currentTexture_1); - for (var x = -1; (x <= 1i); x++) { - for (var y = -1; (y <= 1i); y++) { - var sampleCoord = (vec2i(_arg_0.gid.xy) + vec2i(x, y)); - var clampedCoord = clamp(sampleCoord, vec2i(), (vec2i(dimensions.xy) - vec2i(1))); - var neighborColor = textureLoad(currentTexture_1, clampedCoord, 0); - minColor = min(minColor, neighborColor.xyz); - maxColor = max(maxColor, neighborColor.xyz); - } - } - var historyColorClamped = clamp(historyColor.xyz, minColor, maxColor); - var uv = (vec2f(_arg_0.gid.xy) / vec2f(dimensions.xy)); - var textRegionMinX = 0.7099999785423279f; - var textRegionMaxX = 0.8500000238418579f; - var textRegionMinY = 0.4699999988079071f; - var textRegionMaxY = 0.550000011920929f; - var borderSize = 0.019999999552965164f; - var fadeInX = smoothstep((textRegionMinX - borderSize), (textRegionMinX + borderSize), uv.x); - var fadeOutX = (1f - smoothstep((textRegionMaxX - borderSize), (textRegionMaxX + borderSize), uv.x)); - var fadeInY = smoothstep((textRegionMinY - borderSize), (textRegionMinY + borderSize), uv.y); - var fadeOutY = (1f - smoothstep((textRegionMaxY - borderSize), (textRegionMaxY + borderSize), uv.y)); - var inTextRegion = (((fadeInX * fadeOutX) * fadeInY) * fadeOutY); - var blendFactor = mix(0.8999999761581421f, 0.699999988079071f, inTextRegion); - var resolvedColor = vec4f(mix(currentColor.xyz, historyColorClamped, blendFactor), 1f); - textureStore(outputTexture_3, vec2u(_arg_0.gid.x, _arg_0.gid.y), resolvedColor); - } - - struct fullScreenTriangle_Input_1 { - @builtin(vertex_index) vertexIndex: u32, - } - - struct fullScreenTriangle_Output_2 { - @builtin(position) pos: vec4f, - @location(0) uv: vec2f, - } - - @vertex fn fullScreenTriangle_0(in: fullScreenTriangle_Input_1) -> fullScreenTriangle_Output_2 { - const pos = array(vec2f(-1, -1), vec2f(3, -1), vec2f(-1, 3)); - const uv = array(vec2f(0, 1), vec2f(2, 1), vec2f(0, -1)); - - return fullScreenTriangle_Output_2(vec4f(pos[in.vertexIndex], 0, 1), uv[in.vertexIndex]); - } - - @group(1) @binding(0) var currentTexture_4: texture_2d; - - @group(0) @binding(0) var filteringSampler_5: sampler; - - struct fragmentMain_Input_6 { - @location(0) uv: vec2f, - } - - @fragment fn fragmentMain_3(input: fragmentMain_Input_6) -> @location(0) vec4f { - return textureSample(currentTexture_4, filteringSampler_5, input.uv); - } - - @group(0) @binding(0) var sizeUniform_1: vec3u; - - @group(0) @binding(1) var bezierWriteView_3: texture_storage_2d; - - @group(0) @binding(2) var pointsView_4: array; - - @group(0) @binding(3) var controlPointsView_5: array; - - fn dot2_7(v: vec2f) -> f32 { - return dot(v, v); - } - - fn sdBezier_6(pos: vec2f, A: vec2f, B: vec2f, C: vec2f) -> f32 { - var a = (B - A); - var b = ((A - (B * 2)) + C); - var c = (a * 2f); - var d = (A - pos); - var dotB = max(dot(b, b), 1e-4f); - var kk = (1f / dotB); - var kx = (kk * dot(a, b)); - var ky = ((kk * ((2f * dot(a, a)) + dot(d, b))) / 3f); - var kz = (kk * dot(d, a)); - var res = 0f; - var p = (ky - (kx * kx)); - var p3 = ((p * p) * p); - var q = ((kx * (((2f * kx) * kx) - (3f * ky))) + kz); - var h = ((q * q) + (4f * p3)); - if ((h >= 0f)) { - h = sqrt(h); - var x = ((vec2f(h, -h) - q) * 0.5); - var uv = (sign(x) * pow(abs(x), vec2f(0.3333333432674408))); - var t = clamp(((uv.x + uv.y) - kx), 0f, 1f); - res = dot2_7((d + ((c + (b * t)) * t))); - } - else { - var z = sqrt(-p); - var v = (acos((q / ((p * z) * 2f))) / 3f); - var m = cos(v); - var n = (sin(v) * 1.732050808f); - var t = saturate(((vec3f((m + m), (-n - m), (n - m)) * z) - kx)); - res = min(dot2_7((d + ((c + (b * t.x)) * t.x))), dot2_7((d + ((c + (b * t.y)) * t.y)))); - } - return sqrt(res); - } - - fn wrappedCallback_2(x: u32, y: u32, _arg_2: u32) { - var size = textureDimensions(bezierWriteView_3); - var pixelUV = ((vec2f(f32(x), f32(y)) + 0.5) / vec2f(size)); - var sliderPos = vec2f((-1.0189999997615815f + (pixelUV.x * 2.108999973535538f)), (0.65f - (pixelUV.y * 0.95f))); - var minDist = 1e+10f; - var closestSegment = 0i; - var closestT = 0f; - var epsilon = 0.029999999329447746f; - var xOffset = vec2f(epsilon, 0f); - var yOffset2 = vec2f(0f, epsilon); - var xPlusDist = 1e+10f; - var xMinusDist = 1e+10f; - var yPlusDist = 1e+10f; - var yMinusDist = 1e+10f; - for (var i = 0; (i < (17 - 1)); i++) { - var A = pointsView_4[i]; - var B = pointsView_4[(i + 1i)]; - var C = controlPointsView_5[i]; - var dist = sdBezier_6(sliderPos, A, C, B); - if ((dist < minDist)) { - minDist = dist; - closestSegment = i; - var AB = (B - A); - var AP = (sliderPos - A); - var ABLength = length(AB); - if ((ABLength > 0f)) { - closestT = clamp((dot(AP, AB) / (ABLength * ABLength)), 0f, 1f); - } - else { - closestT = 0f; - } - } - xPlusDist = min(xPlusDist, sdBezier_6((sliderPos + xOffset), A, C, B)); - xMinusDist = min(xMinusDist, sdBezier_6((sliderPos - xOffset), A, C, B)); - yPlusDist = min(yPlusDist, sdBezier_6((sliderPos + yOffset2), A, C, B)); - yMinusDist = min(yMinusDist, sdBezier_6((sliderPos - yOffset2), A, C, B)); - } - var overallProgress = ((f32(closestSegment) + closestT) / f32((17 - 1))); - var normalX = ((xPlusDist - xMinusDist) / (2f * epsilon)); - var normalY = ((yPlusDist - yMinusDist) / (2f * epsilon)); - textureStore(bezierWriteView_3, vec2u(x, y), vec4f(minDist, overallProgress, normalX, normalY)); - } - - struct mainCompute_Input_8 { - @builtin(global_invocation_id) id: vec3u, - } - - @compute @workgroup_size(16, 16, 1) fn mainCompute_0(in: mainCompute_Input_8) { - if (any(in.id >= sizeUniform_1)) { - return; - } - wrappedCallback_2(in.id.x, in.id.y, in.id.z); - }" - `); - }); -}); diff --git a/packages/typegpu/tests/examples/utils/commonMocks.ts b/packages/typegpu/tests/examples/utils/commonMocks.ts index 816c904a10..d8914e2f2c 100644 --- a/packages/typegpu/tests/examples/utils/commonMocks.ts +++ b/packages/typegpu/tests/examples/utils/commonMocks.ts @@ -46,14 +46,6 @@ export function setupCommonMocks() { }); } -export function mockFonts() { - Object.defineProperty(document, 'fonts', { - value: { - load: vi.fn().mockResolvedValue([{}, {}]), - }, - }); -} - export function mockResizeObserver() { vi.stubGlobal( 'ResizeObserver', From 22b03fa6ee21940f1ddb630794d4266af04e5db3 Mon Sep 17 00:00:00 2001 From: Konrad Reczko Date: Wed, 12 Nov 2025 09:59:40 +0100 Subject: [PATCH 4/9] update tests --- .../examples/individual/slime-mold-3d.test.ts | 108 +++++++++++------- 1 file changed, 66 insertions(+), 42 deletions(-) diff --git a/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts b/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts index b018631740..329de3060c 100644 --- a/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts +++ b/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts @@ -80,9 +80,11 @@ describe('slime mold 3d example', () => { wrappedCallback_2(in.id.x, in.id.y, in.id.z); } - @group(1) @binding(0) var oldState_1: texture_storage_3d; + @group(1) @binding(0) var oldState_1: texture_3d; - struct Params_3 { + @group(1) @binding(2) var sampler_2: sampler; + + struct Params_4 { deltaTime: f32, moveSpeed: f32, sensorAngle: f32, @@ -91,37 +93,31 @@ describe('slime mold 3d example', () => { evaporationRate: f32, } - @group(0) @binding(0) var params_2: Params_3; + @group(0) @binding(0) var params_3: Params_4; - @group(1) @binding(1) var newState_4: texture_storage_3d; + @group(1) @binding(1) var newState_5: texture_storage_3d; - struct blur_Input_5 { + struct blur_Input_6 { @builtin(global_invocation_id) gid: vec3u, } - @compute @workgroup_size(4, 4, 4) fn blur_0(_arg_0: blur_Input_5) { + @compute @workgroup_size(4, 4, 4) fn blur_0(_arg_0: blur_Input_6) { var dims = textureDimensions(oldState_1); if ((((_arg_0.gid.x >= dims.x) || (_arg_0.gid.y >= dims.y)) || (_arg_0.gid.z >= dims.z))) { return; } + var uv = ((vec3f(_arg_0.gid) + 0.5) / vec3f(dims)); + var offset = (vec3f(1) / vec3f(dims)); var sum = 0f; - var count = 0f; - for (var offsetZ = -1; (offsetZ <= 1i); offsetZ++) { - for (var offsetY = -1; (offsetY <= 1i); offsetY++) { - for (var offsetX = -1; (offsetX <= 1i); offsetX++) { - var samplePos = (vec3i(_arg_0.gid.xyz) + vec3i(offsetX, offsetY, offsetZ)); - var dimsi = vec3i(dims); - if (((((((samplePos.x >= 0i) && (samplePos.x < dimsi.x)) && (samplePos.y >= 0i)) && (samplePos.y < dimsi.y)) && (samplePos.z >= 0i)) && (samplePos.z < dimsi.z))) { - var value = textureLoad(oldState_1, vec3u(samplePos)).x; - sum = (sum + value); - count = (count + 1f); - } - } + for (var axis = 0; (axis < 3i); axis++) { + for (var sign = -1; (sign <= 1i); sign += 2i) { + var offsetVec = vec3f(select(0f, (offset.x * f32(sign)), (axis == 0i)), select(0f, (offset.y * f32(sign)), (axis == 1i)), select(0f, (offset.z * f32(sign)), (axis == 2i))); + sum = (sum + textureSampleLevel(oldState_1, sampler_2, (uv + offsetVec), 0).x); } } - var blurred = (sum / count); - var newValue = saturate((blurred - params_2.evaporationRate)); - textureStore(newState_4, _arg_0.gid.xyz, vec4f(newValue, 0f, 0f, 1f)); + var blurred = (sum / 6f); + var newValue = saturate((blurred - params_3.evaporationRate)); + textureStore(newState_5, _arg_0.gid.xyz, vec4f(newValue, 0f, 0f, 1f)); } var seed_3: vec2f; @@ -311,21 +307,31 @@ describe('slime mold 3d example', () => { return fullScreenTriangle_Output_2(vec4f(pos[in.vertexIndex], 0, 1), uv[in.vertexIndex]); } - struct Camera_5 { + var seed_6: vec2f; + + fn seed2_5(value: vec2f) { + seed_6 = value; + } + + fn randSeed2_4(seed: vec2f) { + seed2_5(seed); + } + + struct Camera_8 { viewProj: mat4x4f, invViewProj: mat4x4f, position: vec3f, } - @group(0) @binding(0) var cameraData_4: Camera_5; + @group(0) @binding(0) var cameraData_7: Camera_8; - struct RayBoxResult_7 { + struct RayBoxResult_10 { tNear: f32, tFar: f32, hit: bool, } - fn rayBoxIntersection_6(rayOrigin: vec3f, rayDir: vec3f, boxMin: vec3f, boxMax: vec3f) -> RayBoxResult_7 { + fn rayBoxIntersection_9(rayOrigin: vec3f, rayDir: vec3f, boxMin: vec3f, boxMax: vec3f) -> RayBoxResult_10 { var invDir = (vec3f(1) / rayDir); var t0 = ((boxMin - rayOrigin) * invDir); var t1 = ((boxMax - rayOrigin) * invDir); @@ -334,58 +340,76 @@ describe('slime mold 3d example', () => { var tNear = max(max(tmin.x, tmin.y), tmin.z); var tFar = min(min(tmax.x, tmax.y), tmax.z); var hit = ((tFar >= tNear) && (tFar >= 0f)); - return RayBoxResult_7(tNear, tFar, hit); + return RayBoxResult_10(tNear, tFar, hit); } - @group(1) @binding(0) var state_8: texture_3d; + fn item_12() -> f32 { + var a = dot(seed_6, vec2f(23.140779495239258, 232.6168975830078)); + var b = dot(seed_6, vec2f(54.47856521606445, 345.8415222167969)); + seed_6.x = fract((cos(a) * 136.8168f)); + seed_6.y = fract((cos(b) * 534.7645f)); + return seed_6.y; + } + + fn randFloat01_11() -> f32 { + return item_12(); + } - @group(0) @binding(1) var sampler_9: sampler; + @group(1) @binding(0) var state_13: texture_3d; - struct fragmentShader_Input_10 { + @group(0) @binding(1) var sampler_14: sampler; + + struct fragmentShader_Input_15 { @location(0) uv: vec2f, } - @fragment fn fragmentShader_3(_arg_0: fragmentShader_Input_10) -> @location(0) vec4f { + @fragment fn fragmentShader_3(_arg_0: fragmentShader_Input_15) -> @location(0) vec4f { + randSeed2_4(_arg_0.uv); var ndc = vec2f(((_arg_0.uv.x * 2f) - 1f), (1f - (_arg_0.uv.y * 2f))); var ndcNear = vec4f(ndc, -1, 1f); var ndcFar = vec4f(ndc, 1f, 1f); - var worldNear = (cameraData_4.invViewProj * ndcNear); - var worldFar = (cameraData_4.invViewProj * ndcFar); + var worldNear = (cameraData_7.invViewProj * ndcNear); + var worldFar = (cameraData_7.invViewProj * ndcFar); var rayOrigin = (worldNear.xyz / worldNear.w); var rayEnd = (worldFar.xyz / worldFar.w); var rayDir = normalize((rayEnd - rayOrigin)); var boxMin = vec3f(); var boxMax = vec3f(256); - var isect = rayBoxIntersection_6(rayOrigin, rayDir, boxMin, boxMax); + var isect = rayBoxIntersection_9(rayOrigin, rayDir, boxMin, boxMax); if (!isect.hit) { return vec4f(); } - var tStart = max(isect.tNear, 0f); + var jitter = (randFloat01_11() * 20f); + var tStart = max((isect.tNear + jitter), jitter); var tEnd = isect.tFar; - var numSteps = 128; - var stepSize = ((tEnd - tStart) / f32(numSteps)); + var intersectionLength = (tEnd - tStart); + var baseStepsPerUnit = 0.30000001192092896f; + var minSteps = 8i; + var maxSteps = 48i; + var adaptiveSteps = clamp(i32((intersectionLength * baseStepsPerUnit)), minSteps, maxSteps); + var numSteps = adaptiveSteps; + var stepSize = (intersectionLength / f32(numSteps)); var thresholdLo = 0.05999999865889549f; var thresholdHi = 0.25f; var gamma = 1.399999976158142f; - var sigmaT = 0.05000000074505806f; + var sigmaT = 0.10000000149011612f; var albedo = vec3f(0.5699999928474426, 0.4399999976158142, 0.9599999785423279); var transmittance = 1f; var accum = vec3f(); var TMin = 0.0010000000474974513f; - for (var i = 0; (i < numSteps); i++) { - if ((transmittance <= TMin)) { - break; - } + var i = 0i; + while (((i < numSteps) && (transmittance > TMin))) { var t = (tStart + ((f32(i) + 0.5f) * stepSize)); var pos = (rayOrigin + (rayDir * t)); var texCoord = (pos / vec3f(256)); - var sampleValue = textureSampleLevel(state_8, sampler_9, texCoord, 0).x; + var sampleValue = textureSampleLevel(state_13, sampler_14, texCoord, 0).x; var d0 = smoothstep(thresholdLo, thresholdHi, sampleValue); var density = pow(d0, gamma); var alphaSrc = (1f - exp(((-sigmaT * density) * stepSize))); var contrib = (albedo * alphaSrc); accum = (accum + (contrib * transmittance)); transmittance = (transmittance * (1f - alphaSrc)); + i += 1i; } var alpha = (1f - transmittance); return vec4f(accum, alpha); From a083cb1806a77f0be9a26502059dc0f382d34ae1 Mon Sep 17 00:00:00 2001 From: Konrad Reczko Date: Wed, 12 Nov 2025 10:02:15 +0100 Subject: [PATCH 5/9] remove unused feature --- .../src/examples/simulation/slime-mold-3d/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts index 2abe8aeca0..9192e664fe 100644 --- a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts +++ b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts @@ -6,9 +6,7 @@ import { randf } from '@typegpu/noise'; import * as m from 'wgpu-matrix'; const root = await tgpu.init({ - device: { - optionalFeatures: ['float32-filterable', 'timestamp-query'], - }, + device: { optionalFeatures: ['float32-filterable'] }, }); const canFilter = root.enabledFeatures.has('float32-filterable'); const device = root.device; From 7ab184eff9d9623f6695857345427173ca64ea09 Mon Sep 17 00:00:00 2001 From: Aleksander Katan Date: Fri, 21 Nov 2025 11:55:26 +0100 Subject: [PATCH 6/9] Add double buffering --- .../examples/simulation/slime-mold-3d/index.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts index 9192e664fe..bbb2b0e86f 100644 --- a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts +++ b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts @@ -103,15 +103,19 @@ const Params = d.struct({ evaporationRate: d.f32, }); -const agentsData = root.createMutable(d.arrayOf(Agent, NUM_AGENTS)); +const agentsDataBuffers = [0, 1].map(() => + root.createBuffer(d.arrayOf(Agent, NUM_AGENTS)).$usage('storage') +); +const mutableAgentsDataBuffers = agentsDataBuffers.map((b) => b.as('mutable')); root['~unstable'].createGuardedComputePipeline((x) => { 'use gpu'; randf.seed(x / NUM_AGENTS); const pos = randf.inUnitSphere().mul(resolution.x / 4).add(resolution.div(2)); const center = resolution.div(2); const dir = std.normalize(center.sub(pos)); - agentsData.$[x] = Agent({ position: pos, direction: dir }); + mutableAgentsDataBuffers[0].$[x] = Agent({ position: pos, direction: dir }); + mutableAgentsDataBuffers[1].$[x] = Agent({ position: pos, direction: dir }); }).dispatchThreads(NUM_AGENTS); const params = root.createUniform(Params, { @@ -134,7 +138,9 @@ const textures = [0, 1].map(() => ); const computeLayout = tgpu.bindGroupLayout({ + oldAgents: { storage: d.arrayOf(Agent), access: 'readonly' }, oldState: { storageTexture: d.textureStorage3d('r32float', 'read-only') }, + newAgents: { storage: d.arrayOf(Agent), access: 'mutable' }, newState: { storageTexture: d.textureStorage3d('r32float', 'write-only') }, }); @@ -227,7 +233,7 @@ const updateAgents = tgpu['~unstable'].computeFn({ const dims = std.textureDimensions(computeLayout.$.oldState); const dimsf = d.vec3f(dims); - const agent = agentsData.$[gid.x]; + const agent = computeLayout.$.oldAgents[gid.x]; const random = randf.sample(); let direction = std.normalize(agent.direction); @@ -296,7 +302,7 @@ const updateAgents = tgpu['~unstable'].computeFn({ ); } - agentsData.$[gid.x] = Agent({ + computeLayout.$.newAgents[gid.x] = Agent({ position: newPos, direction, }); @@ -466,7 +472,9 @@ const blurPipeline = root['~unstable'] const bindGroups = [0, 1].map((i) => root.createBindGroup(computeLayout, { + oldAgents: agentsDataBuffers[i], oldState: textures[i], + newAgents: agentsDataBuffers[1 - i], newState: textures[1 - i], }) ); From c42f99c8fffa2dff9e7ba9b2b2fb1a8e31a0df24 Mon Sep 17 00:00:00 2001 From: Aleksander Katan Date: Fri, 21 Nov 2025 14:06:31 +0100 Subject: [PATCH 7/9] Update tests --- .../examples/individual/slime-mold-3d.test.ts | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts b/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts index 329de3060c..ca622f4f04 100644 --- a/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts +++ b/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts @@ -59,21 +59,24 @@ describe('slime mold 3d example', () => { direction: vec3f, } - @group(0) @binding(1) var agentsData_10: array; + @group(0) @binding(1) var item_10: array; + + @group(0) @binding(2) var item_12: array; fn wrappedCallback_2(x: u32, _arg_1: u32, _arg_2: u32) { randSeed_3((f32(x) / 8e+5f)); var pos = ((randInUnitSphere_6() * 64.) + vec3f(128)); var center = vec3f(128); var dir = normalize((center - pos)); - agentsData_10[x] = Agent_11(pos, dir); + item_10[x] = Agent_11(pos, dir); + item_12[x] = Agent_11(pos, dir); } - struct mainCompute_Input_12 { + struct mainCompute_Input_13 { @builtin(global_invocation_id) id: vec3u, } - @compute @workgroup_size(256, 1, 1) fn mainCompute_0(in: mainCompute_Input_12) { + @compute @workgroup_size(256, 1, 1) fn mainCompute_0(in: mainCompute_Input_13) { if (any(in.id >= sizeUniform_1)) { return; } @@ -130,14 +133,14 @@ describe('slime mold 3d example', () => { seed_2(seed); } - @group(1) @binding(0) var oldState_4: texture_storage_3d; + @group(1) @binding(1) var oldState_4: texture_storage_3d; struct Agent_6 { position: vec3f, direction: vec3f, } - @group(0) @binding(0) var agentsData_5: array; + @group(1) @binding(0) var oldAgents_5: array; fn item_8() -> f32 { var a = dot(seed_3, vec2f(23.140779495239258, 232.6168975830078)); @@ -176,7 +179,7 @@ describe('slime mold 3d example', () => { evaporationRate: f32, } - @group(0) @binding(1) var params_11: Params_12; + @group(0) @binding(0) var params_11: Params_12; struct SenseResult_13 { weightedDir: vec3f, @@ -227,20 +230,22 @@ describe('slime mold 3d example', () => { return (sign(alignment) * value); } - @group(1) @binding(1) var newState_18: texture_storage_3d; + @group(1) @binding(2) var newAgents_18: array; + + @group(1) @binding(3) var newState_19: texture_storage_3d; - struct updateAgents_Input_19 { + struct updateAgents_Input_20 { @builtin(global_invocation_id) gid: vec3u, } - @compute @workgroup_size(64) fn updateAgents_0(_arg_0: updateAgents_Input_19) { + @compute @workgroup_size(64) fn updateAgents_0(_arg_0: updateAgents_Input_20) { if ((_arg_0.gid.x >= 800000u)) { return; } randSeed_1(((f32(_arg_0.gid.x) / 8e+5f) + 0.1f)); var dims = textureDimensions(oldState_4); var dimsf = vec3f(dims); - var agent = agentsData_5[_arg_0.gid.x]; + var agent = oldAgents_5[_arg_0.gid.x]; var random = randFloat01_7(); var direction = normalize(agent.direction); var senseResult = sense3D_9(agent.position, direction); @@ -285,10 +290,10 @@ describe('slime mold 3d example', () => { var toCenter = normalize((center - newPos)); direction = normalize(((randomDir * 0.3) + (toCenter * 0.7))); } - agentsData_5[_arg_0.gid.x] = Agent_6(newPos, direction); + newAgents_18[_arg_0.gid.x] = Agent_6(newPos, direction); var oldState = textureLoad(oldState_4, vec3u(newPos)).x; var newState = (oldState + 1f); - textureStore(newState_18, vec3u(newPos), vec4f(newState, 0f, 0f, 1f)); + textureStore(newState_19, vec3u(newPos), vec4f(newState, 0f, 0f, 1f)); } struct fullScreenTriangle_Input_1 { From 621f9a950bd0f05ccb0a152680b3da12daeed3e3 Mon Sep 17 00:00:00 2001 From: Aleksander Katan Date: Thu, 27 Nov 2025 12:05:19 +0100 Subject: [PATCH 8/9] Unroll a for loop --- .../simulation/slime-mold-3d/index.ts | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts index bbb2b0e86f..e66737ff0d 100644 --- a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts +++ b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts @@ -321,33 +321,32 @@ const sampler = root['~unstable'].createSampler({ minFilter: canFilter ? 'linear' : 'nearest', }); +const getSummand = tgpu.fn([d.vec3f, d.vec3f], d.f32)((uv, offset) => + std.textureSampleLevel( + blurLayout.$.oldState, + blurLayout.$.sampler, + uv.add(offset), + 0, + ).x +); + const blur = tgpu['~unstable'].computeFn({ in: { gid: d.builtin.globalInvocationId }, workgroupSize: BLUR_WORKGROUP_SIZE, })(({ gid }) => { - const dims = std.textureDimensions(blurLayout.$.oldState); + const dims = d.vec3u(std.textureDimensions(blurLayout.$.oldState)); if (gid.x >= dims.x || gid.y >= dims.y || gid.z >= dims.z) return; const uv = d.vec3f(gid).add(0.5).div(d.vec3f(dims)); - const offset = d.vec3f(1).div(d.vec3f(dims)); let sum = d.f32(); - for (let axis = 0; axis < 3; axis++) { - for (let sign = -1; sign <= 1; sign += 2) { - const offsetVec = d.vec3f( - std.select(0, offset.x * sign, axis === 0), - std.select(0, offset.y * sign, axis === 1), - std.select(0, offset.z * sign, axis === 2), - ); - sum = sum + std.textureSampleLevel( - blurLayout.$.oldState, - blurLayout.$.sampler, - uv.add(offsetVec), - 0, - ).x; - } - } + sum += getSummand(uv, d.vec3f(-1, 0, 0).div(d.vec3f(dims))); + sum += getSummand(uv, d.vec3f(1, 0, 0).div(d.vec3f(dims))); + sum += getSummand(uv, d.vec3f(0, -1, 0).div(d.vec3f(dims))); + sum += getSummand(uv, d.vec3f(0, 1, 0).div(d.vec3f(dims))); + sum += getSummand(uv, d.vec3f(0, 0, -1).div(d.vec3f(dims))); + sum += getSummand(uv, d.vec3f(0, 0, 1).div(d.vec3f(dims))); const blurred = sum / 6.0; const newValue = std.saturate(blurred - params.$.evaporationRate); From b47b413c27a6cd998845a3b41020aba41af23946 Mon Sep 17 00:00:00 2001 From: Aleksander Katan Date: Thu, 27 Nov 2025 15:02:06 +0100 Subject: [PATCH 9/9] Clean up direction updating --- .../simulation/slime-mold-3d/index.ts | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts index e66737ff0d..2bd367117a 100644 --- a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts +++ b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts @@ -234,23 +234,17 @@ const updateAgents = tgpu['~unstable'].computeFn({ const dimsf = d.vec3f(dims); const agent = computeLayout.$.oldAgents[gid.x]; - const random = randf.sample(); let direction = std.normalize(agent.direction); const senseResult = sense3D(agent.position, direction); - - if (senseResult.totalWeight > 0.01) { - const targetDir = std.normalize(senseResult.weightedDir); - direction = std.normalize( - direction.add(targetDir.mul(params.$.turnSpeed * params.$.deltaTime)), - ); - } else { - const perp = getPerpendicular(direction); - const randomOffset = perp.mul( - (random * 2 - 1) * params.$.turnSpeed * params.$.deltaTime, - ); - direction = std.normalize(direction.add(randomOffset)); - } + const targetDirection = std.select( + randf.onHemisphere(direction), + std.normalize(senseResult.weightedDir), + senseResult.totalWeight > 0.01, + ); + direction = std.normalize(direction.add( + targetDirection.mul(params.$.turnSpeed * params.$.deltaTime), + )); const newPos = agent.position.add( direction.mul(params.$.moveSpeed * params.$.deltaTime),