Skip to content

Instantly share code, notes, and snippets.

@rc1
Created March 7, 2017 16:21
Show Gist options
  • Save rc1/fc8ffec73bea07ea4a327b42d2d6f7af to your computer and use it in GitHub Desktop.
Save rc1/fc8ffec73bea07ea4a327b42d2d6f7af to your computer and use it in GitHub Desktop.
Unity Point Lights as Image Effect
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class PointLightsImageEffect : MonoBehaviour {
private Camera _camera;
public Material EffectMaterial;
public Transform pointLight;
void OnEnable()
{
_camera = GetComponent<Camera>();
_camera.depthTextureMode = DepthTextureMode.DepthNormals;
}
[ImageEffectOpaque]
void OnRenderImage(RenderTexture src, RenderTexture dst)
{
// EffectMaterial.SetVector("_WorldSpaceScannerPos", ScannerOrigin.position);
// EffectMaterial.SetFloat("_ScanDistance", ScanDistance);
// Get the camera matrix to convert the view space normals to world normals
Matrix4x4 MV = _camera.cameraToWorldMatrix;
EffectMaterial.SetMatrix( "_CameraMV", MV);
// Update the point light
if ( pointLight != null ) {
EffectMaterial.SetVector( "_LightPosition", pointLight.position );
}
RaycastCornerBlit(src, dst, EffectMaterial);
}
void RaycastCornerBlit(RenderTexture source, RenderTexture dest, Material mat)
{
// Compute Frustum Corners
float camFar = _camera.farClipPlane;
float camFov = _camera.fieldOfView;
float camAspect = _camera.aspect;
float fovWHalf = camFov * 0.5f;
Vector3 toRight = _camera.transform.right * Mathf.Tan(fovWHalf * Mathf.Deg2Rad) * camAspect;
Vector3 toTop = _camera.transform.up * Mathf.Tan(fovWHalf * Mathf.Deg2Rad);
Vector3 topLeft = (_camera.transform.forward - toRight + toTop);
float camScale = topLeft.magnitude * camFar;
topLeft.Normalize();
topLeft *= camScale;
Vector3 topRight = (_camera.transform.forward + toRight + toTop);
topRight.Normalize();
topRight *= camScale;
Vector3 bottomRight = (_camera.transform.forward + toRight - toTop);
bottomRight.Normalize();
bottomRight *= camScale;
Vector3 bottomLeft = (_camera.transform.forward - toRight - toTop);
bottomLeft.Normalize();
bottomLeft *= camScale;
// Custom Blit, encoding Frustum Corners as additional Texture Coordinates
RenderTexture.active = dest;
mat.SetTexture("_MainTex", source);
GL.PushMatrix();
GL.LoadOrtho();
mat.SetPass(0);
GL.Begin(GL.QUADS);
GL.MultiTexCoord2(0, 0.0f, 0.0f);
GL.MultiTexCoord(1, bottomLeft);
GL.Vertex3(0.0f, 0.0f, 0.0f);
GL.MultiTexCoord2(0, 1.0f, 0.0f);
GL.MultiTexCoord(1, bottomRight);
GL.Vertex3(1.0f, 0.0f, 0.0f);
GL.MultiTexCoord2(0, 1.0f, 1.0f);
GL.MultiTexCoord(1, topRight);
GL.Vertex3(1.0f, 1.0f, 0.0f);
GL.MultiTexCoord2(0, 0.0f, 1.0f);
GL.MultiTexCoord(1, topLeft);
GL.Vertex3(0.0f, 1.0f, 0.0f);
GL.End();
GL.PopMatrix();
}
}
Shader "PointLightsImageEffect"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_LightColor ("Light Color", Color) = ( 1.0, 1.0, 1.0 )
_LightPosition ("Light Position", Vector) = ( 0.0, 0.0, 0.0 )
_LightRange ("Light Range", Float) = 1.0
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct VertIn
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 ray : TEXCOORD1;
};
struct VertOut
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float2 uv_depth : TEXCOORD1;
float4 interpolatedRay : TEXCOORD2;
};
VertOut vert (VertIn v)
{
VertOut o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.uv;
o.uv_depth = v.uv.xy;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
o.uv.y = 1 - o.uv.y;
#endif
o.interpolatedRay = v.ray;
return o;
}
sampler2D _MainTex;
// Provided by the camera
sampler2D_float _CameraDepthNormalsTexture;
// Provided by the material property
float4x4 _CameraMV;
float3 _LightPosition;
float4 _LightColor;
float _LightRange;
float3 lightDistanceNormalised ( float3 surfacePosition, float3 lightPosition, float lightRange ) {
float dis = distance( surfacePosition, lightPosition );
return 1.0 - clamp( dis / lightRange, 0.0, 1.0 );
}
fixed4 frag (VertOut i) : SV_Target
{
float4 finalColor;
// Get the surface color
float4 surfaceColor = tex2D(_MainTex, i.uv);
// Get the depth & normal
float rawDepth = 1.0;
float3 viewSpaceNormal;
DecodeDepthNormal( tex2D(_CameraDepthNormalsTexture, i.uv_depth.xy), rawDepth, viewSpaceNormal );
// Get the world position
float4 directionWorldSpace = rawDepth * i.interpolatedRay;
float3 surfaceWorldPosition = _WorldSpaceCameraPos + directionWorldSpace;
// Get the world normal
float3 surfaceWorldNormal = mul((float3x3)_CameraMV, viewSpaceNormal);
// Return the distance from the light
float distanceFromLight = clamp( 1.0 - distance( surfaceWorldPosition, _LightPosition ) / _LightRange, 0, 1 );
// Get the vector from the surface to the light
float3 surfaceToLight = _LightPosition - surfaceWorldPosition;
// Get the brightness of the surface (cosine of the angle of incidence). 1 means it is facing the light. 0 means it's not
float brightness = dot(surfaceWorldNormal, surfaceToLight) / (length(surfaceToLight) * length(surfaceWorldNormal));
brightness = clamp( brightness, 0, 1 );
// Get the intensity of the light based on the distance and the direction
float intensity = lerp( 0, brightness, distanceFromLight );
// Add the light to the surface color... there will be better ways
finalColor = surfaceColor + ( _LightColor * intensity );
return finalColor;
}
ENDCG
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment