-
Notifications
You must be signed in to change notification settings - Fork 21
/
OffScreenParticleCamera.cs
186 lines (159 loc) · 5.87 KB
/
OffScreenParticleCamera.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/// <summary>
/// Off Screen Particle Rendering System
/// ©2015 Disruptor Beam
/// Written by Jason Booth (slipster216@gmail.com)
/// </summary>
using UnityEngine;
using System.Collections;
using System;
[RequireComponent(typeof(Camera))]
public class OffScreenParticleCamera : MonoBehaviour
{
[Tooltip("Layer to render in low resolution")]
public LayerMask downsampleLayers;
public enum Factor
{
Full = 0,
Half = 2,
Quarter = 4,
Eighth = 8
}
[Tooltip("How much should we scale down the rendering. Lower scales have greater chances of artifacting, but better performance")]
public Factor factor = Factor.Half;
[Tooltip("Depth threshold; essentially an edge width for when to use standard bilinear instead of uv offsets")]
[Range(0.0001f, 0.01f)]
public float depthThreshold = 0.005f;
[Tooltip("Clear color for particle camera")]
public Color clearColor = new Color(0, 0, 0, 0);
[Tooltip("Shader for downsampling")]
public Shader downsampleDepthFastShader;
[Tooltip("Shader for upsampling")]
public Shader compositeShader;
private Material compositeMaterial;
private Material downsampleFastMaterial;
private Camera shaderCamera;
private Camera mCamera;
[Serializable]
public class DebugOptions
{
[Tooltip("Draws buffers in top-left side of screen for debugging")]
public bool debugDrawBuffers;
}
public DebugOptions debugOptions = new DebugOptions();
void Awake()
{
mCamera = GetComponent<Camera>();
mCamera.depthTextureMode |= DepthTextureMode.Depth;
}
RenderTexture DownsampleDepth(int ssX, int ssY, Texture src, Material mat, int downsampleFactor)
{
Vector2 offset = new Vector2(1.0f / ssX, 1.0f / ssX);
ssX /= downsampleFactor;
ssY /= downsampleFactor;
RenderTexture lowDepth = RenderTexture.GetTemporary(ssX, ssY, 0);
mat.SetVector("_PixelSize", offset);
mat.SetFloat("_MSAA", QualitySettings.antiAliasing > 0 ? 1.0f : 0.0f);
Graphics.Blit(src, lowDepth, mat);
return lowDepth;
}
void OnDisable()
{
if (compositeMaterial != null)
{
DestroyImmediate(compositeMaterial);
DestroyImmediate(downsampleFastMaterial);
}
}
void EnforceCamera()
{
shaderCamera.CopyFrom(mCamera);
shaderCamera.renderingPath = RenderingPath.Forward; // force forward
shaderCamera.cullingMask = downsampleLayers;
shaderCamera.clearFlags = CameraClearFlags.Nothing;
shaderCamera.depthTextureMode = DepthTextureMode.None;
shaderCamera.useOcclusionCulling = false;
clearColor.a = 0;
shaderCamera.backgroundColor = clearColor;
shaderCamera.clearFlags = CameraClearFlags.Color;
}
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
// make sure everything is assigned correctly
if (!enabled || compositeShader == null || downsampleDepthFastShader == null)
{
if (compositeShader == null)
{
Debug.Log("OffScreenParticle: composite shader not assigned");
}
if (downsampleDepthFastShader == null)
{
Debug.Log("OffScreenParticle: downsample shader not assigned");
}
Graphics.Blit(src, dest);
return;
}
Profiler.BeginSample("Off-Screen Particles");
// setup materials
if (compositeMaterial == null)
{
compositeMaterial = new Material(compositeShader);
}
if (downsampleFastMaterial == null)
{
downsampleFastMaterial = new Material(downsampleDepthFastShader);
}
// setup cameras
if (shaderCamera == null)
{
shaderCamera = new GameObject("ParticleCam", typeof(Camera)).GetComponent<Camera>();
shaderCamera.enabled = false;
shaderCamera.transform.parent = this.transform;
shaderCamera.targetTexture = dest;
}
// just render into the frame buffer if full..
if (factor == Factor.Full)
{
Graphics.Blit(src, dest);
shaderCamera.Render();
Profiler.EndSample();
return;
}
Profiler.BeginSample("Downsample Depth Fast");
RenderTexture lowDepth = DownsampleDepth(Screen.width, Screen.height, src, downsampleFastMaterial, (int)factor);
Profiler.EndSample();
Shader.SetGlobalTexture("_CameraDepthLowRes", lowDepth);
// render particles into buffer
Profiler.BeginSample("Render Particles");
RenderTexture particlesRT = RenderTexture.GetTemporary(Screen.width / (int)factor, Screen.height / (int)factor, 0);
EnforceCamera();
shaderCamera.targetTexture = particlesRT;
shaderCamera.Render();
Profiler.EndSample();
// composite to screen
Vector2 pixelSize = new Vector2(1.0f / lowDepth.width, 1.0f / lowDepth.height);
compositeMaterial.SetVector("_LowResPixelSize", pixelSize);
compositeMaterial.SetVector ("_LowResTextureSize", new Vector2(lowDepth.width, lowDepth.height));
compositeMaterial.SetFloat("_DepthMult", 32.0f);
compositeMaterial.SetFloat("_Threshold", depthThreshold);
compositeMaterial.SetTexture("_ParticleRT", particlesRT);
Profiler.BeginSample("Blit");
Graphics.Blit(src, dest);
Profiler.EndSample();
Profiler.BeginSample("Composite");
Graphics.Blit(particlesRT, dest, compositeMaterial);
Profiler.EndSample();
if (debugOptions.debugDrawBuffers)
{
GL.PushMatrix();
GL.LoadPixelMatrix(0, Screen.width, Screen.height, 0);
Graphics.DrawTexture(new Rect(0, 0, 128, 128), lowDepth);
Graphics.DrawTexture(new Rect(0, 128, 128, 128), src);
Graphics.DrawTexture(new Rect(128, 128, 128, 128), particlesRT);
GL.PopMatrix();
}
// cleanup
RenderTexture.ReleaseTemporary(particlesRT);
RenderTexture.ReleaseTemporary(lowDepth);
Profiler.EndSample();
}
}