2

I'm trying to create an animated sun in HLSL for an XNA project.

I'm generating its texture in the pixel shader by using the Perlin Noise algorithm, which I learned from this site.

This is the hlsl code I wrote for the pixel shader:

sampler myTexture;

struct VS_Output{
   float4 Position : POSITION0;
   float4 Color : COLOR0;
   float2 TextCoord : TEXCOORD0;
};

float CosineInterpolation( float x, float y, float fractional ) {
   float ft = 3.141592f * fractional;
   float f = ( 1.0f - cos( ft ) ) * 0.5f;

   return x * ( 1.0f - f ) + y * f;
}

float Noise(float2 xy)
{
    float2 noise = (frac(sin(dot(xy ,float2(12.9898,78.233)*2.0)) * 43758.5453));
    return abs(noise.x + noise.y) * 0.5;
}

float SmoothNoise( float integer_x, float integer_y ) {
   float corners = ( Noise( float2(integer_x - 1, integer_y - 1) ) + Noise( float2(integer_x + 1, integer_y + 1 )) + Noise( float2(integer_x + 1, integer_y - 1 )) + Noise( float2(integer_x - 1, integer_y + 1 )) ) / 16.0f;
   float sides = ( Noise( float2(integer_x, integer_y - 1 )) + Noise( float2(integer_x, integer_y + 1 )) + Noise( float2(integer_x + 1, integer_y )) + Noise( float2(integer_x - 1, integer_y )) ) / 8.0f;
   float center = Noise( float2(integer_x, integer_y )) / 4.0f;

   return corners + sides + center;
}

float InterpolatedNoise( float x, float y ) {
   float integer_x = x - frac(x), fractional_x = frac(x);
   float integer_y = y - frac(y), fractional_y = frac(y);

   float p1 = SmoothNoise( integer_x, integer_y );
   float p2 = SmoothNoise( integer_x + 1, integer_y );
   float p3 = SmoothNoise( integer_x, integer_y + 1 );
   float p4 = SmoothNoise( integer_x + 1, integer_y + 1 );

   p1 = CosineInterpolation( p1, p2, fractional_x );
   p2 = CosineInterpolation( p3, p4, fractional_x );

   return CosineInterpolation( p1, p2, fractional_y );
}

float CreatePerlinNoise( float x, float y ) {
    float result = 0.0f, amplitude = 0.0f, frequency = 0.0f, persistance = 0.1f;

    for ( int i = 1; i <= 4; i++ ) {
       frequency += 2;
       amplitude += persistance;

       result += InterpolatedNoise( x * frequency, y * frequency ) * amplitude;
    }

    return result;
}

float4 ps_main(VS_Output Input) : COLOR0
{  
   float index = CreatePerlinNoise(Input.TextCoord.x*256.0f, Input.TextCoord.y*256.0f);
   return tex2D(myTexture, index);
}

Basically, in this code by passing the texture coordinate component (TextCoord) to the CreatePelinNoise function, it returns a value that is used as the color index of a gradient texture (myTexture 1px x 256px):

enter image description here

And the result in AMD RenderMonkey is the following:

enter image description here

But at the poles of the sphere there is an unsightly unwanted effect which makes the generated texture non-uniform:

enter image description here

How can I solve this problem and make the generated texture uniform?

Omar
  • 16,329
  • 10
  • 48
  • 66

1 Answers1

4

This happens because you're treating the noise function as a 2D texture, which will be stretched/distorted if projected onto a sphere. You're basically generating a 2D texture in the pixel shader in real time then applying it to the sphere.

If you really understood the Perlin Noise principles, then you are able to generalize your code to more than two dimensions. Ken Perlin himself though of this exact same idea when inventing the noise algorithm; in his own words, "Dip the object into a soup of procedural texture (noise) material".

Just use the pixel coordinates as the inputs of a 3D noise function (your float4 Position variable), keeping the color mapping code intact. This should make the desired effect. If you want it to animate, by the way, generalize it to 4D and vary the 4th parameter with time. If done right, it should result in a nice "lava-planet" effect (look up Perlin's slides for more info).

Oh, and a speedup tip: Instead of using cossine interpolation, use the famous 3*t^2-2*t^3 interpolation curve. Between 0 and 1, it looks almost identical to a harmonic wave but costing way less computationally.

MVittiS
  • 434
  • 3
  • 13
  • Indeed, I already applied the perlin noise combined with the marching cubes algorithm and I achieved a great effect with my terrain. But because of some limitations of XNA, it's actually hard implement this on the GPU. This is why I just wanted to create a simple 2D texture. Although you deserve an upvote for the useful suggestions. – Omar Jan 02 '13 at 00:26
  • I appreciate the upvote. But for the terrain, it isn't just simpler to tesselate using the 2D noise you just described in the pixel shader, instead running on the vertex shader? For me, marching cubes seems to be an overkill if you just want to generate some landscapes. Maybe XNA/Direct3D includes some overhead that discourages this approach; from my background in OpenGL, I know it almost certainly would be faster. – MVittiS Jan 02 '13 at 01:39
  • (the Vertex Shader usage would be faster, not OpenGL itself) – MVittiS Jan 02 '13 at 04:13