Translating a GLSL shader (noise algorithm) to HLSL/CG

I am in the process of translating a few shader functions (for periodic simplex noise) written for WebGL to HLSL.
After taking care of the usual syntax differences the HLSL version compiles, but parts of the result are flipped around - as seen here, left is the original:

The relevant portions of the code (original and translation) are below, for the output above I just used a dummy shader program with the equivalent of fragmentw = psnoise(float2(input.uv.x * 6, input.uv.y * 6), float2(6, 6)) * 0.5 + 0.5; in the fragment shader part.

GLSL code

//
// 2-D tiling simplex noise with fixed gradients,
// without the analytical derivative.
// This function is implemented as a wrapper to "psrnoise",
// at the minimal cost of three extra additions.
//
float psnoise(vec2 pos, vec2 per) {
  return psrnoise(pos, per, 0.0);
}


// Modulo 289, optimizes to code without divisions
vec3 mod289(vec3 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

float mod289(float x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

// Permutation polynomial (ring size 289 = 17*17)
vec3 permute(vec3 x) {
  return mod289(((x*34.0)+1.0)*x);
}

float permute(float x) {
  return mod289(((x*34.0)+1.0)*x);
}

// Hashed 2-D gradients with an extra rotation.
// (The constant 0.0243902439 is 1/41)
vec2 rgrad2(vec2 p, float rot) {
#if 0
// Map from a line to a diamond such that a shift maps to a rotation.
  float u = permute(permute(p.x) + p.y) * 0.0243902439 + rot; // Rotate by shift
  u = 4.0 * fract(u) - 2.0;
  // (This vector could be normalized, exactly or approximately.)
  return vec2(abs(u)-1.0, abs(abs(u+1.0)-2.0)-1.0);
#else
// For more isotropic gradients, sin/cos can be used instead.
  float u = permute(permute(p.x) + p.y) * 0.0243902439 + rot; // Rotate by shift
  u = fract(u) * 6.28318530718; // 2*pi
  return vec2(cos(u), sin(u));
#endif
}


//
// 2-D tiling simplex noise with rotating gradients,
// but without the analytical derivative.
//
float psrnoise(vec2 pos, vec2 per, float rot) {
  // Offset y slightly to hide some rare artifacts
  pos.y += 0.001;
  // Skew to hexagonal grid
  vec2 uv = vec2(pos.x + pos.y*0.5, pos.y);
 
  vec2 i0 = floor(uv);
  vec2 f0 = fract(uv);
  // Traversal order
  vec2 i1 = (f0.x > f0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);

  // Unskewed grid points in (x,y) space
  vec2 p0 = vec2(i0.x - i0.y * 0.5, i0.y);
  vec2 p1 = vec2(p0.x + i1.x - i1.y * 0.5, p0.y + i1.y);
  vec2 p2 = vec2(p0.x + 0.5, p0.y + 1.0);

  // Integer grid point indices in (u,v) space
  i1 = i0 + i1;
  vec2 i2 = i0 + vec2(1.0, 1.0);

  // Vectors in unskewed (x,y) coordinates from
  // each of the simplex corners to the evaluation point
  vec2 d0 = pos - p0;
  vec2 d1 = pos - p1;
  vec2 d2 = pos - p2;

  // Wrap i0, i1 and i2 to the desired period before gradient hashing:
  // wrap points in (x,y), map to (u,v)
  vec3 xw = mod(vec3(p0.x, p1.x, p2.x), per.x);
  vec3 yw = mod(vec3(p0.y, p1.y, p2.y), per.y);
  vec3 iuw = xw + 0.5 * yw;
  vec3 ivw = yw;
 
  // Create gradients from indices
  vec2 g0 = rgrad2(vec2(iuw.x, ivw.x), rot);
  vec2 g1 = rgrad2(vec2(iuw.y, ivw.y), rot);
  vec2 g2 = rgrad2(vec2(iuw.z, ivw.z), rot);

  // Gradients dot vectors to corresponding corners
  // (The derivatives of this are simply the gradients)
  vec3 w = vec3(dot(g0, d0), dot(g1, d1), dot(g2, d2));
 
  // Radial weights from corners
  // 0.8 is the square of 2/sqrt(5), the distance from
  // a grid point to the nearest simplex boundary
  vec3 t = 0.8 - vec3(dot(d0, d0), dot(d1, d1), dot(d2, d2));

  // Set influence of each surflet to zero outside radius sqrt(0.8)
  t = max(t, 0.0);

  // Fourth power of t
  vec3 t2 = t * t;
  vec3 t4 = t2 * t2;
 
  // Final noise value is:
  // sum of ((radial weights) times (gradient dot vector from corner))
  float n = dot(t4, w);
 
  // Rescale to cover the range [-1,1] reasonably well
  return 11.0*n;
}

HLSL/CG code

//
// 2-D tiling simplex noise with fixed gradients,
// without the analytical derivative.
// This function is implemented as a wrapper to "psrnoise",
// at the minimal cost of three extra additions.
//
float psnoise(float2 pos, float2 per) {
  return psrnoise(pos, per, 0.0);
}


// GLSL mod
float modx(float3 x, float y) {
  return x - floor(x * (1.0 / y)) * y;
}
float modx(float x, float y) {
  return x - floor(x * (1.0 / y)) * y;
}

// Modulo 289, optimizes to code without divisions
float3 mod289(float3 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}
float mod289(float x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

// Permutation polynomial (ring size 289 = 17*17)
float3 permute(float3 x) {
  return mod289(((x*34.0)+1.0)*x);
}
float permute(float x) {
  return mod289(((x*34.0)+1.0)*x);
}

// Hashed 2-D gradients with an extra rotation.
// (The constant 0.0243902439 is 1/41)
float2 rgrad2(float2 p, float rot) {
#if 0
// Map from a line to a diamond such that a shift maps to a rotation.
  float u = permute(permute(p.x) + p.y) * 0.0243902439 + rot; // Rotate by shift
  u = 4.0 * frac(u) - 2.0;
  // (This vector could be normalized, exactly or approximately.)
  return float2(abs(u)-1.0, abs(abs(u+1.0)-2.0)-1.0);
#else
// For more isotropic gradients, sin/cos can be used instead.
  float u = permute(permute(p.x) + p.y) * 0.0243902439 + rot; // Rotate by shift
  u = frac(u) * 6.28318530718; // 2*pi
  return float2(cos(u), sin(u));
#endif
}


//
// 2-D tiling simplex noise with rotating gradients,
// but without the analytical derivative.
//
float psrnoise(float2 pos, float2 per, float rot) {
  // Offset y slightly to hide some rare artifacts
  pos.y += 0.001;
  // Skew to hexagonal grid
  float2 uv = float2(pos.x + pos.y*0.5, pos.y);
 
  float2 i0 = floor(uv);
  float2 f0 = frac(uv);
  // Traversal order
  float2 i1 = (f0.x > f0.y) ? float2(1.0, 0.0) : float2(0.0, 1.0);

  // Unskewed grid points in (x,y) space
  float2 p0 = float2(i0.x - i0.y * 0.5, i0.y);
  float2 p1 = float2(p0.x + i1.x - i1.y * 0.5, p0.y + i1.y);
  float2 p2 = float2(p0.x + 0.5, p0.y + 1.0);

  // Integer grid point indices in (u,v) space
  i1 = i0 + i1;
  float2 i2 = i0 + float2(1.0, 1.0);

  // Vectors in unskewed (x,y) coordinates from
  // each of the simplex corners to the evaluation point
  float2 d0 = pos - p0;
  float2 d1 = pos - p1;
  float2 d2 = pos - p2;

  // Wrap i0, i1 and i2 to the desired period before gradient hashing:
  // wrap points in (x,y), map to (u,v)
  float3 xw = modx(float3(p0.x, p1.x, p2.x), per.x);
  float3 yw = modx(float3(p0.y, p1.y, p2.y), per.y);
  float3 iuw = xw + 0.5 * yw;
  float3 ivw = yw;
 
  // Create gradients from indices
  float2 g0 = rgrad2(float2(iuw.x, ivw.x), rot);
  float2 g1 = rgrad2(float2(iuw.y, ivw.y), rot);
  float2 g2 = rgrad2(float2(iuw.z, ivw.z), rot);

  // Gradients dot vectors to corresponding corners
  // (The derivatives of this are simply the gradients)
  float3 w = float3(dot(g0, d0), dot(g1, d1), dot(g2, d2));
 
  // Radial weights from corners
  // 0.8 is the square of 2/sqrt(5), the distance from
  // a grid point to the nearest simplex boundary
  float3 t = 0.8 - float3(dot(d0, d0), dot(d1, d1), dot(d2, d2));

  // Set influence of each surflet to zero outside radius sqrt(0.8)
  t = max(t, 0.0);

  // Fourth power of t
  float3 t2 = t * t;
  float3 t4 = t2 * t2;
 
  // Final noise value is:
  // sum of ((radial weights) times (gradient dot vector from corner))
  float n = dot(t4, w);
 
  // Rescale to cover the range [-1,1] reasonably well
  return 11.0*n;
}

Can anyone see what’s causing the patterned rotation in the result of the shader?

I solved it: The “mod” replacement (modx) I wrote up simply didn’t have the correct return type - one of the variants has to return “float3” instead of “float”. HLSL seems to just accept this and does not error or warn - Some automatic type conversion seems to be happening behind the scenes.

But I also found out that simply using the “%” operator is a simple replacement for GLSL’s “mod” function, which is an even simpler solution, (but with slightly different results that make it non-applicable here).

The hlsl “equivalent” of glsl mod is fmod, which x % y is often equivalent to fmod(x, y) in hlsl (though not always!), unlike % in glsl which is specifically for integers.

However mod and fmod aren’t actually equivalent (nor are mod and hlsl %).

glsl mod(x, y) is:
x - y * floor(x/y)

hlsl fmod(x, y) is:
x - y * trunc(x/y)

The most obvious difference from this is in glsl the output sign depends on the sign of y, in hlsl the sign depends on x:

 mod(1.5, 1) == 0.5    //  glsl mod +, + = +
fmod(1.5, 1) == 0.5    // hlsl fmod +, + = +

 mod(-1.5, -1) == -0.5 //  glsl mod -, - = -
fmod(-1.5, -1) == -0.5 // hlsl fmod -, - = -

 mod(-1.5, 1) == 0.5   //  glsl mod -, + = +
fmod(-1.5, 1) == -0.5  // hlsl fmod -, + = -

 mod(1.5, -1) == -0.5  //  glsl mod +, - = -
fmod(1.5, -1) == 0.5   // hlsl fmod +, - = +

// and the real kicker
 mod(-1.25, 1) == 0.75
fmod(-1.25, 1) == -0.25

I personally like implementing functions like this using a define so I can let the compiler deal with the “return” type. This relies on the compiler not being too strict though, which might cause it to complain on mobile devices.

// glsl style mod
#define mod(x, y) (x - y * floor(x / y))

17 Likes

@bgolus and @Bunny83 , you both people keep saving my life, you should get a medal. I just keep running into your answers over and over, time after time, and they are always spot on. Unity should pay you a salary for your impact in the community. You are polite, wise, clear. Literally can’t ask for a better person to exist in a gamedev community… In any community really!

1 Like

hard agree. I been running into @bgolus left and right and he has made the world a better place for me to live in. unity, pay this man