Sign in to follow this  
Magmatwister

GPU Perlin Noise

Recommended Posts

I've been struggling with this for too long now, I just can't get it working. Whenever I try and pass negative coordinates to the fBM function, I get a load of random, unsmooth noise, as shown by the below picture:

[code]
///////////////////////////////////////////////////////////////////////////////////////////////////////
//Pixel shader where lighting is calculated, as well as the appropriate texture colour for the pixel.//
///////////////////////////////////////////////////////////////////////////////////////////////////////
float HeightMapTX_PS(POSITION_UV_PS_INPUT Input) : SV_TARGET
{
float3 vPosition = ((float3(Input.UV, 0.0f) - 0.5f) * 2.0f) * 23.0f;
return fBm(vPosition, 16, 2.0, 0.5) * 0.5f + 0.5f;
}
///////////////////////////////////////////
////////////////////////////////////////////////////////////
[/code]


[url="http://img834.imageshack.us/i/badperlinnoise.jpg/"]http://img834.images...erlinnoise.jpg/[/url]






I get the following texture If I only use positive coordinates:


[code]

///////////////////////////////////////////////////////////////////////////////////////////////////////
//Pixel shader where lighting is calculated, as well as the appropriate texture colour for the pixel.//
///////////////////////////////////////////////////////////////////////////////////////////////////////
float HeightMapTX_PS(POSITION_UV_PS_INPUT Input) : SV_TARGET
{
float3 vPosition = float3(Input.UV, 0.0f) * 46.0f;
return fBm(vPosition, 16, 2.0, 0.5) * 0.5f + 0.5f;
}
///////////////////////////////////////////
////////////////////////////////////////////////////////////
[/code]


[url="http://img859.imageshack.us/f/badperlinnoise2.jpg/"]http://img859.images...rlinnoise2.jpg/[/url]



I'd appreciate if anyone could help me track down what I'm doing wrong. If you notice, in the top left of both of the above images (you will have to zoom in), there is some really weird lines going through the texture. Below is the full perlin noise effect file, as well as my texture generation functions:

[code]

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// TEXTURES - IMPORTANT! you must pass these textures to
// the effect before generating any values using the improved
// noise basis function (inoise()).
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Texture2D permTexture2d : register(t0);
Texture2D permGradTexture : register(t1);

SamplerState permSampler2d
{
AddressU = Wrap;
AddressV = Wrap;
MAGFILTER = POINT;
MINFILTER = POINT;
MIPFILTER = NONE;
};

SamplerState permGradSampler
{
AddressU = Wrap;
AddressV = Wrap;
MAGFILTER = POINT;
MINFILTER = POINT;
MIPFILTER = NONE;
};

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// FUNCTIONS
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
float3 fade(float3 t)
{
return t * t * t * (t * (t * 6 - 15) + 10); // new curve
}

float4 perm2d(float2 p)
{
return permTexture2d.Sample(permSampler2d, p);
}

float gradperm(float x, float3 p)
{
return dot(permGradTexture.Sample(permGradSampler, x), p);
}

// Improved 3d noise basis function
float inoise(float3 p)
{
float3 P = fmod(floor(p), 256.0); // FIND UNIT CUBE THAT CONTAINS POINT
p -= floor(p); // FIND RELATIVE X,Y,Z OF POINT IN CUBE.
float3 f = fade(p); // COMPUTE FADE CURVES FOR EACH OF X,Y,Z.

P = P / 256.0;

// HASH COORDINATES OF THE 8 CUBE CORNERS
float4 AA = perm2d(P.xy) + P.z;

// AND ADD BLENDED RESULTS FROM 8 CORNERS OF CUBE
return lerp( lerp( lerp( gradperm(AA.x, p ),
gradperm(AA.z, p + float3(-1, 0, 0) ), f.x),
lerp( gradperm(AA.y, p + float3(0, -1, 0) ),
gradperm(AA.w, p + float3(-1, -1, 0) ), f.x), f.y),

lerp( lerp( gradperm(AA.x+(1.0 / 256.0), p + float3(0, 0, -1) ),
gradperm(AA.z+(1.0 / 256.0), p + float3(-1, 0, -1) ), f.x),
lerp( gradperm(AA.y+(1.0 / 256.0), p + float3(0, -1, -1) ),
gradperm(AA.w+(1.0 / 256.0), p + float3(-1, -1, -1) ), f.x), f.y), f.z);
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// FRACTAL FUNCTIONS
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// fractal sum
float fBm(float3 p, int octaves, float lacunarity = 2.0, float gain = 0.5)
{
float freq = 1.0f,
amp = 0.5f;
float sum = 0.0f;
for(int i=0; i < octaves; i++) {
sum += inoise(p*freq)*amp;
freq *= lacunarity;
amp *= gain;
}
return sum;
}

float turbulence(float3 p, int octaves, float lacunarity = 2.0, float gain = 0.5)
{
float sum = 0;
float freq = 1.0, amp = 1.0;
for(int i=0; i < octaves; i++) {
sum += abs(inoise(p*freq))*amp;
freq *= lacunarity;
amp *= gain;
}
return sum;
}

// Ridged multifractal
// See "Texturing & Modeling, A Procedural Approach", Chapter 12
float ridge(float h, float offset)
{
h = abs(h);
h = offset - h;
h = h * h;
return h;
}

float ridgedmf(float3 p, int octaves, float lacunarity, float gain = 0.05, float offset = 1.0)
{
float sum = 0;
float freq = 1.0;
float amp = 0.5;
float prev = 1.0;
for(int i=0; i < octaves; i++)
{
float n = ridge(inoise(p*freq), offset);
sum += n*amp*prev;
prev = n;
freq *= lacunarity;
amp *= gain;
}
return sum;
}
[/code]




[code]

#include "PerlinNoiseShader.h"
#include "CommonFunctions.h"
#include "ErrorHandling.h"



UINT PerlinNoiseShader::Permutation[256] =
{
151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};



PerlinNoiseShader::PerlinNoiseShader()
{
mHR = S_OK;
//mPermutation1D = NULL;
mPermutation2D = NULL;
mGradient1D = NULL;
}





HRESULT PerlinNoiseShader::CreatePermutationTextures(ID3D11Device * pDevice)
{
D3D11_TEXTURE2D_DESC txDesc2D;
ZeroMemory(&txDesc2D, sizeof(D3D11_TEXTURE2D_DESC));
txDesc2D.Width = txDesc2D.Height = 256;
txDesc2D.MipLevels = 1;
txDesc2D.ArraySize = 1;
txDesc2D.SampleDesc.Count = 1;
txDesc2D.SampleDesc.Quality = 0;
txDesc2D.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
txDesc2D.Usage = D3D11_USAGE_IMMUTABLE;
txDesc2D.BindFlags = D3D11_BIND_SHADER_RESOURCE;
txDesc2D.CPUAccessFlags = 0;

XMFLOAT4 * PermutationTX2D = new XMFLOAT4[256*256];

for (int x = 0; x < 256; x++)
{
for(int y = 0; y < 256; y++)
{
int A = Permutation2DSample(x) + y;
int AA = Permutation2DSample(A);
int AB = Permutation2DSample(A + 1);
int B = Permutation2DSample(x + 1) + y;
int BA = Permutation2DSample(B);
int BB = Permutation2DSample(B + 1);
PermutationTX2D[x + (y * 256)] = XMFLOAT4(float(AA / 255.0f), float(AB / 255.0f), float(BA / 255.0f), float(BB / 255.0f));
}
}

D3D11_SUBRESOURCE_DATA InitialData;
ZeroMemory(&InitialData, sizeof(D3D11_SUBRESOURCE_DATA));
InitialData.pSysMem = PermutationTX2D;
InitialData.SysMemPitch = 256 * sizeof(XMFLOAT4);
InitialData.SysMemSlicePitch = 0;


if(SUCCEEDED(mHR))
pDevice->CreateTexture2D(&txDesc2D, &InitialData, &mPermutation2D);

delete[] PermutationTX2D;

if(SUCCEEDED(mHR))
mHR = pDevice->CreateShaderResourceView(mPermutation2D, NULL, &mPermutationView);

return mHR;
}















HRESULT PerlinNoiseShader::CreateGradientTextures(ID3D11Device * pDevice)
{
float Gradients3D[16][3] =
{
{1,1,0},
{-1,1,0},
{1,-1,0},
{-1,-1,0},
{1,0,1},
{-1,0,1},
{1,0,-1},
{-1,0,-1},
{0,1,1},
{0,-1,1},
{0,1,-1},
{0,-1,-1},
{1,1,0},
{0,-1,1},
{-1,1,0},
{0,-1,-1}
};


D3D11_TEXTURE1D_DESC txDesc1D;
ZeroMemory(&txDesc1D, sizeof(D3D11_TEXTURE1D_DESC));
txDesc1D.Width = 256;
txDesc1D.MipLevels = 1;
txDesc1D.ArraySize = 1;
txDesc1D.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
txDesc1D.Usage = D3D11_USAGE_IMMUTABLE;
txDesc1D.BindFlags = D3D11_BIND_SHADER_RESOURCE;
txDesc1D.CPUAccessFlags = 0;

XMFLOAT4 * GradientsInit3D = new XMFLOAT4[256];

for(int x = 0; x < 256; x++)
{
XMVECTOR Vec = XMVectorSet(Gradients3D[Permutation[x] % 16][0],
Gradients3D[Permutation[x] % 16][1],
Gradients3D[Permutation[x] % 16][2],
1.0f);

XMStoreFloat4(&GradientsInit3D[x], Vec);
}

D3D11_SUBRESOURCE_DATA InitialData;
ZeroMemory(&InitialData, sizeof(D3D11_SUBRESOURCE_DATA));
InitialData.pSysMem = GradientsInit3D;
InitialData.SysMemPitch = 256 * sizeof(XMFLOAT4);
InitialData.SysMemSlicePitch = 0;

mHR = pDevice->CreateTexture1D(&txDesc1D, &InitialData, &mGradient1D);

delete[] GradientsInit3D;


if(SUCCEEDED(mHR))
mHR = pDevice->CreateShaderResourceView(mGradient1D, NULL, &mGradientView);

return mHR;
}








HRESULT PerlinNoiseShader::InitialiseShader(ID3D11Device * pDevice)
{
mHR = CreateGradientTextures(pDevice);

if(SUCCEEDED(mHR))
mHR = CreatePermutationTextures(pDevice);

if(FAILED(mHR))
{
GeneralErrorHandler::OutputDxError(mHR, "PerlinNoiseShader::Initialise", NULL);
GeneralErrorHandler::LogErrorToFile("PerlinNoiseShader::Initialise", "Failed to create noise textures", "", "ErrorLog.txt");
}

return mHR;
}







void PerlinNoiseShader::SetBuffers(ID3D11DeviceContext * pDeviceContext)
{
pDeviceContext->PSSetShaderResources(0, 1, &mPermutationView);
pDeviceContext->PSSetShaderResources(1, 1, &mGradientView);
}
[/code]

Share this post


Link to post
Share on other sites
(see my old thread on [url="http://www.gamedev.net/topic/534306-simplex-noise-artefacts/"]this topic[/url])

I am not near my dev machine right now, so I can't double check against my own shader code. However, the gist of it is that the modulo operator (i.e. fmod() in HLSL) is sign preserving, and you don't want to be using negative offsets into the permutation table - this is what causes the artefacts.

Share this post


Link to post
Share on other sites
I believe that Ken Perlin (of Perlin Noise fame) had written a new algorithm designed for generating noise more efficiently on the GPU, and with similar results to Perlin Noise, called Simplex Noise. You might want to look into using that instead if you're having problems with Perlin Noise, since it's more geared for GPU generation.

Share this post


Link to post
Share on other sites
[quote name='DragonGeo2' timestamp='1302555305' post='4797266']
I believe that Ken Perlin (of Perlin Noise fame) had written a new algorithm designed for generating noise more efficiently on the GPU, and with similar results to Perlin Noise, called Simplex Noise. You might want to look into using that instead if you're having problems with Perlin Noise, since it's more geared for GPU generation.[/quote]
Simplex noise suffers from the same issues with regards to the sign-retaining modulo operator. Although, one can implement it on the GPU to use the built-in texture wrapping modes instead of a modulo, in which case the problem obviously disappears...

Share this post


Link to post
Share on other sites
[quote name='swiftcoder' timestamp='1302580292' post='4797372']
[quote name='DragonGeo2' timestamp='1302555305' post='4797266']
I believe that Ken Perlin (of Perlin Noise fame) had written a new algorithm designed for generating noise more efficiently on the GPU, and with similar results to Perlin Noise, called Simplex Noise. You might want to look into using that instead if you're having problems with Perlin Noise, since it's more geared for GPU generation.[/quote]
Simplex noise suffers from the same issues with regards to the sign-retaining modulo operator. Although, one can implement it on the GPU to use the built-in texture wrapping modes instead of a modulo, in which case the problem obviously disappears...
[/quote]

Hi again, I now have access to my dev pc and unfortunately, your fix has improved the problem, but not entirely fixed it, though It may be that I am missing something terribly obvious :P
As before, only one quadrant is perfect and it is at its worst when both coordinates are negative. My project is really at a standstill because of this but I appreciate that you may not be able/want to assist me. Thanks for the help so far anyway. Here is the updated code:

[code]

// Improved 3d noise basis function
float inoise(float3 p)
{
float3 P = fmod(floor(p), 256.0); // FIND UNIT CUBE THAT CONTAINS POINT
P.x = (P.x < 0) ? -P.x : P.x;
P.y = (P.y < 0) ? -P.y : P.y;
P.z = (P.z < 0) ? -P.z : P.z;

p -= floor(p); // FIND RELATIVE X,Y,Z OF POINT IN CUBE.
float3 f = fade(p); // COMPUTE FADE CURVES FOR EACH OF X,Y,Z.


P = P / 256.0;

// HASH COORDINATES OF THE 8 CUBE CORNERS
float4 AA = perm2d(P.xy) + P.z;

// AND ADD BLENDED RESULTS FROM 8 CORNERS OF CUBE
return lerp( lerp( lerp( gradperm(AA.x, p ),
gradperm(AA.z, p + float3(-1, 0, 0) ), f.x),
lerp( gradperm(AA.y, p + float3(0, -1, 0) ),
gradperm(AA.w, p + float3(-1, -1, 0) ), f.x), f.y),

lerp( lerp( gradperm(AA.x+(1.0 / 256.0), p + float3(0, 0, -1) ),
gradperm(AA.z+(1.0 / 256.0), p + float3(-1, 0, -1) ), f.x),
lerp( gradperm(AA.y+(1.0 / 256.0), p + float3(0, -1, -1) ),
gradperm(AA.w+(1.0 / 256.0), p + float3(-1, -1, -1) ), f.x), f.y), f.z);
}
[/code]


[url="http://imageshack.us/photo/my-images/854/badperlinnoise3.png/"]http://imageshack.us...rlinnoise3.png/[/url]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this