Skip to content

Instantly share code, notes, and snippets.

@Farfarer
Created March 8, 2013 08:57
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Farfarer/158b23f694c2c995f93e to your computer and use it in GitHub Desktop.
Save Farfarer/158b23f694c2c995f93e to your computer and use it in GitHub Desktop.
Lit Particles in Unity. More files and revisions to be added. Apply Camera2World.js to your Cameras. Use one of the shaders on your particles. This will not work in the Scene viewport or in Game viewport if the game is not playing. It will only work in the Game viewport if the game is playing.
// Helper macros specific to particles for lighting and shadows.
// Required.
// Keep this in the same directory as the other lit particle shader files.
#ifdef AUTOLIGHT_INCLUDED
// ---- Screen space shadows
#if defined (SHADOWS_SCREEN)
#define TRANSFER_SHADOW_PARTICLE(a) a._ShadowCoord = ComputeScreenPos(o.pos);
#endif
// ---- Depth map shadows
#if defined (SHADOWS_DEPTH) && defined (SPOT)
#define TRANSFER_SHADOW_PARTICLE(a) a._ShadowCoord = mul (unity_World2Shadow, posW);
#endif
// ---- Point light shadows
#if defined (SHADOWS_CUBE)
#define TRANSFER_SHADOW_PARTICLE(a) a._ShadowCoord = posW - _LightPositionRange.xyz;
#endif
// ---- Shadows off
#if !defined (SHADOWS_SCREEN) && !defined (SHADOWS_DEPTH) && !defined (SHADOWS_CUBE)
#define TRANSFER_SHADOW_PARTICLE(a)
#endif
// ------------ Light helpers --------
#ifdef POINT
#define TRANSFER_VERTEX_TO_FRAGMENT_PARTICLE(a) a._LightCoord = mul(_LightMatrix0, posW).xyz; TRANSFER_SHADOW_PARTICLE(a)
#endif
#ifdef SPOT
#define TRANSFER_VERTEX_TO_FRAGMENT_PARTICLE(a) a._LightCoord = mul(_LightMatrix0, posW); TRANSFER_SHADOW_PARTICLE(a)
#endif
#ifdef DIRECTIONAL
#define TRANSFER_VERTEX_TO_FRAGMENT_PARTICLE(a) TRANSFER_SHADOW_PARTICLE(a)
#endif
#ifdef POINT_COOKIE
#define TRANSFER_VERTEX_TO_FRAGMENT_PARTICLE(a) a._LightCoord = mul(_LightMatrix0, posW).xyz; TRANSFER_SHADOW_PARTICLE(a)
#endif
#ifdef DIRECTIONAL_COOKIE
#define TRANSFER_VERTEX_TO_FRAGMENT_PARTICLE(a) a._LightCoord = mul(_LightMatrix0, posW).xy; TRANSFER_SHADOW_PARTICLE(a)
#endif
#endif //AUTOLIGHT_INCLUDED
#ifdef UNITY_CG_INCLUDED
// Light direction from world position
inline float3 WorldSpaceLightDirFromWorldPos( in float3 worldPos )
{
#ifndef USING_LIGHT_MULTI_COMPILE
return _WorldSpaceLightPos0.xyz - worldPos * _WorldSpaceLightPos0.w;
#else
#ifndef USING_DIRECTIONAL_LIGHT
return _WorldSpaceLightPos0.xyz - worldPos;
#else
return _WorldSpaceLightPos0.xyz;
#endif
#endif
}
// Shadow caster pass helpers
// Currently unused as particles billboard and so cast shadows on themselves.
#ifdef SHADOWS_CUBE
#define TRANSFER_SHADOW_CASTER_PARTICLE(o) o.vec = mul(_Camera2World, v.vertex).xyz - _LightPositionRange.xyz; o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
#else
#if defined(UNITY_MIGHT_NOT_HAVE_DEPTH_TEXTURE)
#define TRANSFER_SHADOW_CASTER_PARTICLE(o) o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.pos.z += unity_LightShadowBias.x; \
float clamped = max(o.pos.z, o.pos.w*UNITY_NEAR_CLIP_VALUE); o.pos.z = lerp(o.pos.z, clamped, unity_LightShadowBias.y); o.hpos = o.pos;
#else
#define TRANSFER_SHADOW_CASTER_PARTICLE(o) o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.pos.z += unity_LightShadowBias.x; \
float clamped = max(o.pos.z, o.pos.w*UNITY_NEAR_CLIP_VALUE); o.pos.z = lerp(o.pos.z, clamped, unity_LightShadowBias.y);
#endif
#endif
// Shadow collector pass helpers
#ifdef SHADOW_COLLECTOR_PASS
#define TRANSFER_SHADOW_COLLECTOR_PARTICLE(o) \
o.pos = mul(UNITY_MATRIX_MVP, v.vertex); \
float4 wpos = mul(_Camera2World, v.vertex); \
float4 opos = mul(_World2Object, wpos); \
o._WorldPosViewZ.xyz = wpos; \
o._WorldPosViewZ.w = -mul( UNITY_MATRIX_MV, v.vertex ).z; \
o._ShadowCoord0 = mul(unity_World2Shadow[0], wpos).xyz; \
o._ShadowCoord1 = mul(unity_World2Shadow[1], wpos).xyz; \
o._ShadowCoord2 = mul(unity_World2Shadow[2], wpos).xyz; \
o._ShadowCoord3 = mul(unity_World2Shadow[3], wpos).xyz;
#endif
#endif //UNITY_CG_INCLUDED
#pragma strict
// Apply this to all of the cameras that will be rendering lit particles.
function OnPreCull () {
Shader.SetGlobalMatrix ("_Camera2World", this.camera.cameraToWorldMatrix );
}
// Additive particle.
// Does not receive shadows.
Shader "Custom/Lit Particle Additive" {
Properties {
_Color ("Main Color", Color) = (1.0, 1.0, 1.0, 1.0)
_MainTex ("Color (RGBA)", 2D) = "white" {}
}
SubShader {
Pass {
Tags {"Queue" = "Transparent" "RenderType" = "Transparent" "LightMode" = "ForwardBase"}
Blend One One
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#include "AutoLightParticle.cginc"
#pragma target 3.0
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
float3 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 normal : TEXCOORD1;
float3 lightDir : TEXCOORD2;
LIGHTING_COORDS(3,4)
};
float4x4 _Camera2World;
uniform float4 _MainTex_ST;
v2f vert (appdata v) {
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.normal = v.normal;
float4 posW = mul (_Camera2World, v.vertex);
o.lightDir = WorldSpaceLightDirFromWorldPos(posW.xyz);
TRANSFER_VERTEX_TO_FRAGMENT_PARTICLE(o);
return o;
}
float4 _LightColor0;
fixed4 _Color;
sampler2D _MainTex;
fixed4 frag(v2f i) : COLOR {
fixed4 c = tex2D( _MainTex, i.uv ) * _Color;
fixed atten = LIGHT_ATTENUATION(i);
fixed NdotL = dot(i.normal, i.lightDir) * 0.5 + 0.5;
c.rgb *= UNITY_LIGHTMODEL_AMBIENT * 2;
c.rgb += c.rgb * _LightColor0.rgb * NdotL * atten * 2;
return c;
}
ENDCG
}
Pass {
Tags {"Queue" = "Transparent" "RenderType" = "Transparent" "LightMode" = "ForwardAdd"}
Blend One One
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdadd
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#include "AutoLightParticle.cginc"
#pragma target 3.0
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
float3 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 normal : TEXCOORD1;
float3 lightDir : TEXCOORD2;
LIGHTING_COORDS(3,4)
};
float4x4 _Camera2World;
uniform float4 _MainTex_ST;
v2f vert (appdata v) {
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.normal = v.normal;
float4 posW = mul (_Camera2World, v.vertex);
o.lightDir = WorldSpaceLightDirFromWorldPos(posW.xyz);
TRANSFER_VERTEX_TO_FRAGMENT_PARTICLE(o);
return o;
}
float4 _LightColor0;
fixed4 _Color;
sampler2D _MainTex;
fixed4 frag(v2f i) : COLOR {
fixed4 c = tex2D( _MainTex, i.uv ) * _Color;
fixed atten = LIGHT_ATTENUATION(i);
fixed NdotL = dot(i.normal, i.lightDir) * 0.5 + 0.5;
c.rgb *= _LightColor0.rgb * NdotL * atten * 2;
return c;
}
ENDCG
}
}
FallBack Off
}
// Cutout particle.
// Receives shadows.
Shader "Custom/Lit Particle Cutout" {
Properties {
_Color ("Main Color", Color) = (1.0, 1.0, 1.0, 1.0)
_MainTex ("Color (RGBA)", 2D) = "white" {}
_Cutoff ("Alpha Cutoff", Range(0,1) ) = 0.5
}
SubShader {
Pass {
Tags {"Queue" = "Geometry" "RenderType" = "TransparentCutout" "LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#include "AutoLightParticle.cginc"
#pragma target 3.0
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
float3 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 normal : TEXCOORD1;
float3 lightDir : TEXCOORD2;
LIGHTING_COORDS(3,4)
};
float4x4 _Camera2World;
uniform float4 _MainTex_ST;
v2f vert (appdata v) {
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.normal = v.normal;
float4 posW = mul (_Camera2World, v.vertex);
o.lightDir = WorldSpaceLightDirFromWorldPos(posW.xyz);
TRANSFER_VERTEX_TO_FRAGMENT_PARTICLE(o);
return o;
}
float4 _LightColor0;
fixed4 _Color;
sampler2D _MainTex;
float _Cutoff;
fixed4 frag(v2f i) : COLOR {
fixed4 c = tex2D( _MainTex, i.uv ) * _Color;
clip( c.a - _Cutoff );
fixed atten = LIGHT_ATTENUATION(i);
fixed NdotL = saturate(dot(i.normal, i.lightDir));
c.rgb *= UNITY_LIGHTMODEL_AMBIENT * 2;
c.rgb += c.rgb * _LightColor0.rgb * NdotL * atten * 2;
return c;
}
ENDCG
}
Pass {
Tags {"Queue" = "Geometry" "RenderType" = "TransparentCutout" "LightMode" = "ForwardAdd"}
Blend One One
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdadd
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#include "AutoLightParticle.cginc"
#pragma target 3.0
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
float3 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 normal : TEXCOORD1;
float3 lightDir : TEXCOORD2;
LIGHTING_COORDS(3,4)
};
float4x4 _Camera2World;
uniform float4 _MainTex_ST;
v2f vert (appdata v) {
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.normal = v.normal;
float4 posW = mul (_Camera2World, v.vertex);
o.lightDir = WorldSpaceLightDirFromWorldPos(posW.xyz);
TRANSFER_VERTEX_TO_FRAGMENT_PARTICLE(o);
return o;
}
float4 _LightColor0;
fixed4 _Color;
sampler2D _MainTex;
float _Cutoff;
fixed4 frag(v2f i) : COLOR {
fixed4 c = tex2D( _MainTex, i.uv ) * _Color;
clip( c.a - _Cutoff );
fixed atten = LIGHT_ATTENUATION(i);
fixed NdotL = dot(i.normal, i.lightDir) * 0.5 + 0.5;
c.rgb *= _LightColor0.rgb * NdotL * atten * 2;
return c;
}
ENDCG
}
// SHADOW CASTER
// Currently removed as particles billboard towards the camera and so end up casting shadows on themselves.
/*
Pass {
Name "Caster"
Tags { "LightMode" = "ShadowCaster" }
Offset 1, 1
Fog {Mode Off}
ZWrite On ZTest LEqual Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
#include "AutoLightParticle.cginc"
struct v2f {
V2F_SHADOW_CASTER;
float2 uv : TEXCOORD1;
};
uniform float4 _MainTex_ST;
float4x4 _Camera2World;
v2f vert( appdata_base v )
{
v2f o;
TRANSFER_SHADOW_CASTER_PARTICLE(o)
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
uniform fixed4 _Color;
float4 frag( v2f i ) : COLOR
{
fixed4 texcol = tex2D( _MainTex, i.uv );
clip( texcol.a*_Color.a - _Cutoff );
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}*/
// SHADOW COLLECTOR
Pass {
Name "ShadowCollector"
Tags { "LightMode" = "ShadowCollector" }
Fog {Mode Off}
ZWrite On ZTest LEqual
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#pragma multi_compile_shadowcollector
#define SHADOW_COLLECTOR_PASS
#include "UnityCG.cginc"
#include "AutoLightParticle.cginc"
struct v2f {
V2F_SHADOW_COLLECTOR;
float2 uv : TEXCOORD5;
};
uniform float4 _MainTex_ST;
float4x4 _Camera2World;
v2f vert (appdata_base v)
{
v2f o;
TRANSFER_SHADOW_COLLECTOR_PARTICLE(o)
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
uniform fixed4 _Color;
fixed4 frag (v2f i) : COLOR
{
fixed4 texcol = tex2D( _MainTex, i.uv );
clip( texcol.a*_Color.a - _Cutoff );
SHADOW_COLLECTOR_FRAGMENT(i)
}
ENDCG
}
}
FallBack Off
}
@Kadae
Copy link

Kadae commented Apr 16, 2021

C#

private Camera _camera;

void Awake()
{
    _camera = GetComponent<Camera>();
}

private void OnPreCull () {
    Shader.SetGlobalMatrix ("_Camera2World", _camera.cameraToWorldMatrix );
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment