# Advice on my CoolDownTimer effects

This topic is 1626 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi all.

I've been looking in to making a cooldown timer effect, When you press a button and it has the shading going around.

After my third go I think I found a good way to do it but some of the math like the atan2 im doing a flip on to get 360.

Ok I know angle's again why??? only thing I know I'm a Ex metal fabricator.

Any how the cooldown timer is in the pixel shader using the texture coords and I pass in a angle based on time 1 degree or more a second

depends on how fast you want it to spin(go around).

It looks like a mess is there a neater way of doing it.

What way would you try.

//heres the pixel shader this is my test code it works I display red for now for range between 0 and 180

and green when I flip the angle.

float4 DrawPStimer(GS_OUT pIn) : SV_TARGET

{

float2 start = float2(0.0,0.0);//points up

//we need to turn the texture coords into a direction vector ???

//texcoord need to be converted to its 0, 0 pos

float2 e = float2(0.5,0.5);

float2 t = pIn.texC - e;

float2 p = normalize(start - t );

float a2 = atan2(p.y, p.x);

if(a2 <= currentangle && a2 > 0.0)

return float4(1.0f, 0.0f, 0.0f, 1.0f);

if(a2 <= 0)

{

return float4(0.0f, 1.0f, 0.0f, 1.0f);

}

return float4(0.0f, 0.0f, 0.0f, 0.0f);

}

here is a image with the current angle set to 275

0 degrees is to the left 180 right

new image now

Edited by ankhd

##### Share on other sites

Ok I know angle's again why??? only thing I know I'm a Ex metal fabricator.

Why ? Because of "don't use angles but vectors" ? Well in this case it is an angle relation, so IMO I wouldn't bother. You can improve a bit though, e.g. bring your angle into the atan2 range of -pi..pi, so you can avoid the ifs (one of which was superfluous anyway):

    // CooldownAngle is a shader constant. Or you could do the shift and radian conversion from the app already
float currentangle = radians(CooldownAngle - 180.0);
// -0.5 will expand to float2(0.5, 0.5)
float2 t = pIn.texC - 0.5;
// note I sign-flipped p again because of the shift, so we can use atan2 directly
float a2 = atan2(t.y, t.x);
// the following is the "float version" of an if(). true -> 1, false -> 0.
float d = currentangle - a2 > 0.0;
// which we now use for the alpha
return float4(1, 1, 1, d);

Since atan2 produces quite a bunch of instructions here some alternatives if you got performance problems:
http://www.gamedev.net/topic/555891-warcraft-iiiwow-style-button-cooldown/
http://www.gamedev.net/topic/613851-resolvedworld-of-warcraft-image-effect-spell-cooldown-with-source-code/

Then again: Implementation-wise your approach is probably the simplest

PS: By the way, you image link is broken.

##### Share on other sites

Hi. Thanks for the Ideas, I tryed them but I could not get them to work the same way with my new bit of code I could do the texc - 0.5.

your code works I ran that.

Heres the whole new code is there a way to use your float routine.

the c++ part updates the angle and converts it to rads now

/heres my new code. what tricks are there to turn this monster into a baby.??????

float2 start = float2(0.0f,0.0f);//points up

float4 sam = float4(0.0f, 0.0f, 0.0f, 1.0f);

float4 s = float4(0.0f, 0.0f, 0.0f, 1.0f);

//if we are at 360 then we just go standard colour the timer is up

{

return float4(0.0f, 0.0f, 0.0f, 0.0f);

}

//we need to turn the texture coords into a direction vector ???

//texcoord need to be converted to its 0, 0 pos

float2 t = pIn.texC - 0.5;

float2 p = normalize(start - t );

float a2 = atan2(p.y, p.x);

if(a2 <= currentangle && a2 > 0.0)

{

sam = gTex.Sample( TexS, pIn.texC ) * gSelectedColour;//the button has a selected colour add this before grey scale

s = float4(sam.r, sam.r, sam.r, gAlpha);

return s * intensity;

}

if(a2 <= 0)

{

sam = gTex.Sample( TexS, pIn.texC ) * gSelectedColour;

s = float4(sam.r, sam.r, sam.r, gAlpha);

return s * intensity;

}

}

return float4(0.0f, 0.0f, 0.0f, 0.0f);


Edited by ankhd

##### Share on other sites
The idea of my snippet was that you can now use that d value for further masking/lerping/whatever. Not sure I follow your code, maybe like so (after my snippet):

    // since you gonna use only one channel...
float sam = Diffuse.Sample( Sampler, tex ).r * gSelectedColour.r;
// ... swizzle that to all three channels. And combine d with alpha
float4 s = float4(sam.rrr, gAlpha * d);
return s * intensity;


PS: Please cleanup your code before posting (e.g. remove the commented statements) and use code tags

##### Share on other sites

Hi.

Thats some nice work unbird. I Thank you for your time.

How would I go about making the pie shape have the original image colour and the lapsed time area grey scale.

I've changes it around but can't get it like my image.

I would really like to keep your antialiased version.

##### Share on other sites
Easy. Though to increase contrast I'd also darken it a bit
    // [ AA code like before ]
// original color
float3 color = Diffuse.Sample(Sampler, tex).rgb;
// grayscale version (aka intensity calculation)
float gray = dot(color, float3(0.3, 0.59, 0.11));
// darken
gray *= 0.6;
// lerp the two colors.
return float4(lerp(color.rgb, gray.rrr, alpha), 1);


PS:

Shall I explain ?

##### Share on other sites

Hey again.

Yeah if you have the time my self and anyone who finds this post would love to here the facts behind the Magic.

I have 1 more question.

The cooldowntimers pixel shader is in a second pass and I am leaving the angle as a trigger not to render any thing and Im using this here

//needed here this pixel shader is called in a second pass and when the angle is 360 we don't render any thing at all
if(currentangle >= (360.0))
{
clip(-1);
return  float4(0.0f, 0.0f, 0.0f, 1.0f);//never gets here now
}



is it ok to use the clip function in this way.????

Heres the product I like it.......

//this here needs the angle in degrees passed in to the shader
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
float4 DrawPStimer(GS_OUT pIn) : SV_TARGET
{
// antialias transition width in pixels
float Transition = 2.5;
//pixel in texel size. Assuming we have a GUI-typical pixel perfect
// ortho projection this is just 1.0/width of your rectangle.
float PixelSize =1.0/128.0;//image width

//if we are at 360 then we just go standard colour the timer is up
//needed here this pixel shader is called in a second pass and when the angle is 360 we don't render any thing at all
if(currentangle >= (360.0))
{
clip(-1);
return  float4(0.0f, 0.0f, 0.0f, 1.0f);
}

// normalized point coordinate (2D homogenous for plane evaluation)
float3 p = float3(pIn.texC - 0.5, 1);

// first plane for 2D plane equation (a*x + b*y + c), depends on the cooldown angle
float3 plane1;
// plane offset to avoid later bleed (just found through trial and error by playing with tweak UI)
float offset = lerp(0, 2 * Transition, saturate(currentangle / 180.0 - 1));
// c value of plane equation:
plane1.z = offset * PixelSize;

// estimate distance to plane using pixel size
float dist1 = dot(plane1, p) / PixelSize;
// ... to calculate a anti-alias transition value
float fade1 = saturate(dist1 / Transition);

// second plane, fixed (horizontal)
float3 plane2 = float3(0, -1, 0);
float dist2 = dot(plane2, p) / PixelSize;
float fade2 = saturate(dist2  / Transition);

float alpha;
[flatten]
if(currentangle < 180.0)
{
// intersection of the two masks
}
else
{
// union of the two masks
}
// uncomment the following line if you want to see both planes in action

// use this alpha for some effect, here just fade to black
float4 color = gTex.Sample(TexS, pIn.texC);
//return float4(color.rgb * alpha, 0.5);//gAlpha
//new gray scale around elapsed
//area the pie shape is image colour
// grayscale version (aka intensity calculation)
float gray = dot(color, float3(0.3, 0.59, 0.11)) * intensity;
// darken
gray *= 0.6;
// lerp the two colors.
return float4(lerp(color.rgb, gray.rrr, alpha), gAlpha);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////


##### Share on other sites

Maybe remove that if statement and put clip on top of the shader like this:

//if(currentangle >= (360.0))
//{
//   clip(-1);
//    return float4(0.0f, 0.0f, 0.0f, 1.0f);//never gets here now
//}

clip(359.999 - currentangle);



Better yet skip whole shader if you can calculate currentangle on cpu.

Edited by belfegor

##### Share on other sites

is it ok to use the clip function in this way.?

Sure. Just know that clip may have a performance impact when there are lots of diverging pixels (pixels that get clipped and pixels that don't). Unlikely in this case since you clip the whole rectangle anyway.

Anyway, for future reference here some alternatives:
• Since you don't want do draw anything when currentangle >= 360°, don't issue a draw call at all (PS: Beaten by belfegor )
• If these rectangles are batched and since you are using a geometry shader: Don't generate triangles in your GS at all when currentangle >= 360°.
• Looks like you're using alpha blending. An alpha of 0 will have the same effect.
Now for the explanation:

The idea is to use a Plane equation, in 2D that is:

f(x,y) = a*x + b*y + c

x,y are our coordinates in texture space (though offset by 0.5, i.e. centered). a and b are the plane's normal, which we get using sincos of the cooldown angle. c is the distance of the plane to the origin (here 0, we define our origin at the center of the quad).

If we evaluate above formula we get the signed distance to the plane.
Here a snippet and screenshot which shows that (Note that dot(float3(x,y,1), float3(a,b,c)) == a*x + b*y + c):
    float3 p = float3(tex - 0.5, 1);
float3 plane1;
plane1.z = 0;
float dist = dot(p, plane1);
if(dist < 0)
return float4(-dist, 0, 0, 1);
else
return float4(0, dist, 0, 1);


This distance is in the space we used for the plane and the point p. Since we want to AA in pixel space, we need to transform this distance using /PixelSize.
Using a transition value gives use a nice AA mask (saturate just makes sure we stay in 0..1 range).

We do this again with another plane (this time fixed). Now we combine them. If the cooldown angle is < 180° then we intersect, otherwise we use the union. The mask values are [0..1] floats, so using min for intersection and max for union is one way. Alternatively one can use e.g. multiplication for intersection.

There was a nasty problem though: When the plane normals were exactly opposite, the combined masks produced this ("bleed"):

Like said, here I played just around to see how I had to offset one plane so this would never happen

I actually started with a more complex approach using Rendering Vector Art on the GPU(25.5 Antialiasing). This uses hardware derivatives (ddx, ddy) and is therefore foolproof, i.e. and does not need to provide the PixelSize. But for a GUI effect I think this is overkill.

1. 1
2. 2
Rutin
19
3. 3
khawk
15
4. 4
5. 5
A4L
13

• 13
• 26
• 10
• 11
• 44
• ### Forum Statistics

• Total Topics
633743
• Total Posts
3013644
×

## Important Information

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!