Well i got asked a question in a comment on how the terrains work in the LieroXNA project so I thought i would do a quick post on this rather than reply in the comments.
The way the terrains work has changed a few times over the course of development of the project for various reasons mostly related to the incompatabilities of the 360 and my previous methods. I wont go into those previous methods here but if anyone reading this has any questions dont hesitate to ask in a comment or email me. I would also like to say this may not be the most efficient method as I was only getting the terrains up as a proof of concept first then was planning on refining later.
Before i can explain how the terrain is deformed i must first talk about how the terrain is composed. There are currently three layers to the level, an indestuctable layer, a destructable layer and a background layer.
Destructable Layer | Indestructable Layer |
There is also a "destroyed mask" that is used to create areas at the start that have already been destroyed. This is mainly used so that you dont have to burrow for ages at the start to get to your opposition. Combined together you end up with the final result below.
Destroyed Mask | Final Result |
Each texture layer at the moment is 2048x2048 (which is the maximum texture resolution the 360 supports) and is in the format Color (8bpp). The indestructable and the destructable layer are then sent to the GPU for use in rendering and particle collisions by the GPU particle system.
Objects such as players and weapons that are updated by the CPU however cannot perform collision checks on a texture that is on the GPU (without calling getData() which causes read-back which is horribly slow). So the destructable and the instructable layers are duplicated in the initial load of the level and saved as a large two dimensional array of Colors on the CPU.
You may ask why waste memory by storing the full Color component on the CPU when only a single bit is required per pixel to determaine if the terrain is solid or not. The reason for this is that by storing the full color component exploded particles can become the colour of the terrain that they exploded from rather than all of them being the same color.
When an explosion occurs a few things need to take place to make the terrain deform correctly. A hole of the correct size must be cut away from the Texture on the GPU so that the particles react correctly to the deformed terrain, also this new hole must be mirrored in the collision data on the CPU so that the player doesnt collide with things they think are destroyed terrain.
Another 2048x2048 "explosion map" texture is used to handle the deformation of the GPU texture. A series of explosion masks have been created that represent each explosion size:
Exp32 | Exp64 | Exp128 |
When an explosion occurs it is added to a list of other explosions that have occured that frame. When the render of the level is called the list of explosions that have happened that frame are iterated through and rendered to the 2048x2048 "explosion map" map texture in the correct world location that the explosion occured. This "explosion map" is then passed to a texture render shader which using render targets updates the terrain texture:
float4 PixelShader(float2 texCoord : TEXCOORD0) : COLOR0
{
float4 outCol = float4(1,0,0,0);
float4 explosinCol = tex2D(ExplosionSampler, texCoord);
if (explosinCol.x!=0)
{
outCol = tex2D(TextureSampler, texCoord);
}
float4 indestr = tex2D(IndestructableSampler, texCoord);
if (indestr.w!=0){ outCol = indestr; }
return outCol;
}
Note how the shader takes into account the indestructable layer so that the explosion doest destroy parts of the indestructable level.
The CPU collision data 'texture' is updated at the time of the explosion. When game loads each explosion mask is duplicated on the CPU much in the same way the indestructable and the destructable layers are. When an explosion occurs each pixel in the CPU explosion mask is compared against the CPU destructable layer and depending if the pixel is "indestructable" or not the pixel is set to be destroyed.
If you remember from back at the start I talked about the "Destroyed Mask" but didnt explain how it destroyed the terrain. Well it basically acts as a large explosion mask. At the init of the level the Destroyed Mask is rendered to the "explosion map" giving it an initial destroyed appearence. The same is applied to the CPU.
And thats about it for how the terrain deformation works in the project at the moment. As i said i played around with many differnt ways of doing it including negative rendering and methods that dont involve using a 2048x2048 temporary buffer texture but for now this is the method that it uses.
Thank you very much for the detailed information!
ReplyDeleteI think I understand the general idea but I don't know anything about shaders so that part is a little confusing. I also don't know much about particles in general so understanding "GPU particles" is probably much more difficult. Do you have any links or information about them?
Also, why do you have an explosion mask and a destroyed mask? It seems like you would only need the destroyed mask to be a mask for ALL destroyed terrain (initial and otherwise) and then you would just use small explosion textures that are rendered to the destroyed mask (eg. on top of it).
Another alternative (which you've probably thought of) is using the stencil buffer to hide destroyed terrain.
Anyways, thanks for the excellent article!
Hi John,
ReplyDeleteI released one of my final year projects on this very site along with documentation and source of GPU particles here: http://www.artificial-studios.co.uk/?p=163
Also microsoft released a sample on GPU particle systems here: http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Particle-3D-Sample.aspx
Also Catalin Zima released a tutorial over at ziggyware on Vertex textures which are used in the GPU particle systems i use: http://www.ziggyware.com/news.php?readmore=446
I havent actually looked that much into using the stencil buffer actually, i may do that to see if there is any improvement in performance. Ye also perhaps im using one too many textures there, ill take another look at it ;)
Mike