Simplex Noise Texture Lookups?

Started by
15 comments, last by unbird 10 years, 11 months ago

Hi, I've been really struggling to find information on how to generate a terrain using noise on the HLSL. Currently I have a terrain generated on the gpu using a couple of octaves of simplex noise but it runs a little slow. To speed it up I am trying to use a texture for the permutation table, but I cant find much information on it. First of all, I'm not sure how to fill the texture with values from 0-255, so I'm currently doing this on the cpu side and passing it in as a shader resource:


int index = 0;
int data[256];
for( int i = 0; i < 256; i++ )
{
    data = ( p[index] );
    index++;
}
 

Would this work the same way as an array filled from 0-255? Or am I doing it completely wrong?

Then in the simplex noise function in the effect file, I dont understand how to use it as the permutation table. Normally I just use:


uint ii = i & 255;
uint jj = j & 255;
uint gi0 = perm[ii+perm[jj]] % 12; 
uint gi1 = perm[ii+i1+perm[jj+j1]] % 12; 
uint gi2 = perm[ii+1+perm[jj+1]] % 12;  
 

Where perm was the array, to get the hashed gradients, but with the texture I nee to use:


PermTex.SampleLevel( SampleType, ??, 0 )

Any help would be amazing.

Advertisement
Simplex noise on the GPU. Nice.

Texture sampling uses normalized co-ordinates [0..1], in your case something like ii / 256.0 (make sure to use a float division!). You probably also want to set your sampler to point sampling and wrap, similar to the example at the bottom of the SampleLevel documentation. The texture format can be any of single channel UInt, e.g. R8_UInt - just make sure they match the type you're feeding from the application side (you're currently using int, so rather R32_SInt/UInt).

I do this by generating a simplex based texture as part of my compilation cycyle and use the texture in the game engine just like any other texture. In the shader I sample the noise texture using Wrap and just use the camera world position XZ, suitably scaled up or down depending on the density of the nosie required, as the texture UV parameter. Because Vertex Texture Fetch only supports Point sampling, you need to roll your own Wrap method. (at least this is true for XNA4 and Vector4 textures - other implementations might not have that restriction).

So I need to wrap the texture when I create it to a 512 texture? Also, I used the texture I created as the texture for the terrain to make sure it was being passed in correctly and for some reason the only value that will output some form of texture is DXGI_FORMAT_R8_TYPELESS, otherwise I just get a black screen.

So for what to use as the texture parameter, something like this?


float ii = (i & 255)/256;
float jj = (j & 255)/256;


float gi0 = PermTex.SampleLevel( SampleType, ii+ (PermTex.SampleLevel( SampleType, jj, 0 )), 0 ) ;
 

Ok, I need to qualify my earlier reply; In XNA4 Vector4 textures can only use Point Sampling in Vertex Shaders. I have no information on whether this is different for SlimDX or other frameworks, so you might be able to rely on the sampler to do the Wrap for you.

So I vary the height of the vertex like this;


// Now sample the noise texture to add some variation into the nearby tiles.
float4 noiseCoordinates = (float4)0;
noiseCoordinates.x = heightMapUV.x * (Param_HeightMapWorldSize ) * 10;
noiseCoordinates.y = heightMapUV.y * (Param_HeightMapWorldSize ) * 10; 
noiseCoordinates.z = 0.0f;
noiseCoordinates.w = 0.0f; // First mip map
		
float4 noiseHeight = tex2Dlod(VertexNoiseTextureSampler, noiseCoordinates);
worldPosition.y += (noiseHeight - 0.5f) * 2;
Please apologize for giving bad advice earlier. You mentioning a TYPELESS format made me curious. I was wrong: One cannot sample from S/UInt formats, as it seems:

[6056] D3D11: ERROR: ID3D11DeviceContext::Draw: The Shader Resource View in slot 0 of the Pixel Shader unit is using the Format (R8_UINT). This format does not support 'Sample', 'SampleLevel', 'SampleBias' or 'SampleGrad', at least one of which may being used on the Resource by the shader. This mismatch is invalid if the shader actually uses the view (e.g. it is not skipped due to shader code branching). [ EXECUTION ERROR #371: DEVICE_DRAW_RESOURCE_FORMAT_SAMPLE_UNSUPPORTED ]

So, instead:
  • use a format you can sample from, e.g. a _FLOAT type. Oh, and by float division I meant ii / 256.0. You will get bad results with ii / 256 wink.png
  • Use Load instead of Sample and wrap manually.
Always make sure your normalization/indexing is sane: texture sampling uses normalized co-ordinates, Load uses texel, i.e. integer, co-ordinates.

Concerning wrapping. Either you wrap manually (your & 255), or you let the sampler do it. No need to do both. For Load, of course, you need to wrap manually. Alternatively - something I have seen in some Perlin implementation - is to use a bigger array (and duplicate the data) so you can save you the wrapping (Is this what you meant with the 512 wide texture ?).

Hey, thanks for all your help, I'm still struggling to get this working. I'm now using DXGI_FORMAT_R16_FLOAT and that shows up when using it as a texture for the terrain so I know it has been created correctly, but when I try SampleLevel or Load, no matter what a pass in as co-ordinates it still outputs the terrain as if gi0,2 and 3 where equal to zero. I've made the texture 512 wide, and put divided ii by 256.0 and still nothing. I also tried converting the texture to a 1D texture instead and still nothing. Any ideas?

Not particularly. Create your device with D3D11_CREATE_DEVICE_DEBUG and watch the debug output whether something is failing. Also show your shader, setup and render call codes maybe we spot something.

Ok, heres my the code to set up the texture:


static int p[] = {151,160,137...131};
int index = 0;
float data[512];
float data2[256];

for( int i = 0; i < 256; i++ )
{
   data2 = ( p[index] );
   index++;
}
for(int i=0;i<256;i++)
{
data = data2;
}
for(int i=0;i<256;i++)
{
data[i+256] = data2;
}


ID3D11Texture1D* PermTexture = NULL; 
ID3D11ShaderResourceView* PermTextureSRV = NULL;
D3D11_TEXTURE1D_DESC desc;
desc.Width = 512;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format =  DXGI_FORMAT_R16_FLOAT; 
desc.Usage = D3D11_USAGE_IMMUTABLE;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA dataDesc;
dataDesc.pSysMem = data;
dataDesc.SysMemPitch = 512;
device->CreateTexture1D( &desc, &dataDesc, &PermTexture );

D3D11_SHADER_RESOURCE_VIEW_DESC descSRV;
ZeroMemory( &descSRV, sizeof( descSRV ) );
descSRV.Format = DXGI_FORMAT_R16_FLOAT;
descSRV.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;
descSRV.Texture1D.MipLevels = 1;
descSRV.Texture1D.MostDetailedMip = 0;
device->CreateShaderResourceView( PermTexture, &descSRV, &PermTextureSRV );

deviceContext->PSSetShaderResources(0, 1, &PermTextureSRV); 

And then in the shader I have


Texture1D<float>   tex : register(t0); 


SamplerState SampleType
{
    Filter = MIN_MAG_MIP_POINT;
    AddressU = Wrap;
    AddressV = Wrap;
};


float test = tried lots of values here;


float gi0 = tex.SampleLevel( SampleType, test, 0 ) ;
 

R16_FLOAT is a half-float, but you're initualizing the texture with floats, which would be R32_FLOAT.

This topic is closed to new replies.

Advertisement