Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I have a similar question as the one posed here, except that I don't wish to use a 1D Color Palette.

I simply wish to have it display 1 color of my choosing (red, for example). I plan to use this as a "shield" effect for a 2D ship.

I also wish to understand how it works a little bit better, as I'll be the first to admit that shaders in general are not my strongest suit. I'm not asking for an overview of HLSL (as that is too broad of a subject), just an explanation of how this shader works, and the best way to implement it in a 2D game.

Code examples would be ideal (even if they are theoretical) but if the answer is explained well enough, I might be able to manage with plain old text.

This is also in XNA 4.0.

Thanks in advance.

share|improve this question
    
Anyone? I'm really stuck on this... –  electroflame Dec 1 '11 at 2:55
    
That's a very small and simple shader in that link. Honestly what you need to do is go through each line of that shader and look up everything related in a reference so you know what it does. "uniform" "sampler1D" "tex1D()" "length()" "float4" "POSITION" and "COLOR" are the keywords. That's the only way you'll understand it any better. Worst case scenario is you create a 1D texture of all red and use that. –  Patrick Hughes Dec 1 '11 at 3:42
    
@Patrick I appreciate your comment, but it appears to me that the shader is incomplete. There are a lot of "/* ... */", which, to me, imply "Put in some code here". That's mainly my problem. If it was only "copy this whole thing and it will work" I'd have had no problem with it. Perhaps if someone could tell me if I need any other code in that shader to make it work? –  electroflame Dec 1 '11 at 4:28
    
@electroflame: Copy-and-paste coding should be discouraged wherever possible. That shader has 100% of the information you need to get done what you need to get done. It's up to you to use that information to produce the effect you want. –  Nicol Bolas Dec 1 '11 at 6:27
add comment

1 Answer 1

up vote 4 down vote accepted

Diatribe: You should be trying to figure this out yourself - I was myself in the exact same position as you a year ago; and now I am doing soft-edged water etc. without copying code because I figured it out myself. I will try to explain the thought process you should be using - but you won't get one complete snippet. Furthermore, he uses a Texture1D - which we don't have in XNA; so my answer will vary slightly.

To start you should first search on how to use Effects, and how to use custom ones with a SpriteBatch (if that is what you are using).

Firstly, if you look at the video you will notice that the waves are moving outwards from the center - in order for a shader to perform any form of animation over time you need to provide it with knowledge of time. Therefore provide it as a uniform parameter (this means that it will retain its value over an entire frame):

uniform float time; // Set this to the current total elapsed game time each frame.

Now we would like it to move outwards from the center; that means we need to define 'center':

uniform float2 center; // The center of the ship in local texture coordinates (most probably 0.5f, 0.5f)

Note that this center is local to the texture - in other words don't pass the coordinate in in terms of screen space. Also, remember that you perform Texture lookups on the GPU using 0 -> 1 (not 0 -> size of texture in pixels). In most cases you could probably just set center to 0.5f, 0.5f.

Finally we need to access the texture - to read colors from a texture you use a sampler - this will contain the graphic for the ship.

sampler BaseTexture : register(s0); // According to how sprite batch works.

And one for the shield - which we will repeat over the entire area of the ship (you will need to see this).

sampler ShieldTexture : register(s1);

A pixel shader automatically 'loops' over every pixel (it's not actually what happens, but you can see it this way), so we will get some information from the GPU.

float4 ShieldPixelShader(float2 inTex : TEXCOORD0) : COLOR
{
   /* ... */
}

What this means is that we have a function that takes the current texture coordinate and returns the resulting color. The GPU will fill in the value of inTex for you (because of the TEXCOORD0 semantic).

The first thing we need to do is figure out how far the current pixel is from the center of the ship:

float dist = length(inTex - center);

Now we need to figure out how opaque to make the second texture based on that distance and the time. The distance will be in the range 0->1 (because of how texture coordinates work); and we want the 'bright spot' to move from 0->1, reset and repeat - this sounds mightily like a modular problem. The % returns the remainder of division - and it conveniently restricts a value to a range (in a repeating fashion) - so for example i % 5 will do this:

0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, ...

What's also great is that it also works on real numbers (things like 0.5) - so if we just take time % 1 it will get us our repeating expanding circle:

float targetDist = time % 1;

Now all we do is figure out how close we are to targetDist, so we can see how opaque we want to make the second texture:

float distDiff = 1 - min(0, abs(targetDist - dist) - 0.5);

That subtraction and min is so that we can contract the size of the gradient (remember it occurs in a small space around the current desired distance, not all the way from 0.5, 0.5).

Now we can finally get to the bit where we mix the two textures, remember that I said we are going to repeat the shield texture. Another nifty the thing the GPU can do for us is wrap textures, that is, if you ask for a pixel at 1.1 it will actually grab it from 0.1 (if you think about it you will see that they are also just using %/mod for this). So in order to repeat a texture we can just multiply it by the number of times we want it repeated:

float2 shieldCoord = inTex * 20; // Repeat it 20 times.

Finally we grab the colors for each texture:

float4 shipColor = tex2D(BaseTexture, inTex);
float4 shieldColor = tex2D(ShieldTexture, shieldCoord);

Finally we perform the simplistic alpha mix:

float4 finalColor = (shipColor * (1 - distDiff)) + (shieldColor * distDiff);
finalColor.a = shipColor.a; // Use the ships alpha (transparency).

And return it to the graphics card:

return finalColor;

Side note: You will need to turn off premultiplied alpha blending for this shader to work - search around to find out how to do this.

share|improve this answer
    
This is perfect. I can get it working from here. Also, I apologize if I sounded as if I wanted a working chunk of code - I don't. I just needed some explanations on how to achieve something like this, and you provided exactly what I was looking for. I'm not new at this, so it's a little embarassing that I came across as "Gimme some code!". –  electroflame Dec 1 '11 at 18:00
    
I've got it almost completely working, I just need clarification on one thing. Is the time that I pass in literally GameTime.ElapsedGameTime.TotalSeconds, or did you mean something else? I thought that it would have to be within the 0 -> 1 range like everything else, and GameTime.ElapsedGameTime.TotalSeconds doesn't appear to work. (Also, I'm passing in the gametime as a float, is that right?) –  electroflame Dec 1 '11 at 19:57
    
@electroflame You usually use TotalMilliseconds, but TotalSeconds would work 100%. Remember the %/mod makes it 0->1 (repeating as time goes by). –  Jonathan Dickinson Dec 1 '11 at 22:56
    
@electroflame also remember to experiment! It's the best way to learn - C# compiles really quickly, so: F5; check; tweak; F5;... isn't such a pain. XNA is really great for hashing out stuff pretty fast. I didn't test this myself - so some of my stuff might be off; but if you really try you will get it. Also Google "PIX Shader Debugging" to get epic insight into what your shaders are doing. –  Jonathan Dickinson Dec 1 '11 at 22:57
    
@electroflame I actually need to test the behavior of mod with floats on the GPU - but according to IEEE standards value % 1 will get you a value between 0 and 1. –  Jonathan Dickinson Dec 1 '11 at 23:04
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.