Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
LightingPlugin.cs
Go to the documentation of this file.
1 using System;
2 using System.Linq;
3 using System.Collections.Generic;
4 
5 using SiliconStudio.Paradox.BinPacking;
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;
12 
13 namespace SiliconStudio.Paradox.Effects
14 {
16  {
17  // Storage for temporary variables
18  [ThreadStatic] static Matrix[] projections;
19  [ThreadStatic] static Matrix[] views;
20  [ThreadStatic] static Matrix[] shadowsViewProj;
21  [ThreadStatic] static Vector3[] points;
22  [ThreadStatic] static Vector3[] directions;
23 
24  /// <summary>
25  /// Light position.
26  /// </summary>
27  internal static readonly ParameterKey<Vector3> ShadowLightOffset = ShadowMapCasterBaseKeys.shadowLightOffset;
28 
29  /// <summary>
30  /// Array[5] of intermediate ShadowMapData.
31  /// </summary>
32  internal static readonly ParameterKey<ShadowMapData> ViewProjectionArray = ParameterKeys.Value<ShadowMapData>();
33 
34  /// <summary>
35  /// Offset of the shadow map.
36  /// </summary>
37  internal static readonly ParameterKey<Vector3> Offsets = ParameterKeys.ArrayValue(new Vector3[4]);
38 
39  /// <summary>
40  /// Screen coordinates for shadow map region in ShadowMapTexture.
41  /// </summary>
42  internal static readonly ParameterKey<Vector4> CascadeTextureCoords = ParameterKeys.ArrayValue(new Vector4[4]);
43 
44  /// <summary>
45  /// Screen coordinates for shadow map region in ShadowMapTexture, including border.
46  /// </summary>
47  internal static readonly ParameterKey<Vector4> CascadeTextureCoordsBorder = ParameterKeys.ArrayValue(new Vector4[4]);
48 
49  private RasterizerState casterRasterizerState;
50  private DepthStencilState depthStencilStateZStandard;
51  private List<ShadowMap> shadowMaps = new List<ShadowMap>();
52  private GuillotinePacker guillotinePacker = new GuillotinePacker();
53  private EffectOld[] blurEffects;
54 
55  public LightingPlugin()
56  {
57  AtlasSize = 2048;
58  BlurCount = 1;
59  }
60 
61  /// <summary>
62  /// Gets or sets the main plugin this instance is attached to.
63  /// </summary>
64  /// <value>
65  /// The main plugin.
66  /// </value>
67  public MainPlugin MainPlugin { get; set; }
68 
69  public int AtlasSize { get; set; }
70 
71  public int BlurCount { get; set; }
72 
73  internal static readonly ParameterKey<int> ShadowMapLightCount = ParameterKeys.Value(0);
74 
75  internal static readonly ParameterKey<ShadowMapReceiverInfo[]> ReceiverInfo = ParameterKeys.ArrayValue<ShadowMapReceiverInfo>();
76 
77  internal static readonly ParameterKey<ShadowMapReceiverVsmInfo[]> ReceiverVsmInfo = ParameterKeys.ArrayValue<ShadowMapReceiverVsmInfo>();
78 
79  /// <summary>
80  /// Gets the post RenderPass.
81  /// </summary>
82  internal RenderPass PostPass { get; private set; }
83 
84  protected DepthStencilBuffer ShadowMapDepth { get; private set; }
85 
86  protected Texture2D ShadowMapVsm { get; private set; }
87 
88  public void AddShadowMap(ShadowMap shadowMap)
89  {
90  shadowMaps.Add(shadowMap);
91  shadowMap.Passes = new RenderPass[shadowMap.LevelCount];
92  shadowMap.Plugins = new RenderTargetsPlugin[shadowMap.LevelCount];
93 
94  for (int i = 0; i < shadowMap.Passes.Length; i++)
95  {
96  shadowMap.Passes[i] = new RenderPass(string.Format("Pass {0}", i)) { Parameters = new ParameterCollection(string.Format("Parameters ShadowMap {0}", i)) };
97  shadowMap.Passes[i].Parameters.AddSources(MainPlugin.ViewParameters);
98  shadowMap.Passes[i].Parameters.AddSources(shadowMap.CasterParameters);
99 
100  unsafe
101  {
102  int currentPassIndex = i;
103  shadowMap.Passes[i].Parameters.AddDynamic(TransformationKeys.ViewProjection,
104  ParameterDynamicValue.New(LightingPlugin.ViewProjectionArray, (ref ShadowMapData input, ref Matrix output) =>
105  {
106  fixed (Matrix* vpPtr = &input.ViewProjCaster0)
107  {
108  output = vpPtr[currentPassIndex];
109  }
110  }));
111  shadowMap.Passes[i].Parameters.AddDynamic(LightingPlugin.ShadowLightOffset,
112  ParameterDynamicValue.New(LightingPlugin.ViewProjectionArray, (ref ShadowMapData input, ref Vector3 output) =>
113  {
114  fixed (Vector3* vpPtr = &input.Offset0)
115  {
116  output = vpPtr[currentPassIndex];
117  }
118  }));
119  }
120 
121  shadowMap.Plugins[i] = new RenderTargetsPlugin
122  {
123  Services = Services,
124  EnableClearTarget = false,
125  EnableClearDepth = false,
126  RenderPass = shadowMap.Passes[i],
127  RenderTarget = null,
128  };
129  shadowMap.Plugins[i].Apply();
130  }
131 
132  RenderPass.Passes.InsertRange(0, shadowMap.Passes);
133 
134  // Dynamic value used for ViewProjectionArray key
135  var dynamicViewProjectionArray = ParameterDynamicValue.New(
136  TransformationKeys.View, TransformationKeys.Projection, LightKeys.LightDirection, LightKeys.LightColor, LightingPlugin.Offsets, (ref Matrix view, ref Matrix projection, ref Vector3 lightDirection, ref Color3 lightColor, ref Vector3[] offsets, ref ShadowMapData result) =>
137  {
138  if (projections == null)
139  {
140  // Preallocates temporary variables (thread static)
141  projections = new Matrix[4];
142  views = new Matrix[4];
143  shadowsViewProj = new Matrix[8];
144  points = new Vector3[8];
145  directions = new Vector3[4];
146  }
147 
148  Matrix inverseView, inverseProjection;
149  Matrix.Invert(ref projection, out inverseProjection);
150  Matrix.Invert(ref view, out inverseView);
151 
152  // Frustum in World Space
153  for (int i = 0; i < 8; ++i)
154  Vector3.TransformCoordinate(ref FrustrumBasePoints[i], ref inverseProjection, out points[i]);
155 
156  for (int i = 0; i < 4; i++)
157  {
158  directions[i] = Vector3.Normalize(points[i + 4] - points[i]);
159  }
160 
161  // TODO Make these factors configurable. They need also to be correctly tweaked.
162  float shadowDistribute = 1.0f / shadowMap.LevelCount;
163  float znear = 1.0f;
164  float zfar = shadowMap.ShadowDistance;
165 
166  var shadowOffsets = new Vector3[shadowMap.LevelCount];
167  var boudingBoxVectors = new Vector3[shadowMap.LevelCount * 2];
168  var direction = Vector3.Normalize(lightDirection);
169 
170  // Fake value
171  // It will be setup by next loop
172  Vector3 side = Vector3.UnitX;
173  Vector3 up = Vector3.UnitX;
174 
175  // Select best Up vector
176  foreach (var vectorUp in VectorUps)
177  {
178  if (Vector3.Dot(direction, vectorUp) < (1.0 - 0.0001))
179  {
180  side = Vector3.Normalize(Vector3.Cross(vectorUp, direction));
181  up = Vector3.Normalize(Vector3.Cross(direction, side));
182  break;
183  }
184  }
185 
186  for (int cascadeLevel = 0; cascadeLevel < shadowMap.LevelCount; ++cascadeLevel)
187  {
188  float k0 = (float)(cascadeLevel + 0) / shadowMap.LevelCount;
189  float k1 = (float)(cascadeLevel + 1) / shadowMap.LevelCount;
190  float min = (float)(znear * Math.Pow(zfar / znear, k0)) * (1.0f - shadowDistribute) + (znear + (zfar - znear) * k0) * shadowDistribute;
191  float max = (float)(znear * Math.Pow(zfar / znear, k1)) * (1.0f - shadowDistribute) + (znear + (zfar - znear) * k1) * shadowDistribute;
192 
193  for (int j = 0; j < shadowMap.LevelCount; j++)
194  {
195  boudingBoxVectors[j] = points[j] + directions[j] * min;
196  boudingBoxVectors[j + shadowMap.LevelCount] = points[j] + directions[j] * max;
197  }
198  var boundingBox = BoundingBox.FromPoints(boudingBoxVectors);
199 
200  var radius = (boundingBox.Maximum - boundingBox.Minimum).Length() * 0.5f;
201  var target = Vector3.TransformCoordinate(boundingBox.Center, inverseView);
202 
203  // Snap camera to texel units (so that shadow doesn't jitter)
204  var shadowMapHalfSize = shadowMap.ShadowMapSize * 0.5f;
205  float x = (float)Math.Ceiling(Vector3.Dot(target, up) * shadowMapHalfSize / radius) * radius / shadowMapHalfSize;
206  float y = (float)Math.Ceiling(Vector3.Dot(target, side) * shadowMapHalfSize / radius) * radius / shadowMapHalfSize;
207  float z = Vector3.Dot(target, direction);
208  //target = up * x + side * y + direction * R32G32B32_Float.Dot(target, direction);
209  target = up * x + side * y + direction * z;
210 
211  views[cascadeLevel] = Matrix.LookAtLH(target - direction * zfar * 0.5f, target + direction * zfar * 0.5f, up); // View;
212  projections[cascadeLevel] = Matrix.OrthoOffCenterLH(-radius, radius, -radius, radius, znear / zfar, zfar); // Projection
213 
214  //float leftX = shadowMap.Level == CascadeShadowMapLevel.X1 ? 0.5f : 0.25f;
215  //float leftY = shadowMap.Level == CascadeShadowMapLevel.X4 ? 0.25f : 0.5f;
216  //float centerX = 0.5f * (cascadeLevel % 2) + leftX;
217  //float centerY = 0.5f * (cascadeLevel / 2) + leftY;
218 
219  var cascadeTextureCoords = shadowMap.TextureCoordsBorder[cascadeLevel];
220 
221  float leftX = (float)shadowMap.ShadowMapSize / (float)AtlasSize * 0.5f;
222  float leftY = (float)shadowMap.ShadowMapSize / (float)AtlasSize * 0.5f;
223  float centerX = 0.5f * (cascadeTextureCoords.X + cascadeTextureCoords.Z);
224  float centerY = 0.5f * (cascadeTextureCoords.Y + cascadeTextureCoords.W);
225 
226  shadowsViewProj[cascadeLevel] = views[cascadeLevel] * projections[cascadeLevel];
227  shadowsViewProj[cascadeLevel + 4] = shadowsViewProj[cascadeLevel]
228  * Matrix.Scaling(leftX, -leftY, 0.5f) // Texture0 mapping offsets/scaling
229  * Matrix.Translation(centerX, centerY, 0.5f);
230 
231  var shadowVInverse = Matrix.Invert(views[cascadeLevel]);
232  shadowOffsets[cascadeLevel] = new Vector3(shadowVInverse.M41, shadowVInverse.M42, shadowVInverse.M43);
233  }
234 
235  result.LightColor = lightColor;
236 
237  unsafe
238  {
239  fixed (Matrix* resultPtr = &result.ViewProjCaster0)
240  Utilities.Write((IntPtr)resultPtr, shadowsViewProj, 0, shadowsViewProj.Length);
241 
242  fixed (Vector3* resultPtr = &result.Offset0)
243  Utilities.Write((IntPtr)resultPtr, shadowOffsets, 0, shadowOffsets.Length);
244  }
245  });
246 
247  shadowMap.Parameters.SetDefault(LightKeys.LightDirection);
248  shadowMap.Parameters.SetDefault(Offsets);
249  shadowMap.Parameters.SetDefault(ViewProjectionArray);
250  shadowMap.Parameters.AddDynamic(ViewProjectionArray, dynamicViewProjectionArray);
251  shadowMap.CasterParameters.Set(EffectPlugin.RasterizerStateKey, null);
252  shadowMap.Texture = shadowMap.Filter is ShadowMapFilterVsm ? ShadowMapVsm : ShadowMapDepth.Texture;
253  }
254 
255  public void RemoveShadowMap(ShadowMap shadowMap)
256  {
257  shadowMaps.Remove(shadowMap);
258  RenderPass.Passes.RemoveAll(shadowMap.Passes.Contains);
259  }
260 
261  public void UpdateShadowMaps(ThreadContext context)
262  {
263  guillotinePacker.Clear(ShadowMapDepth.Description.Width, ShadowMapDepth.Description.Height);
264 
265  // Allocate shadow maps in the atlas and update texture coordinates.
266  foreach (var shadowMap in shadowMaps)
267  {
268  //int widthFactor = shadowMap.Level == CascadeShadowMapLevel.X1 ? 1 : 2;
269  //int heightFactor = shadowMap.Level == CascadeShadowMapLevel.X4 ? 2 : 1;
270 
271  //var shadowMapWidth = shadowMap.ShadowMapSize * widthFactor;
272  //var shadowMapHeight = shadowMap.ShadowMapSize * heightFactor;
273 
274  var cascadeTextureCoords = new Vector4[shadowMap.LevelCount];
275  var cascadeTextureCoordsBorder = new Vector4[shadowMap.LevelCount];
276  for (int i = 0; i < shadowMap.LevelCount; ++i)
277  {
278  var rect = guillotinePacker.Insert(shadowMap.ShadowMapSize, shadowMap.ShadowMapSize);
279 
280  // Texture0 array support should help when this break (in complex scenes)
281  if (rect.Width == 0)
282  throw new InvalidOperationException("Could not allocate enough texture space for shadow map texture.");
283 
284  Vector4 cascadeTextureCoord;
285  cascadeTextureCoord.X = (float)(rect.X) / guillotinePacker.Width;
286  cascadeTextureCoord.Y = (float)(rect.Y) / guillotinePacker.Height;
287  cascadeTextureCoord.Z = (float)(rect.X + rect.Width) / guillotinePacker.Width;
288  cascadeTextureCoord.W = (float)(rect.Y + rect.Height) / guillotinePacker.Height;
289 
290  cascadeTextureCoords[i] = cascadeTextureCoord;
291 
292  cascadeTextureCoord.X += 0.01f;
293  cascadeTextureCoord.Y += 0.01f;
294  cascadeTextureCoord.Z -= 0.01f;
295  cascadeTextureCoord.W -= 0.01f;
296 
297  cascadeTextureCoordsBorder[i] = cascadeTextureCoord;
298 
299  shadowMap.Plugins[i].Viewport = new Viewport(rect.X, rect.Y, shadowMap.ShadowMapSize, shadowMap.ShadowMapSize);
300  shadowMap.Plugins[i].DepthStencil = ShadowMapDepth;
301  }
302 
303  shadowMap.Parameters.Set(CascadeTextureCoords, cascadeTextureCoords);
304  shadowMap.Parameters.Set(CascadeTextureCoordsBorder, cascadeTextureCoordsBorder);
305 
306  shadowMap.CasterParameters.Set(EffectPlugin.RasterizerStateKey, casterRasterizerState);
307  shadowMap.CasterParameters.Set(EffectPlugin.DepthStencilStateKey, depthStencilStateZStandard);
308 
309  shadowMap.TextureCoordsBorder = cascadeTextureCoordsBorder;
310  }
311  }
312 
313  public override void Initialize()
314  {
315  base.Initialize();
316 
317  blurEffects = new EffectOld[]
318  {
319  this.EffectSystemOld.BuildEffect("VsmBlurH")
320  .Using(new PostEffectSeparateShaderPlugin())
321  .Using(new BasicShaderPlugin("PostEffectBlurHVsm"))
322  .KeepAliveBy(this),
323 
324  this.EffectSystemOld.BuildEffect("VsmBlurV")
325  .Using(new PostEffectSeparateShaderPlugin())
326  .Using(new BasicShaderPlugin("PostEffectBlur"))
327  .KeepAliveBy(this)
328  };
329 
330  if (OfflineCompilation)
331  return;
332 
333  RenderSystem.GlobalPass.StartPass += UpdateShadowMaps;
334  }
335 
336  protected override void Destroy()
337  {
338  base.Destroy();
339 
340  if (OfflineCompilation)
341  return;
342 
343  RenderSystem.GlobalPass.StartPass -= UpdateShadowMaps;
344  }
345 
346  public override void Load()
347  {
348  base.Load();
349 
350  if (OfflineCompilation)
351  return;
352 
353  // Declare post render pass
354  PostPass = new RenderPass("PostPass").KeepAliveBy(ActiveObjects);
355  RenderPass.AddPass(PostPass);
356 
357 
358  var depthStencilTexture = Texture2D.New(GraphicsDevice, AtlasSize, AtlasSize, PixelFormat.D32_Float, TextureFlags.DepthStencil | TextureFlags.ShaderResource).KeepAliveBy(ActiveObjects);
359  var depthStencilBuffer = depthStencilTexture.ToDepthStencilBuffer(false);
360  ShadowMapDepth = depthStencilBuffer;
361 
362  //MainTargetPlugin.Parameters.Set(ShadowMapKeys.Texture0, ShadowMapDepth);
363 
364  // Setup clear of this target
365  var renderTargetPlugin = new RenderTargetsPlugin
366  {
367  Services = Services,
368  EnableClearDepth = true,
369  EnableSetTargets = false,
371  RenderTarget = null,
372  DepthStencil = depthStencilBuffer,
373  };
374  renderTargetPlugin.Apply();
375 
376  // Use Default ZTest for GBuffer
377  depthStencilStateZStandard = DepthStencilState.New(GraphicsDevice, new DepthStencilStateDescription().Default()).KeepAliveBy(ActiveObjects);
378  depthStencilStateZStandard.Name = "ZStandard";
379 
380  Parameters.Set(EffectPlugin.DepthStencilStateKey, depthStencilStateZStandard);
381 
382  casterRasterizerState = RasterizerState.New(GraphicsDevice, new RasterizerStateDescription(CullMode.Back)).KeepAliveBy(ActiveObjects);
383 
384  // Variance Shadow Mapping
385  // Create the blur temporary texture
386  var shadowMapTextureDesc = ShadowMapDepth.Description;
387  var shadowMapBlurH = Texture2D.New(GraphicsDevice, shadowMapTextureDesc.Width, shadowMapTextureDesc.Height, PixelFormat.R32G32_Float, TextureFlags.ShaderResource | TextureFlags.RenderTarget).KeepAliveBy(ActiveObjects);
388  var shadowMapBlurV = Texture2D.New(GraphicsDevice, shadowMapTextureDesc.Width, shadowMapTextureDesc.Height, PixelFormat.R32G32_Float, TextureFlags.ShaderResource | TextureFlags.RenderTarget).KeepAliveBy(ActiveObjects);
389 
390  Texture2D textureSourceH = ShadowMapDepth.Texture;
391  Texture2D textureSourceV = shadowMapBlurH;
392  RenderTarget renderTargetH = shadowMapBlurH.ToRenderTarget();
393  RenderTarget renderTargetV = shadowMapBlurV.ToRenderTarget();
394 
395  var blurQuadMesh = new EffectMesh[2];
396  for (int j = 0; j < BlurCount; j++)
397  {
398  for (int i = 0; i < 2; ++i)
399  {
400  blurQuadMesh[i] = new EffectMesh(j > 0 ? blurEffects[1] : blurEffects[i]).KeepAliveBy(ActiveObjects);
401  blurQuadMesh[i].Parameters.Set(PostEffectBlurKeys.Coefficients, new[] { 0.2270270270f, 0.3162162162f, 0.3162162162f, 0.0702702703f, 0.0702702703f });
402  var unit = i == 0 ? Vector2.UnitX : Vector2.UnitY;
403  blurQuadMesh[i].Parameters.Set(PostEffectBlurKeys.Offsets, new[] { Vector2.Zero, unit * -1.3846153846f, unit * +1.3846153846f, unit * -3.2307692308f, unit * +3.2307692308f });
404 
405  PostPass.AddPass(blurQuadMesh[i].EffectPass);
406 
407  RenderSystem.GlobalMeshes.AddMesh(blurQuadMesh[i]);
408  }
409 
410  blurQuadMesh[0].Parameters.Set(TexturingKeys.Texture0, textureSourceH);
411  blurQuadMesh[1].Parameters.Set(TexturingKeys.Texture0, textureSourceV);
412  blurQuadMesh[0].Parameters.Set(RenderTargetKeys.RenderTarget, renderTargetH);
413  blurQuadMesh[1].Parameters.Set(RenderTargetKeys.RenderTarget, renderTargetV);
414 
415  textureSourceH = shadowMapBlurV;
416  textureSourceV = shadowMapBlurH;
417  }
418 
419  ShadowMapVsm = shadowMapBlurV;
420 
421  // Final texture for VSM is result of blur
422  //MainTargetPlugin.Parameters.Set(ShadowMapKeys.Texture0, shadowMapBlurV);
423  }
424 
425  public override void Unload()
426  {
427  RenderPass.RemovePass(PostPass);
428 
429  base.Unload();
430  }
431 
432  /// <summary>
433  /// Base points for frustrum
434  /// </summary>
435  private static readonly Vector3[] FrustrumBasePoints =
436  {
437  new Vector3(-1.0f,-1.0f,-1.0f), new Vector3(1.0f,-1.0f,-1.0f), new Vector3(-1.0f,1.0f,-1.0f), new Vector3(1.0f,1.0f,-1.0f),
438  new Vector3(-1.0f,-1.0f, 1.0f), new Vector3(1.0f,-1.0f, 1.0f), new Vector3(-1.0f,1.0f, 1.0f), new Vector3(1.0f,1.0f, 1.0f),
439  };
440 
441  private static readonly Vector3[] VectorUps = new[] { Vector3.UnitZ, Vector3.UnitY, Vector3.UnitX };
442  }
443 }
Key of an effect parameter.
Definition: ParameterKey.cs:15
static void Dot(ref Vector3 left, ref Vector3 right, out float result)
Calculates the dot product of two vectors.
Definition: Vector3.cs:585
int ShadowMapSize
Gets or sets the size of the shadow map (default: 1024)
Definition: ShadowMap.cs:36
TODO: Update summary.
Contains depth-stencil state for the device.
Represents a color in the form of rgb.
Definition: Color3.cs:41
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
Definition: DirectXTexP.h:191
Represents a three dimensional mathematical vector.
Definition: Vector3.cs:42
static void Translation(ref Vector3 value, out Matrix result)
Creates a translation matrix using the specified offsets.
Definition: Matrix.cs:2672
Represents a ShadowMap.
Definition: ShadowMap.cs:11
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.
CullMode
Indicates triangles facing a particular direction are not drawn.
Definition: CullMode.cs:14
Represents a four dimensional mathematical vector.
Definition: Vector4.cs:42
Defines the window dimensions of a render-target surface onto which a 3D volume projects.
Definition: Viewport.cs:14
Plugin used for the main rendering view.
Definition: MainPlugin.cs:15
A Texture 2D frontend to SharpDX.Direct3D11.Texture2D.
Definition: Texture2D.cs:37
Level10 render pass using a depth buffer and a render target.
SiliconStudio.Core.Mathematics.Vector3 Vector3
static void TransformCoordinate(ref Vector3 coordinate, ref Matrix transform, out Vector3 result)
Performs a coordinate transformation using the given SiliconStudio.Core.Mathematics.Matrix.
Definition: Vector3.cs:1188
RenderPass is a hierarchy that defines how to collect and render meshes.
Definition: RenderPass.cs:19
void UpdateShadowMaps(ThreadContext context)
A container to handle a hierarchical collection of effect variables.
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t size_t z
Definition: DirectXTexP.h:191
Represents a 4x4 mathematical matrix.
Definition: Matrix.cs:47