GPU Perlin Noise

Started by
3 comments, last by Magmatwister 12 years, 10 months ago
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:


///////////////////////////////////////////////////////////////////////////////////////////////////////
//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;
}
///////////////////////////////////////////
////////////////////////////////////////////////////////////



http://img834.images...erlinnoise.jpg/






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




///////////////////////////////////////////////////////////////////////////////////////////////////////
//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;
}
///////////////////////////////////////////
////////////////////////////////////////////////////////////



http://img859.images...rlinnoise2.jpg/



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:



//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 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;
}







#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);
}
Currently trying to make a planet renderer. After many hours of work, somehow I know It'll never be complete.
Also, If I help you, please give me an ++
Advertisement
(see my old thread on this topic)

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.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

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.

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.

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...

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]


[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.

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:



// 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);
}



http://imageshack.us...rlinnoise3.png/
Currently trying to make a planet renderer. After many hours of work, somehow I know It'll never be complete.
Also, If I help you, please give me an ++

This topic is closed to new replies.

Advertisement