-
-
Save belzecue/dbb565692499413f7b323e533511144d to your computer and use it in GitHub Desktop.
Doom glow - fake volumetric light glow effect in Unity by Tili_us
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
// This file is in the public domain. Where | |
// a public domain declaration is not recognized, you are granted | |
// a license to freely use, modify, and redistribute this file in | |
// any way you choose. | |
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
// Unity remake of a fake volumetric light glow effect | |
// DoomGlow code by tim.tili.sabo@gmail.com -- http://yzergame.com/doomGlare.html | |
// Ideas by Simon https://simonschreibt.de/gat/doom-3-volumetric-glow/ | |
public class DoomGlow : MonoBehaviour | |
{ | |
public Color color = new Color(0.0f, 1.0f, 1.0f, 0.95f); | |
public float pushDistance = 0.3f; | |
public bool showBottom = false; | |
const int edgeCount = 4; | |
const int triCount = 18 * 3; | |
Vector3[] quadPoints = new Vector3[edgeCount]; | |
Vector3 centerPoint; | |
Vector4[] screenQuadPoints = new Vector4[edgeCount]; | |
MeshFilter meshFilter; | |
Mesh mesh; | |
int[] triBuffer = new int[triCount]; | |
Vector3[] vertBuffer = new Vector3[triCount]; | |
Color[] colorBuffer = new Color[triCount]; | |
// helper struct | |
private class EdgePush { | |
public Vector3 test1; | |
public Vector3 test2; | |
public Vector3 w1; | |
public Vector3 w2; | |
public Vector3 sharedPoint; | |
} | |
EdgePush[] pushedEdges = new EdgePush[4]; | |
void Start() | |
{ | |
quadPoints[0] = new Vector3(0, 0, 0); | |
quadPoints[1] = new Vector3(1, 0, 0); | |
quadPoints[2] = new Vector3(1, 0, 1); | |
quadPoints[3] = new Vector3(0, 0, 1); | |
centerPoint = quadPoints[0] + quadPoints[1] + quadPoints[2] + quadPoints[3]; | |
centerPoint *= 0.25f; | |
meshFilter = GetComponent<MeshFilter>(); | |
mesh = new Mesh(); | |
meshFilter.mesh = mesh; | |
pushedEdges[0] = new EdgePush(); | |
pushedEdges[1] = new EdgePush(); | |
pushedEdges[2] = new EdgePush(); | |
pushedEdges[3] = new EdgePush(); | |
// because I can't be arsed to re-use verts | |
for (int i = 0; i < triCount; ++i) { | |
triBuffer[i] = i; | |
} | |
} | |
// Optimised to allocate zero bytes per frame | |
void Update() | |
{ | |
//lets project the points to screen space | |
for (int i = 0; i < quadPoints.Length; ++i) | |
{ | |
screenQuadPoints[i] = GetScreenFromWorld(quadPoints[i]); | |
} | |
// get normal of quad | |
Vector3 u = quadPoints[1] - quadPoints[0]; | |
Vector3 v = quadPoints[2] - quadPoints[0]; | |
Vector3 normal = Vector3.Cross(u, v); | |
normal.Normalize(); | |
float dot = Vector3.Dot((centerPoint - Camera.main.transform.position).normalized, normal); | |
float alpha = LinearMap(dot, 0.001f, 0.1f, 0.0f, 1.0f); | |
if (showBottom) { | |
alpha = LinearMap(Mathf.Abs(dot), 0.001f, 0.1f, 0.0f, 1.0f); | |
} | |
// now calculate the directions for each line of the quad | |
for (int i = 0; i < edgeCount; ++i) { | |
Vector3 diff = screenQuadPoints[(i + 1)%edgeCount] - screenQuadPoints[i]; | |
Vector2 edge = new Vector2(diff.x, diff.y); | |
// Find the axis perpendicular to the current edge | |
Vector2 axis = new Vector2(edge.y, -edge.x); | |
axis.Normalize(); | |
// flip the axis when looking at the bottom | |
if (showBottom && dot < 0) { | |
axis *= -1.0f; | |
} | |
// push the edge away using that axis and store those points | |
Vector2 p1 = ToVec2(screenQuadPoints[i]) + axis * pushDistance; | |
Vector2 p2 = ToVec2(screenQuadPoints[(i + 1)%edgeCount]) + axis * pushDistance; | |
Vector3 pushedPointInWS1 = GetWorldFromScreen(new Vector4(p1.x, p1.y, screenQuadPoints[i].z, 1.0f)); | |
Vector3 pushedPointInWS2 = GetWorldFromScreen(new Vector4(p2.x, p2.y, screenQuadPoints[(i + 1)%edgeCount].z, 1.0f)); | |
pushedEdges[i].test1 = pushedPointInWS1; | |
pushedEdges[i].test2 = pushedPointInWS2; | |
Vector3 pushedPoint1 = pushedPointInWS1 - quadPoints[i]; | |
pushedPoint1.Normalize(); // find the world space direction of the point and normalize it | |
pushedEdges[i].w1 = quadPoints[i] + pushedPoint1 * pushDistance; // now we push in world space! | |
Vector3 pushedPoint = pushedPointInWS2 - quadPoints[(i + 1)%edgeCount]; | |
pushedPoint.Normalize(); // find the world space direction of the point and normalize it | |
pushedEdges[i].w2 = quadPoints[(i + 1)%edgeCount] + pushedPoint * pushDistance; // now we push in world space! | |
} | |
// this needs data from other edges so it is in another loop | |
for (int i = 0; i < edgeCount; ++i) { | |
// calc averaged point between the 2 edges | |
Vector3 p = pushedEdges[i].w1 + (pushedEdges[(i+edgeCount-1)%edgeCount].w2 - pushedEdges[i].w1) * 0.5f; // this finds the world space point halfway between 2 points | |
Vector3 dir = p - quadPoints[i]; // get the direction from the point itself | |
dir.Normalize(); | |
pushedEdges[i].sharedPoint = quadPoints[i] + dir * pushDistance; // now we push in world space! | |
} | |
// fill buffers to store vertex info | |
int currentIndex = 0; | |
// setup some colors | |
Color colorFilled = new Color(1.0f, 1.0f, 1.0f, alpha * color.a); | |
Color colorEdge = new Color(color.r, color.g, color.b, 0.0f); | |
// fill the original quad | |
FillBuffer(quadPoints[0], ref colorFilled, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(quadPoints[1], ref colorFilled, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(quadPoints[2], ref colorFilled, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(quadPoints[0], ref colorFilled, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(quadPoints[2], ref colorFilled, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(quadPoints[3], ref colorFilled, ref currentIndex, vertBuffer, colorBuffer); | |
for (int i = 0; i < edgeCount; ++i) { | |
// render pushed quads | |
FillBuffer(quadPoints[i], ref colorFilled, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(pushedEdges[i].w1, ref colorEdge, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(quadPoints[(i + 1)%edgeCount], ref colorFilled, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(pushedEdges[i].w1, ref colorEdge, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(pushedEdges[i].w2, ref colorEdge, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(quadPoints[(i + 1)%edgeCount], ref colorFilled, ref currentIndex, vertBuffer, colorBuffer); | |
// render connections between the newly generated quads | |
FillBuffer(pushedEdges[i].sharedPoint, ref colorEdge, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(pushedEdges[i].w1, ref colorEdge, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(quadPoints[i], ref colorFilled, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(pushedEdges[i].sharedPoint, ref colorEdge, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(quadPoints[i], ref colorFilled, ref currentIndex, vertBuffer, colorBuffer); | |
FillBuffer(pushedEdges[(i+edgeCount-1)%edgeCount].w2, ref colorEdge, ref currentIndex, vertBuffer, colorBuffer); | |
} | |
// fill mesh | |
mesh.vertices = vertBuffer; | |
mesh.colors = colorBuffer; | |
mesh.triangles = triBuffer; | |
mesh.RecalculateBounds(); | |
meshFilter.mesh = mesh; | |
} | |
Vector4 GetScreenFromWorld(Vector3 world) | |
{ | |
return Camera.main.WorldToScreenPoint(world); | |
} | |
Vector3 GetWorldFromScreen(Vector4 screen) | |
{ | |
return Camera.main.ScreenToWorldPoint(screen); | |
} | |
Vector2 ToVec2(Vector3 v) { | |
return new Vector2(v.x, v.y); | |
} | |
void FillBuffer(Vector3 pos, ref Color color, ref int currentIndex, Vector3[] vertBuffer, Color[] colorBuffer) { | |
vertBuffer[currentIndex] = pos; | |
colorBuffer[currentIndex] = color; | |
currentIndex++; | |
} | |
float LinearMap(float inVal, float inFrom, float inTo, float outFrom, float outTo) | |
{ | |
float inScale = (inFrom != inTo) ? ((inVal - inFrom) / (inTo - inFrom)) : 0.0f; | |
inScale = Mathf.Clamp(inScale, 0.0f, 1.0f); | |
return Mathf.Lerp(outFrom, outTo, inScale); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment