For a certain feature that I have in mind, I want to use a shader to render some vector-data to an off-screen buffer, and then, by passing the buffer back into the shader as an input, to perform some iterative mathematics on that data.
(Regarding the matter of passing the buffer back in as an input, to be more specific: I'm ping-ponging between two buffers, each with identical setup and shaders, with the output of each being an input of the other--thus effectively having the shader's output be one of its inputs.)
As part of that, I want the vector-data to "fade back to zero" over time--but more than that, I want it to do so in a "springy" way: to not just fall to zero, but to overshoot a little, accelerate to a stop, then head back in the opposite direction, overshoot a little less--and so on until it settles back at zero.
The basics I have working: I'm rendering my vector-data, and I can perform non-springy "fading back to zero". But my attempts at implementing that "springiness" have thus far been thwarted.
While I've had a few different incorrect results, for the most part I seem to get the vector-data overshooting too far: always hitting the maximum allowed value in the opposite direction, and thus never reducing, let alone settling.
This would seem to imply that my acceleration is insufficient to overcome the generated "speed"--but changing the acceleration scalar just slows down the overall effect, without changing the result. :/
As to details, in short, I'm storing two vectors: the actual data that I'm interested in, and a "speed"-vector that represents how that "actual data" is falling back towards zero.
Each frame, the speed-vector has acceleration applied, scaled by the actual data's distance from zero, and then that speed-vector is applied to the actual data. Both have the delta-time for the current frame applied.
Then the new data for the current frame is generated, and applied within the appropriate region of the output (a small, fuzzy circle at the centre).
Finally, both the "actual data" and the "speed-vector" are rendered to textures, ready to become inputs for the next frame.
Here below is pseudocode for the shader:
// The vector-data and the data for the "speed" of that vector-data
// are stored in textures
// Read in the data rendered by the previous frame, and convert
// to a vector (much as with a normal-map)
previousPixel = texture(previousBufferTexture + offset)
previousVectorValue = vec2(previousPixel.x * 2 - 1, previousPixel.y * 2 - 1)
// Do the same with the "speed"-data
previousDirectionOfMotionPixel = texture(previousDirectionOfMotionTexture + offset)
previousDirectionOfMotion = vec2(previousDirectionOfMotionPixel.x * 2 - 1, previousDirectionOfMotionPixel.y * 2 - 1)
// Apply one frame's worth of acceleration to the "speed"-vector
//
// The "min" function is used to prevent high delta-ts--especially
// on startup--from magnifying small values to produce "springiness"
// where there should be little to none.
previousDirectionOfMotion -= previousVectorValue * min(1, deltaTime * 300)
// Then apply the "speed"-vector to the actual data.
//
// Again, the "min" function is intended to thwart over-large delta-ts
previousVectorValue += previousDirectionOfMotion * min(0.05, 4 * deltaTime)
// Now, generate data for the new frame
// This part I'm confident is as intended, so I'm glossing over it.
// The "selector"-value just restricts its application (see below)
// to a small region of the output.
newVectorData, newVectorSelector = generateNewValue()
// Combine the new data with the old data, using the selector
finalVectorData = newVectorData * newVectorSelector + previousVectorValue * (1 - newVectorSelector)
// Convert vector-values to colour-values--again, much as with a normal-map.
//
// I'm actually doing this in the render-lines (see below) on my end, but
// doing it has I have it here should be clearer.
vectorDataRenderResult = (finalVectorData + 1) * 0.5
directionOfMotionRenderResult = (previousDirectionOfMotion + 1) * 0.5
// And finally, render the colour-values out.
color1 = (vectorDataRenderResult, 0, 1)
color2 = (directionOfMotionRenderResult, 0, 1)
As for what I've tried: Off the top of my head, I've messed with the scalars applied to accleration and speed; I've tried clamping the speed; and I've tried using the "min"-function in various ways, including varying the applied minima.
In the end, it seems that the answer was a combination of two factors: