I’m trying to port the Reintegration Tracking fluid simulation shader (https://www.shadertoy.com/view/tldcW7) from TouchDesigner 2021.15240 to TD 2025. The simulation works perfectly in 2021 but produces noise/NaN artifacts in 2025.The problem:
In TD 2021 (GLSL 4.2), the simulation runs stably and produces correct fluid droplet behavior with transparent background. In TD 2025 (GLSL 4.3), the feedback loop becomes unstable — the particle mass fills the entire screen uniformly instead of forming droplets, and NaN values propagate through the buffer over time.
The original shader uses packHalf2x16 / unpackHalf2x16 to encode particle position and velocity into a single float. I suspect the issue is that in GLSL 4.3, packHalf2x16 produces NaN when velocity values exceed the half-float range (~65504), whereas in GLSL 4.2 / WebGL this overflow was handled differently (clamped or wrapped).
What I’ve tried:
-
Replacing packHalf2x16 with packSnorm2x16 — eliminates NaN but breaks the simulation physics because the value range is different
-
Adding isnan / isinf guards in decode and saveParticle — partially helps but simulation still becomes unstable
-
Adding division-by-zero protection in the Reintegration function (max(P.M.x, 1e-6))
Adding clamp on velocity and position before encoding
I have attached both the TD 2021 and TD 2025 versions of the project files below for reference. I would really appreciate it if anyone could take a look and help figure out what needs to be changed to make this work correctly in TD 2025. Thank you so much in advance!
flow_2021.1524.toe (9.7 KB)
flow_2025.3246.toe (10.1 KB)
Hi there,
I’m not sure about the packing functions and how they are changed, but what you can do is instead of just outputting 1 buffer, outputting 2. (glslTOP > # of color buffers). 1 for the P.X and P.V values (both vec2) and 1 for the P.M value (leaving G and A to 0). You would need to change some code to retrieve the particle struct. This way you don’t have to use those encoding functions.
It makes in my opinion unnecessary more complex, understandable in ShaderToy, but in Touch might be easier to just output them as 16bit floats and have multiple feedbackTOPs.
Hope this helps.
Cheers,
tim
One undefined behavior with floats in GLSL is how denormalized floats are handled. Some implementations can flush those those into 0.0. When you are encoding arbitrary data from 2xFloat16→uint→Float32. You may end up with with a set of bits that represent a denormalized 32-bit float.
Thanks for the explanation about denormalized floats! I’ve tried several approaches to fix this but none have worked so far:
-
Replacing packHalf2x16 with packSnorm2x16 — This eliminates NaN but breaks the simulation physics because the value range is limited to [-1, 1], which is too small for the velocity values.
-
Adding isnan/isinf guards in decode and saveParticle — Partially helped but the simulation still became unstable over time.
-
Using a custom integer-based pack/unpack function — Replaced packHalf2x16 entirely with a manual uint encoding, but this still produces denormalized float bit patterns when converted back to float32.
-
Scaling velocity before encoding — Tried clamping and scaling velocity to fit within [-1, 1] before encoding with packSnorm2x16, but this distorts the simulation behavior.
-
Changing Pixel Format to 32-bit float (RGBA) on the Feedback TOP and all GLSL TOPs — No change in behavior.
-
Using 2 color buffers (as suggested by tim) — Split position/velocity into buf0 and mass into buf1 to avoid encoding entirely, but I’m having difficulty getting the multiple buffer connections working correctly in TD 2025.
Could you clarify what the correct fix would be for the denormalized float issue? Specifically, is there a way to encode the data so that the resulting float32 bit pattern is never denormalized? Or is there another approach you would recommend?
Thank you!
I think Tim’s approach is the correct one, so you should focus on that. If you can give more details about the issues you are facing with that, we can help there.
Thanks Malcolm! I’ve been trying Tim’s approach but I’m running into issues with the multiple color buffer setup in TD 2025. Here’s what I’ve done so far:
-
Set # of Color Buffers to 2 on both the Diffusion and Force GLSL TOPs
-
In the pixel shader, declared two outputs:
glsl
layout(location = 0) out vec4 fragColor;
layout(location = 1) out vec4 fragColor1;
- Writing position/velocity to
fragColor and mass to fragColor1
The problem I’m having:
The GLSL TOP only seems to have one output connector in the network, so I can’t find a way to feed fragColor1 (buffer 1) back separately through the Feedback TOP. The Feedback TOP only has one input and one Target TOP parameter, so it appears to only capture buffer 0.
My questions:
-
How do I access buffer 1 from a GLSL TOP with 2 color buffers as a separate input to another GLSL TOP?
-
Does the Feedback TOP automatically preserve all color buffers, or do I need a separate Feedback TOP for each buffer?
-
In the next GLSL TOP, how do I read buffer 1 from the previous node — is it through sTD2DInputs[1] if I connect the same node twice, or is there another method?
You can use a Render Select TOP to select any color buffer from a previous GLSL TOP. See examples in Help → Operator Snippets → Render Select TOP
hi tim,
I’ve implemented the 2 color buffer approach using Render Select TOPs. After applying this method, all nodes — Diffusion, Force, FB_return, and Final — display completely black with pixel values of R:0 G:0 B:0. There are no compile errors in any of the shaders.
The network is set up as follows:
-
Diffusion and Force both have # of Color Buffers = 2
-
4 Render Select TOPs extract buf0 and buf1 from both Diffusion and Force
-
2 Feedback TOPs feed back into Diffusion’s input 0 and input 1
-
Final reads buf0 and buf1 from Force via Render Select TOPs
The data stored in each buffer:
Is there something specific about how the Feedback TOP handles multiple color buffers in TD 2025 that would cause all buffers to read as zero? Or is there a different way to structure the feedback loop when using 2 color buffers with Render Select TOPs?
Thank you.
flow_2025.3246fix.toe (9.2 KB)
“I tried the 2 color buffer approach but feedback2 always outputs zeros even though FB_return2 has correct data. It seems like there’s a cook order issue with two separate feedback loops. Could you explain the correct way to feed back 2 color buffers in TD?”
flow_2025.3246fix.toe (9.2 KB)
FYI there’s no need to reply the same answer to multiple people in one thread - all participants will see a notification if somebody added new post in this thread.
This version seems to work a bit better, not sure how it should look though
flow_2025.3247fix.toe (9.2 KB)
Thank you for your help. I’m sorry, I don’t use forums very often, so I thought ‘reply’ was for replying to a specific person."
1 Like