Skip to content

Instantly share code, notes, and snippets.

@rc1
Created March 7, 2017 16:21

Revisions

  1. rc1 created this gist Mar 7, 2017.
    99 changes: 99 additions & 0 deletions PointLightsImageEffect.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,99 @@
    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();
    }
    }
    113 changes: 113 additions & 0 deletions PointLightsImageEffect.shader
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,113 @@
    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
    }
    }
    }