Tuesday, June 14, 2011

GPU Planet Update

Jeez, I can’t believe it’s been almost 2 years since I last did anything with this… I picked it up again a few weeks ago and tried to get into it again, made a little progress and thought I’d share.

A major problem getting back into it was migrating from XNA 3.1 to XNA 4 – there are lots and lots of changes, one of the worst I found was the restrictions placed on shaders and the Reach and HiDef profiles. I found an article which described how to target Reach and HiDef with the same project which was very handy, otherwise I’d have to develop 2 separate projects in parallel if I wanted to use both of my laptops for developing (one’s far more portable than the other but has to be Reach). So with a lot of help from this cheat sheet I got my project running again, and tried to learn a bit about shaders to see if I could get GPU noise back on shaders less than version 3_0_0.

In the process, I used a ping-pong technique to create Conway’s Game Of Life simulation (much better in 1080p):

The next stage then was to get back to the original goal of getting the GPU to generate all the noise as I found out doing noise in software was far too slow.

The plan was to pass the vertices to the shaders, let them calculate the noise then get back a heightmap which I can use to update the vertices. One of the problems was that the vertices form a curved surface and I need to flatten it for the pixel shader to give me back a regular heightmap. As each of my patches has texture coordinates that go from 0,0 to 1,1 I can create a flat image that the pixel shader could work with by using the texture coordinate to generate the position in clip space and storing the original position –which the shader will use to get the noise- in a texture coordinate. So for example, a point at 123, 234, 345 with texture coordinate 0.5,0.5 would end up in clip space at position 0,0 (clip space goes from –1 to 1) with the position stored as a texture coordinate. There’s a problem with halfpixel offsets as Drilian describes, in the end my shaders look something like this:

struct vertexInput {
float4 position : POSITION;
float2 texcoord : TEXCOORD;
};

struct vertexOutput {
float4 hPosition : POSITION;
float3 wPosition : TEXCOORD1;
};

float texel;
vertexOutput VS_flat(vertexInput IN)
{
vertexOutput OUT;
float clipx = 2 * IN.texcoord.x - 1;
float clipy = 2 * -IN.texcoord.y + 1;
float4 clipPosition = float4(clipx - texel, clipy + texel, 0, 1);
OUT.hPosition = clipPosition;

OUT.wPosition = IN.position.xyz;

return OUT;
}

float4 PS_noise(vertexOutput IN): COLOR
{
float3 p = 0;
p = IN.wPosition;
return inoise(p)*0.5+0.5;
}



And this technique let me create fBm on a 2_0_0 pixel shader by adding each stage of fBm in separate ping-pong passes:





I used this in my original project and tried to update the vertices given the heightmap values generated by the shader. This didn’t work initially, I ended up with lots of cracks because of the way the pixel shader interpolates.



JellyEngineXna 2011-06-11 20-31-45-30



The upper left point (at tex coord 0,0) would have the correct noise value but I couldn’t get the lower right point to draw the correct noise value at tex coord 1,1. When 2 patches met they would have different height values for the same vertex. In the end I managed to get around it by rendering using a line-strip instead of a triangle-strip. This picture shows the same noise values along the edges of the patches:



JellyEngineXna 2011-06-14 10-47-12-57



So, although it looks like I’ve made almost no progress since about 3 years ago (yikes!), I have! … made almost no progress that is :) The main difference is that this planet can render at run time – all the other planets had to have some pre-rendering of some sort, here’s a little fly around at 17x17 vertices per patch so you can see popping and subdividing:





In the end… it might be better to generate all the noise every time, for the vertices and for the pixels, caching the vertex info and texture is very heavy on memory. Hmm what’ll I work on next for an update in 2013…