Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using UnityEngine;
- [ExecuteInEditMode]
- public class DirectionalComplementFillRig : MonoBehaviour
- {
- [Header("Lights")]
- public Light keyLight;
- public Light bounceLight;
- public Light fillLight;
- [Header("Intensity Settings")]
- [Range(0.0f, 2.0f)]
- public float bounceIntensityMultiplier = 0.5f;
- [Range(0.0f, 2.0f)]
- public float fillIntensityMultiplier = 0.35f;
- [Header("Rotation Settings")]
- public bool syncBounceRotation = true;
- public bool syncFillRotation = true;
- public Vector3 fillReferenceUp = Vector3.up;
- private void LateUpdate()
- {
- UpdateRig();
- }
- private void OnValidate()
- {
- UpdateRig();
- }
- private void UpdateRig()
- {
- if (keyLight == null)
- return;
- /*
- We read the key light as the source of truth.
- The artist only needs to adjust the key light color,
- intensity, and rotation. The bounce and fill lights are
- derived from it automatically.
- */
- Color keyColor = keyLight.color;
- float keyIntensity = keyLight.intensity;
- /*
- We calculate the bounce color as the complementary color
- of the key light.
- Example:
- blue moonlight produces warm orange bounce light.
- */
- Color bounceColor = GetComplementaryColor(keyColor);
- float bounceIntensity = keyIntensity * bounceIntensityMultiplier;
- UpdateBounceLight(bounceColor, bounceIntensity);
- UpdateFillLight(keyColor, keyIntensity, bounceColor, bounceIntensity);
- }
- private void UpdateBounceLight(Color bounceColor, float bounceIntensity)
- {
- if (bounceLight == null)
- return;
- /*
- We assign the calculated complementary color and reduced
- intensity to the bounce light.
- */
- bounceLight.color = bounceColor;
- bounceLight.intensity = bounceIntensity;
- /*
- We keep the bounce light parallel to the key light, but
- rotated 180 degrees.
- Directional lights shine along their local forward axis.
- This makes the bounce light point back toward the key light,
- as if warm bounced light is returning from the opposite side.
- */
- if (syncBounceRotation)
- {
- bounceLight.transform.rotation =
- Quaternion.LookRotation(-keyLight.transform.forward, keyLight.transform.up);
- }
- }
- private void UpdateFillLight(
- Color keyColor,
- float keyIntensity,
- Color bounceColor,
- float bounceIntensity)
- {
- if (fillLight == null)
- return;
- /*
- We calculate the fill color as a weighted average between
- the key light color and the bounce light color.
- Because the bounce light is usually half intensity, the fill
- remains biased toward the key light while still picking up
- some of the complementary warmth.
- */
- fillLight.color = CalculateWeightedColor(
- keyColor,
- keyIntensity,
- bounceColor,
- bounceIntensity
- );
- /*
- We keep the fill light weaker than the key.
- The fill should reveal form and edge detail in shadowed
- areas without flattening the whole lighting setup.
- */
- fillLight.intensity = keyIntensity * fillIntensityMultiplier;
- /*
- We rotate the fill light perpendicular to the key/bounce axis.
- Key and bounce are opposite each other.
- Fill is placed sideways across that axis, producing a rim-like
- or edge-catching light on areas that would otherwise fall into
- shadow.
- */
- if (syncFillRotation)
- {
- Vector3 keyDir = keyLight.transform.forward.normalized;
- Vector3 fillDir = Vector3.Cross(keyDir, fillReferenceUp);
- /*
- If the key light is nearly parallel to our reference up
- vector, the cross product becomes too small to be useful.
- In that case, we fall back to Vector3.right so we still
- get a stable perpendicular direction.
- */
- if (fillDir.sqrMagnitude < 0.001f)
- fillDir = Vector3.Cross(keyDir, Vector3.right);
- fillLight.transform.rotation =
- Quaternion.LookRotation(fillDir.normalized, fillReferenceUp);
- }
- }
- private static Color GetComplementaryColor(Color source)
- {
- /*
- We calculate the complementary color by converting RGB to HSV,
- rotating the hue by 180 degrees, and converting back to RGB.
- This preserves the original saturation and value, while moving
- the hue to the opposite side of the color wheel.
- */
- Color.RGBToHSV(source, out float h, out float s, out float v);
- h += 0.5f;
- if (h > 1.0f)
- h -= 1.0f;
- Color result = Color.HSVToRGB(h, s, v);
- result.a = source.a;
- return result;
- }
- private static Color CalculateWeightedColor(
- Color colorA,
- float weightA,
- Color colorB,
- float weightB)
- {
- /*
- We blend two colors using their light intensities as weights.
- Formula:
- result = ((A * weightA) + (B * weightB)) / totalWeight
- */
- float totalWeight = weightA + weightB;
- if (totalWeight <= 0.0001f)
- return Color.black;
- return new Color(
- ((colorA.r * weightA) + (colorB.r * weightB)) / totalWeight,
- ((colorA.g * weightA) + (colorB.g * weightB)) / totalWeight,
- ((colorA.b * weightA) + (colorB.b * weightB)) / totalWeight,
- 1.0f
- );
- }
- }
Advertisement