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

Joined:
Jul 29, 2017
Posts:
2
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.

Code (GLSL):
1.
2. //
3. // 2-D tiling simplex noise with fixed gradients,
4. // without the analytical derivative.
5. // This function is implemented as a wrapper to "psrnoise",
6. // at the minimal cost of three extra additions.
7. //
8. float psnoise(vec2 pos, vec2 per) {
9.   return psrnoise(pos, per, 0.0);
10. }
11.
12.
13. // Modulo 289, optimizes to code without divisions
14. vec3 mod289(vec3 x) {
15.   return x - floor(x * (1.0 / 289.0)) * 289.0;
16. }
17.
18. float mod289(float x) {
19.   return x - floor(x * (1.0 / 289.0)) * 289.0;
20. }
21.
22. // Permutation polynomial (ring size 289 = 17*17)
23. vec3 permute(vec3 x) {
24.   return mod289(((x*34.0)+1.0)*x);
25. }
26.
27. float permute(float x) {
28.   return mod289(((x*34.0)+1.0)*x);
29. }
30.
31. // Hashed 2-D gradients with an extra rotation.
32. // (The constant 0.0243902439 is 1/41)
33. vec2 rgrad2(vec2 p, float rot) {
34. #if 0
35. // Map from a line to a diamond such that a shift maps to a rotation.
36.   float u = permute(permute(p.x) + p.y) * 0.0243902439 + rot; // Rotate by shift
37.   u = 4.0 * fract(u) - 2.0;
38.   // (This vector could be normalized, exactly or approximately.)
39.   return vec2(abs(u)-1.0, abs(abs(u+1.0)-2.0)-1.0);
40. #else
42.   float u = permute(permute(p.x) + p.y) * 0.0243902439 + rot; // Rotate by shift
43.   u = fract(u) * 6.28318530718; // 2*pi
44.   return vec2(cos(u), sin(u));
45. #endif
46. }
47.
48.
49. //
50. // 2-D tiling simplex noise with rotating gradients,
51. // but without the analytical derivative.
52. //
53. float psrnoise(vec2 pos, vec2 per, float rot) {
54.   // Offset y slightly to hide some rare artifacts
55.   pos.y += 0.001;
56.   // Skew to hexagonal grid
57.   vec2 uv = vec2(pos.x + pos.y*0.5, pos.y);
58.
59.   vec2 i0 = floor(uv);
60.   vec2 f0 = fract(uv);
61.   // Traversal order
62.   vec2 i1 = (f0.x > f0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
63.
64.   // Unskewed grid points in (x,y) space
65.   vec2 p0 = vec2(i0.x - i0.y * 0.5, i0.y);
66.   vec2 p1 = vec2(p0.x + i1.x - i1.y * 0.5, p0.y + i1.y);
67.   vec2 p2 = vec2(p0.x + 0.5, p0.y + 1.0);
68.
69.   // Integer grid point indices in (u,v) space
70.   i1 = i0 + i1;
71.   vec2 i2 = i0 + vec2(1.0, 1.0);
72.
73.   // Vectors in unskewed (x,y) coordinates from
74.   // each of the simplex corners to the evaluation point
75.   vec2 d0 = pos - p0;
76.   vec2 d1 = pos - p1;
77.   vec2 d2 = pos - p2;
78.
79.   // Wrap i0, i1 and i2 to the desired period before gradient hashing:
80.   // wrap points in (x,y), map to (u,v)
81.   vec3 xw = mod(vec3(p0.x, p1.x, p2.x), per.x);
82.   vec3 yw = mod(vec3(p0.y, p1.y, p2.y), per.y);
83.   vec3 iuw = xw + 0.5 * yw;
84.   vec3 ivw = yw;
85.
86.   // Create gradients from indices
87.   vec2 g0 = rgrad2(vec2(iuw.x, ivw.x), rot);
88.   vec2 g1 = rgrad2(vec2(iuw.y, ivw.y), rot);
89.   vec2 g2 = rgrad2(vec2(iuw.z, ivw.z), rot);
90.
91.   // Gradients dot vectors to corresponding corners
92.   // (The derivatives of this are simply the gradients)
93.   vec3 w = vec3(dot(g0, d0), dot(g1, d1), dot(g2, d2));
94.
95.   // Radial weights from corners
96.   // 0.8 is the square of 2/sqrt(5), the distance from
97.   // a grid point to the nearest simplex boundary
98.   vec3 t = 0.8 - vec3(dot(d0, d0), dot(d1, d1), dot(d2, d2));
99.
100.   // Set influence of each surflet to zero outside radius sqrt(0.8)
101.   t = max(t, 0.0);
102.
103.   // Fourth power of t
104.   vec3 t2 = t * t;
105.   vec3 t4 = t2 * t2;
106.
107.   // Final noise value is:
108.   // sum of ((radial weights) times (gradient dot vector from corner))
109.   float n = dot(t4, w);
110.
111.   // Rescale to cover the range [-1,1] reasonably well
112.   return 11.0*n;
113. }
114.
Code (HLSL):
1.
2. //
3. // 2-D tiling simplex noise with fixed gradients,
4. // without the analytical derivative.
5. // This function is implemented as a wrapper to "psrnoise",
6. // at the minimal cost of three extra additions.
7. //
8. float psnoise(float2 pos, float2 per) {
9.   return psrnoise(pos, per, 0.0);
10. }
11.
12.
13. // GLSL mod
14. float modx(float3 x, float y) {
15.   return x - floor(x * (1.0 / y)) * y;
16. }
17. float modx(float x, float y) {
18.   return x - floor(x * (1.0 / y)) * y;
19. }
20.
21. // Modulo 289, optimizes to code without divisions
22. float3 mod289(float3 x) {
23.   return x - floor(x * (1.0 / 289.0)) * 289.0;
24. }
25. float mod289(float x) {
26.   return x - floor(x * (1.0 / 289.0)) * 289.0;
27. }
28.
29. // Permutation polynomial (ring size 289 = 17*17)
30. float3 permute(float3 x) {
31.   return mod289(((x*34.0)+1.0)*x);
32. }
33. float permute(float x) {
34.   return mod289(((x*34.0)+1.0)*x);
35. }
36.
37. // Hashed 2-D gradients with an extra rotation.
38. // (The constant 0.0243902439 is 1/41)
39. float2 rgrad2(float2 p, float rot) {
40. #if 0
41. // Map from a line to a diamond such that a shift maps to a rotation.
42.   float u = permute(permute(p.x) + p.y) * 0.0243902439 + rot; // Rotate by shift
43.   u = 4.0 * frac(u) - 2.0;
44.   // (This vector could be normalized, exactly or approximately.)
45.   return float2(abs(u)-1.0, abs(abs(u+1.0)-2.0)-1.0);
46. #else
48.   float u = permute(permute(p.x) + p.y) * 0.0243902439 + rot; // Rotate by shift
49.   u = frac(u) * 6.28318530718; // 2*pi
50.   return float2(cos(u), sin(u));
51. #endif
52. }
53.
54.
55. //
56. // 2-D tiling simplex noise with rotating gradients,
57. // but without the analytical derivative.
58. //
59. float psrnoise(float2 pos, float2 per, float rot) {
60.   // Offset y slightly to hide some rare artifacts
61.   pos.y += 0.001;
62.   // Skew to hexagonal grid
63.   float2 uv = float2(pos.x + pos.y*0.5, pos.y);
64.
65.   float2 i0 = floor(uv);
66.   float2 f0 = frac(uv);
67.   // Traversal order
68.   float2 i1 = (f0.x > f0.y) ? float2(1.0, 0.0) : float2(0.0, 1.0);
69.
70.   // Unskewed grid points in (x,y) space
71.   float2 p0 = float2(i0.x - i0.y * 0.5, i0.y);
72.   float2 p1 = float2(p0.x + i1.x - i1.y * 0.5, p0.y + i1.y);
73.   float2 p2 = float2(p0.x + 0.5, p0.y + 1.0);
74.
75.   // Integer grid point indices in (u,v) space
76.   i1 = i0 + i1;
77.   float2 i2 = i0 + float2(1.0, 1.0);
78.
79.   // Vectors in unskewed (x,y) coordinates from
80.   // each of the simplex corners to the evaluation point
81.   float2 d0 = pos - p0;
82.   float2 d1 = pos - p1;
83.   float2 d2 = pos - p2;
84.
85.   // Wrap i0, i1 and i2 to the desired period before gradient hashing:
86.   // wrap points in (x,y), map to (u,v)
87.   float3 xw = modx(float3(p0.x, p1.x, p2.x), per.x);
88.   float3 yw = modx(float3(p0.y, p1.y, p2.y), per.y);
89.   float3 iuw = xw + 0.5 * yw;
90.   float3 ivw = yw;
91.
92.   // Create gradients from indices
93.   float2 g0 = rgrad2(float2(iuw.x, ivw.x), rot);
94.   float2 g1 = rgrad2(float2(iuw.y, ivw.y), rot);
95.   float2 g2 = rgrad2(float2(iuw.z, ivw.z), rot);
96.
97.   // Gradients dot vectors to corresponding corners
98.   // (The derivatives of this are simply the gradients)
99.   float3 w = float3(dot(g0, d0), dot(g1, d1), dot(g2, d2));
100.
101.   // Radial weights from corners
102.   // 0.8 is the square of 2/sqrt(5), the distance from
103.   // a grid point to the nearest simplex boundary
104.   float3 t = 0.8 - float3(dot(d0, d0), dot(d1, d1), dot(d2, d2));
105.
106.   // Set influence of each surflet to zero outside radius sqrt(0.8)
107.   t = max(t, 0.0);
108.
109.   // Fourth power of t
110.   float3 t2 = t * t;
111.   float3 t4 = t2 * t2;
112.
113.   // Final noise value is:
114.   // sum of ((radial weights) times (gradient dot vector from corner))
115.   float n = dot(t4, w);
116.
117.   // Rescale to cover the range [-1,1] reasonably well
118.   return 11.0*n;
119. }
120.
121.

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

Joined:
Jul 29, 2017
Posts:
2
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).

Last edited: Jul 29, 2017
3. ### bgolus

Joined:
Dec 7, 2012
Posts:
12,265
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:

Code (csharp):
1.  mod(1.5, 1) == 0.5    //  glsl mod +, + = +
2. fmod(1.5, 1) == 0.5    // hlsl fmod +, + = +
3.
4.  mod(-1.5, -1) == -0.5 //  glsl mod -, - = -
5. fmod(-1.5, -1) == -0.5 // hlsl fmod -, - = -
6.
7.  mod(-1.5, 1) == 0.5   //  glsl mod -, + = +
8. fmod(-1.5, 1) == -0.5  // hlsl fmod -, + = -
9.
10.  mod(1.5, -1) == -0.5  //  glsl mod +, - = -
11. fmod(1.5, -1) == 0.5   // hlsl fmod +, - = +
12.
13. // and the real kicker
14.  mod(-1.25, 1) == 0.75
15. 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))

4. ### Pacosmico

Joined:
Jan 16, 2014
Posts:
14
@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!

domportera likes this.
5. ### domportera

Joined:
Sep 12, 2013
Posts:
23
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