noradninja

AutoLightRig

May 7th, 2026
120
0
Never
3
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 5.97 KB | None | 0 0
  1. using UnityEngine;
  2.  
  3. [ExecuteInEditMode]
  4. public class DirectionalComplementFillRig : MonoBehaviour
  5. {
  6.     [Header("Lights")]
  7.     public Light keyLight;
  8.     public Light bounceLight;
  9.     public Light fillLight;
  10.  
  11.     [Header("Intensity Settings")]
  12.     [Range(0.0f, 2.0f)]
  13.     public float bounceIntensityMultiplier = 0.5f;
  14.  
  15.     [Range(0.0f, 2.0f)]
  16.     public float fillIntensityMultiplier = 0.35f;
  17.  
  18.     [Header("Rotation Settings")]
  19.     public bool syncBounceRotation = true;
  20.     public bool syncFillRotation = true;
  21.  
  22.     public Vector3 fillReferenceUp = Vector3.up;
  23.  
  24.     private void LateUpdate()
  25.     {
  26.         UpdateRig();
  27.     }
  28.  
  29.     private void OnValidate()
  30.     {
  31.         UpdateRig();
  32.     }
  33.  
  34.     private void UpdateRig()
  35.     {
  36.         if (keyLight == null)
  37.             return;
  38.  
  39.         /*
  40.             We read the key light as the source of truth.
  41.  
  42.             The artist only needs to adjust the key light color,
  43.             intensity, and rotation. The bounce and fill lights are
  44.             derived from it automatically.
  45.         */
  46.  
  47.         Color keyColor = keyLight.color;
  48.         float keyIntensity = keyLight.intensity;
  49.  
  50.         /*
  51.             We calculate the bounce color as the complementary color
  52.             of the key light.
  53.  
  54.             Example:
  55.             blue moonlight produces warm orange bounce light.
  56.         */
  57.  
  58.         Color bounceColor = GetComplementaryColor(keyColor);
  59.         float bounceIntensity = keyIntensity * bounceIntensityMultiplier;
  60.  
  61.         UpdateBounceLight(bounceColor, bounceIntensity);
  62.         UpdateFillLight(keyColor, keyIntensity, bounceColor, bounceIntensity);
  63.     }
  64.  
  65.     private void UpdateBounceLight(Color bounceColor, float bounceIntensity)
  66.     {
  67.         if (bounceLight == null)
  68.             return;
  69.  
  70.         /*
  71.             We assign the calculated complementary color and reduced
  72.             intensity to the bounce light.
  73.         */
  74.  
  75.         bounceLight.color = bounceColor;
  76.         bounceLight.intensity = bounceIntensity;
  77.  
  78.         /*
  79.             We keep the bounce light parallel to the key light, but
  80.             rotated 180 degrees.
  81.  
  82.             Directional lights shine along their local forward axis.
  83.             This makes the bounce light point back toward the key light,
  84.             as if warm bounced light is returning from the opposite side.
  85.         */
  86.  
  87.         if (syncBounceRotation)
  88.         {
  89.             bounceLight.transform.rotation =
  90.                 Quaternion.LookRotation(-keyLight.transform.forward, keyLight.transform.up);
  91.         }
  92.     }
  93.  
  94.     private void UpdateFillLight(
  95.         Color keyColor,
  96.         float keyIntensity,
  97.         Color bounceColor,
  98.         float bounceIntensity)
  99.     {
  100.         if (fillLight == null)
  101.             return;
  102.  
  103.         /*
  104.             We calculate the fill color as a weighted average between
  105.             the key light color and the bounce light color.
  106.  
  107.             Because the bounce light is usually half intensity, the fill
  108.             remains biased toward the key light while still picking up
  109.             some of the complementary warmth.
  110.         */
  111.  
  112.         fillLight.color = CalculateWeightedColor(
  113.             keyColor,
  114.             keyIntensity,
  115.             bounceColor,
  116.             bounceIntensity
  117.         );
  118.  
  119.         /*
  120.             We keep the fill light weaker than the key.
  121.  
  122.             The fill should reveal form and edge detail in shadowed
  123.             areas without flattening the whole lighting setup.
  124.         */
  125.  
  126.         fillLight.intensity = keyIntensity * fillIntensityMultiplier;
  127.  
  128.         /*
  129.             We rotate the fill light perpendicular to the key/bounce axis.
  130.  
  131.             Key and bounce are opposite each other.
  132.             Fill is placed sideways across that axis, producing a rim-like
  133.             or edge-catching light on areas that would otherwise fall into
  134.             shadow.
  135.         */
  136.  
  137.         if (syncFillRotation)
  138.         {
  139.             Vector3 keyDir = keyLight.transform.forward.normalized;
  140.  
  141.             Vector3 fillDir = Vector3.Cross(keyDir, fillReferenceUp);
  142.  
  143.             /*
  144.                 If the key light is nearly parallel to our reference up
  145.                 vector, the cross product becomes too small to be useful.
  146.  
  147.                 In that case, we fall back to Vector3.right so we still
  148.                 get a stable perpendicular direction.
  149.             */
  150.  
  151.             if (fillDir.sqrMagnitude < 0.001f)
  152.                 fillDir = Vector3.Cross(keyDir, Vector3.right);
  153.  
  154.             fillLight.transform.rotation =
  155.                 Quaternion.LookRotation(fillDir.normalized, fillReferenceUp);
  156.         }
  157.     }
  158.  
  159.     private static Color GetComplementaryColor(Color source)
  160.     {
  161.         /*
  162.             We calculate the complementary color by converting RGB to HSV,
  163.             rotating the hue by 180 degrees, and converting back to RGB.
  164.  
  165.             This preserves the original saturation and value, while moving
  166.             the hue to the opposite side of the color wheel.
  167.         */
  168.  
  169.         Color.RGBToHSV(source, out float h, out float s, out float v);
  170.  
  171.         h += 0.5f;
  172.  
  173.         if (h > 1.0f)
  174.             h -= 1.0f;
  175.  
  176.         Color result = Color.HSVToRGB(h, s, v);
  177.         result.a = source.a;
  178.  
  179.         return result;
  180.     }
  181.  
  182.     private static Color CalculateWeightedColor(
  183.         Color colorA,
  184.         float weightA,
  185.         Color colorB,
  186.         float weightB)
  187.     {
  188.         /*
  189.             We blend two colors using their light intensities as weights.
  190.  
  191.             Formula:
  192.             result = ((A * weightA) + (B * weightB)) / totalWeight
  193.         */
  194.  
  195.         float totalWeight = weightA + weightB;
  196.  
  197.         if (totalWeight <= 0.0001f)
  198.             return Color.black;
  199.  
  200.         return new Color(
  201.             ((colorA.r * weightA) + (colorB.r * weightB)) / totalWeight,
  202.             ((colorA.g * weightA) + (colorB.g * weightB)) / totalWeight,
  203.             ((colorA.b * weightA) + (colorB.b * weightB)) / totalWeight,
  204.             1.0f
  205.         );
  206.     }
  207. }
Advertisement
Comments
  • User was banned
  • User was banned
  • User was banned
Add Comment
Please, Sign In to add comment