# ShaderToy (GLSL) porting to HLSL

Discussion in 'Shaders' started by angelomoro, Oct 24, 2019.

1. ### angelomoro

Joined:
Sep 4, 2018
Posts:
13
Hello, I'm trying to port the 2D Vector Field Flow shader from: https://www.shadertoy.com/view/4s23DG

That I break as simple as this:

Code (CSharp):
1. // 2D vector field visualization by Morgan McGuire, @morgan3d, http://casual-effects.com
3. const float ARROW_TILE_SIZE = 32.0;
4. // Used for ARROW_LINE_STYLE
5. const float ARROW_HEAD_LENGTH = ARROW_TILE_SIZE / 6.0;
6. // Computes the center pixel of the tile containing pixel pos
7. vec2 arrowTileCenterCoord(vec2 pos)
8. {
9.     return (floor(pos / ARROW_TILE_SIZE) + 0.5) * ARROW_TILE_SIZE;
10. }
11. // v = field sampled at tileCenterCoord(p), scaled by the length
12. // desired in pixels for arrows
13. // Returns 1.0 where there is an arrow pixel.
14. float arrow(vec2 p, vec2 v)
15. {
16.     // Make everything relative to the center, which may be fractional
17.     p -= arrowTileCenterCoord(p);
18.
19.     float mag_v = length(v), mag_p = length(p);
20.
21.     if (mag_v > 0.0)
22.     {
23.         // Non-zero velocity case
24.         vec2 dir_p = p / mag_p, dir_v = v / mag_v;
25.
26.         // We can't draw arrows larger than the tile radius, so clamp magnitude.
27.         // Enforce a minimum length to help see direction
28.         mag_v = clamp(mag_v, 5.0, ARROW_TILE_SIZE / 2.0);
29.         // Arrow tip location
30.         v = dir_v * mag_v;
31.
32.         // Define a 2D implicit surface so that the arrow is antialiased.
33.         // In each line, the left expression defines a shape and the right controls
34.         // how quickly it fades in or out.
35.         float dist = 2.0 / 4.0 -
36.             max(abs(dot(p, vec2(dir_v.y, -dir_v.x))), // Width
37.                 abs(dot(p, dir_v)) - mag_v + ARROW_HEAD_LENGTH / 2.0); // Length
38.
39.         return clamp(1.0 + dist, 0.0, 1.0);
40.     }
41.     else
42.     {
43.         // Center of the pixel is always on the arrow
44.         return max(0.0, 1.2 - mag_p);
45.     }
46. }
47. /////////////////////////////////////////////////////////////////////
48. // The vector field; use your own function or texture
49. vec2 field(vec2 pos) {
50.     return vec2(cos(pos.x * 0.01 + pos.y * 0.01) + cos(pos.y * 0.005 + iTime),
51.                 2.0 * cos(pos.y * 0.01  + iTime * 0.3)) * 0.5;
52. }
53. void mainImage(out vec4 fragColor, in vec2 fragCoord)
54. {
55.     vec2 mainColor = field(arrowTileCenterCoord(fragCoord.xy));
56.     float arrowColor = 1.0 - arrow(fragCoord.xy, mainColor * ARROW_TILE_SIZE * 0.4);
57.     vec4 fieldColor = vec4(field(fragCoord.xy) * 0.5 + 0.5, 0.5, 1.0);
58.
59.     fragColor = arrowColor * fieldColor;
60. }
Into the HLSL as follows:

Code (CSharp):
1.     struct vsin
2.     {
3.         float4 vertex : POSITION;
4.         float2 uv : TEXCOORD0;
5.     };
6.
7.     vsin vert (appdata v)
8.     {
9.         vsin o;
10.         o.vertex = UnityObjectToClipPos(v.vertex);
11.         o.uv = v.uv;
12.         return o;
13.     }
14.
15.     const float ARROW_TILE_SIZE = 32.0;
16.
17.     // Computes the center pixel of the tile containing pixel pos
18.     float2 arrowTileCenterCoord(float2 pos)
19.     {
20.         float x = floor(pos.x / ARROW_TILE_SIZE) + 0.5;
21.         float y = floor(pos.y / ARROW_TILE_SIZE) + 0.5;
22.         return float2(x, y) * ARROW_TILE_SIZE;
23.     }
24.
25.     // v = field sampled at tileCenterCoord(p), scaled by the length
26.     // desired in pixels for arrows
27.     // Returns 1.0 where there is an arrow pixel.
28.     float arrowMorgan(float2 p, float2 v)
29.     {
30.         // Make everything relative to the center, which may be fractional
31.         p -= arrowTileCenterCoord(p);
32.
33.         float arrow_head_length = ARROW_TILE_SIZE / 6.0;
34.
35.         float mag_v = length(v), mag_p = length(p);
36.
37.         if (mag_v > 0.0) {
38.             // Non-zero velocity case
39.             float2 dir_p = p / mag_p, dir_v = v / mag_v;
40.
41.             // We can't draw arrows larger than the tile radius, so clamp magnitude.
42.             // Enforce a minimum length to help see direction
43.             mag_v = clamp(mag_v, 5.0, ARROW_TILE_SIZE / 2.0);
44.
45.             // Arrow tip location
46.             v = dir_v * mag_v;
47.
48.             // Define a 2D implicit surface so that the arrow is antialiased.
49.             // In each line, the left expression defines a shape and the right controls
50.             // how quickly it fades in or out.
51.
52.             float dist = 10.0 / 4.0 -
53.                 max(abs(dot(p, float2(dir_v.y, -dir_v.x))), // Width
54.                     abs(dot(p, dir_v)) - mag_v + arrow_head_length / 2.0); // Length
55.
56.             return clamp(1.0 + dist, 0.0, 1.0);
57.         }
58.         else {
59.             // Center of the pixel is always on the arrow
60.             return max(0.0, 1.2 - mag_p);
61.         }
62.     }
63.
64.     // The vector field; use your own function or texture
65.     float2 field(float2 pos)
66.     {
67.         float x = cos(pos.x * 0.01 + pos.y * 0.01) + cos(pos.y * 0.005 + _Time.x);
68.         float y = 2.0 * cos(pos.y * 0.01 + _Time.y * 0.3) * 0.5;
69.         return float2(x, y);
70.     }
71.
73.     {
74.         Cull Off ZWrite Off ZTest Always
75.
76.         // 1 : Flow Vector
77.         Pass
78.         {
79.             CGPROGRAM
80.             #pragma vertex vert
81.             #pragma fragment fragVisualize
82.
83.             float4 fragVisualize (vsin i) : SV_Target
84.             {
85.                 float2 fieldDir = field(arrowTileCenterCoord(i.vertex));
86.                 float4 arrowColor = 1.0 - arrowMorgan(i.vertex, fieldDir * ARROW_TILE_SIZE * 0.4);
87.                 float4 fieldColor = float4(field(i.vertex) * 0.5 + 0.5, 0.5, 1.0);
88.                 return arrowColor * fieldColor;
89.             }
90.             ENDCG
91.         }
92.     }
But for some reason, the shader in unity does not create the lines as seen in the Shader Toy example:  I'm using the following documentation in order to create the shader:

- https://docs.unity3d.com/Manual/SL-VertexProgramInputs.html
- https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-pguide

Know issues:

- it can the calculation of the center pixel in the shader unity using the arrowTileCenterCoord method?
- does it have to be the fact that "UV coordinates in GLSL have 0 at the top and increase downwards, in HLSL 0 is at the bottom and increases upwards, so you may need to use uv.y = 1 – uv.y at some point"?

Any help is really appreciated 2. ### Przemyslaw_Zaworski

Joined:
Jun 9, 2017
Posts:
317
Code (CSharp):
2. {
4.     {
5.         Pass
6.         {
7.             CGPROGRAM
8.             #pragma vertex VSMain
9.             #pragma fragment PSMain
10.
11.             static const float PI = 3.1415927;
12.             static const int   ARROW_V_STYLE = 1;
13.             static const int   ARROW_LINE_STYLE = 2;
14.             static const int   ARROW_STYLE = ARROW_LINE_STYLE;
15.             static const float ARROW_TILE_SIZE = 64.0;
16.             static const float ARROW_HEAD_ANGLE = 45.0 * PI / 180.0;
17.             static const float ARROW_HEAD_LENGTH = ARROW_TILE_SIZE / 6.0;
18.             static const float ARROW_SHAFT_THICKNESS = 3.0;
19.
20.             float2 arrowTileCenterCoord(float2 pos)
21.             {
22.                 return (floor(pos / ARROW_TILE_SIZE) + 0.5) * ARROW_TILE_SIZE;
23.             }
24.
25.             float arrow(float2 p, float2 v)
26.             {
27.                 p -= arrowTileCenterCoord(p);
28.                 float mag_v = length(v), mag_p = length(p);
29.                 if (mag_v > 0.0)
30.                 {
31.                     float2 dir_p = p / mag_p, dir_v = v / mag_v;
32.                     mag_v = clamp(mag_v, 5.0, ARROW_TILE_SIZE / 2.0);
33.                     v = dir_v * mag_v;
34.                     float dist;
35.                     if (ARROW_STYLE == ARROW_LINE_STYLE)
36.                     {
37.                         dist = max(ARROW_SHAFT_THICKNESS / 4.0 - max(abs(dot(p, float2(dir_v.y, -dir_v.x))),
38.                             abs(dot(p, dir_v)) - mag_v + ARROW_HEAD_LENGTH / 2.0),
39.                             min(0.0, dot(v - p, dir_v) - cos(ARROW_HEAD_ANGLE / 2.0) * length(v - p)) * 2.0 +
40.                             min(0.0, dot(p, dir_v) + ARROW_HEAD_LENGTH - mag_v));
41.                     }
42.                     else
43.                     {
44.                         dist = min(0.0, mag_v - mag_p) * 2.0 +
45.                                 min(0.0, dot(normalize(v - p), dir_v) - cos(ARROW_HEAD_ANGLE / 2.0)) * 2.0 * length(v - p) +
46.                                 min(0.0, dot(p, dir_v) + 1.0) +
47.                                 min(0.0, cos(ARROW_HEAD_ANGLE / 2.0) - dot(normalize(v * 0.33 - p), dir_v)) * mag_v * 0.8;
48.                     }
49.                     return clamp(1.0 + dist, 0.0, 1.0);
50.                 }
51.                 else
52.                 {
53.                     return max(0.0, 1.2 - mag_p);
54.                 }
55.             }
56.
57.             float2 field(float2 pos)
58.             {
59.                 return float2(cos(pos.x*0.01+pos.y*0.01)+cos(pos.y*0.005+_Time.g), 2.0*cos(pos.y*0.01+_Time.g*0.3)) * 0.5;
60.             }
61.
62.             void VSMain (inout float4 vertex:POSITION, inout float2 uv:TEXCOORD0)
63.             {
64.                 vertex = UnityObjectToClipPos(vertex);
65.             }
66.
67.             float4 PSMain (float4 vertex:POSITION, float2 uv:TEXCOORD0) : SV_Target
68.             {
69.                 float2 fragCoord = uv * float2(1024,1024);
70.                 return (1.0 - arrow(fragCoord.xy, field(arrowTileCenterCoord(fragCoord.xy)) * ARROW_TILE_SIZE * 0.4)) * float4(field(fragCoord.xy) * 0.5 + 0.5, 0.5, 1.0);
71.             }
72.
73.             ENDCG
74.         }
75.     }
76. }
77. angelomoro and Olmi like this.
3. ### Olmi

Joined:
Nov 29, 2012
Posts:
1,553
It might be beneficial to point out what the mistakes were, instead of providing functioning code, so that others can learn too.

Unless you use the static word in front of the constants you are defining, the values will be zero inside the shader. You would assign them from C# side. You can try for example to define a preset value to test this:

Code (CSharp):
1. static const float4 TestColor = { 0,0,1,1 };
Now try to return that in the fragment program and see the difference when the variable is defined static. With static you get the blue color, without black (all zero).

That's why your arrows got messed up and that zero ARROW_TILE_SIZE shrunk your arrows to nothingness...

And another issue is the UV scale that often causes some issues with ShaderToy porting attempts; If you take typical Unity input UV, it's between 0-1 while those coordinates are the image pixel dimensions. That's why @Przemyslaw_Zaworski multiplied those UVs with 1024.

angelomoro likes this.
4. ### angelomoro

Joined:
Sep 4, 2018
Posts:
13
@Przemyslaw_Zaworski that is amazing, I was cracking down this code for days. Thanks for the quick reply.

@Olmi thank you very much for explaining the issues with the porting. It's very helpful to know that while reading the new code. As you say to understand the mistakes.