I’ve been using Shadershop as a tool to explore useful procedural functions. In this case I needed procedural hatching. A square wave was quick to define:

In this case, all that we need to do is call a couple of floor functions. Frequency, phase and amplitude are easy enough to adjust after the fact just as you would any other periodic function. (A simpler method to create a square wave is to use a rounding function, but I wanted to see where Shadershop’s approach took me.)
I then extrapolated this function into a vector2, skewed it with a 2×2 matrix, and voila. Shadershop makes figuring this kind of thing out much easier by using a visual workflow and graphing your function as you go. Very handy!
You can do something similar with a 1d random function fract(sin(x)*999) to achieve a periodic 2d noise pattern.
A simple crosshatch shader in unity can be made by adding two of the above square wave functions. In this case I used a min and max function to change the “pulse length” of the square wave. You can get a similar look by using the more common approach of establishing a sine wave which you then clip/threshold. I’m not sure which method would be faster… Here’s the basic shader:
Shader "Unlit/Test" { Properties { _Density ("Density", Range(1,400)) = 150 _Rotation ("Rotation", Range(0,360)) = 45.0 _Width ("Width", Range(0,1)) = 0.4 } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; float _Density; float _Rotation; float _Width; v2f vert (float4 pos : POSITION, float2 uv : TEXCOORD0) { v2f o; o.vertex = UnityObjectToClipPos(pos); o.uv = uv * _Density; return o; } fixed4 frag (v2f i) : SV_Target { float sn, cs; sincos(radians(_Rotation), sn, cs); float2x2 mt = float2x2(cs, -sn, sn, cs); float2 c = i.uv; c.xy = mul ( c.xy, mt ); float hatch = max(abs(floor((c.x/2 - 0.5)/-1) + floor(c.x/2)+1), abs(floor(((c.x-_Width)/2 - 0.5)/-1) + floor((c.x-_Width)/2)+1)) - min(abs(floor((c.y/2 - 0.5)/-1) + floor(c.y/2)+1), abs(floor(((c.y-_Width)/2 - 0.5)/-1) + floor((c.y-_Width)/2)+1)); return hatch; } ENDCG } } }
The problem with this kind of approach is that it’s overly complex. All of that min/max/floor stuff can be replaced with a much shorter step function. Same look, much more direct.
{ float sn, cs; sincos(radians(_Rotation), sn, cs); float2x2 mt = float2x2(cs, -sn, sn, cs); float2 c = i.uv; c.xy = mul ( c.xy, mt ); float hatch = abs(step(1-_Width,sin(c.x))) + abs(step(1-_Width,sin(c.y))); return hatch; }
I added some lighting to the shader and here’s the result. Procedural and written entirely in notepad!