mirror of
https://github.com/maxartz15/VolumetricLighting.git
synced 2025-06-13 23:26:20 +02:00
Added the project.
This commit is contained in:
143
Assets/AreaLight/Scripts/AreaLight.Direct.cs
Normal file
143
Assets/AreaLight/Scripts/AreaLight.Direct.cs
Normal file
@ -0,0 +1,143 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public partial class AreaLight : MonoBehaviour
|
||||
{
|
||||
[HideInInspector]
|
||||
public Mesh m_Cube;
|
||||
[HideInInspector]
|
||||
public Shader m_ProxyShader;
|
||||
Material m_ProxyMaterial;
|
||||
static Texture2D s_TransformInvTexture_Specular;
|
||||
static Texture2D s_TransformInvTexture_Diffuse;
|
||||
static Texture2D s_AmpDiffAmpSpecFresnel;
|
||||
|
||||
Dictionary<Camera, CommandBuffer> m_Cameras = new Dictionary<Camera, CommandBuffer>();
|
||||
static CameraEvent kCameraEvent = CameraEvent.AfterLighting;
|
||||
|
||||
bool InitDirect()
|
||||
{
|
||||
if (m_ProxyShader == null || m_Cube == null)
|
||||
return false;
|
||||
|
||||
// Proxy
|
||||
m_ProxyMaterial = new Material(m_ProxyShader);
|
||||
m_ProxyMaterial.hideFlags = HideFlags.HideAndDontSave;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetUpLUTs()
|
||||
{
|
||||
if (s_TransformInvTexture_Diffuse == null)
|
||||
s_TransformInvTexture_Diffuse = AreaLightLUT.LoadLUT(AreaLightLUT.LUTType.TransformInv_DisneyDiffuse);
|
||||
if (s_TransformInvTexture_Specular == null)
|
||||
s_TransformInvTexture_Specular = AreaLightLUT.LoadLUT(AreaLightLUT.LUTType.TransformInv_GGX);
|
||||
if (s_AmpDiffAmpSpecFresnel == null)
|
||||
s_AmpDiffAmpSpecFresnel = AreaLightLUT.LoadLUT(AreaLightLUT.LUTType.AmpDiffAmpSpecFresnel);
|
||||
|
||||
m_ProxyMaterial.SetTexture("_TransformInv_Diffuse", s_TransformInvTexture_Diffuse);
|
||||
m_ProxyMaterial.SetTexture("_TransformInv_Specular", s_TransformInvTexture_Specular);
|
||||
m_ProxyMaterial.SetTexture("_AmpDiffAmpSpecFresnel", s_AmpDiffAmpSpecFresnel);
|
||||
}
|
||||
|
||||
void Cleanup()
|
||||
{
|
||||
for(var e = m_Cameras.GetEnumerator(); e.MoveNext();)
|
||||
{
|
||||
var cam = e.Current;
|
||||
if (cam.Key != null && cam.Value != null)
|
||||
{
|
||||
cam.Key.RemoveCommandBuffer (kCameraEvent, cam.Value);
|
||||
}
|
||||
}
|
||||
m_Cameras.Clear();
|
||||
}
|
||||
|
||||
static readonly float[,] offsets = new float[4,2] {{1, 1}, {1, -1}, {-1, -1}, {-1, 1}};
|
||||
|
||||
CommandBuffer GetOrCreateCommandBuffer(Camera cam)
|
||||
{
|
||||
if(cam == null)
|
||||
return null;
|
||||
|
||||
CommandBuffer buf = null;
|
||||
if(!m_Cameras.ContainsKey(cam)) {
|
||||
buf = new CommandBuffer();
|
||||
buf.name = /*"Area light: " +*/ gameObject.name;
|
||||
m_Cameras[cam] = buf;
|
||||
cam.AddCommandBuffer(kCameraEvent, buf);
|
||||
cam.depthTextureMode |= DepthTextureMode.Depth;
|
||||
} else {
|
||||
buf = m_Cameras[cam];
|
||||
buf.Clear();
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
public void SetUpCommandBuffer()
|
||||
{
|
||||
if (InsideShadowmapCameraRender())
|
||||
return;
|
||||
|
||||
Camera cam = Camera.current;
|
||||
CommandBuffer buf = GetOrCreateCommandBuffer(cam);
|
||||
|
||||
buf.SetGlobalVector("_LightPos", transform.position);
|
||||
buf.SetGlobalVector("_LightColor", GetColor());
|
||||
SetUpLUTs();
|
||||
|
||||
// Needed as we're using the vert_deferred vertex shader from UnityDeferredLibrary.cginc
|
||||
// TODO: Make the light render as quad if it intersects both near and far plane.
|
||||
// (Also missing: rendering as front faces when near doesn't intersect, stencil optimisations)
|
||||
buf.SetGlobalFloat("_LightAsQuad", 0);
|
||||
|
||||
// A little bit of bias to prevent the light from lighting itself - the source quad
|
||||
float z = 0.01f;
|
||||
Transform t = transform;
|
||||
|
||||
Matrix4x4 lightVerts = new Matrix4x4();
|
||||
for (int i = 0; i < 4; i++)
|
||||
lightVerts.SetRow(i, t.TransformPoint(new Vector3(m_Size.x * offsets[i,0], m_Size.y * offsets[i,1], z) * 0.5f));
|
||||
buf.SetGlobalMatrix("_LightVerts", lightVerts);
|
||||
|
||||
if (m_Shadows)
|
||||
SetUpShadowmapForSampling(buf);
|
||||
|
||||
Matrix4x4 m = Matrix4x4.TRS(new Vector3(0, 0, 10.0f), Quaternion.identity, Vector3.one * 20);
|
||||
buf.DrawMesh(m_Cube, t.localToWorldMatrix * m, m_ProxyMaterial, 0, m_Shadows ? /*shadows*/ 0 : /*no shadows*/ 1);
|
||||
}
|
||||
|
||||
void SetKeyword(string keyword, bool on)
|
||||
{
|
||||
if (on)
|
||||
m_ProxyMaterial.EnableKeyword(keyword);
|
||||
else
|
||||
m_ProxyMaterial.DisableKeyword(keyword);
|
||||
}
|
||||
|
||||
void ReleaseTemporary(ref RenderTexture rt)
|
||||
{
|
||||
if (rt == null)
|
||||
return;
|
||||
|
||||
RenderTexture.ReleaseTemporary(rt);
|
||||
rt = null;
|
||||
}
|
||||
|
||||
Color GetColor()
|
||||
{
|
||||
if (QualitySettings.activeColorSpace == ColorSpace.Gamma)
|
||||
return m_Color * m_Intensity;
|
||||
|
||||
return new Color(
|
||||
Mathf.GammaToLinearSpace(m_Color.r * m_Intensity),
|
||||
Mathf.GammaToLinearSpace(m_Color.g * m_Intensity),
|
||||
Mathf.GammaToLinearSpace(m_Color.b * m_Intensity),
|
||||
1.0f
|
||||
);
|
||||
}
|
||||
|
||||
}
|
12
Assets/AreaLight/Scripts/AreaLight.Direct.cs.meta
Normal file
12
Assets/AreaLight/Scripts/AreaLight.Direct.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f166f8247b9668b45bd44bb7d17f3e1d
|
||||
timeCreated: 1453074905
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
303
Assets/AreaLight/Scripts/AreaLight.Shadow.cs
Normal file
303
Assets/AreaLight/Scripts/AreaLight.Shadow.cs
Normal file
@ -0,0 +1,303 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
public partial class AreaLight : MonoBehaviour
|
||||
{
|
||||
Camera m_ShadowmapCamera;
|
||||
Transform m_ShadowmapCameraTransform;
|
||||
|
||||
[HideInInspector]
|
||||
public Shader m_ShadowmapShader;
|
||||
[HideInInspector]
|
||||
public Shader m_BlurShadowmapShader;
|
||||
Material m_BlurShadowmapMaterial;
|
||||
RenderTexture m_Shadowmap = null;
|
||||
RenderTexture m_BlurredShadowmap = null;
|
||||
Texture2D m_ShadowmapDummy = null;
|
||||
|
||||
int m_ShadowmapRenderTime = -1;
|
||||
int m_BlurredShadowmapRenderTime = -1;
|
||||
FogLight m_FogLight = null;
|
||||
|
||||
public enum TextureSize
|
||||
{
|
||||
x512 = 512,
|
||||
x1024 = 1024,
|
||||
x2048 = 2048,
|
||||
x4096 = 4096,
|
||||
}
|
||||
|
||||
void UpdateShadowmap(int res)
|
||||
{
|
||||
if (m_Shadowmap != null && m_ShadowmapRenderTime == Time.renderedFrameCount)
|
||||
return;
|
||||
|
||||
// Create the camera
|
||||
if (m_ShadowmapCamera == null)
|
||||
{
|
||||
if (m_ShadowmapShader == null)
|
||||
{
|
||||
Debug.LogError("AreaLight's shadowmap shader not assigned.", this);
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject go = new GameObject("Shadowmap Camera");
|
||||
go.AddComponent(typeof(Camera));
|
||||
m_ShadowmapCamera = go.GetComponent<Camera>();
|
||||
go.hideFlags = HideFlags.HideAndDontSave;
|
||||
m_ShadowmapCamera.enabled = false;
|
||||
m_ShadowmapCamera.clearFlags = CameraClearFlags.SolidColor;
|
||||
m_ShadowmapCamera.renderingPath = RenderingPath.Forward;
|
||||
// exp(EXPONENT) for ESM, white for VSM
|
||||
// m_ShadowmapCamera.backgroundColor = new Color(Mathf.Exp(EXPONENT), 0, 0, 0);
|
||||
m_ShadowmapCamera.backgroundColor = Color.white;
|
||||
m_ShadowmapCameraTransform = go.transform;
|
||||
m_ShadowmapCameraTransform.parent = transform;
|
||||
m_ShadowmapCameraTransform.localRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
if (m_Angle == 0.0f)
|
||||
{
|
||||
m_ShadowmapCamera.orthographic = true;
|
||||
m_ShadowmapCameraTransform.localPosition = Vector3.zero;
|
||||
m_ShadowmapCamera.nearClipPlane = 0;
|
||||
m_ShadowmapCamera.farClipPlane = m_Size.z;
|
||||
m_ShadowmapCamera.orthographicSize = 0.5f * m_Size.y;
|
||||
m_ShadowmapCamera.aspect = m_Size.x / m_Size.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ShadowmapCamera.orthographic = false;
|
||||
float near = GetNearToCenter();
|
||||
m_ShadowmapCameraTransform.localPosition = -near * Vector3.forward;
|
||||
m_ShadowmapCamera.nearClipPlane = near;
|
||||
m_ShadowmapCamera.farClipPlane = near + m_Size.z;
|
||||
m_ShadowmapCamera.fieldOfView = m_Angle;
|
||||
m_ShadowmapCamera.aspect = m_Size.x / m_Size.y;
|
||||
}
|
||||
|
||||
ReleaseTemporary(ref m_Shadowmap);
|
||||
m_Shadowmap = RenderTexture.GetTemporary(res, res, 24, RenderTextureFormat.Shadowmap);
|
||||
m_Shadowmap.name = "AreaLight Shadowmap";
|
||||
m_Shadowmap.filterMode = FilterMode.Bilinear;
|
||||
m_Shadowmap.wrapMode = TextureWrapMode.Clamp;
|
||||
|
||||
m_ShadowmapCamera.targetTexture = m_Shadowmap;
|
||||
|
||||
// Clear. RenderWithShader() should clear too, but it doesn't.
|
||||
// TODO: Check if it's a bug.
|
||||
m_ShadowmapCamera.cullingMask = 0;
|
||||
m_ShadowmapCamera.Render();
|
||||
m_ShadowmapCamera.cullingMask = m_ShadowCullingMask;
|
||||
|
||||
// We might be rendering inside PlaneReflections, which invert culling. Disable temporarily.
|
||||
var oldCulling = GL.invertCulling;
|
||||
GL.invertCulling = false;
|
||||
|
||||
m_ShadowmapCamera.RenderWithShader(m_ShadowmapShader, "RenderType");
|
||||
|
||||
// Back to whatever was the culling mode.
|
||||
GL.invertCulling = oldCulling;
|
||||
|
||||
m_ShadowmapRenderTime = Time.renderedFrameCount;
|
||||
}
|
||||
|
||||
public RenderTexture GetBlurredShadowmap()
|
||||
{
|
||||
UpdateBlurredShadowmap();
|
||||
return m_BlurredShadowmap;
|
||||
}
|
||||
|
||||
RenderTexture[] temp;
|
||||
|
||||
void UpdateBlurredShadowmap()
|
||||
{
|
||||
if (m_BlurredShadowmap != null && m_BlurredShadowmapRenderTime == Time.renderedFrameCount)
|
||||
return;
|
||||
|
||||
InitFogLight();
|
||||
|
||||
int startRes = (int)m_ShadowmapRes;
|
||||
int targetRes = (int)m_FogLight.m_ShadowmapRes;
|
||||
|
||||
// To make things easier, blurred shadowmap is at most half the size of the regular.
|
||||
if (isActiveAndEnabled && m_Shadows)
|
||||
{
|
||||
targetRes = Mathf.Min(targetRes, startRes/2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the area light or the shadows on it are disabled, we can
|
||||
// just get the most convenient resolution for us.
|
||||
startRes = 2 * startRes;
|
||||
}
|
||||
|
||||
UpdateShadowmap(startRes);
|
||||
|
||||
RenderTexture originalRT = RenderTexture.active;
|
||||
|
||||
// Downsample
|
||||
ReleaseTemporary(ref m_BlurredShadowmap);
|
||||
InitMaterial(ref m_BlurShadowmapMaterial, m_BlurShadowmapShader);
|
||||
int downsampleSteps = (int)Mathf.Log(startRes / targetRes, 2);
|
||||
if (temp == null || temp.Length != downsampleSteps)
|
||||
temp = new RenderTexture[downsampleSteps];
|
||||
// RFloat for ESM, RGHalf for VSM
|
||||
RenderTextureFormat format = RenderTextureFormat.RGHalf;
|
||||
|
||||
for(int i = 0, currentRes = startRes/2; i < downsampleSteps; i++)
|
||||
{
|
||||
temp[i] = RenderTexture.GetTemporary(currentRes, currentRes, 0, format, RenderTextureReadWrite.Linear);
|
||||
temp[i].name = "AreaLight Shadow Downsample";
|
||||
temp[i].filterMode = FilterMode.Bilinear;
|
||||
temp[i].wrapMode = TextureWrapMode.Clamp;
|
||||
m_BlurShadowmapMaterial.SetVector("_TexelSize", new Vector4(0.5f/currentRes, 0.5f/currentRes, 0, 0));
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
m_BlurShadowmapMaterial.SetTexture("_Shadowmap", m_Shadowmap);
|
||||
InitShadowmapDummy();
|
||||
m_BlurShadowmapMaterial.SetTexture("_ShadowmapDummy", m_ShadowmapDummy);
|
||||
m_BlurShadowmapMaterial.SetVector("_ZParams", GetZParams());
|
||||
m_BlurShadowmapMaterial.SetFloat("_ESMExponent", m_FogLight.m_ESMExponent);
|
||||
Blur(m_Shadowmap, temp[i], /*sample & convert shadowmap*/ 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_BlurShadowmapMaterial.SetTexture("_MainTex", temp[i - 1]);
|
||||
Blur(temp[i - 1], temp[i], /*regular sample*/ 1);
|
||||
}
|
||||
|
||||
currentRes /= 2;
|
||||
}
|
||||
|
||||
for (int i = 0; i < downsampleSteps - 1; i++)
|
||||
RenderTexture.ReleaseTemporary(temp[i]);
|
||||
|
||||
m_BlurredShadowmap = temp[downsampleSteps - 1];
|
||||
|
||||
// Blur
|
||||
if (m_FogLight.m_BlurIterations > 0)
|
||||
{
|
||||
RenderTexture tempBlur = RenderTexture.GetTemporary (targetRes, targetRes, 0, format, RenderTextureReadWrite.Linear);
|
||||
tempBlur.name = "AreaLight Shadow Blur";
|
||||
tempBlur.filterMode = FilterMode.Bilinear;
|
||||
tempBlur.wrapMode = TextureWrapMode.Clamp;
|
||||
|
||||
m_BlurShadowmapMaterial.SetVector("_MainTex_TexelSize", new Vector4(1.0f/targetRes, 1.0f/targetRes, 0, 0));
|
||||
|
||||
float blurSize = m_FogLight.m_BlurSize;
|
||||
for(int i = 0; i < m_FogLight.m_BlurIterations; i++)
|
||||
{
|
||||
m_BlurShadowmapMaterial.SetFloat ("_BlurSize", blurSize);
|
||||
Blur(m_BlurredShadowmap, tempBlur, /*vertical blur*/2);
|
||||
Blur(tempBlur, m_BlurredShadowmap, /*horizontal blur*/3);
|
||||
blurSize *= 1.2f;
|
||||
}
|
||||
|
||||
RenderTexture.ReleaseTemporary(tempBlur);
|
||||
}
|
||||
|
||||
RenderTexture.active = originalRT;
|
||||
|
||||
m_BlurredShadowmapRenderTime = Time.renderedFrameCount;
|
||||
}
|
||||
|
||||
// Normally would've used Graphics.Blit(), but it breaks picking in the scene view.
|
||||
// TODO: bug report
|
||||
void Blur(RenderTexture src, RenderTexture dst, int pass)
|
||||
{
|
||||
RenderTexture.active = dst;
|
||||
m_BlurShadowmapMaterial.SetTexture("_MainTex", src);
|
||||
m_BlurShadowmapMaterial.SetPass(pass);
|
||||
RenderQuad();
|
||||
}
|
||||
|
||||
void RenderQuad()
|
||||
{
|
||||
GL.Begin(GL.QUADS);
|
||||
GL.TexCoord2( 0, 0);
|
||||
GL.Vertex3 (-1, 1, 0);
|
||||
GL.TexCoord2( 0, 1);
|
||||
GL.Vertex3 (-1,-1, 0);
|
||||
GL.TexCoord2( 1, 1);
|
||||
GL.Vertex3 ( 1,-1, 0);
|
||||
GL.TexCoord2( 1, 0);
|
||||
GL.Vertex3 ( 1, 1, 0);
|
||||
GL.End();
|
||||
}
|
||||
|
||||
void SetUpShadowmapForSampling(CommandBuffer buf)
|
||||
{
|
||||
UpdateShadowmap((int)m_ShadowmapRes);
|
||||
|
||||
buf.SetGlobalTexture("_Shadowmap", m_Shadowmap);
|
||||
InitShadowmapDummy();
|
||||
m_ProxyMaterial.SetTexture("_ShadowmapDummy", m_ShadowmapDummy);
|
||||
buf.SetGlobalMatrix("_ShadowProjectionMatrix", GetProjectionMatrix());
|
||||
|
||||
float texelsInMap = (int)m_ShadowmapRes;
|
||||
float relativeTexelSize = texelsInMap / 2048.0f;
|
||||
|
||||
buf.SetGlobalFloat("_ShadowReceiverWidth", relativeTexelSize * m_ReceiverSearchDistance / texelsInMap);
|
||||
|
||||
buf.SetGlobalFloat("_ShadowReceiverDistanceScale", m_ReceiverDistanceScale * 0.5f / 10.0f); // 10 samples in shader
|
||||
|
||||
Vector2 shadowLightWidth = new Vector2(m_LightNearSize, m_LightFarSize) * relativeTexelSize / texelsInMap;
|
||||
buf.SetGlobalVector("_ShadowLightWidth", shadowLightWidth);
|
||||
|
||||
buf.SetGlobalFloat("_ShadowBias", m_ShadowBias);
|
||||
}
|
||||
|
||||
void InitMaterial(ref Material material, Shader shader)
|
||||
{
|
||||
if (material)
|
||||
return;
|
||||
|
||||
if (!shader)
|
||||
{
|
||||
Debug.LogError("Missing shader");
|
||||
return;
|
||||
}
|
||||
|
||||
material = new Material(shader);
|
||||
material.hideFlags = HideFlags.HideAndDontSave;
|
||||
}
|
||||
|
||||
void InitShadowmapDummy()
|
||||
{
|
||||
if(m_ShadowmapDummy != null)
|
||||
return;
|
||||
m_ShadowmapDummy = new Texture2D(1, 1, TextureFormat.Alpha8, false, true);
|
||||
m_ShadowmapDummy.filterMode = FilterMode.Point;
|
||||
m_ShadowmapDummy.SetPixel(0, 0, new Color(0f, 0f, 0f, 0f));
|
||||
m_ShadowmapDummy.Apply(false, true);
|
||||
}
|
||||
|
||||
void InitFogLight()
|
||||
{
|
||||
if (m_FogLight != null)
|
||||
return;
|
||||
|
||||
// It should always be here, because it triggered this code path in the first place.
|
||||
m_FogLight = GetComponent<FogLight>();
|
||||
}
|
||||
|
||||
bool InsideShadowmapCameraRender()
|
||||
{
|
||||
RenderTexture target = Camera.current.targetTexture;
|
||||
return target != null && target.format == RenderTextureFormat.Shadowmap;
|
||||
}
|
||||
|
||||
Vector4 GetZParams()
|
||||
{
|
||||
float n = GetNearToCenter();
|
||||
float f = n + m_Size.z;
|
||||
// linear z, 0 near, 1 far
|
||||
// linearz = A * (z + 1.0) / (z + B);
|
||||
// A = n/(n - f)
|
||||
// B = (n + f)/(n - f)
|
||||
|
||||
return new Vector4(n/(n - f), (n + f)/(n - f), 0, 0);
|
||||
}
|
||||
}
|
12
Assets/AreaLight/Scripts/AreaLight.Shadow.cs.meta
Normal file
12
Assets/AreaLight/Scripts/AreaLight.Shadow.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 263b59280b7c4e04aa70d3c9cb1a5586
|
||||
timeCreated: 1455014110
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
297
Assets/AreaLight/Scripts/AreaLight.cs
Normal file
297
Assets/AreaLight/Scripts/AreaLight.cs
Normal file
@ -0,0 +1,297 @@
|
||||
using UnityEngine;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
public partial class AreaLight : MonoBehaviour
|
||||
{
|
||||
public bool m_RenderSource = true;
|
||||
public Vector3 m_Size = new Vector3(1, 1, 2);
|
||||
[Range(0, 179)]
|
||||
public float m_Angle = 0.0f;
|
||||
[MinValue(0)]
|
||||
public float m_Intensity = 0.8f;
|
||||
public Color m_Color = Color.white;
|
||||
|
||||
[Header("Shadows")]
|
||||
public bool m_Shadows = false;
|
||||
public LayerMask m_ShadowCullingMask = ~0;
|
||||
public TextureSize m_ShadowmapRes = TextureSize.x2048;
|
||||
[MinValue(0)]
|
||||
public float m_ReceiverSearchDistance = 24.0f;
|
||||
[MinValue(0)]
|
||||
public float m_ReceiverDistanceScale = 5.0f;
|
||||
[MinValue(0)]
|
||||
public float m_LightNearSize = 4.0f;
|
||||
[MinValue(0)]
|
||||
public float m_LightFarSize = 22.0f;
|
||||
[Range(0, 0.1f)]
|
||||
public float m_ShadowBias = 0.001f;
|
||||
|
||||
MeshRenderer m_SourceRenderer;
|
||||
Mesh m_SourceMesh;
|
||||
[HideInInspector]
|
||||
public Mesh m_Quad;
|
||||
Vector2 m_CurrentQuadSize = Vector2.zero;
|
||||
Vector3 m_CurrentSize = Vector3.zero;
|
||||
float m_CurrentAngle = -1.0f;
|
||||
|
||||
bool m_Initialized = false;
|
||||
MaterialPropertyBlock m_props;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if(!Init())
|
||||
return;
|
||||
UpdateSourceMesh();
|
||||
}
|
||||
|
||||
bool Init()
|
||||
{
|
||||
if (m_Initialized)
|
||||
return true;
|
||||
|
||||
if (m_Quad == null || !InitDirect())
|
||||
return false;
|
||||
|
||||
m_SourceRenderer = GetComponent<MeshRenderer>();
|
||||
m_SourceRenderer.enabled = true;
|
||||
m_SourceMesh = Instantiate<Mesh>(m_Quad);
|
||||
m_SourceMesh.hideFlags = HideFlags.HideAndDontSave;
|
||||
MeshFilter mfs = gameObject.GetComponent<MeshFilter>();
|
||||
mfs.sharedMesh = m_SourceMesh;
|
||||
|
||||
Transform t = transform;
|
||||
if (t.localScale != Vector3.one)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.LogError("AreaLights don't like to be scaled. Setting local scale to 1.", this);
|
||||
#endif
|
||||
t.localScale = Vector3.one;
|
||||
}
|
||||
|
||||
SetUpLUTs();
|
||||
|
||||
m_Initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
m_props = new MaterialPropertyBlock();
|
||||
|
||||
if(!Init())
|
||||
return;
|
||||
UpdateSourceMesh();
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if(!Application.isPlaying)
|
||||
Cleanup();
|
||||
else
|
||||
for(var e = m_Cameras.GetEnumerator(); e.MoveNext();)
|
||||
if(e.Current.Value != null)
|
||||
e.Current.Value.Clear();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (m_ProxyMaterial != null)
|
||||
DestroyImmediate(m_ProxyMaterial);
|
||||
if (m_SourceMesh != null)
|
||||
DestroyImmediate(m_SourceMesh);
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
static Vector3[] vertices = new Vector3[4];
|
||||
|
||||
void UpdateSourceMesh()
|
||||
{
|
||||
m_Size.x = Mathf.Max(m_Size.x, 0);
|
||||
m_Size.y = Mathf.Max(m_Size.y, 0);
|
||||
m_Size.z = Mathf.Max(m_Size.z, 0);
|
||||
|
||||
Vector2 quadSize = m_RenderSource && enabled ? new Vector2(m_Size.x, m_Size.y) : Vector2.one * 0.0001f;
|
||||
if (quadSize != m_CurrentQuadSize)
|
||||
{
|
||||
|
||||
float x = quadSize.x * 0.5f;
|
||||
float y = quadSize.y * 0.5f;
|
||||
// To prevent the source quad from getting into the shadowmap, offset it back a bit.
|
||||
float z = -0.001f;
|
||||
vertices[0].Set(-x, y, z);
|
||||
vertices[1].Set( x, -y, z);
|
||||
vertices[2].Set( x, y, z);
|
||||
vertices[3].Set(-x, -y, z);
|
||||
|
||||
m_SourceMesh.vertices = vertices;
|
||||
|
||||
m_CurrentQuadSize = quadSize;
|
||||
}
|
||||
|
||||
if (m_Size != m_CurrentSize || m_Angle != m_CurrentAngle)
|
||||
{
|
||||
// Set the bounds of the mesh to large, so that they drive rendering of the entire light
|
||||
// TODO: Make the bounds tight around the shape of the light. Right now they're just tight around
|
||||
// the shadow frustum, which is fine if the shadows are enable (ok, maybe far plane should be more clever),
|
||||
// but doesn't make sense if shadows are disabled.
|
||||
m_SourceMesh.bounds = GetFrustumBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (!gameObject.activeInHierarchy || !enabled)
|
||||
{
|
||||
Cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!Init())
|
||||
return;
|
||||
|
||||
UpdateSourceMesh();
|
||||
|
||||
if(Application.isPlaying)
|
||||
for(var e = m_Cameras.GetEnumerator(); e.MoveNext();)
|
||||
if(e.Current.Value != null)
|
||||
e.Current.Value.Clear();
|
||||
}
|
||||
|
||||
void OnWillRenderObject()
|
||||
{
|
||||
if(!Init())
|
||||
return;
|
||||
|
||||
// TODO: This is just a very rough guess. Need to properly calculate the surface emission
|
||||
// intensity based on light's intensity.
|
||||
Color color = new Color(
|
||||
Mathf.GammaToLinearSpace(m_Color.r),
|
||||
Mathf.GammaToLinearSpace(m_Color.g),
|
||||
Mathf.GammaToLinearSpace(m_Color.b),
|
||||
1.0f);
|
||||
m_props.SetVector("_EmissionColor", color * m_Intensity);
|
||||
m_SourceRenderer.SetPropertyBlock(m_props);
|
||||
|
||||
SetUpCommandBuffer();
|
||||
}
|
||||
|
||||
float GetNearToCenter()
|
||||
{
|
||||
if (m_Angle == 0.0f)
|
||||
return 0;
|
||||
|
||||
return m_Size.y * 0.5f / Mathf.Tan(m_Angle * 0.5f * Mathf.Deg2Rad);
|
||||
}
|
||||
|
||||
Matrix4x4 GetOffsetMatrix(float zOffset)
|
||||
{
|
||||
Matrix4x4 m = Matrix4x4.identity;
|
||||
m.SetColumn(3, new Vector4(0, 0, zOffset, 1));
|
||||
return m;
|
||||
}
|
||||
|
||||
public Matrix4x4 GetProjectionMatrix(bool linearZ = false)
|
||||
{
|
||||
Matrix4x4 m;
|
||||
|
||||
if (m_Angle == 0.0f)
|
||||
{
|
||||
m = Matrix4x4.Ortho(-0.5f * m_Size.x, 0.5f * m_Size.x, -0.5f * m_Size.y, 0.5f * m_Size.y, 0, -m_Size.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
float near = GetNearToCenter();
|
||||
if (linearZ)
|
||||
{
|
||||
m = PerspectiveLinearZ(m_Angle, m_Size.x/m_Size.y, near, near + m_Size.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
m = Matrix4x4.Perspective(m_Angle, m_Size.x/m_Size.y, near, near + m_Size.z);
|
||||
m = m * Matrix4x4.Scale(new Vector3(1, 1, -1));
|
||||
}
|
||||
m = m * GetOffsetMatrix(near);
|
||||
}
|
||||
|
||||
return m * transform.worldToLocalMatrix;
|
||||
}
|
||||
|
||||
public Vector4 MultiplyPoint(Matrix4x4 m, Vector3 v)
|
||||
{
|
||||
Vector4 res;
|
||||
res.x = m.m00 * v.x + m.m01 * v.y + m.m02 * v.z + m.m03;
|
||||
res.y = m.m10 * v.x + m.m11 * v.y + m.m12 * v.z + m.m13;
|
||||
res.z = m.m20 * v.x + m.m21 * v.y + m.m22 * v.z + m.m23;
|
||||
res.w = m.m30 * v.x + m.m31 * v.y + m.m32 * v.z + m.m33;
|
||||
return res;
|
||||
}
|
||||
|
||||
Matrix4x4 PerspectiveLinearZ(float fov, float aspect, float near, float far)
|
||||
{
|
||||
// A vector transformed with this matrix should get perspective division on x and y only:
|
||||
// Vector4 vClip = MultiplyPoint(PerspectiveLinearZ(...), vEye);
|
||||
// Vector3 vNDC = Vector3(vClip.x / vClip.w, vClip.y / vClip.w, vClip.z);
|
||||
// vNDC is [-1, 1]^3 and z is linear, i.e. z = 0 is half way between near and far in world space.
|
||||
|
||||
float rad = Mathf.Deg2Rad * fov * 0.5f;
|
||||
float cotan = Mathf.Cos(rad) / Mathf.Sin(rad);
|
||||
float deltainv = 1.0f / (far - near);
|
||||
Matrix4x4 m;
|
||||
|
||||
m.m00 = cotan / aspect; m.m01 = 0.0f; m.m02 = 0.0f; m.m03 = 0.0f;
|
||||
m.m10 = 0.0f; m.m11 = cotan; m.m12 = 0.0f; m.m13 = 0.0f;
|
||||
m.m20 = 0.0f; m.m21 = 0.0f; m.m22 = 2.0f * deltainv; m.m23 = - (far + near) * deltainv;
|
||||
m.m30 = 0.0f; m.m31 = 0.0f; m.m32 = 1.0f; m.m33 = 0.0f;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
public Vector4 GetPosition()
|
||||
{
|
||||
Transform t = transform;
|
||||
|
||||
if (m_Angle == 0.0f)
|
||||
{
|
||||
Vector3 dir = -t.forward;
|
||||
return new Vector4(dir.x, dir.y, dir.z, 0);
|
||||
}
|
||||
|
||||
Vector3 pos = t.position - GetNearToCenter() * t.forward;
|
||||
return new Vector4(pos.x, pos.y, pos.z, 1);
|
||||
}
|
||||
|
||||
Bounds GetFrustumBounds()
|
||||
{
|
||||
if (m_Angle == 0.0f)
|
||||
return new Bounds(Vector3.zero, m_Size);
|
||||
|
||||
float tanhalffov = Mathf.Tan(m_Angle * 0.5f * Mathf.Deg2Rad);
|
||||
float near = m_Size.y * 0.5f / tanhalffov;
|
||||
float z = m_Size.z;
|
||||
float y = (near + m_Size.z) * tanhalffov * 2.0f;
|
||||
float x = m_Size.x * y / m_Size.y;
|
||||
return new Bounds(Vector3.forward * m_Size.z * 0.5f, new Vector3(x, y, z));
|
||||
}
|
||||
|
||||
void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = Color.white;
|
||||
|
||||
if (m_Angle == 0.0f)
|
||||
{
|
||||
Gizmos.matrix = transform.localToWorldMatrix;
|
||||
Gizmos.DrawWireCube(new Vector3(0, 0, 0.5f * m_Size.z), m_Size);
|
||||
return;
|
||||
}
|
||||
|
||||
float near = GetNearToCenter();
|
||||
Gizmos.matrix = transform.localToWorldMatrix * GetOffsetMatrix(-near);
|
||||
|
||||
Gizmos.DrawFrustum(Vector3.zero, m_Angle, near + m_Size.z, near, m_Size.x/m_Size.y);
|
||||
|
||||
Gizmos.matrix = transform.localToWorldMatrix;
|
||||
Gizmos.color = Color.yellow;
|
||||
Bounds bounds = GetFrustumBounds();
|
||||
Gizmos.DrawWireCube(bounds.center, bounds.size);
|
||||
}
|
||||
}
|
16
Assets/AreaLight/Scripts/AreaLight.cs.meta
Normal file
16
Assets/AreaLight/Scripts/AreaLight.cs.meta
Normal file
@ -0,0 +1,16 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 792909cacf2415c48a1d7eebef426b5d
|
||||
timeCreated: 1453121021
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- m_Cube: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
|
||||
- m_ProxyShader: {fileID: 4800000, guid: 2b28be3523d190a41910088d1ee68ef7, type: 3}
|
||||
- m_Quad: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
|
||||
- m_ShadowmapShader: {fileID: 4800000, guid: 2c4cd42b134f7864fa579350e9cf6896, type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8204
Assets/AreaLight/Scripts/AreaLightLUT.DisneyDiffuse.cs
Normal file
8204
Assets/AreaLight/Scripts/AreaLightLUT.DisneyDiffuse.cs
Normal file
File diff suppressed because it is too large
Load Diff
12
Assets/AreaLight/Scripts/AreaLightLUT.DisneyDiffuse.cs.meta
Normal file
12
Assets/AreaLight/Scripts/AreaLightLUT.DisneyDiffuse.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9cbf86f9b2114924a91acc43796a307b
|
||||
timeCreated: 1454419446
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
12304
Assets/AreaLight/Scripts/AreaLightLUT.GGX.cs
Normal file
12304
Assets/AreaLight/Scripts/AreaLightLUT.GGX.cs
Normal file
File diff suppressed because it is too large
Load Diff
12
Assets/AreaLight/Scripts/AreaLightLUT.GGX.cs.meta
Normal file
12
Assets/AreaLight/Scripts/AreaLightLUT.GGX.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b0041ac2ad0ef8459f7565f64f4d731
|
||||
timeCreated: 1453066169
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
68
Assets/AreaLight/Scripts/AreaLightLUT.Load.cs
Normal file
68
Assets/AreaLight/Scripts/AreaLightLUT.Load.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using UnityEngine;
|
||||
|
||||
public partial class AreaLightLUT
|
||||
{
|
||||
const int kLUTResolution = 64;
|
||||
const int kLUTMatrixDim = 3;
|
||||
|
||||
public enum LUTType
|
||||
{
|
||||
TransformInv_DisneyDiffuse,
|
||||
TransformInv_GGX,
|
||||
AmpDiffAmpSpecFresnel
|
||||
}
|
||||
|
||||
public static Texture2D LoadLUT(LUTType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case LUTType.TransformInv_DisneyDiffuse: return LoadLUT(s_LUTTransformInv_DisneyDiffuse);
|
||||
case LUTType.TransformInv_GGX: return LoadLUT(s_LUTTransformInv_GGX);
|
||||
case LUTType.AmpDiffAmpSpecFresnel: return LoadLUT(s_LUTAmplitude_DisneyDiffuse, s_LUTAmplitude_GGX, s_LUTFresnel_GGX);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static Texture2D CreateLUT(TextureFormat format, Color[] pixels)
|
||||
{
|
||||
Texture2D tex = new Texture2D(kLUTResolution, kLUTResolution, format, false /*mipmap*/, true /*linear*/);
|
||||
tex.hideFlags = HideFlags.HideAndDontSave;
|
||||
tex.wrapMode = TextureWrapMode.Clamp;
|
||||
tex.SetPixels(pixels);
|
||||
tex.Apply();
|
||||
return tex;
|
||||
}
|
||||
|
||||
static Texture2D LoadLUT(double[,] LUTTransformInv)
|
||||
{
|
||||
const int count = kLUTResolution * kLUTResolution;
|
||||
Color[] pixels = new Color[count];
|
||||
|
||||
// transformInv
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// Only columns 0, 2, 4 and 6 contain interesting values (at least in the case of GGX).
|
||||
pixels[i] = new Color( (float)LUTTransformInv[i, 0],
|
||||
(float)LUTTransformInv[i, 2],
|
||||
(float)LUTTransformInv[i, 4],
|
||||
(float)LUTTransformInv[i, 6]);
|
||||
}
|
||||
|
||||
return CreateLUT(TextureFormat.RGBAHalf, pixels);
|
||||
}
|
||||
|
||||
static Texture2D LoadLUT(float[] LUTScalar0, float[] LUTScalar1, float[] LUTScalar2)
|
||||
{
|
||||
const int count = kLUTResolution * kLUTResolution;
|
||||
Color[] pixels = new Color[count];
|
||||
|
||||
// amplitude
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
pixels[i] = new Color(LUTScalar0[i], LUTScalar1[i], LUTScalar2[i], 0);
|
||||
}
|
||||
|
||||
return CreateLUT(TextureFormat.RGBAHalf, pixels);
|
||||
}
|
||||
}
|
12
Assets/AreaLight/Scripts/AreaLightLUT.Load.cs.meta
Normal file
12
Assets/AreaLight/Scripts/AreaLightLUT.Load.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c08d6c4951e30c42a4bd6209323dfb8
|
||||
timeCreated: 1453070529
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user