Ginrin: from polka dots to a glint

What changed

- Before: about 60% of scales (150 of every 256) were filled with a white dot, brightest in the middle of the scale. At gallery and blog size, the koi looked spotted.

- After: about 35% of scales (90 of every 256) get a thin crescent of light on their rear rim, the tail-side edge, and a faint shadow on the front rim. It is strongest along the back and fades toward the flanks, where light from above would not reach.

Why

Ginrin (gin = silver, rin = scale) koi have reflective scales that flash as light catches their edges. The look is a glitter on the edges, not a pattern of dots. The shadow on the front rim matters on white koi: white plus a white highlight is invisible, but a highlight next to a slight shadow still reads.

The code

Before: a soft dot, brightest at the centre of the scale, on 150 of every 256 scales.

if (genome.Has(KoiMutation.Ginrin) && (cellHash & 0xFF) < 150)
  // edge is 0 at the centre, 1 at the rim  
  sparkle = (1f - edge) * (1f - edge) * 0.45f;   

After: 90 of every 256 scales, lit only on a thin band near the rim, only on the tail side, and only near the spine.

if (genome.Has(KoiMutation.Ginrin) && (cellHash & 0xFF) < 90)
    sparkle = GinrinGlint(edge, local, ay, cellHash);

static float GinrinGlint(float edge, Vector2 local, float ay, uint cellHash)
{
    float rim = Mathf.Clamp01(1f - Mathf.Abs(edge - 0.55f) / 0.3f);
    rim *= rim;
    float rear = Mathf.Clamp01(local.x * 2.2f + 0.2f);   
    float front = Mathf.Clamp01(-local.x * 2.2f - 0.2f); 
    float back = Mathf.Clamp01(1f - ay * 2.4f);        
    float strength = 0.4f + 0.25f * ((cellHash >> 8) & 0xFF) / 255f; 
    return rim * back * (rear * strength - front * 0.16f);
}

The one-line idea: `(1 - edge)²` peaks in the middle of the scale, which is a dot. Moving the peak to the rim and keeping only one side of it turns the dot into a crescent.

Next Steps

Real Ginrin flashes as the koi turns. Making the glint move with the koi's angle means moving it from the baked texture into the shader.

Previous
Previous

A thicker tail root

Next
Next

Procedural koi: where it starts