Here's a starting point, using a float variable _Density
to control how many repeats you get.
float3 n = normalize(IN.worldNormal);
// Get longitude (x) in the range -1...1, and latitude (y) in the range (-0.5...0.5).
float2 longLat = float2(atan2(n.z, n.x), asin(n.y)) / 3.141592653589f;
// Count how many rows we can fit top to bottom at our selected density.
float rowCount = ceil(_Density);
// If it's odd, the 0th row sits at the equator. Otherwise, we need to shift it half a row.
float even = fmod(rowCount + 1.0f, 2.0f);
// Scale and rount our latitude to find which row this fragment is in.
float y = longLat.y * rowCount;
float row = round(y - 0.5f * even);
// Compute the radius around the sphere along the center of this row.
float rowRadians = (row + 0.5f * even) * 3.141592653589f / rowCount;
float rowRadius = cos(rowRadians);
// Round this to get the number of texture repeats horizontally.
float rowRepeats = round(rowRadius * _Density * 2.0f);
// Make our texture coordinates.
float2 uv;
uv.x = longLat.x * rowRepeats/2.0f;
uv.y = y - row + (1.0f - even) * 0.5f;
// TODO: Calculate mipmap/gradients to avoid artifacts at seams.
fixed4 c = tex2D(_MainTex, uv).a * _Color;
Because we jump around UV space when we transition between rows, or where a row wraps-around on itself, we get some shimmery mipmapping artifacts at these transitions. An improved version would calculate which mip level to sample with tex2Dlod
, or better yet, compute gradient vectors to use for anisotropic filtering with tex2Dgrad
, to avoid the artifacts in the auto-calculated versions.
Another potential improvement would be to stamp one copy of the texture at each of the north and south poles, so that the tightest ring isn't quite so pinched in the middle.