Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
CubemapBlendRenderer.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 
4 using System;
5 using System.Collections.Generic;
6 
7 using SiliconStudio.Core;
8 using SiliconStudio.Core.Mathematics;
9 using SiliconStudio.Paradox.Effects.Modules.Processors;
10 using SiliconStudio.Paradox.Engine;
11 using SiliconStudio.Paradox.EntityModel;
12 using SiliconStudio.Paradox.Graphics;
13 using SiliconStudio.Paradox.Shaders;
14 using SiliconStudio.Paradox.Shaders.Compiler;
15 
16 namespace SiliconStudio.Paradox.Effects.Modules.Renderers
17 {
18  /// <summary>
19  /// Blends the cubemaps at the defined locations.
20  /// </summary>
22  {
23  #region Static members
24 
25  /// <summary>
26  /// The key to set each cubemap parameter.
27  /// </summary>
28  public static ParameterKey<ShaderMixinParameters[]> Cubemaps = ParameterKeys.New<ShaderMixinParameters[]>();
29 
30  /// <summary>
31  /// The key of the cubemap.
32  /// </summary>
33  public static ParameterKey<ParameterKey> CubemapKey = ParameterKeys.New<ParameterKey>();
34 
35  /// <summary>
36  /// The number of cubemap in the shader.
37  /// </summary>
38  public static ParameterKey<int> CubemapCount = ParameterKeys.New<int>();
39 
40  /// <summary>
41  /// Flag to enable multiple render target.
42  /// </summary>
43  public static ParameterKey<bool> UseMultipleRenderTargets = ParameterKeys.New<bool>();
44 
45  #endregion
46 
47  #region Private members
48 
49  /// <summary>
50  /// The blend effects.
51  /// </summary>
52  private Dictionary<int, Effect> cubemapBlendEffects;
53 
54  /// <summary>
55  /// A flag to use multiple render target to blend the cubemaps in one call.
56  /// </summary>
57  private bool useMultipleRenderTargets;
58 
59  /// <summary>
60  /// Cached list of cubemaps.
61  /// </summary>
62  private List<Tuple<TextureCube, float>> selectedCubemaps;
63 
64  /// <summary>
65  /// Cached ParameterCollection used by the blending effect.
66  /// </summary>
67  private ParameterCollection blendEffectParameters;
68 
69  #endregion
70 
71  #region Constructor
72 
73  public CubemapBlendRenderer(IServiceRegistry services) : base(services)
74  {
75  cubemapBlendEffects = new Dictionary<int, Effect>();
76  useMultipleRenderTargets = false;
77  selectedCubemaps = new List<Tuple<TextureCube, float>>();
78  blendEffectParameters = new ParameterCollection();
79  }
80 
81  #endregion
82 
83  #region Public methods
84 
85  /// <inheritdoc/>
86  public override void Load()
87  {
88  base.Load();
89 
90  useMultipleRenderTargets = GraphicsDevice.Features.Profile >= GraphicsProfile.Level_10_0;
91 
92  for (var maxBlendCount = 2; maxBlendCount < 5; ++maxBlendCount)
93  {
94  var compilerParameter = new CompilerParameters();
95  var compilerParameterChild = new ShaderMixinParameters[maxBlendCount];
96  for (var i = 0; i < maxBlendCount; ++i)
97  {
98  var param = new ShaderMixinParameters();
99  param.Add(CubemapBlendRenderer.CubemapKey, GetTextureCubeKey(i));
100  compilerParameterChild[i] = param;
101  }
102  compilerParameter.Set(CubemapBlendRenderer.Cubemaps, compilerParameterChild);
103  compilerParameter.Set(CubemapBlendRenderer.CubemapCount, maxBlendCount);
104  compilerParameter.Set(CubemapBlendRenderer.UseMultipleRenderTargets, useMultipleRenderTargets);
105  cubemapBlendEffects.Add(maxBlendCount, EffectSystem.LoadEffect("CubemapBlendEffect", compilerParameter));
106  }
107  }
108 
109  #endregion
110 
111  #region Protected methods
112 
113  protected override void OnRendering(RenderContext context)
114  {
115  var entitySystem = Services.GetServiceAs<EntitySystem>();
116  var cubemapBlendProcessor = entitySystem.GetProcessor<CubemapBlendProcessor>();
117  if (cubemapBlendProcessor == null)
118  return;
119 
120  var cubemapSourceProcessor = entitySystem.GetProcessor<CubemapSourceProcessor>();
121  if (cubemapSourceProcessor == null)
122  return;
123 
124  var textureCubes = cubemapSourceProcessor.Cubemaps;
125 
126  foreach (var cubemap in cubemapBlendProcessor.Cubemaps)
127  {
128  if (cubemap.Value.Texture == null)
129  continue;
130 
131  var maxCubemapBlend = cubemap.Value.MaxBlendCount;
132  var position = cubemap.Key.Transformation.Translation;
133 
134  maxCubemapBlend = maxCubemapBlend > textureCubes.Count ? textureCubes.Count : maxCubemapBlend;
135 
136  // TODO: better use a list?
137  Effect cubemapBlendEffect = null;
138  while (maxCubemapBlend > 1)
139  {
140  if (cubemapBlendEffects.TryGetValue(maxCubemapBlend, out cubemapBlendEffect) && cubemapBlendEffect != null)
141  break;
142  --maxCubemapBlend;
143  }
144 
145  if (cubemapBlendEffect == null)
146  continue;
147 
148  // TODO: take the k most important cubemaps
149  selectedCubemaps.Clear();
150  //FindClosestCubemaps(textureCubes, position, maxCubemapBlend, selectedCubemaps);
151  FindMostInfluencialCubemaps(textureCubes, position, maxCubemapBlend, selectedCubemaps);
152 
153  // compute blending indices & set parameters
154  // TODO: change weight computation
155  maxCubemapBlend = maxCubemapBlend > selectedCubemaps.Count ? selectedCubemaps.Count : maxCubemapBlend;
156 
157  // TODO: if there is only one texture and size matches, copy to the destination texture without shaders?
158  // TODO: or use the source texture as the current cubemap
159 
160  var totalWeight = 0f;
161  for (var i = 0; i < maxCubemapBlend; ++i)
162  totalWeight += selectedCubemaps[i].Item2;
163  var blendIndices = new float[maxCubemapBlend];
164  blendEffectParameters.Clear();
165  for (var i = 0; i < maxCubemapBlend; ++i)
166  {
167  blendIndices[i] = selectedCubemaps[i].Item2 / totalWeight;
168  blendEffectParameters.Set(GetTextureCubeKey(i), selectedCubemaps[i].Item1);
169  }
170  blendEffectParameters.Set(CubemapBlenderBaseKeys.BlendIndices, blendIndices);
171 
172  // clear target
173  // TODO: custom clear color?
174  GraphicsDevice.Clear(cubemap.Value.Texture.ToRenderTarget(ViewType.Full, 0, 0), Color.Black);
175 
176  // set states
177  GraphicsDevice.SetDepthStencilState(GraphicsDevice.DepthStencilStates.None);
178 
179  // render each face
180  if (useMultipleRenderTargets)
181  {
182  GraphicsDevice.SetRenderTargets(cubemap.Value.RenderTargets);
183  GraphicsDevice.DrawQuad(cubemapBlendEffect, blendEffectParameters);
184  }
185  else
186  {
187  for (int i = 0; i < 6; ++i)
188  {
189  // set the render targets
190  GraphicsDevice.SetRenderTarget(cubemap.Value.RenderTargets[i]);
191  blendEffectParameters.Set(CubemapBlenderKeys.ViewIndex, i);
192  GraphicsDevice.DrawQuad(cubemapBlendEffect, blendEffectParameters);
193  }
194  }
195  }
196  }
197 
198  private void FindClosestCubemaps(Dictionary<Entity, CubemapSourceComponent> textureCubes, Vector3 position, int maxCubemapBlend, List<Tuple<TextureCube, float>> selectedCubemaps)
199  {
200  foreach (var tex in textureCubes)
201  {
202  var d = (tex.Key.Transformation.Translation - position).LengthSquared();
203  var influence = 1.0f / (d + 1);
204  var insertIndex = 0;
205  for (; insertIndex < maxCubemapBlend; ++insertIndex)
206  {
207  if (insertIndex >= selectedCubemaps.Count || influence > selectedCubemaps[insertIndex].Item2)
208  break;
209  }
210  if (insertIndex < maxCubemapBlend)
211  selectedCubemaps.Insert(insertIndex, Tuple.Create(tex.Value.Texture, influence));
212  }
213  }
214 
215  private void FindMostInfluencialCubemaps(Dictionary<Entity, CubemapSourceComponent> textureCubes, Vector3 position, int maxCubemapBlend, List<Tuple<TextureCube, float>> selectedCubemaps)
216  {
217  foreach (var tex in textureCubes)
218  {
219  // compute influence
220  // TODO: other profile than linear?
221  var d = (tex.Key.Transformation.Translation - position).Length();
222  var influence = 1 - (d / tex.Value.InfluenceRadius);
223  if (influence > 1)
224  influence = 1;
225  else if (influence <= 0)
226  continue;
227 
228  var insertIndex = 0;
229  for (; insertIndex < maxCubemapBlend; ++insertIndex)
230  {
231  if (insertIndex >= selectedCubemaps.Count || influence > selectedCubemaps[insertIndex].Item2)
232  break;
233  }
234  if (insertIndex < maxCubemapBlend)
235  selectedCubemaps.Insert(insertIndex, Tuple.Create(tex.Value.Texture, influence));
236  }
237  }
238 
239  // Sebastien Largarde's weights
240  // http://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
241  private void FindMostInfluencialCubemaps2(Dictionary<Entity, CubemapSourceComponent> textureCubes, Vector3 position, int maxCubemapBlend, List<Tuple<TextureCube, float>> selectedCubemaps)
242  {
243  var inflSum = 0f;
244  var invInflSum = 0f;
245  var influences = new Dictionary<Entity, float>();
246  var blendValues = new Dictionary<Entity, float>();
247 
248  foreach (var tex in textureCubes)
249  {
250  // TODO: add boxes and inner boundaries
251  var d = (tex.Key.Transformation.Translation - position).Length();
252  inflSum += d;
253  invInflSum += 1 - d;
254  influences.Add(tex.Key, d);
255  }
256 
257  var sumBlend = 0f;
258  var n = textureCubes.Count;
259  foreach (var influence in influences)
260  {
261  var infl = influence.Value;
262  var blendFactor = (1 - (infl / inflSum)) / (n - 1);
263  blendFactor *= (1 - infl) / invInflSum;
264  sumBlend += blendFactor;
265  blendValues.Add(influence.Key, blendFactor);
266  }
267 
268  if (sumBlend == 0f)
269  sumBlend = 1f;
270 
271  var invSumBlend = 1 / sumBlend;
272  foreach (var tex in textureCubes)
273  {
274  var influence = blendValues[tex.Key] * invSumBlend;
275 
276  var insertIndex = 0;
277  for (; insertIndex < maxCubemapBlend; ++insertIndex)
278  {
279  if (insertIndex >= selectedCubemaps.Count || influence > selectedCubemaps[insertIndex].Item2)
280  break;
281  }
282  if (insertIndex < maxCubemapBlend)
283  selectedCubemaps.Insert(insertIndex, Tuple.Create(tex.Value.Texture, influence));
284  }
285  }
286 
287  #endregion
288 
289  #region Helpers
290 
291  private ParameterKey<Texture> GetTextureCubeKey(int i)
292  {
293  switch (i)
294  {
295  case 0:
296  return TexturingKeys.TextureCube0;
297  case 1:
298  return TexturingKeys.TextureCube1;
299  case 2:
300  return TexturingKeys.TextureCube2;
301  case 3:
302  return TexturingKeys.TextureCube3;
303  default:
304  throw new IndexOutOfRangeException();
305  }
306  }
307 
308  #endregion
309  }
310 }
Key of an effect parameter.
Definition: ParameterKey.cs:15
static readonly Color Black
Black color.
override void Load()
Loads this instance. This method is called when a RenderPass is attached (directly or indirectly) to ...
Performs render pipeline transformations attached to a specific RenderPass.
Definition: Renderer.cs:13
Represents a three dimensional mathematical vector.
Definition: Vector3.cs:42
A service registry is a IServiceProvider that provides methods to register and unregister services...
Thread-local storage context used during rendering.
Manage a collection of entities.
Definition: EntitySystem.cs:22
Represents a 32-bit color (4 bytes) in the form of RGBA (in byte order: R, G, B, A).
Definition: Color.cs:16
A container to handle a hierarchical collection of effect variables.