contact_shadow
Darken pixels where one form touches another along a colour ramp, adding a line one or two steps darker so an object does not appear to float.
Instructions
Darken the pixels where one form meets another, along its ramp.
Ambient occlusion as pixel artists actually draw it: a line one or two steps darker
where two shapes touch. It is what stops a shaded object looking like it is floating
in front of the thing it is standing on.
Args:
ramp: Colours darkest first. Darkened pixels stay on this ramp.
occluder_color: The colour of the form casting the occlusion, for example the
ground a character stands on, or the blade a crossguard meets.
radius: How far the darkening reaches from the occluder, in pixels. 1 or 2 is
usually right; beyond that it reads as a drop shadow rather than contact.
depth: How many ramp steps darker, at the contact line. 1 is the common choice.
direction: Degrees, restricting occlusion to one side. Omit for all directions,
which is what you want for a contact line; set it when only one side of the
form is actually touching something.
tolerance: How close a pixel must be to a ramp entry to be darkened. Pixels
further away are left alone, so this does not spill onto other materials.
occluder_tolerance: How close a pixel must be to `occluder_color` to count as the
occluder, as a weighted RGB distance. Defaults to `tolerance`, which is what
this did before it was a separate argument. It wants to be **tight**: the
occluder is a flat fill you named, while `tolerance` has to stay loose enough
for shaded art that varies, and one number cannot be both. At the shared
default of 24 a ground colour within 24 of the subject's own ramp makes the
subject its own occluder: measured on a 24x24 sprite with a subject painted
`#5a5a7a` and ground `#60607e`, `occluder_pixels` came back as 312 (the 144
ground pixels plus all 168 of the subject) and nothing was darkened at all.
The result carries a warning naming any ramp entry this cannot be told apart
from, because that overlap is invisible otherwise.
layer: Target layer (default: top layer).
frame: Target frame, 1-based.
Refuses a pass that darkened nothing rather than reporting `darkened_pixels: 0` as a
success, and the refusal carries the three counts that say which of the possible
causes it was. A contact shadow that changed no pixel is not a contact shadow.
Run this **before** the passes that repaint the occluder, not after. The occluder is
found by colour, so a `shade_region_by_light` or `shift_along_ramp` that has already
moved the ground off `occluder_color` leaves this matching a handful of pixels and
reporting the handful as a success.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ramp | Yes | ||
| depth | No | Default: 1. | |
| frame | No | Default: 1. | |
| layer | No | ||
| radius | No | Default: 1. | |
| filename | Yes | ||
| direction | No | ||
| tolerance | No | Default: 24.0. | |
| occluder_color | Yes | ||
| occluder_tolerance | No |