Sign in to follow this  
Kuro

Passing time value into a shader

Recommended Posts

Kuro    284
I have a shader which uses a time value for a sin() calculation to create a pulsing effect. The time value is measured as the # of seconds since the program started and is passed in as a float to the shader. The problem is that after a certain number of seconds (say, 10000), the time value starts to become imprecise due to the limitations of floating point. To prevent the time value from becoming too large, I have to do something stupid like this: if (shaderTime > 10000.0f) shaderTime = 0.0f; So once every 10000 seconds, you might notice the pulse suddenly flicker for a split second. I know this bug would happen very rarely so in theory this is an acceptable workaround, but it is a sloppy solution I'd prefer to avoid... If I knew the period of the function was 2PI, I could do something like this: if (shaderTime > SomeVeryLargeMultipleOfTwoPi) shaderTime -= SomeVeryLargeMultipleOfTwoPi; This would prevent that flicker from happening, but then if you ever changed the period of the function in the shader (e.g. sin(1.5 * PI)) then I think this wouldn't work. Sorry for the long post, hope that makes sense! Thanks Kuro

Share this post


Link to post
Share on other sites
Sc4Freak    643
I doubt you'll ever run into any precision issues using a 32-bit floating point number. They're imprecise, but not that imprecise.

If it really becomes a problem, you could try using an integer and express program time in milliseconds.

Share this post


Link to post
Share on other sites
Krohm    5031
Quote:
Original post by Kuro, slightly modified
if (shaderTime > 10000.0f)
shaderTime -= 10000.0f; <-- changed!


So once every 10000 seconds, you might notice the pulse suddenly flicker for a split second.

Well, it would just take to subtract a number instead of setting it...
Because of the inner workings of CPU FP implementation, you shouldn't allow your value to go too big just to restart it once in a while. It's better to to this often - say at 10 seconds instead of 10.000, although it probably makes unnoticeable differences in this context.
Quote:
Original post by Kuro, slightly modified
This would prevent that flicker from happening, but then if you ever changed the period of the function in the shader (e.g. sin(1.5 * PI)) then I think this wouldn't work.
This is an important issue and a good question: how do you figure out your period mod doesn't mess up the normal appearance?

I have to say current shader implementations are a bit weak with this regard. Some higher-level shader implementations have explicit notion of "waveforms" and can extract their period for tracking. I like this solution pretty much but obviously the amount of machinery required can quickly go out of control.

Share this post


Link to post
Share on other sites
ET3D    810
Why not calculate the sin(value) outside the shader? Not only will you have more control, but it should save the shader work.

Share this post


Link to post
Share on other sites
Stereo    256
The sine effect most likely depends on some interpolated value from the vertex shader (like texture coordinates), so he can't do that.

Share this post


Link to post
Share on other sites
Buckeye    10747
It sounds like you have an issue that has nothing to do with the time value passed to the shader. I don't believe for a minute that you're losing precision at floating-point values on the order of 10000. That's only 5 significant digits and floats should have no problem up to 15 significant digits.

I would suggest looking into other possible problems that may be occurring after your program has run for two and a half hours. For instance, a small memory leak once per frame would be dangerously large after 3 hours, particularly if, for some reason, GPU memory is involved.

Do you see the same problem if you pass the shader fake large time values after just a few seconds of real runtime and then increment that large value each frame?

Share this post


Link to post
Share on other sites
emeyex    382
I would tend to agree with Buckeye, I've seen this behavior on platforms that support half-precision floating point (and where I was using that explicitly), but when switching to the default "float" in the shader (which, bear in mind, isn't necessarily always 32 bits), I didn't have any issues.

Assuming precision WERE the issue, you could always extract your period values onto the cpu and set them as shader constants; this would allow you to offset the time value on the cpu for each shader that needed it, and would keep your those two values (the shader time and the period) synced up better.

Share this post


Link to post
Share on other sites
Kuro    284
Thanks for the replies guys.

I think emeyex's solution is the only way that would completely fix the precision problem. It would be more work because you need to pass in separate shader constants for each shader that uses a different period in its effects but it would get the job done.

Assuming you wanted just 1 global time constant that's used for all the effects, it would be really nice if you could define the time variable in your program as a double (64-bit float). However, is it possible for shaders to work with double precision?

Btw, the reason I am doing the sin() inside the shader is like Stereo said, it has a dependency on both time and some other variable which is interpolated from the vertex shader.

Also some people were wondering how I ended up getting myself into a situation where I hit the limits of floating point precision. The truth is, I haven't- I'd have to leave a game running for days, and even if I didn't bother fixing it, the worst that would happen would be that the screen might flicker for a split second and then go back to normal. So yes, I'm being really nitpicky, it just feels really sloppy to write code that has a glitch in it and saying "that will never happen!"

[Edited by - Kuro on February 20, 2008 7:38:23 PM]

Share this post


Link to post
Share on other sites
Krohm    5031
Quote:
Original post by Kuro
Assuming you wanted just 1 global time constant that's used for all the effects, it would be really nice if you could define the time variable in your program as a double (64-bit float). However, is it possible for shaders to work with double precision?
I'll try to say again what we're written above. It is unlikely this is an issue of insufficient precision.
Your initial "quirk" issue happened because your method was essentially calling for it.
Note that range (number of digits) isn't the only thing that takes major importance in FP processing. There are various kinds of FP errors and one depends on operation sequence.
As said, this probably doesn't make sense for extracting colors but I am uncertain this is not a problem at all (for example, you can tell the difference from old-interpolation noise to the more recent interpolator when applying reflection operators).
Quote:
Original post by Kuro
So yes, I'm being really nitpicky, it just feels really sloppy to write code that has a glitch in it and saying "that will never happen!"
Just subtract instead of setting and it'll be reduced by a factor of 100.

Share this post


Link to post
Share on other sites
Kuro    284
Not sure if I'm missing something but I don't think subtracting an arbitrary number would work, Krohm.

You suggested this code:

if(value > 10000.0f)
value -= 10000.0f;


However, sin(10000.5f) != sin(0.5f).

If you wanted to use subtraction to fix the problem, you'd need to subtract a number that was a multiple of the period used in the shaders. But the problem is that if you have multiple shaders which each use a different period, then there is no such number.

Share this post


Link to post
Share on other sites
ET3D    810
Quote:
Original post by Kuro
But the problem is that if you have multiple shaders which each use a different period, then there is no such number.

If there aren't too many, multiplying the periods could produce such a number.

A better solution would be to use a fixed cycle outside and multiply it inside the shader. For example, if your outside range is 0 to 1, and the shader is using a simple sine, it would do "sin(time * 2 * PI)".

Share this post


Link to post
Share on other sites
Krohm    5031
Quote:
Original post by Kuro
Not sure if I'm missing something but I don't think subtracting an arbitrary number would work, Krohm.

You suggested this code:

if(value > 10000.0f)
value -= 10000.0f;
No, it wouldn't. The number was an admittedly oversimplified example. As I wrote myself, a truly correct approach in regards would be to track wave periods AND phases. The point is not the number but the subtraction. My fault for simplificating it too much.
You're right in bonking on this again, it's better to be clear and avoid somebody gets it wrong. Thank you.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this