/// <summary> /// Creates a dynamic parameter key for texel size updated from the texture size. /// </summary> /// <param name="textureKey">Key of the texture to take the size from</param> /// <returns>A dynamic TexelSize parameter key updated according to the specified texture</returns> public static ParameterKey <Vector2> CreateDynamicTexelSizeParameterKey(ParameterKey <Texture> textureKey) { if (textureKey == null) { throw new ArgumentNullException("textureKey"); } return(ParameterKeys.NewDynamic(ParameterDynamicValue.New <Vector2, Texture>(textureKey, UpdateTexelSize))); }
static TransformationKeys() { View = ParameterKeys.New(Matrix.Identity); Projection = ParameterKeys.New(Matrix.Identity); World = ParameterKeys.New(Matrix.Identity); WorldView = ParameterKeys.NewDynamic(ParameterDynamicValue.New <Matrix, Matrix, Matrix>(World, View, Matrix.MultiplyRef)); WorldViewProjection = ParameterKeys.NewDynamic(ParameterDynamicValue.New <Matrix, Matrix, Matrix>(World, ViewProjection, Matrix.MultiplyRef)); ProjScreenRay = ParameterKeys.NewDynamic(ParameterDynamicValue.New <Vector2, Matrix>(Projection, ExtractProjScreenRay)); Eye = ParameterKeys.NewDynamic(ParameterDynamicValue.New <Vector4, Matrix>(View, ViewToEye)); EyeMS = ParameterKeys.NewDynamic(ParameterDynamicValue.New <Vector4, Matrix>(WorldView, WorldViewToEyeMS)); WorldInverse = ParameterKeys.NewDynamic(ParameterDynamicValue.New <Matrix, Matrix>(World, InvertMatrix)); WorldInverseTranspose = ParameterKeys.NewDynamic(ParameterDynamicValue.New <Matrix, Matrix>(WorldInverse, TransposeMatrix)); ViewInverse = ParameterKeys.NewDynamic(ParameterDynamicValue.New <Matrix, Matrix>(View, InvertMatrix)); ProjectionInverse = ParameterKeys.NewDynamic(ParameterDynamicValue.New <Matrix, Matrix>(Projection, InvertMatrix)); WorldViewInverse = ParameterKeys.NewDynamic(ParameterDynamicValue.New <Matrix, Matrix>(WorldView, InvertMatrix)); WorldScale = ParameterKeys.NewDynamic(ParameterDynamicValue.New <Vector3, Matrix>(World, ExtractScale)); }
public void AddShadowMap(ShadowMap shadowMap) { shadowMaps.Add(shadowMap); shadowMap.Passes = new RenderPass[shadowMap.LevelCount]; shadowMap.Plugins = new RenderTargetsPlugin[shadowMap.LevelCount]; for (int i = 0; i < shadowMap.Passes.Length; i++) { shadowMap.Passes[i] = new RenderPass(string.Format("Pass {0}", i)) { Parameters = new ParameterCollection(string.Format("Parameters ShadowMap {0}", i)) }; shadowMap.Passes[i].Parameters.AddSources(MainPlugin.ViewParameters); shadowMap.Passes[i].Parameters.AddSources(shadowMap.CasterParameters); unsafe { int currentPassIndex = i; shadowMap.Passes[i].Parameters.AddDynamic(TransformationKeys.ViewProjection, ParameterDynamicValue.New(LightingPlugin.ViewProjectionArray, (ref ShadowMapData input, ref Matrix output) => { fixed(Matrix * vpPtr = &input.ViewProjCaster0) { output = vpPtr[currentPassIndex]; } })); shadowMap.Passes[i].Parameters.AddDynamic(LightingPlugin.ShadowLightOffset, ParameterDynamicValue.New(LightingPlugin.ViewProjectionArray, (ref ShadowMapData input, ref Vector3 output) => { fixed(Vector3 * vpPtr = &input.Offset0) { output = vpPtr[currentPassIndex]; } })); } shadowMap.Plugins[i] = new RenderTargetsPlugin { Services = Services, EnableClearTarget = false, EnableClearDepth = false, RenderPass = shadowMap.Passes[i], RenderTarget = null, }; shadowMap.Plugins[i].Apply(); } RenderPass.Passes.InsertRange(0, shadowMap.Passes); // Dynamic value used for ViewProjectionArray key var dynamicViewProjectionArray = ParameterDynamicValue.New( 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) => { if (projections == null) { // Preallocates temporary variables (thread static) projections = new Matrix[4]; views = new Matrix[4]; shadowsViewProj = new Matrix[8]; points = new Vector3[8]; directions = new Vector3[4]; } Matrix inverseView, inverseProjection; Matrix.Invert(ref projection, out inverseProjection); Matrix.Invert(ref view, out inverseView); // Frustum in World Space for (int i = 0; i < 8; ++i) { Vector3.TransformCoordinate(ref FrustrumBasePoints[i], ref inverseProjection, out points[i]); } for (int i = 0; i < 4; i++) { directions[i] = Vector3.Normalize(points[i + 4] - points[i]); } // TODO Make these factors configurable. They need also to be correctly tweaked. float shadowDistribute = 1.0f / shadowMap.LevelCount; float znear = 1.0f; float zfar = shadowMap.ShadowDistance; var shadowOffsets = new Vector3[shadowMap.LevelCount]; var boudingBoxVectors = new Vector3[shadowMap.LevelCount * 2]; var direction = Vector3.Normalize(lightDirection); // Fake value // It will be setup by next loop Vector3 side = Vector3.UnitX; Vector3 up = Vector3.UnitX; // Select best Up vector foreach (var vectorUp in VectorUps) { if (Vector3.Dot(direction, vectorUp) < (1.0 - 0.0001)) { side = Vector3.Normalize(Vector3.Cross(vectorUp, direction)); up = Vector3.Normalize(Vector3.Cross(direction, side)); break; } } for (int cascadeLevel = 0; cascadeLevel < shadowMap.LevelCount; ++cascadeLevel) { float k0 = (float)(cascadeLevel + 0) / shadowMap.LevelCount; float k1 = (float)(cascadeLevel + 1) / shadowMap.LevelCount; float min = (float)(znear * Math.Pow(zfar / znear, k0)) * (1.0f - shadowDistribute) + (znear + (zfar - znear) * k0) * shadowDistribute; float max = (float)(znear * Math.Pow(zfar / znear, k1)) * (1.0f - shadowDistribute) + (znear + (zfar - znear) * k1) * shadowDistribute; for (int j = 0; j < shadowMap.LevelCount; j++) { boudingBoxVectors[j] = points[j] + directions[j] * min; boudingBoxVectors[j + shadowMap.LevelCount] = points[j] + directions[j] * max; } var boundingBox = BoundingBox.FromPoints(boudingBoxVectors); var radius = (boundingBox.Maximum - boundingBox.Minimum).Length() * 0.5f; var target = Vector3.TransformCoordinate(boundingBox.Center, inverseView); // Snap camera to texel units (so that shadow doesn't jitter) var shadowMapHalfSize = shadowMap.ShadowMapSize * 0.5f; float x = (float)Math.Ceiling(Vector3.Dot(target, up) * shadowMapHalfSize / radius) * radius / shadowMapHalfSize; float y = (float)Math.Ceiling(Vector3.Dot(target, side) * shadowMapHalfSize / radius) * radius / shadowMapHalfSize; float z = Vector3.Dot(target, direction); //target = up * x + side * y + direction * R32G32B32_Float.Dot(target, direction); target = up * x + side * y + direction * z; views[cascadeLevel] = Matrix.LookAtLH(target - direction * zfar * 0.5f, target + direction * zfar * 0.5f, up); // View; projections[cascadeLevel] = Matrix.OrthoOffCenterLH(-radius, radius, -radius, radius, znear / zfar, zfar); // Projection //float leftX = shadowMap.Level == CascadeShadowMapLevel.X1 ? 0.5f : 0.25f; //float leftY = shadowMap.Level == CascadeShadowMapLevel.X4 ? 0.25f : 0.5f; //float centerX = 0.5f * (cascadeLevel % 2) + leftX; //float centerY = 0.5f * (cascadeLevel / 2) + leftY; var cascadeTextureCoords = shadowMap.TextureCoordsBorder[cascadeLevel]; float leftX = (float)shadowMap.ShadowMapSize / (float)AtlasSize * 0.5f; float leftY = (float)shadowMap.ShadowMapSize / (float)AtlasSize * 0.5f; float centerX = 0.5f * (cascadeTextureCoords.X + cascadeTextureCoords.Z); float centerY = 0.5f * (cascadeTextureCoords.Y + cascadeTextureCoords.W); shadowsViewProj[cascadeLevel] = views[cascadeLevel] * projections[cascadeLevel]; shadowsViewProj[cascadeLevel + 4] = shadowsViewProj[cascadeLevel] * Matrix.Scaling(leftX, -leftY, 0.5f) // Texture0 mapping offsets/scaling * Matrix.Translation(centerX, centerY, 0.5f); var shadowVInverse = Matrix.Invert(views[cascadeLevel]); shadowOffsets[cascadeLevel] = new Vector3(shadowVInverse.M41, shadowVInverse.M42, shadowVInverse.M43); } result.LightColor = lightColor; unsafe { fixed(Matrix * resultPtr = &result.ViewProjCaster0) Utilities.Write((IntPtr)resultPtr, shadowsViewProj, 0, shadowsViewProj.Length); fixed(Vector3 * resultPtr = &result.Offset0) Utilities.Write((IntPtr)resultPtr, shadowOffsets, 0, shadowOffsets.Length); } }); shadowMap.Parameters.SetDefault(LightKeys.LightDirection); shadowMap.Parameters.SetDefault(Offsets); shadowMap.Parameters.SetDefault(ViewProjectionArray); shadowMap.Parameters.AddDynamic(ViewProjectionArray, dynamicViewProjectionArray); shadowMap.CasterParameters.Set(EffectPlugin.RasterizerStateKey, null); shadowMap.Texture = shadowMap.Filter is ShadowMapFilterVsm ? ShadowMapVsm : ShadowMapDepth.Texture; }
public override void Initialize() { base.Initialize(); boundingBoxPass = new RenderPass("BoundingBoxPass"); minMaxPass = new RenderPass("MinmaxPass"); lightShaftPass = new RenderPass("LightShaftPass"); filterUpscalePass = new RenderPass("UpscalePass"); RenderPass.AddPass(boundingBoxPass, minMaxPass, lightShaftPass, filterUpscalePass); var useUpScaling = false; var bbRenderTargetPlugin = new RenderTargetsPlugin("BoundingBoxRenderTargetPlugin") { EnableSetTargets = true, EnableClearTarget = true, RenderPass = boundingBoxPass, Services = Services, }; var minMaxEffectBuilder = this.EffectSystemOld.BuildEffect("MinMax") .Using(new MinMaxShaderPlugin("MinMaxShaderPlugin") { RenderPassPlugin = bbRenderTargetPlugin }) .Using(new BasicShaderPlugin("TransformationWVP") { RenderPassPlugin = bbRenderTargetPlugin }); var minmaxEffectBuilder = this.EffectSystemOld.BuildEffect("LightShaftsMinMax") .Using(new PostEffectSeparateShaderPlugin() { RenderPass = minMaxPass }) .Using(new BasicShaderPlugin("ForwardShadowMapBase") { RenderPass = minMaxPass }) .Using(new BasicShaderPlugin(new ShaderClassSource("PostEffectMinMax", "ShadowMapUtils.shadowMapTexture", "PointSampler", 4, 4, 0.0, 1.0)) { RenderPass = minMaxPass }); var lightShaftsEffectBuilder = this.EffectSystemOld.BuildEffect("LightShafts") .Using(new PostEffectSeparateShaderPlugin() { RenderPass = lightShaftPass }) .Using(new StateShaderPlugin() { UseBlendState = !useUpScaling, RenderPass = lightShaftPass }) //.Using(new BasicShaderPlugin(new ShaderClassSource("PostEffectLightShafts", Debug ? 1 : 0, RenderContext.IsZReverse ? 1 : 0, StepCount)) { RenderPass = lightShaftPass }); .Using(new BasicShaderPlugin(new ShaderClassSource("PostEffectLightShafts", Debug ? 1 : 0, false ? 1 : 0, StepCount)) { RenderPass = lightShaftPass }); if (OfflineCompilation) { minMaxEffectBuilder.InstantiatePermutation(); minmaxEffectBuilder.InstantiatePermutation(); lightShaftsEffectBuilder.InstantiatePermutation(); return; } Parameters.AddSources(ViewParameters); Parameters.Set(RenderTargetKeys.DepthStencilSource, DepthStencil.Texture); Parameters.Set(TexturingKeys.Sampler, GraphicsDevice.SamplerStates.PointClamp); // BoundingBox prepass var gbufferDesc = RenderTarget.Description; var bbRenderTarget = Texture.New2D(GraphicsDevice, gbufferDesc.Width / 8, gbufferDesc.Height / 8, PixelFormat.R32G32_Float, TextureFlags.ShaderResource | TextureFlags.RenderTarget); // Use MinMax Plugin bbRenderTargetPlugin.RenderTarget = bbRenderTarget.ToRenderTarget(); bbRenderTargetPlugin.Parameters.AddSources(Parameters); bbRenderTargetPlugin.Apply(); EffectOld minMaxEffect = minMaxEffectBuilder.InstantiatePermutation(); // Add meshes foreach (var bbMeshData in BoundingBoxes) { // Mesh for MinPass var bbMesh = new EffectMesh(minMaxEffect, bbMeshData).KeepAliveBy(this); // Add mesh // boundingBoxPass.AddPass(bbMesh.EffectMeshPasses[0].EffectPass); RenderSystem.GlobalMeshes.AddMesh(bbMesh); } // MinMax render target var minMaxRenderTarget = Texture.New2D(GraphicsDevice, 256, 256, PixelFormat.R32G32_Float, TextureFlags.ShaderResource | TextureFlags.RenderTarget); minMaxPass.Parameters = new ParameterCollection(null); minMaxPass.Parameters.AddSources(ShadowMap.Parameters); minMaxPass.Parameters.AddSources(Parameters); minMaxPass.Parameters.AddDynamic(PostEffectMinMaxKeys.MinMaxCoords, ParameterDynamicValue.New(LightingPlugin.CascadeTextureCoords, (ref Vector4[] cascadeTextureCoords, ref Vector4 output) => { output = cascadeTextureCoords[0]; }, autoCheckDependencies: false)); EffectOld minmaxEffect = minmaxEffectBuilder.InstantiatePermutation(); var minMaxMesh = new EffectMesh(minmaxEffect).KeepAliveBy(this); minMaxMesh.Parameters.Set(RenderTargetKeys.RenderTarget, minMaxRenderTarget.ToRenderTarget()); RenderSystem.GlobalMeshes.AddMesh(minMaxMesh); // Light Shafts effect var blendStateDesc = new BlendStateDescription(); blendStateDesc.SetDefaults(); blendStateDesc.AlphaToCoverageEnable = false; blendStateDesc.IndependentBlendEnable = false; blendStateDesc.RenderTargets[0].BlendEnable = true; blendStateDesc.RenderTargets[0].AlphaBlendFunction = BlendFunction.Add; blendStateDesc.RenderTargets[0].AlphaSourceBlend = Blend.One; blendStateDesc.RenderTargets[0].AlphaDestinationBlend = Blend.One; blendStateDesc.RenderTargets[0].ColorBlendFunction = BlendFunction.Add; blendStateDesc.RenderTargets[0].ColorSourceBlend = Blend.One; blendStateDesc.RenderTargets[0].ColorDestinationBlend = Blend.One; blendStateDesc.RenderTargets[0].ColorWriteChannels = ColorWriteChannels.All; var additiveBlending = BlendState.New(GraphicsDevice, blendStateDesc); additiveBlending.Name = "LightShaftAdditiveBlend"; var shaftRenderTarget = useUpScaling ? Texture.New2D(GraphicsDevice, gbufferDesc.Width / 2, gbufferDesc.Height / 2, PixelFormat.R16G16B16A16_Float, TextureFlags.ShaderResource | TextureFlags.RenderTarget).ToRenderTarget() : RenderTarget; lightShaftPass.Parameters = new ParameterCollection(); lightShaftPass.Parameters.AddSources(ShadowMap.Parameters); lightShaftPass.Parameters.AddSources(Parameters); this.lightShaftsEffect = lightShaftsEffectBuilder.InstantiatePermutation(); var mesh = new EffectMesh(lightShaftsEffect).KeepAliveBy(this); mesh.Parameters.Set(TexturingKeys.Texture0, minMaxRenderTarget); mesh.Parameters.Set(TexturingKeys.Texture1, bbRenderTarget); mesh.Parameters.Set(RenderTargetKeys.RenderTarget, shaftRenderTarget); if (!useUpScaling) { mesh.Parameters.Set(EffectPlugin.BlendStateKey, additiveBlending); } RenderSystem.GlobalMeshes.AddMesh(mesh); // Bilateral Gaussian filtering for up-sampling if (useUpScaling) { var bbRenderTargetUpScaleH = Texture.New2D(GraphicsDevice, gbufferDesc.Width, gbufferDesc.Height / 2, PixelFormat.R16G16B16A16_Float, TextureFlags.ShaderResource | TextureFlags.RenderTarget); var bbRenderTargetUpScaleV = RenderTarget; //var bbRenderTargetUpScaleV = GraphicsDevice.RenderTarget2D.New(gbufferDesc.Width, gbufferDesc.Height, PixelFormat.HalfVector4); var blurEffects = new EffectOld[] { this.EffectSystemOld.BuildEffect("BilateralGaussianFiltering") .Using(new PostEffectSeparateShaderPlugin()) .Using(new BasicShaderPlugin(new ShaderClassSource("PostEffectBilateralGaussian", 0))), this.EffectSystemOld.BuildEffect("BilateralGaussianFiltering") .Using(new StateShaderPlugin() { UseBlendState = true }) .Using(new PostEffectSeparateShaderPlugin()) .Using(new BasicShaderPlugin(new ShaderClassSource("PostEffectBilateralGaussian", 1))), }; Texture2D textureSourceH = (Texture2D)shaftRenderTarget.Texture; Texture2D textureSourceV = bbRenderTargetUpScaleH; RenderTarget renderTargetH = bbRenderTargetUpScaleH.ToRenderTarget(); RenderTarget renderTargetV = bbRenderTargetUpScaleV; var blurQuadMesh = new EffectMesh[2]; for (int i = 0; i < 2; ++i) { blurQuadMesh[i] = new EffectMesh(blurEffects[i]).KeepAliveBy(this); filterUpscalePass.AddPass(blurQuadMesh[i].EffectPass); RenderSystem.GlobalMeshes.AddMesh(blurQuadMesh[i]); } blurQuadMesh[0].Parameters.Set(TexturingKeys.Texture0, textureSourceH); blurQuadMesh[1].Parameters.Set(TexturingKeys.Texture0, textureSourceV); blurQuadMesh[0].Parameters.Set(RenderTargetKeys.RenderTarget, renderTargetH); blurQuadMesh[1].Parameters.Set(RenderTargetKeys.RenderTarget, renderTargetV); // Additive blending for 2nd render target blurQuadMesh[1].Parameters.Set(EffectPlugin.BlendStateKey, additiveBlending); } }
public override void Load() { base.Load(); if (OfflineCompilation) { return; } var renderTargets = new RenderTarget[2]; DepthStencilBuffer depthStencilBuffer = null; Texture2D depthStencilTexture = null; Parameters.AddSources(MainPlugin.ViewParameters); Parameters.RegisterParameter(EffectPlugin.BlendStateKey); var filteredPasses = new FastList <RenderPass>(); RenderPass.UpdatePasses += updatePassesAction = (RenderPass currentRenderPass, ref FastList <RenderPass> currentPasses) => { var originalPasses = currentPasses; filteredPasses.Clear(); currentPasses = filteredPasses; Parameters.Set(PickingFrameIndex, ++currentPickingFrameIndex); Request[] requests; lock (pendingRequests) { // No picking request or no mesh to pick? if (pendingRequests.Count == 0) { return; } requests = pendingRequests.ToArray(); pendingRequests.Clear(); } foreach (var request in requests) { requestResults.Add(request); } if (originalPasses == null) { return; } // Count mesh passes int meshIndex = 0; foreach (var pass in originalPasses) { meshIndex += pass.Passes.Count; } // No mesh to pick? if (meshIndex == 0) { return; } // Copy mesh passes and assign indices var meshPasses = new EffectMesh[meshIndex]; meshIndex = 0; foreach (var pass in RenderPass.Passes) { throw new NotImplementedException(); //foreach (var effectMeshPass in pass.Meshes) //{ // meshPasses[meshIndex] = (EffectMesh)effectMeshPass; // // Prefix increment so that 0 means no rendering. // effectMeshPass.Parameters.Set(PickingMeshIndex, ++meshIndex); //} } // For now, it generates one rendering per picking. // It would be quite easy to optimize it by make Picking shader works on multiple picking points at a time. foreach (var request in requests) { var pickingRenderPass = new RenderPass("Picking"); pickingRenderPass.StartPass.AddFirst = (threadContext) => { threadContext.GraphicsDevice.Clear(renderTargets[0], Color.Black); threadContext.GraphicsDevice.Clear(renderTargets[1], Color.Black); threadContext.Parameters.Set(PickingScreenPosition, request.Location); threadContext.GraphicsDevice.SetViewport(new Viewport(0, 0, renderTargets[0].Description.Width, renderTargets[0].Description.Height)); threadContext.GraphicsDevice.Clear(depthStencilBuffer, DepthStencilClearOptions.DepthBuffer); threadContext.GraphicsDevice.SetRenderTargets(depthStencilBuffer, renderTargets); }; pickingRenderPass.EndPass.AddLast = (threadContext) => { threadContext.Parameters.Reset(PickingScreenPosition); threadContext.GraphicsDevice.Copy(renderTargets[0].Texture, request.ResultTextures[0]); threadContext.GraphicsDevice.Copy(renderTargets[1].Texture, request.ResultTextures[1]); }; //pickingRenderPass.PassesInternal = originalPasses; throw new NotImplementedException(); request.MeshPasses = meshPasses; currentPasses.Add(pickingRenderPass); request.HasResults = true; // Wait 2 frames before pulling the results. request.FrameCounter = 2; } }; RenderSystem.GlobalPass.EndPass.AddLast = CheckPickingResults; var backBuffer = GraphicsDevice.BackBuffer; int pickingArea = 1 + PickingDistance * 2; renderTargets[0] = Texture.New2D(GraphicsDevice, pickingArea, pickingArea, PixelFormat.R32_UInt, TextureFlags.ShaderResource | TextureFlags.RenderTarget).ToRenderTarget().KeepAliveBy(ActiveObjects); renderTargets[1] = Texture.New2D(GraphicsDevice, pickingArea, pickingArea, PixelFormat.R32G32B32A32_Float, TextureFlags.ShaderResource | TextureFlags.RenderTarget).ToRenderTarget().KeepAliveBy(ActiveObjects); depthStencilTexture = Texture.New2D(GraphicsDevice, pickingArea, pickingArea, PixelFormat.D32_Float, TextureFlags.ShaderResource | TextureFlags.DepthStencil).KeepAliveBy(ActiveObjects); depthStencilBuffer = depthStencilTexture.ToDepthStencilBuffer(false); Parameters.AddDynamic(PickingMatrix, ParameterDynamicValue.New(PickingScreenPosition, (ref Vector2 pickingPosition, ref Matrix picking) => { // Move center to picked position, and zoom (it is supposed to stay per-pixel according to render target size) picking = Matrix.Translation(1.0f - (pickingPosition.X) / backBuffer.Width * 2.0f, -1.0f + (pickingPosition.Y) / backBuffer.Height * 2.0f, 0.0f) * Matrix.Scaling((float)backBuffer.Width / (float)pickingArea, (float)backBuffer.Height / (float)pickingArea, 1.0f); })); }
public void SetupLighting(EffectShaderPass effectPass, object permutationKeyObject) { var permutationKey = (LightingPermutation.KeyInfo)permutationKeyObject; if (permutationKey.PerPixelDirectionalLightCount > 0) { effectPass.Shader.Mixins.Add(new ShaderClassSource("LightMultiDirectionalShadingPerPixel", permutationKey.PerPixelDirectionalLightCount)); // Light colors effectPass.Parameters.AddDynamic(LightMultiDirectionalShadingPerPixelKeys.LightColorsWithGamma, ParameterDynamicValue.New(LightingPermutation.Key, (ref LightingPermutation lightPermutation, ref Color3[] lightColorsWithGamma) => LightColorsUpdate(lightPermutation.PerPixelDirectionalLights, lightColorsWithGamma) , autoCheckDependencies: false)); // LightDirectionVS effectPass.Parameters.AddDynamic(LightMultiDirectionalShadingPerPixelKeys.LightDirectionsVS, ParameterDynamicValue.New(LightingPermutation.Key, TransformationKeys.View, (ref LightingPermutation lightPermutation, ref Matrix view, ref Vector3[] lightDirectionVS) => { int index = 0; foreach (var lightBinding in lightPermutation.PerPixelDirectionalLights) { var lightDirection = ((DirectionalLight)lightBinding.Light).LightDirection; LightKeys.LightDirectionVSUpdate(ref lightDirection, ref view, ref lightDirectionVS[index++]); } }, autoCheckDependencies: false)); } if (permutationKey.PerPixelDiffuseDirectionalLightCount > 0) { effectPass.Shader.Mixins.Add(new ShaderClassSource("LightMultiDirectionalShadingDiffusePerPixel", permutationKey.PerPixelDiffuseDirectionalLightCount)); // Light colors effectPass.Parameters.AddDynamic(LightMultiDirectionalShadingDiffusePerPixelKeys.LightColorsWithGamma, ParameterDynamicValue.New(LightingPermutation.Key, (ref LightingPermutation lightPermutation, ref Color3[] lightColorsWithGamma) => LightColorsUpdate(lightPermutation.PerPixelDiffuseDirectionalLights, lightColorsWithGamma) , autoCheckDependencies: false)); // LightDirectionVS effectPass.Parameters.AddDynamic(LightMultiDirectionalShadingDiffusePerPixelKeys.LightDirectionsVS, ParameterDynamicValue.New(LightingPermutation.Key, TransformationKeys.View, (ref LightingPermutation lightPermutation, ref Matrix view, ref Vector3[] lightDirectionVS) => { int index = 0; foreach (var lightBinding in lightPermutation.PerPixelDiffuseDirectionalLights) { var lightDirection = ((DirectionalLight)lightBinding.Light).LightDirection; LightKeys.LightDirectionVSUpdate(ref lightDirection, ref view, ref lightDirectionVS[index++]); } }, autoCheckDependencies: false)); } if (permutationKey.PerVertexDirectionalLightCount > 0) { effectPass.Shader.Mixins.Add(new ShaderClassSource("LightMultiDirectionalShadingPerVertex", permutationKey.PerVertexDirectionalLightCount)); // Light colors effectPass.Parameters.AddDynamic(LightMultiDirectionalShadingPerVertexKeys.LightColorsWithGamma, ParameterDynamicValue.New(LightingPermutation.Key, (ref LightingPermutation lightPermutation, ref Color3[] lightColorsWithGamma) => LightColorsUpdate(lightPermutation.PerVertexDirectionalLights, lightColorsWithGamma) , autoCheckDependencies: false)); // LightDirectionWS effectPass.Parameters.AddDynamic(LightMultiDirectionalShadingPerVertexKeys.LightDirectionsWS, ParameterDynamicValue.New(LightingPermutation.Key, (ref LightingPermutation lightPermutation, ref Vector3[] lightDirectionWS) => { int index = 0; foreach (var lightBinding in lightPermutation.PerVertexDirectionalLights) { lightDirectionWS[index++] = ((DirectionalLight)lightBinding.Light).LightDirection; } }, autoCheckDependencies: false)); } if (permutationKey.PerVertexDiffusePixelSpecularDirectionalLightCount > 0) { effectPass.Shader.Mixins.Add(new ShaderClassSource("LightMultiDirectionalShadingSpecularPerPixel", permutationKey.PerVertexDiffusePixelSpecularDirectionalLightCount)); // Light colors effectPass.Parameters.AddDynamic(LightMultiDirectionalShadingSpecularPerPixelKeys.LightColorsWithGamma, ParameterDynamicValue.New(LightingPermutation.Key, (ref LightingPermutation lightPermutation, ref Color3[] lightColorsWithGamma) => LightColorsUpdate(lightPermutation.PerVertexDiffusePixelSpecularDirectionalLights, lightColorsWithGamma) , autoCheckDependencies: false)); // LightDirectionWS effectPass.Parameters.AddDynamic(LightMultiDirectionalShadingSpecularPerPixelKeys.LightDirectionsWS, ParameterDynamicValue.New(LightingPermutation.Key, (ref LightingPermutation lightPermutation, ref Vector3[] lightDirectionWS) => { int index = 0; foreach (var lightBinding in lightPermutation.PerVertexDiffusePixelSpecularDirectionalLights) { lightDirectionWS[index++] = ((DirectionalLight)lightBinding.Light).LightDirection; } }, autoCheckDependencies: false)); } }
public void SetupShadersPermutationReceiver(EffectShaderPass effectPass, object permutationKey) { var currentShadowMapsPermutation = (ShadowMapPermutationArray)permutationKey; if (currentShadowMapsPermutation.ShadowMaps.Count == 0) { return; } var shadowsArray = new ShaderArraySource(); effectPass.Shader.Mixins.Add("ShadowMapReceiver"); effectPass.Shader.Compositions.Add("shadows", shadowsArray); // Group by shadow map types (one group can be processed in the same loop). // Currently based on filtering type and number of cascades (but some more parameters might affect this later as new type of shadow maps are introduced). var shadowMapTypes = currentShadowMapsPermutation.ShadowMaps.GroupBy(x => Tuple.Create(x.ShadowMap.Filter.GetType(), x.ShadowMap.LevelCount, x.ShadowMap.Texture)); int shadowMapTypeIndex = 0; foreach (var shadowMapType in shadowMapTypes) { var shadowMapTypeCopy = shadowMapType.ToArray(); var shadowMixin = new ShaderMixinSource(); // Currently use shadow map count, but we should probably use next power of two to limit number of permutations. var maxShadowMapCount = shadowMapTypeCopy.Length; // Setup shadow mapping shadowMixin.Mixins.Add(new ShaderClassSource("ShadowMapCascadeBase", shadowMapType.Key.Item2, 0, maxShadowMapCount, 1)); //shadowMixin.Mixins.Add(new ShaderClassSource("LightDirectionalShading", shadowMapPermutation.Index)); // Setup the filter // TODO: Use static based on type of filter? (should use Key instead of First()). shadowMixin.Mixins.Add(shadowMapType.First().ShadowMap.Filter.GenerateShaderSource(maxShadowMapCount)); shadowsArray.Add(shadowMixin); // Register keys for this shadow map type var shadowSubKey = string.Format(".shadows[{0}]", shadowMapTypeIndex++); effectPass.Parameters.Set(ShadowMapKeys.Texture.AppendKey(shadowSubKey), shadowMapType.Key.Item3); effectPass.Parameters.Set(LightingPlugin.ShadowMapLightCount.AppendKey(shadowSubKey), shadowMapTypeCopy.Length); effectPass.Parameters.AddDynamic(LightingPlugin.ReceiverInfo.AppendKey(shadowSubKey), ParameterDynamicValue.New(ShadowMapPermutationArray.Key, TransformationKeys.World, TransformationKeys.ViewProjection, (ref ShadowMapPermutationArray shadowMapPermutations, ref Matrix world, ref Matrix viewProj, ref ShadowMapReceiverInfo[] output) => { unsafe { for (int i = 0; i < shadowMapTypeCopy.Length; ++i) { var permutationParameters = shadowMapTypeCopy[i].ShadowMap.Parameters; // TODO: Optimize dictionary access this using SetKeyMapping var shadowMapData = permutationParameters.Get(LightingPlugin.ViewProjectionArray); var textureCoords = permutationParameters.Get(LightingPlugin.CascadeTextureCoordsBorder); var distanceMax = permutationParameters.Get(ShadowMapKeys.DistanceMax); var lightDirection = permutationParameters.Get(LightKeys.LightDirection); Matrix *vpPtr = &shadowMapData.ViewProjReceiver0; fixed(Matrix * wvpPtr = &output[i].WorldViewProjReceiver0) { for (int j = 0; j < 4; ++j) { Matrix.Multiply(ref world, ref vpPtr[j], ref wvpPtr[j]); } } output[i].Offset0 = shadowMapData.Offset0; output[i].Offset1 = shadowMapData.Offset1; output[i].Offset2 = shadowMapData.Offset2; output[i].Offset3 = shadowMapData.Offset3; fixed(Vector4 * targetPtr = &output[i].CascadeTextureCoordsBorder0) fixed(Vector4 * sourcePtr = &textureCoords[0]) { for (int j = 0; j < 4; ++j) { targetPtr[j] = sourcePtr[j]; } } output[i].ShadowLightDirection = lightDirection; LightKeys.LightDirectionVSUpdate(ref output[i].ShadowLightDirection, ref viewProj, ref output[i].ShadowLightDirectionVS); output[i].ShadowMapDistance = distanceMax; output[i].ShadowLightColor = shadowMapData.LightColor; } } }, autoCheckDependencies: false)); // Always update (permutation won't get updated, only its content)! // Register VSM filter if necessary if (shadowMapType.Key.Item1 == typeof(ShadowMapFilterVsm)) { effectPass.Parameters.AddDynamic(LightingPlugin.ReceiverVsmInfo.AppendKey(shadowSubKey), ParameterDynamicValue.New(ShadowMapPermutationArray.Key, (ref ShadowMapPermutationArray shadowMapPermutations, ref ShadowMapReceiverVsmInfo[] output) => { for (int i = 0; i < shadowMapTypeCopy.Length; ++i) { var permutationParameters = shadowMapTypeCopy[i].ShadowMap.Parameters; output[i].BleedingFactor = permutationParameters.Get(ShadowMapFilterVsm.VsmBleedingFactor); output[i].MinVariance = permutationParameters.Get(ShadowMapFilterVsm.VsmMinVariance); } }, autoCheckDependencies: false)); } } }
static CameraKeys() { ZProjection = ParameterKeys.NewDynamic(ParameterDynamicValue.New <Vector2, float, float>(NearClipPlane, FarClipPlane, ZProjectionACalculate)); }