Jump to content

  • Log In with Google      Sign In   
  • Create Account

Interested in a FREE copy of HTML5 game maker Construct 2?

We'll be giving away three Personal Edition licences in next Tuesday's GDNet Direct email newsletter!

Sign up from the right-hand sidebar on our homepage and read Tuesday's newsletter for details!


Simplex Noise Texture Lookups?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
16 replies to this topic

#1 gigaworm   Members   -  Reputation: 122

Like
0Likes
Like

Posted 11 April 2013 - 12:51 PM

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[i] = ( 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.


Edited by gigaworm, 11 April 2013 - 12:53 PM.


Sponsor:

#2 unbird   Crossbones+   -  Reputation: 5499

Like
0Likes
Like

Posted 12 April 2013 - 08:22 AM

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

#3 PhillipHamlyn   Members   -  Reputation: 454

Like
1Likes
Like

Posted 13 April 2013 - 05:56 AM

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


Edited by PhillipHamlyn, 13 April 2013 - 06:03 AM.


#4 gigaworm   Members   -  Reputation: 122

Like
0Likes
Like

Posted 13 April 2013 - 09:34 AM

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

 


Edited by gigaworm, 13 April 2013 - 09:35 AM.


#5 PhillipHamlyn   Members   -  Reputation: 454

Like
1Likes
Like

Posted 14 April 2013 - 11:07 AM

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;


#6 unbird   Crossbones+   -  Reputation: 5499

Like
0Likes
Like

Posted 15 April 2013 - 01:38 PM

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

Edited by unbird, 15 April 2013 - 01:40 PM.


#7 gigaworm   Members   -  Reputation: 122

Like
0Likes
Like

Posted 20 April 2013 - 06:43 AM

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?



#8 unbird   Crossbones+   -  Reputation: 5499

Like
0Likes
Like

Posted 20 April 2013 - 11:33 AM

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.

#9 gigaworm   Members   -  Reputation: 122

Like
0Likes
Like

Posted 21 April 2013 - 05:35 PM

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[i] = ( p[index] );
   index++;
}
for(int i=0;i<256;i++)
{
data[i] = data2[i];
}
for(int i=0;i<256;i++)
{
data[i+256] = data2[i];
}


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

 

 



#10 unbird   Crossbones+   -  Reputation: 5499

Like
0Likes
Like

Posted 22 April 2013 - 03:47 AM

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



#11 gigaworm   Members   -  Reputation: 122

Like
0Likes
Like

Posted 22 April 2013 - 07:44 PM

I tried that before but I get no output using it as a texture, and it doesnt change the terrain either :(



#12 Scoob Droolins   Members   -  Reputation: 238

Like
1Likes
Like

Posted 24 April 2013 - 12:45 PM

If you're interested in a non-texture method for Perlin simplex noize, I found this code here: https://github.com/ashima/webgl-noise/wiki.  I've used it in a pixel shader to generate really nice fine-grained noise when using screen X,Y as a parameter to the method.

 

float3 permute(float3 x) { return fmod(((x*34.0)+1.0)*x, 289.0); }

// Perlin simplex noise
float snoise(float2 v)
{
  const float4 C = float4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
  float2 i  = floor(v + dot(v, C.yy) );
  float2 x0 = v -   i + dot(i, C.xx);
  float2 i1;
  i1 = (x0.x > x0.y) ? float2(1.0, 0.0) : float2(0.0, 1.0);
//  i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
//  i1.y = 1.0 - i1.x;
  float4 x12 = x0.xyxy + C.xxzz;
  x12.xy -= i1;
  i = fmod(i, 289.0);
  float3 p = permute( permute( i.y + float3(0.0, i1.y, 1.0 )) + i.x + float3(0.0, i1.x, 1.0 ));
  float3 m = max(0.5 - float3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
  m = m*m ;
  m = m*m ;
  float3 x = 2.0 * frac(p * C.www) - 1.0;
  float3 h = abs(x) - 0.5;
  float3 ox = floor(x + 0.5);
  float3 a0 = x - ox;
  m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
  float3 g;
  g.x  = a0.x  * x0.x  + h.x  * x0.y;
  g.yz = a0.yz * x12.xz + h.yz * x12.yw;
  return 130.0 * dot(m, g);
}

 

 



#13 unbird   Crossbones+   -  Reputation: 5499

Like
0Likes
Like

Posted 27 April 2013 - 06:43 AM

SampleLevel ? Wait, are you sampling in the vertex shader ? Then you need to set the texture to this stage:

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

Also: Check with PIX/Graphics Debugger if your setup is correct (textures bound, texture data, render target and viewport correctly set, etc.)

@Scoob Droolins : Wow, Permute with ALU instructions alone. I'm surprised. I once tried that - and failed. Produced quite some noticable non-randomness. Thanks for sharing.

#14 gigaworm   Members   -  Reputation: 122

Like
0Likes
Like

Posted 27 April 2013 - 02:56 PM

Ah of course! its now changing the terrain which is good. I'm still trying to figure out what to pass in as co-ordinates though and for some reason when I use for example 0 which should point to 151, the array for the gradient doesn't get an out of bounds error which it should if the value is not between 0-11. I thought that maybe the value was just a fraction of that value or something so I tried adding 100 to gi0 after the look up so it would break out but it didnt, but it does if I set it to 100, whats going on?



#15 unbird   Crossbones+   -  Reputation: 5499

Like
0Likes
Like

Posted 27 April 2013 - 04:04 PM

Now you lost me, I can't quite follow. What array ? You now (should) have a texture which wraps, you won't get an "out of bounds" here. Again: Texture co-ordinates are normalized (0..1). Then again: With a proper perlin noise in place you can feed it any scale (aka frequency) you like. That's the whole beauty of perlin noise.
 
Can you post your (complete) shader code and what you feed it (your terrain mesh) ?
 
PS: Maybe playing with perlin alone, i.e. decoupled from your terrain stuff and generate some textures first (this time in the pixel shader) would be a good idea. It's also easier to check if it actually produces correct results.

Edit: Sorry, now I get it (was too late yesterday and reading more carefully helps tongue.png). So you still have an array for the gradients. Not sure what the shader produces with out of bounds, but it probably won't wrap automatically. Static arrays may get translated to a sequence of "ifs" (in assembly movc IIRC). So wrap manually or use a texture for the gradient too.

By the way: It "breaks" with a constant value of 100, since then the compiler can check that at compile time. Well, that's just my guess without seeing code.

#16 gigaworm   Members   -  Reputation: 122

Like
0Likes
Like

Posted 28 April 2013 - 03:19 PM

Yeah the array is for the gradient but it only has 12 values in it, so if you tried passing 13 for example, it wouldn't work and I pass the values that are obtained through the texture lookup into it. 

 

I've been messing about with it some more and I've used some if statements to try and debug it a little and find out what values are getting returned from the texture. A few things I've noticed are that if I make a texture of 10 width using:

 

float data[10] = {10,10,10,10,10,10,10,10,10,10};

 

and set it up the same way as I posted before with R32 Floats then some interesting things happen. If I sample to the first point in the texture using 0.1 as the co-ord and change the 10 to other numbers to see what value I'm getting then using 1 returns ~5.5 instead of 1 and using 4 returns exactly 7 instead of 4. Any idea why this is?



#17 unbird   Crossbones+   -  Reputation: 5499

Like
0Likes
Like

Posted 28 April 2013 - 05:11 PM

Sounds like linear sampling instead of point. And 0.1 is actually the second texel - or rather somewhere close, since 0.1 cannot be exactly represented with floats. The first texel is at 0.0.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS