forked from yaoya/ScriptableRenderLoop
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBasicRenderLoop.cs
More file actions
190 lines (165 loc) · 8.14 KB
/
Copy pathBasicRenderLoop.cs
File metadata and controls
190 lines (165 loc) · 8.14 KB
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
187
188
189
190
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
// Very basic scriptable rendering loop example:
// - Use with BasicRenderLoopShader.shader (the loop expects "BasicPass" pass type to exist)
// - Supports up to 8 enabled lights in the scene (directional, point or spot)
// - Does the same physically based BRDF as the Standard shader
// - No shadows
// - This loop also does not setup lightmaps, light probes, reflection probes or light cookies
[ExecuteInEditMode]
public class BasicRenderLoop : RenderPipeline
{
#if UNITY_EDITOR
[UnityEditor.MenuItem("Renderloop/Create BasicRenderLoop")]
static void CreateBasicRenderLoop()
{
var instance = ScriptableObject.CreateInstance<BasicRenderLoop>();
UnityEditor.AssetDatabase.CreateAsset(instance, "Assets/BasicRenderLoopTutorial/BasicRenderLoop.asset");
}
#endif
private ShaderPassName shaderPassBasic;
public void OnEnable()
{
Rebuild();
}
public override void Initialize()
{
shaderPassBasic = new ShaderPassName("BasicPass");
}
// Main entry point for our scriptable render loop
public override void Render(Camera[] cameras, RenderLoop loop)
{
foreach (var camera in cameras)
{
// Culling
CullingParameters cullingParams;
if (!CullResults.GetCullingParameters (camera, out cullingParams))
continue;
CullResults cull = CullResults.Cull (ref cullingParams, loop);
// Setup camera for rendering (sets render target, view/projection matrices and other
// per-camera built-in shader variables).
loop.SetupCameraProperties (camera);
// clear depth buffer
var cmd = new CommandBuffer();
cmd.ClearRenderTarget(true, false, Color.black);
loop.ExecuteCommandBuffer(cmd);
cmd.Release();
// Setup global lighting shader variables
SetupLightShaderVariables (cull.visibleLights, loop);
// Draw opaque objects using BasicPass shader pass
var settings = new DrawRendererSettings (cull, camera, shaderPassBasic);
settings.sorting.flags = SortFlags.CommonOpaque;
settings.inputFilter.SetQueuesOpaque ();
loop.DrawRenderers (ref settings);
// Draw skybox
loop.DrawSkybox (camera);
// Draw transparent objects using BasicPass shader pass
settings.sorting.flags = SortFlags.CommonTransparent;
settings.inputFilter.SetQueuesTransparent ();
loop.DrawRenderers (ref settings);
loop.Submit ();
}
}
// Setup lighting variables for shader to use
static void SetupLightShaderVariables (VisibleLight[] lights, RenderLoop loop)
{
// We only support up to 8 visible lights here. More complex approaches would
// be doing some sort of per-object light setups, but here we go for simplest possible
// approach.
const int kMaxLights = 8;
// Just take first 8 lights. Possible improvements: sort lights by intensity or distance
// to the viewer, so that "most important" lights in the scene are picked, and not the 8
// that happened to be first.
int lightCount = Mathf.Min (lights.Length, kMaxLights);
// Prepare light data
Vector4[] lightColors = new Vector4[kMaxLights];
Vector4[] lightPositions = new Vector4[kMaxLights];
Vector4[] lightSpotDirections = new Vector4[kMaxLights];
Vector4[] lightAtten = new Vector4[kMaxLights];
for (var i = 0; i < lightCount; ++i)
{
VisibleLight light = lights[i];
lightColors[i] = light.finalColor;
if (light.lightType == LightType.Directional)
{
// light position for directional lights is: (-direction, 0)
var dir = light.localToWorld.GetColumn (2);
lightPositions[i] = new Vector4 (-dir.x, -dir.y, -dir.z, 0);
}
else
{
// light position for point/spot lights is: (position, 1)
var pos = light.localToWorld.GetColumn (3);
lightPositions[i] = new Vector4 (pos.x, pos.y, pos.z, 1);
}
// attenuation set in a way where distance attenuation can be computed:
// float lengthSq = dot(toLight, toLight);
// float atten = 1.0 / (1.0 + lengthSq * LightAtten[i].z);
// and spot cone attenuation:
// float rho = max (0, dot(normalize(toLight), SpotDirection[i].xyz));
// float spotAtt = (rho - LightAtten[i].x) * LightAtten[i].y;
// spotAtt = saturate(spotAtt);
// and the above works for all light types, i.e. spot light code works out
// to correct math for point & directional lights as well.
float rangeSq = light.range * light.range;
float quadAtten = (light.lightType == LightType.Directional) ? 0.0f : 25.0f / rangeSq;
// spot direction & attenuation
if (light.lightType == LightType.Spot)
{
var dir = light.localToWorld.GetColumn (2);
lightSpotDirections[i] = new Vector4 (-dir.x, -dir.y, -dir.z, 0);
float radAngle = Mathf.Deg2Rad * light.spotAngle;
float cosTheta = Mathf.Cos (radAngle * 0.25f);
float cosPhi = Mathf.Cos (radAngle * 0.5f);
float cosDiff = cosTheta - cosPhi;
lightAtten[i] = new Vector4 (cosPhi, (cosDiff != 0.0f) ? 1.0f / cosDiff : 1.0f, quadAtten, rangeSq);
}
else
{
// non-spot light
lightSpotDirections[i] = new Vector4 (0, 0, 1, 0);
lightAtten[i] = new Vector4 (-1, 1, quadAtten, rangeSq);
}
}
// ambient lighting spherical harmonics values
const int kSHCoefficients = 7;
Vector4[] shConstants = new Vector4[kSHCoefficients];
SphericalHarmonicsL2 ambientSH = RenderSettings.ambientProbe * RenderSettings.ambientIntensity;
GetShaderConstantsFromNormalizedSH (ref ambientSH, shConstants);
// setup global shader variables to contain all the data computed above
CommandBuffer cmd = new CommandBuffer();
cmd.SetGlobalVectorArray ("globalLightColor", lightColors);
cmd.SetGlobalVectorArray ("globalLightPos", lightPositions);
cmd.SetGlobalVectorArray ("globalLightSpotDir", lightSpotDirections);
cmd.SetGlobalVectorArray ("globalLightAtten", lightAtten);
cmd.SetGlobalVector ("globalLightCount", new Vector4 (lightCount, 0, 0, 0));
cmd.SetGlobalVectorArray ("globalSH", shConstants);
loop.ExecuteCommandBuffer (cmd);
cmd.Dispose ();
}
// Prepare L2 spherical harmonics values for efficient evaluation in a shader
static void GetShaderConstantsFromNormalizedSH (ref SphericalHarmonicsL2 ambientProbe, Vector4[] outCoefficients)
{
for (int channelIdx = 0; channelIdx < 3; ++channelIdx)
{
// Constant + Linear
// In the shader we multiply the normal is not swizzled, so it's normal.xyz.
// Swizzle the coefficients to be in { x, y, z, DC } order.
outCoefficients[channelIdx].x = ambientProbe[channelIdx, 3];
outCoefficients[channelIdx].y = ambientProbe[channelIdx, 1];
outCoefficients[channelIdx].z = ambientProbe[channelIdx, 2];
outCoefficients[channelIdx].w = ambientProbe[channelIdx, 0] - ambientProbe[channelIdx, 6];
// Quadratic polynomials
outCoefficients[channelIdx + 3].x = ambientProbe[channelIdx, 4];
outCoefficients[channelIdx + 3].y = ambientProbe[channelIdx, 5];
outCoefficients[channelIdx + 3].z = ambientProbe[channelIdx, 6] * 3.0f;
outCoefficients[channelIdx + 3].w = ambientProbe[channelIdx, 7];
}
// Final quadratic polynomial
outCoefficients[6].x = ambientProbe[0, 8];
outCoefficients[6].y = ambientProbe[1, 8];
outCoefficients[6].z = ambientProbe[2, 8];
outCoefficients[6].w = 1.0f;
}
}