Skip to content

Instantly share code, notes, and snippets.

@markeahogan
Created January 30, 2017 21:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save markeahogan/2ad6958fd7d70c8624895788abb96951 to your computer and use it in GitHub Desktop.
Save markeahogan/2ad6958fd7d70c8624895788abb96951 to your computer and use it in GitHub Desktop.
Set a global exposure shader variable based on light probes and raycasts to lights
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 hardcoded 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 apply it instantly
lastExposure = Application.isPlaying ? Mathf.Lerp(lastExposure, exposure, Mathf.Clamp01(Time.deltaTime * lerpSpeed)) : exposure;
if (float.IsNaN(lastExposure)) { lastExposure = DEFAULT_EXPOSURE; }
//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