Created
January 29, 2017 20:55
-
-
Save PopupAsylum/fb79067bbd174ac2eae9606976f7d0ac to your computer and use it in GitHub Desktop.
Sets a global exposure shader variable based on light probes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
using UnityEngine.Rendering; | |
[ExecuteInEditMode] | |
public class Exposure : MonoBehaviour { | |
const float DEFAULT_EXPOSURE = 3f; | |
const string EXPOSURE_PROPERTY = "_PA_Exposure"; | |
// Linear color space luminence from Unity built-in shaders | |
static readonly Vector3 UNITY_COLORSPACE_LUMINANCE = new Vector3(0.0396819152f, 0.458021790f, 0.00609653955f); | |
// is the exposure for this camera fixed or dynamic | |
public bool fixedExposure = false; | |
// if the light probes dont contain direct light then it must be added seperately | |
public bool addDirectLight = true; | |
// Overall multiplier for exposure, if fixed exposure is set this is the exposure used | |
public float multiplier = DEFAULT_EXPOSURE; | |
// The speed at which the exposure adapts to changes | |
public float lerpSpeed = 2f; | |
// The layers that direct light will be cast against | |
public LayerMask layerMask; | |
private float lastExposure; | |
RaycastHit[] hit; | |
Light[] lights; | |
private void Start() { | |
lastExposure = DEFAULT_EXPOSURE; | |
if (addDirectLight) { | |
hit = new RaycastHit[1]; | |
lights = FindObjectsOfType<Light>(); | |
} | |
} | |
private void OnPreRender() { | |
UpdateExposure(); | |
} | |
private void OnPostRender() { | |
ResetExposure(); | |
} | |
void UpdateExposure () { | |
//if fixed exposure just set the global exposure directly | |
if (fixedExposure) { | |
Shader.SetGlobalFloat(EXPOSURE_PROPERTY, multiplier); | |
return; | |
} | |
SphericalHarmonicsL2 probe; | |
// fill the interpolated probe, the camera has an empty renderer for a performance boost | |
LightProbes.GetInterpolatedProbe(transform.position, GetComponent<Renderer>(), out probe); | |
//sampler the color from the probe in the cameras direction | |
Vector3 color = ShadeSH9(probe, transform.forward); | |
//calculate luminocity | |
float luma = Vector3.Dot(color, UNITY_COLORSPACE_LUMINANCE); | |
if (addDirectLight) { | |
//Raycast to each light to determine if its shadowed or not | |
for (int i = 0; i < lights.Length; i++) { | |
Light light = lights[i]; | |
Vector3 directionToLight = light.type == LightType.Directional ? -light.transform.forward : (light.transform.position - transform.position).normalized; | |
//raycast to the light (note the 100 unit hardcode distance) | |
if (Physics.RaycastNonAlloc(new Ray(transform.position, directionToLight), hit, 100, layerMask.value) > 0) { | |
//if its not shadowed then add the lights intensity multiplied by dot product of the cameras forward direction and the direction to the light | |
float lightLuma = light.intensity * Vector3.Dot(new Vector3(light.color.r, light.color.g, light.color.b), UNITY_COLORSPACE_LUMINANCE); | |
lightLuma *= Mathf.Clamp01(Vector3.Dot(transform.forward, directionToLight)); | |
luma += lightLuma; | |
} | |
} | |
} | |
float exposure = 1 / luma * multiplier; | |
//in play mode lerp the exposure for a gradual change, in the editor applu=y it instantly | |
lastExposure = Application.isPlaying ? Mathf.Lerp(lastExposure, exposure, Mathf.Clamp01(Time.deltaTime * lerpSpeed)) : exposure; | |
if (float.IsNaN(lastExposure)) { lastExposure = 1f; } | |
//set the exposure | |
Shader.SetGlobalFloat(EXPOSURE_PROPERTY, lastExposure); | |
} | |
void ResetExposure() { | |
Shader.SetGlobalFloat(EXPOSURE_PROPERTY, DEFAULT_EXPOSURE); | |
} | |
// Get the color from a lightprobe | |
// https://forum.unity3d.com/threads/getinterpolatedlightprobe-interpreting-the-coefficients.209223/#post-2124031 | |
Vector3 ShadeSH9(SphericalHarmonicsL2 l, Vector3 n) { | |
var bx = n.x * n.y; | |
var by = n.y * n.z; | |
var bz = n.z * n.z * 3; | |
var bw = n.z * n.x; | |
var c = n.x * n.x - n.y * n.y; | |
return new Vector3 { | |
x = l[0, 3] * n.x + l[0, 1] * n.y + l[0, 2] * n.z + l[0, 0] - l[0, 6] + l[0, 4] * bx + l[0, 5] * by + l[0, 6] * bz + l[0, 7] * bw + c * l[0, 8], | |
y = l[1, 3] * n.x + l[1, 1] * n.y + l[1, 2] * n.z + l[1, 0] - l[1, 6] + l[1, 4] * bx + l[1, 5] * by + l[1, 6] * bz + l[1, 7] * bw + c * l[1, 8], | |
z = l[2, 3] * n.x + l[2, 1] * n.y + l[2, 2] * n.z + l[2, 0] - l[2, 6] + l[2, 4] * bx + l[2, 5] * by + l[2, 6] * bz + l[2, 7] * bw + c * l[2, 8] | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment