Posts Tagged ‘deferred’

RealKo Engine: Airplane asset and their functionality

Sunday, June 14th, 2009

Welcome back my friends.

Lots of improvement in term of airplane asset and its related functionality inside the engine have been done. They are now quite close to stable.

For what I have planned before dumpping myself into the code is this.
For the airplane asset configuration, there are many type of airplane such as “Boeing 747 - 100 class”, or “Boeing 747 - 400 lcass”, thus I plan to let the user who implements upon my engine to edit the config file. By inserting the information about all type of airplanes that will be used in the simulation, in this way the engine will know how to treat each type (but not too flexible, due to the limitation on particular artist-method => there are many ways to do things, so the limitation on this will surely comes to play.) and hold all of the information.

Anytime user wants to create new airplane entity and put it to fly over the airport. He/She just grabs the desire type of airplane information (which now already loaded prior to the content-load period)  from the engine, and create one.
As you may see the flexibility for creating process,  content-loading process, and clean code can be achieved. Thus user can be more focus on the core part of the simulation itself, not wasting time too much on misc steps to create just one another airplane entity.

In fact RealKo Engine relies heavily on the config files. All of the model which are airport, airplane, skydome/clouddome, and others to come will use the config files. You may ask it’s a bit of cumbersome to edit the config file and thus giving the slow on setting up the scene. But it takes time at first but then after user specifies all the assets’ information, user can now be freely from that part and move into the actual core of the simulation. The final result is better and better. Anyway not all the engine uses config files, it’s up to the situation and case. For RealKo Engine, I must le t the user freely specifies new type of asset, enable them to modify the resource (not worry about file name, name of file can be changed, but just reenter that name in config file.), and it some assets are modified by user, they can reedit the config file and keep going. So at this point I think you have got what I mean. ;)

Okay, let’s talk about airplane. Airplane has lights, right? That’s true. But my clients said that Suvarnabhumi Airport can hold about 400 airplanes at once! If you think along with me, one airplane can have at least about 5-6 lights attached to itself. RealKo Engine powered by the Deferred Shading Rendering System, thus it can support more light (specially for many small point lights, as they’re cheap.). But not for this, if we use real lights attached to each airplane.
If we do so, 400 x 5 = 2000 lights will be on the scene at once, hauhhhhh!!!!! At first glance this is not possible at all.
Nevertheless that I already know it’s not the solution I will take and go on with it, but I will test it anyway.
By inserting the lights into the scene for 2000 lights, my GTX 275 is pulled down to earth at the rate of 2 fps. Immediately I switch back to another solution ….

Another solution suggested by my artist leader (P.Yod), he said that we will use the textures instead of lights. I agree and it’s the similar thought as mine as well. One drawback if we use the textured light is that “far far away, we may not be able to see the light, and some visualization will be gone”. If the textured light is lit by light sources in the scene, absolutely we will not be able to see the light of airplane in the night (in which we must have light shows well and clearly in that time!!).

After P.Yod sent me the airplane resource, and I saw the textured light is alpha texture. The solution just come out directly from my head. We will draw it externally after deferred shading process is finished thus this will give the best result as the particle system that I have faced it before and solved it. By drawing the alpha textured light on top of deferred shading result, the light-calculation will not be taken into effect, and we see it clearly in the night, for the day time, the alpha textured light will be blended nicely with the pixel already resided in the color buffer and thus it will automatically reduce the strengh of textured light attached to airplane. WOW!!!! (In the night of course, mostly the color in the color buffer is quite close or in the theme of black color, thus it will step up the strength of texture lights attached to airplane.)

There are 4 types of light for airplane.
1. Window light, light coming from the inside airplane through the window. Thus simulating the night time.
2. Head light, turn on before landing process begins.
3. Signal light, always blinking at night
4. Ground light, will be turn on when airplane landed or touch the runway (thus simulate the realistic light, but infact it’s just the textured light.)

To draw those lights stuff correctly, I decide to create just another effect file for those light-type.
I categorize the light into 2 types.
1. Non-blinking light
2. Blinkging light

Both of them is similar in code, but just when doing this stuff for blinking light, I use the current time from engine, and put it into sin() function to calculate the intensity to be applied to texture light. Thus it will be like blinking!! That’s all, simple enough.

See that section of code from my effect file to draw the blinking light below.

float4 PixelShader(BasicPSInput input): COLOR0
{
	float4 color = tex2D(diffuseSampler, input.TexCoords);
 
	//calculate the light intensity used specially for the signal light (blinking effect)
	float intensity = sin(time);
 
	if(intensity < 0)
		intensity = 0;
 
	color *= intensity;
 
	return color;
}

I think you already get the idea from seeing the code above.
If the intensity is below 0, then I will not take any risk to multiply the color with negative number, I just want it to be dissappered. So let it be zero, 0 will be better.

When combining the particle scene with the airplane, in the time of collision or just normal weather effect.
The visual effect is more enhanced in the way we can feel it, and appreciate the result it can give us.
(Remember that deferred shading is backed the engine up, so the lighting calculation is more neatly and at least cost as it can be, thus giving another advantage.)

Enough for chitchat now, let’s see the real stuff below.
Please enjoy!!!

Integrate Creator Club’s particle system into deferred shading system.

Tuesday, May 26th, 2009

From what I have done now, integrating the particle system from creator club into my deferred shading system. I have 2 options as this case also can be applied to you too for the approaches I gonna use to integrate this stuff.

First, it’s the simplest one which can be done without much effect. NO modifications are need for ParticleEffect.fx file of the particle system, thus giving you the most comfortable one. What the first one does exactly? Well it does render the particle scene on top of the finished deferred scene by just combining their textures. In this way, although the work is well reduced, but the particles still visible through the solid objects. Thus it the early simple work now comes to the cumbersome, in which you much get the depth buffer from deferred scene, whether or not it comes from your render target or the graphics device’s current depth buffer. We use it to just occlude the particles that must not be seen if they are behind our objects in deferred scene. Also, this is not enough if we choose not to modify any part of the ParticleEffect.fx file, then we must get particle scene’s depth buffer out too. In conclusion, we need two more textures of depth buffer externally from the ParticleEffect.fx to correctly draw the particles on screen, and thus another effect to combine them so more passes required for the final scene. But if we choose to modify some parts of that file, we need to calculate the location of the depth buffer to sample the depth at the particular position of a single particle. Compare them and then correctly draw. Yes, I think it can be called the nice way too. But this is not the approach I will use, as I want the overall rendering pass to not be increased anymore.

Second approach, this is what the approach I chose. The reason is to reduce as much the render pass for my rendering system. It requires no additional rendering pass at all, as it will now become the part of the deferred shading itself. For what I done with this approach is this, I have treated the particle scene as the normal scene for deferred shading. It can be called for updating, and drawing like the normal scene existed in deferred pass. In this way, we reduce at much the overhead to switch between the effect, and time to produce the final scene.

How to do it? I have to add some codes into the ParticleEffect.fx file.

The following is the list for my addtion to the code. List of  addition
1.  add the copy of transformed position (vector4) for vertex output structure => we only do the calculatation for depth value here

2.  add the receiving parameter (corresponding to 1.)

3.  add the output of pixel shader structure, writes to all the related and only neccessary render target

4. add the part of code inside pixel shader to write the output.

Let’s see the real action below

We must add the copy of transformed position (vector4) into the vertex output, thus we can store our particle’s depth into the depth render target, thus this will solve our “particle through solid object” problem.

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float Size : PSIZE0;
    float4 Color : COLOR0;
    float4 Rotation : COLOR1;
    float4 CopyPosition: TEXCOORD0;
};

Note above that we use TEXCOORD0 semantic to hold our data. And now take a look at the vertex shader function, as we will add single line to copy the transformed position (vector4) and route it to the output. See below

// Custom vertex shader animates particles entirely on the GPU.
VertexShaderOutput VertexShader(VertexShaderInput input)
{
    VertexShaderOutput output;
 
    // Compute the age of the particle.
    float age = CurrentTime - input.Time;
 
    // Apply a random factor to make different particles age at different rates.
    age *= 1 + input.Random.x * DurationRandomness;
 
    // Normalize the age into the range zero to one.
    float normalizedAge = saturate(age / Duration);
 
    // Compute the particle position, size, color, and rotation.
    output.Position = ComputeParticlePosition(input.Position, input.Velocity,
                                              age, normalizedAge);
 
    output.Size = ComputeParticleSize(output.Position, input.Random.y, normalizedAge);
    output.Color = ComputeParticleColor(output.Position, input.Random.z, normalizedAge);
    output.Rotation = ComputeParticleRotation(input.Random.w, age);
    output.CopyPosition = output.Position;
 
    return output;
}

See the line “output.CopyPosition = output.Position”. Isn’t it simple? Now we move to the pixel shader part. But before moving into it, I must tell you what about my GBuffer layout first, so you can adapt it into your own and work correctly.

For my layout, I have 4 render targets as follow

RT1, Color  (ARGB with 8 bit pixel format  for each channel), this render target just contain the albedo color
RT2
, Depth t (32 bit float format using 32 bit for red channel), this render target contains pure depth value
RT3,
LightOcclusion (same format as RT2), this render target contains information about whether or not the light will lit each pixel (for example the skydome, you dont want to light it, so settings this value to 1 to disable light effect when drawing skydome.)
RT4,
Normal (same format as RT1), in fact you can use 16-bit for x and y, then calculate z using 1 - x^2 - y^2.

Okay, let’s see the code

struct PixelShaderOutput
{
	float4 Color: COLOR0;
	float4 Depth: COLOR1;
	float4 LightOcclusion: COLOR2;
};

You may ask at this point. Why I don’t include the normal as output in this structure? Because the hlsl rule, as when you include the output part inside the structure, you must write it too, whether or not you do not want to be messed with its value. Thus as you may expect, I rearrange the order or render target just to take the normal to the last and not include it in the output structure, as I don’t want to modify its current value in the buffer. Because of we don’t want any one of the particle to affect the lighting calculation at the later stage, so do not mess with the current normal resided in the buffer already. Leaving this way, the lighting will not concern about the existence of individual particle. (But at the later you will see that I have set the value of lightOcclusion to 0, contradict to what I have told you, it’s because if we turn off the lighting effect on individual particle in which that particle already draw on top of our normal scene, we will see the square of pure albedo color. Thus if your scene is in night, then your rain particle system is enabled, you will see the bright square everywhere which again not what we want.) Now let’s have a look about input structure, and its pixel shader function (separate for the type of output, as you will learn that the particle system created from creators club is well implemented. The type includes “Non rotating particle”, and “Rotating particle” version).

For non-rotating version.

// Pixel shader input structure for particles that do not rotate.
struct NonRotatingPixelShaderInput
{
    float4 Color : COLOR0;
 
#ifdef XBOX
    float2 TextureCoordinate : SPRITETEXCOORD;
#else
    float2 TextureCoordinate : TEXCOORD0;
#endif
 
    float4 CopyPosition: TEXCOORD1;
};
 
// Pixel shader for drawing particles that do not rotate.
PixelShaderOutput NonRotatingPixelShader(NonRotatingPixelShaderInput input)
{
	PixelShaderOutput output;
 
	//write to the GBuffer
        output.Color = tex2D(Sampler, input.TextureCoordinate) * input.Color;
	output.Depth = input.CopyPosition.z / input.CopyPosition.w;
	output.LightOcclusion = 0;
 
	return output;
}

For rotating version

// Pixel shader input structure for particles that can rotate.
struct RotatingPixelShaderInput
{
    float4 Color : COLOR0;
    float4 Rotation : COLOR1;
 
#ifdef XBOX
    float2 TextureCoordinate : SPRITETEXCOORD;
#else
    float2 TextureCoordinate : TEXCOORD0;
#endif
 
    float4 CopyPosition: TEXCOORD1;
};
 
// Pixel shader for drawing particles that can rotate. It is not actually
// possible to rotate a point sprite, so instead we rotate our texture
// coordinates. Leaving the sprite the regular way up but rotating the
// texture has the exact same effect as if we were able to rotate the
// point sprite itself.
PixelShaderOutput RotatingPixelShader(RotatingPixelShaderInput input)
{
    PixelShaderOutput output;
 
    float2 textureCoordinate = input.TextureCoordinate;
 
    // We want to rotate around the middle of the particle, not the origin,
    // so we offset the texture coordinate accordingly.
    textureCoordinate -= 0.5;
 
    // Apply the rotation matrix, after rescaling it back from the packed
    // color interpolator format into a full -1 to 1 range.
    float4 rotation = input.Rotation * 2 - 1;
 
    textureCoordinate = mul(textureCoordinate, float2x2(rotation));
 
    // Point sprites are squares. So are textures. When we rotate one square
    // inside another square, the corners of the texture will go past the
    // edge of the point sprite and get clipped. To avoid this, we scale
    // our texture coordinates to make sure the entire square can be rotated
    // inside the point sprite without any clipping.
    textureCoordinate *= sqrt(2);
 
    // Undo the offset used to control the rotation origin.
    textureCoordinate += 0.5;
 
    //write to the GBuffer
    output.Color = tex2D(Sampler, textureCoordinate) * input.Color;
    output.Depth = input.CopyPosition.z / input.CopyPosition.w;
    output.LightOcclusion = 0;
 
    return output;
}

Now, we have done it.
For all the detail I described to you so far. I have done integrated it into my RealKo Engine.
I open for your suggestion and comment about the approach I made, you can suggest the better way to do it. Thus note for my designing, I chose the one that is not that too much difficult to implement, and does not require too much time (with close to deadline of my project), thus all they have come to be the way as I explained to you so far.

Farewell for now, and see you next time.