Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
LightingPrepassPlugin.cs
Go to the documentation of this file.
1 // Copyright (c) 2011 Silicon Studio
2 
3 using System;
4 using System.Collections.Generic;
5 using SiliconStudio.Paradox.DataModel;
6 using SiliconStudio.Paradox.Effects.Modules;
7 using SiliconStudio.Paradox.Games;
8 using SiliconStudio.Paradox.Graphics;
9 using SiliconStudio.Core;
10 using SiliconStudio.Core.Collections;
11 using SiliconStudio.Core.Mathematics;
13 
14 namespace SiliconStudio.Paradox.Effects
15 {
16  /// <summary>
17  /// Deffered lighting plugin.
18  /// </summary>
20  {
21  private EffectOld[] lightDeferredEffects = new EffectOld[2];
22  private RenderPass debugRenderPass;
23  private IGraphicsDeviceService graphicsDeviceService;
24 
25  internal List<LightingPrepassShaderPlugin.LightData>[] Tiles;
26 
27  public const int TileCountX = 16;
28  public const int TileCountY = 10;
29  public const int MaxLightsPerTileDrawCall = 64;
30 
31  /// <summary>
32  /// Initializes a new instance of the <see cref="LightingPrepassPlugin"/> class.
33  /// </summary>
34  public LightingPrepassPlugin() : this("DefferedLighting")
35  {
36  }
37 
38  /// <summary>
39  /// Initializes a new instance of the <see cref="LightingPrepassPlugin"/> class.
40  /// </summary>
41  /// <param name="name">The name.</param>
42  public LightingPrepassPlugin(string name) : base(name)
43  {
44  }
45 
46  internal RenderTarget LightTexture { get; set; }
47 
48  public EffectOld Lights { get; private set; }
49 
50  public string[] BasePlugins { get; set; }
51 
52  public GBufferPlugin GBufferPlugin { get; set; }
53 
54  public override void Initialize()
55  {
56  base.Initialize();
57  graphicsDeviceService = Services.GetSafeServiceAs<IGraphicsDeviceService>();
58  }
59 
60  public override void Load()
61  {
62  base.Load();
63 
64  Parameters.AddSources(GBufferPlugin.MainPlugin.ViewParameters);
65 
66  Lights = this.EffectSystemOld.BuildEffect("Lights").Using(new LightPlugin()).InstantiatePermutation();
67 
68  // TODO: Check if released properly.
69  for (int i = 0; i < 2; ++i)
70  {
71  if (i == 1)
72  {
73  if (!Debug)
74  continue;
75 
76  // Add the debug as an overlay on the main pass
77  debugRenderPass = new RenderPass("LightPrePassDebug").KeepAliveBy(ActiveObjects);
78  GBufferPlugin.MainPlugin.RenderPass.AddPass(debugRenderPass);
79  }
80 
81  var debug = i == 1;
82  var renderPass = i == 1 ? debugRenderPass : RenderPass;
83 
84  var lightDeferredEffectBuilder = this.EffectSystemOld.BuildEffect("LightPrePass" + (debug ? "Debug" : string.Empty)).KeepAliveBy(ActiveObjects);
85  foreach (var effectPlugin in BasePlugins)
86  {
87  lightDeferredEffectBuilder.Using(new BasicShaderPlugin(effectPlugin) { Services = Services, RenderPassPlugin = this, RenderPass = renderPass });
88  }
89  lightDeferredEffectBuilder.Using(new LightingPrepassShaderPlugin("LightPreShaderPass" + (debug ? "Debug" : string.Empty)) { Services = Services, RenderPassPlugin = this, RenderPass = renderPass, Debug = debug });
90 
91  lightDeferredEffects[i] = lightDeferredEffectBuilder.InstantiatePermutation().KeepAliveBy(ActiveObjects);
92  }
93 
94  if (OfflineCompilation)
95  return;
96 
97  // Create lighting accumulation texture (that LightPlugin will use)
98  var mainBackBuffer = graphicsDeviceService.GraphicsDevice.BackBuffer;
99  var lightTexture = Texture2D.New(GraphicsDevice, mainBackBuffer.Width, mainBackBuffer.Height, PixelFormat.R16G16B16A16_Float, TextureFlags.ShaderResource | TextureFlags.RenderTarget);
100  lightTexture.Name = "LightTexture";
101  LightTexture = lightTexture.ToRenderTarget();
102 
103  // Set Parameters for this plugin
104  Parameters.Set(LightDeferredShadingKeys.LightTexture, lightTexture);
105 
106  // Set GBuffer Texture0
107  Parameters.Set(GBufferBaseKeys.GBufferTexture, (Texture2D)GBufferPlugin.RenderTarget.Texture);
108 
109  // Set parameters for MainPlugin
110  GBufferPlugin.MainTargetPlugin.Parameters.Set(LightDeferredShadingKeys.LightTexture, lightTexture);
111 
112  CreatePrePassMesh(RenderPass, false);
113  if (Debug)
114  CreatePrePassMesh(debugRenderPass, true);
115  }
116 
117  public override void Unload()
118  {
119  GBufferPlugin.MainPlugin.RenderPass.RemovePass(debugRenderPass);
120  debugRenderPass = null;
121 
122  base.Unload();
123  }
124 
125  private void CreatePrePassMesh(RenderPass renderPass, bool debug)
126  {
127  var lightDeferredEffect = lightDeferredEffects[debug ? 1 : 0];
128 
129  Tiles = new List<LightingPrepassShaderPlugin.LightData>[TileCountX * TileCountY];
130  for (int i = 0; i < Tiles.Length; ++i)
131  {
132  Tiles[i] = new List<LightingPrepassShaderPlugin.LightData>();
133  }
134 
135  renderPass.StartPass.AddLast = (threadContext) =>
136  {
137  // TODO THIS IS NOT ACCURATE TO TAKE THE CURRENT BACKBUFFER
138  var mainBackBuffer = graphicsDeviceService.GraphicsDevice.BackBuffer;
139  threadContext.GraphicsDevice.SetViewport(new Viewport(0, 0, mainBackBuffer.Width, mainBackBuffer.Height));
140  if (threadContext.FirstContext)
141  {
142  if (debug)
143  {
144  threadContext.GraphicsDevice.SetRenderTarget(GBufferPlugin.MainTargetPlugin.RenderTarget);
145  }
146  else
147  {
148  threadContext.GraphicsDevice.Clear(LightTexture, new Color(0.0f, 0.0f, 0.0f, 0.0f));
149  threadContext.GraphicsDevice.SetRenderTarget(LightTexture);
150  }
151  }
152 
153  for (int i = 0; i < Tiles.Length; ++i)
154  Tiles[i].Clear();
155 
156  var lights = Lights;
157  var lightAttenuationCutoff = 0.1f;
158 
159  Matrix viewMatrix;
160  var mainParameters = GBufferPlugin.MainPlugin.ViewParameters;
161  mainParameters.Get(TransformationKeys.View, out viewMatrix);
162  Matrix projMatrix;
163  mainParameters.Get(TransformationKeys.Projection, out projMatrix);
164 
165  for (int index = 0; index < lights.Meshes.Count; index++)
166  {
167  LightingPrepassShaderPlugin.LightData lightData;
168 
169  var lightMesh = lights.Meshes[index];
170  Vector3 lightPos;
171  lightMesh.Parameters.TryGet(LightKeys.LightPosition, out lightPos);
172  Vector3.TransformCoordinate(ref lightPos, ref viewMatrix, out lightData.LightPosVS);
173  lightMesh.Parameters.TryGet(LightKeys.LightColor, out lightData.DiffuseColor);
174  lightMesh.Parameters.TryGet(LightKeys.LightIntensity, out lightData.LightIntensity);
175  lightMesh.Parameters.TryGet(LightKeys.LightRadius, out lightData.LightRadius);
176 
177  // ------------------------------------------------------------------------------------------
178  // TEMPORARY FIX FOR DEFERRED LIGHTS
179  // ------------------------------------------------------------------------------------------
180  //lightData2[index].DiffuseColor.Pow(1 / 4.0f);
181  //lightData2[index].LightIntensity = (float)Math.Pow(lightData2[index].LightIntensity, 1.0f / 2.2f);
182  // ------------------------------------------------------------------------------------------
183 
184  // Linearize color
185  lightData.DiffuseColor.Pow(Color.DefaultGamma);
186  lightData.LightIntensity = (float)Math.Pow(lightData.LightIntensity, Color.DefaultGamma);
187 
188  float lightDistanceMax = CalculateMaxDistance(lightData.LightIntensity, lightData.LightRadius, lightAttenuationCutoff);
189  var clipRegion = ComputeClipRegion(lightData.LightPosVS, lightDistanceMax, ref projMatrix);
190 
191  var tileStartX = (int)((clipRegion.X * 0.5f + 0.5f) * TileCountX);
192  var tileEndX = (int)((clipRegion.Z * 0.5f + 0.5f) * TileCountX);
193  var tileStartY = (int)((clipRegion.Y * 0.5f + 0.5f) * TileCountY);
194  var tileEndY = (int)((clipRegion.W * 0.5f + 0.5f) * TileCountY);
195 
196  // Check if this light is really visible (not behind us)
197  if (lightData.LightPosVS.Z + lightDistanceMax < 0.0f)
198  continue;
199 
200  for (int y = tileStartY; y <= tileEndY; ++y)
201  {
202  if (y < 0 || y >= TileCountY)
203  continue;
204  for (int x = tileStartX; x <= tileEndX; ++x)
205  {
206  if (x < 0 || x >= TileCountX)
207  continue;
208  Tiles[y * TileCountX + x].Add(lightData);
209  }
210  }
211  }
212  };
213 
214  var lightDeferredMesh = new EffectMesh(lightDeferredEffect).KeepAliveBy(ActiveObjects);
215  RenderSystem.GlobalMeshes.AddMesh(lightDeferredMesh);
216 
217  renderPass.EndPass.AddLast = (context) =>
218  {
219  // Clear thread context overridden variables.
220  context.Parameters.Reset(LightingPrepassShaderPlugin.LightCount);
221  context.Parameters.Reset(LightingPrepassShaderPlugin.LightInfos);
222  context.Parameters.Reset(LightingPrepassShaderPlugin.TileIndex);
223  context.Parameters.Reset(TransformationKeys.Projection);
224  context.Parameters.Reset(RenderTargetKeys.DepthStencilSource);
225  };
226 
227  var tileRenderPasses = new RenderPass[TileCountX * TileCountY];
228  for (int i = 0; i < tileRenderPasses.Length; ++i)
229  {
230  int tileIndex = i;
231  tileRenderPasses[i] = new RenderPass("Lighting Tile");
232  tileRenderPasses[i].StartPass.AddLast = (context) => { context.Parameters.Set(LightingPrepassShaderPlugin.TileIndex, tileIndex); };
233  throw new NotImplementedException();
234  //tileRenderPasses[i].Meshes.Add(lightDeferredMesh);
235  }
236 
237  throw new NotImplementedException();
238  //renderPass.UpdatePasses += (RenderPass currentRenderPass, ref FastList<RenderPass> currentPasses) =>
239  // {
240  // lightDeferredEffect.Passes[0].Passes.Clear();
241  // lightDeferredEffect.Passes[0].Passes.AddRange(tileRenderPasses);
242  // };
243  }
244 
245  float CalculateMaxDistance(float lightIntensity, float lightRadius, float cutoff)
246  {
247  // DMax = r + r * (sqrt(Li/Ic) - 1) = r * sqrt(Li/Ic)
248  return (float)(lightRadius * Math.Sqrt(lightIntensity / cutoff));
249  }
250 
251  void UpdateClipRegionRoot(float nc, // Tangent plane x/y normal coordinate (view space)
252  float lc, // Light x/y coordinate (view space)
253  float lz, // Light z coordinate (view space)
254  float lightRadius,
255  float cameraScale, // Project scale for coordinate (_11 or _22 for x/y respectively)
256  ref float clipMin,
257  ref float clipMax)
258  {
259  float nz = (lightRadius - nc * lc) / lz;
260  float pz = (lc * lc + lz * lz - lightRadius * lightRadius) /
261  (lz - (nz / nc) * lc);
262 
263  if (pz > 0.0f)
264  {
265  float c = -nz * cameraScale / nc;
266  if (nc > 0.0f)
267  { // Left side boundary
268  clipMin = Math.Max(clipMin, c);
269  }
270  else
271  { // Right side boundary
272  clipMax = Math.Min(clipMax, c);
273  }
274  }
275  }
276 
277  void UpdateClipRegion(float lc, // Light x/y coordinate (view space)
278  float lz, // Light z coordinate (view space)
279  float lightRadius,
280  float cameraScale, // Project scale for coordinate (_11 or _22 for x/y respectively)
281  ref float clipMin,
282  ref float clipMax)
283  {
284  float rSq = lightRadius * lightRadius;
285  float lcSqPluslzSq = lc * lc + lz * lz;
286  float d = rSq * lc * lc - lcSqPluslzSq * (rSq - lz * lz);
287 
288  if (d > 0)
289  {
290  float a = lightRadius * lc;
291  float b = (float)Math.Sqrt(d);
292  float nx0 = (a + b) / lcSqPluslzSq;
293  float nx1 = (a - b) / lcSqPluslzSq;
294 
295  UpdateClipRegionRoot(nx0, lc, lz, lightRadius, cameraScale, ref clipMin, ref clipMax);
296  UpdateClipRegionRoot(nx1, lc, lz, lightRadius, cameraScale, ref clipMin, ref clipMax);
297  }
298  }
299 
300  private Vector4 ComputeClipRegion(Vector3 lightPosView, float lightRadius, ref Matrix projection)
301  {
302  // Early out with empty rectangle if the light is too far behind the view frustum
303  var clipRegion = new Vector4(1.0f, 1.0f, 0.0f, 0.0f);
304  //if (lightPosView.z + lightRadius >= mCameraNearFar.x) {
305  var clipMin = new Vector2(-1.0f, -1.0f);
306  var clipMax = new Vector2(1.0f, 1.0f);
307 
308  UpdateClipRegion(lightPosView.X, lightPosView.Z, lightRadius, projection.M11, ref clipMin.X, ref clipMax.X);
309  UpdateClipRegion(lightPosView.Y, lightPosView.Z, lightRadius, projection.M22, ref clipMin.Y, ref clipMax.Y);
310 
311  clipRegion = new Vector4(clipMin.X, clipMin.Y, clipMax.X, clipMax.Y);
312  //}
313 
314  return clipRegion;
315  }
316  }
317 }
Service providing method to access GraphicsDevice life-cycle.
SiliconStudio.Paradox.Games.Mathematics.Vector2 Vector2
float Y
The Y component of the vector.
Definition: Vector3.cs:84
function b
function a
virtual RenderTarget RenderTarget
Gets or sets the render target.
Plugin used to render to a GBuffer from a MainPlugin.
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
Definition: DirectXTexP.h:191
LightingPrepassPlugin(string name)
Initializes a new instance of the LightingPrepassPlugin class.
float X
The X component of the vector.
Definition: Vector3.cs:78
Represents a three dimensional mathematical vector.
Definition: Vector3.cs:42
All-in-One Buffer class linked SharpDX.Direct3D11.Buffer.
Performs primitive-based rendering, creates resources, handles system-level variables, adjusts gamma ramp levels, and creates shaders. See The+GraphicsDevice+class to learn more about the class.
void Pow(float exponent)
Raises the exponent for each components.
Definition: Vector3.cs:232
LightingPrepassPlugin()
Initializes a new instance of the LightingPrepassPlugin class.
Represents a four dimensional mathematical vector.
Definition: Vector4.cs:42
SiliconStudio.Core.Mathematics.Color Color
Definition: ColorPicker.cs:14
Defines the window dimensions of a render-target surface onto which a 3D volume projects.
Definition: Viewport.cs:14
Represents a 32-bit color (4 bytes) in the form of RGBA (in byte order: R, G, B, A).
Definition: Color.cs:16
SiliconStudio.Paradox.Graphics.Buffer Buffer
A Texture 2D frontend to SharpDX.Direct3D11.Texture2D.
Definition: Texture2D.cs:37
readonly Texture Texture
The underlying texture.
Definition: RenderTarget.cs:17
float Z
The Z component of the vector.
Definition: Vector3.cs:90
RenderPass is a hierarchy that defines how to collect and render meshes.
Definition: RenderPass.cs:19
Represents a 4x4 mathematical matrix.
Definition: Matrix.cs:47