Beispiel #1
0
        private void SetupOutlineRenderingStep()
        {
            _solidColorEffectWithOutline = new SolidColorEffect();

            _solidColorEffectWithOutline.Color = Colors.Orange.ToColor4();
            _solidColorEffectWithOutline.OverrideModelColor = true;
            _solidColorEffectWithOutline.OutlineThickness   = 0;
            _solidColorEffectWithOutline.WriteMaxDepthValue = false;

            _solidColorEffectWithOutline.InitializeResources(MainDXViewportView.DXScene.DXDevice);

            _disposables.Add(_solidColorEffectWithOutline);


            _backgroundColor = new Color4(1, 1, 1, 0);


            var stencilSetToOneDescription = new DepthStencilStateDescription()
            {
                IsDepthEnabled = true,
                DepthWriteMask = DepthWriteMask.All,

                DepthComparison  = Comparison.LessEqual,
                IsStencilEnabled = true,
                StencilReadMask  = 0xFF,
                StencilWriteMask = 0xFF,
                FrontFace        =
                {
                    Comparison         = Comparison.Always,
                    DepthFailOperation = StencilOperation.Keep,
                    FailOperation      = StencilOperation.Keep,
                    PassOperation      = StencilOperation.Replace // The value that is set as reference is set in the ContextStateManger when _deviceContext.OutputMerger.SetDepthStencilState is called - the value is set to 1.
                }
            };

            stencilSetToOneDescription.BackFace = stencilSetToOneDescription.FrontFace;

            _stencilSetToOneDepthStencilState = new DepthStencilState(MainDXViewportView.DXScene.Device, stencilSetToOneDescription);

            if (MainDXViewportView.DXScene.DXDevice.IsDebugDevice)
            {
                _stencilSetToOneDepthStencilState.DebugName = "StencilSetToOneDepthStencilState";
            }

            _disposables.Add(_stencilSetToOneDepthStencilState);


            var renderWhenStencilIsNotOneDescription = new DepthStencilStateDescription()
            {
                IsDepthEnabled = true,
                DepthWriteMask = DepthWriteMask.All,

                DepthComparison  = Comparison.LessEqual,
                IsStencilEnabled = true,
                StencilReadMask  = 0xFF,
                StencilWriteMask = 0xFF,
                FrontFace        =
                {
                    Comparison         = Comparison.Greater, // render only when 1 is greater then stencil value
                    DepthFailOperation = StencilOperation.Keep,
                    FailOperation      = StencilOperation.Keep,
                    PassOperation      = StencilOperation.Keep
                }
            };

            renderWhenStencilIsNotOneDescription.BackFace = stencilSetToOneDescription.FrontFace;

            _renderWhenStencilIsNotOneState = new DepthStencilState(MainDXViewportView.DXScene.Device, renderWhenStencilIsNotOneDescription);

            if (MainDXViewportView.DXScene.DXDevice.IsDebugDevice)
            {
                _renderWhenStencilIsNotOneState.DebugName = "RenderWhenStencilIsNotOneState";
            }

            _disposables.Add(_renderWhenStencilIsNotOneState);


            var renderingStepsGroup = new RenderingStepsGroup("Render outline group", MainDXViewportView.DXScene.RenderingSteps);

            renderingStepsGroup.BeforeRunningStep += (object sender, DirectX.RenderingEventArgs args) =>
            {
                var renderingContext = args.RenderingContext;

                // Set new back buffer where we will render outline objects
                SetupOutlineBackBuffers(renderingContext);

                // Set new DepthStencilState that will also set stencil value to 1 for each rendered pixel
                renderingContext.ContextStatesManager.DepthStencilState = _stencilSetToOneDepthStencilState;

                _addOutlineRenderingStep.SourceTexture = _outlineShaderResourceView;
            };

            renderingStepsGroup.AfterRunningStep += (object sender, DirectX.RenderingEventArgs args) =>
            {
                var renderingContext = args.RenderingContext;

                // Reset the saved back buffer
                RestoreBackBuffers(renderingContext);
            };


            // The first step in rendering outlines is to render selected objects with the selected solid color.
            // This is done with creating a custom RenderObjectsRenderingStep
            // and overriding the OverrideEffect to use SolidColorEffect and _stencilSetToOneDepthStencilState.
            // We also render only the selected objects - this is done with using FilterObjectsFunction.
            var renderOutlinesObjectsRenderingStep = new RenderObjectsRenderingStep("Render outlined objects")
            {
                OverrideEffect            = _solidColorEffectWithOutline,
                OverrideDepthStencilState = _stencilSetToOneDepthStencilState,

                FilterObjectsFunction = delegate(RenderablePrimitiveBase objectToRender)
                {
                    // IMPORTANT:
                    // This delegate is highly performance critical because it is called for each object in each frame.
                    // Therefore do not access any WPF's DependencyProperties there.

                    var wpfGeometryModel3DNode = objectToRender.OriginalObject as WpfGeometryModel3DNode;

                    if (wpfGeometryModel3DNode == null)
                    {
                        return(false);
                    }

                    // NOTE:
                    // Here we do a simple check for object name in a List<string>.
                    // If you have a lot of selected objects, use HashSet<string> instead.
                    //
                    // The reason why the check is done by the object name is that this way
                    // we can simply connect the WPF object (that is selected) and the SceneNode object that is created from the WPF object.
                    // So if we define the name for the WPF objects, then the SceneNode objects that are created from them will also have the same name.
                    //
                    // But if it is not possible to name WPF objects of if you have duplicate names,
                    // then you will need to store SceneNode objects in HashSet instead of object names.
                    //
                    // To use this we need to get the SceneNode instances that are created from WPF objects.
                    // One option is to call MainDXViewportView.GetSceneNodeForWpfObject(wpfObject) for each WPF object (this must be called after the SceneNodes are initialized - for example in DXSceneInitialized).
                    // Another option is to wait until SceneNodes are created from WPF objects (in DXSceneInitialized event handler or if the scene was already created after calling MainDXViewportView.Update()).
                    // Then go through all SceneNodes in the hierarchy (you can use MainDXViewportView.DXScene.RootNode.ForEachChildNode method)
                    // and for each WpfGeometryModel3DNode gets its GeometryModel3D and build a Dictionary with WPF (GeometryModel3D) and SceneNode (WpfGeometryModel3DNode) objects.
                    // Then when you select for which WPF objects you want to draw outline, create a HashSet<WpfGeometryModel3DNode> with list of WpfGeometryModel3DNode objects.

                    return(_selectedObjectNames.Contains(wpfGeometryModel3DNode.Name));
                }
            };

            renderingStepsGroup.Children.Add(renderOutlinesObjectsRenderingStep);


            // Add two ExpandPostProcess that will make the outline bigger
            int outlineWidth = (int)OutlineSizeSlider.Value;

            _horizontalExpandPostProcess = new Ab3d.DirectX.PostProcessing.ExpandPostProcess(isVerticalRenderingPass: false, expansionWidth: outlineWidth, backgroundColor: _backgroundColor);
            _verticalExpandPostProcess   = new Ab3d.DirectX.PostProcessing.ExpandPostProcess(isVerticalRenderingPass: true, expansionWidth: outlineWidth, backgroundColor: _backgroundColor);

            _disposables.Add(_horizontalExpandPostProcess);
            _disposables.Add(_verticalExpandPostProcess);

            var expandPostProcesses = new List <PostProcessBase>();

            expandPostProcesses.Add(_horizontalExpandPostProcess);
            expandPostProcesses.Add(_verticalExpandPostProcess);


            // We could also blur the outline to make it bigger, but Expand creates better results
            //var horizontalBlurPostProcess = new Ab3d.DirectX.PostProcessing.SimpleBlurPostProcess(isVerticalBlur: false, filterWidth: 5);
            //var verticalBlurPostProcess   = new Ab3d.DirectX.PostProcessing.SimpleBlurPostProcess(isVerticalBlur: true, filterWidth: 5);
            //horizontalBlurPostProcess.InitializeResources(MainDXViewportView.DXScene.DXDevice);
            //verticalBlurPostProcess.InitializeResources(MainDXViewportView.DXScene.DXDevice);

            //blurPostProcesses.Add(horizontalBlurPostProcess);
            //blurPostProcesses.Add(verticalBlurPostProcess);


            _expandObjectsPostProcessesRenderingSteps = new RenderPostProcessingRenderingStep("Expand objects rendering step", expandPostProcesses);
            renderingStepsGroup.Children.Add(_expandObjectsPostProcessesRenderingSteps);


            MainDXViewportView.DXScene.RenderingSteps.AddAfter(MainDXViewportView.DXScene.DefaultInitializeRenderingStep, renderingStepsGroup);


            _addOutlineRenderingStep = new RenderTextureRenderingStep(RenderTextureRenderingStep.TextureChannelsCount.FourChannels, "Render outline over 3D scene")
            {
                Offsets                 = new Vector4(0, 0, 0, 0),                                                     // preserve original colors
                Factors                 = new Vector4(1, 1, 1, 1),
                TargetViewport          = new ViewportF(0, 0, 1f, 1f),                                                 // render to full screen
                CustomBlendState        = MainDXViewportView.DXScene.DXDevice.CommonStates.NonPremultipliedAlphaBlend, // alpha blend
                CustomDepthStencilState = _renderWhenStencilIsNotOneState,                                             // only render when stencil value is less then 1 (not where the objects are rendered)
            };

            _addOutlineRenderingStep.BeforeRunningStep += delegate(object sender, DirectX.RenderingEventArgs args)
            {
                var renderingContext = args.RenderingContext;

                renderingContext.SetBackBuffer(renderingContext.CurrentBackBuffer, renderingContext.CurrentBackBufferDescription, renderingContext.CurrentRenderTargetView, _outlineDepthStencilView, false);
                //renderingContext.DeviceContext.OutputMerger.SetTargets(_outlineDepthStencilView, renderingContext.CurrentRenderTargetView);
            };

            MainDXViewportView.DXScene.RenderingSteps.AddBefore(MainDXViewportView.DXScene.DefaultCompleteRenderingStep, _addOutlineRenderingStep);
        }
        private void SetupExpandPostProcessOutlines()
        {
            var dxScene = MainDXViewportView.DXScene;

            _blackOutlineEffect = EnsureSolidColorEffect();

            if (_blackOutlineEffect == null)
            {
                return;
            }

            // Reset values that may be changed when using SolidColorEffectWithOutlines
            _blackOutlineEffect.DepthBias = 0;
            _blackOutlineEffect.OverrideRasterizerState = null;
            _blackOutlineEffect.OutlineThickness        = 0;
            _blackOutlineEffect.WriteMaxDepthValue      = true;

            _renderObjectOutlinesRenderingStep = EnsureRenderObjectsRenderingStep(dxScene);

            if (!dxScene.RenderingSteps.Contains(_renderObjectOutlinesRenderingStep))
            {
                dxScene.RenderingSteps.AddBefore(dxScene.DefaultRenderObjectsRenderingStep, _renderObjectOutlinesRenderingStep);
            }


            int outlineWidth = (int)OutlineWidthComboBox.SelectedItem;

            if (_prepareExpandObjectsPostProcessingRenderingStep == null)
            {
                // Expand post process is done in two passes (one horizontal and one vertical)
                _horizontalExpandPostProcess = new Ab3d.DirectX.PostProcessing.ExpandPostProcess(isVerticalRenderingPass: false, expansionWidth: outlineWidth, backgroundColor: dxScene.BackgroundColor);
                _verticalExpandPostProcess   = new Ab3d.DirectX.PostProcessing.ExpandPostProcess(isVerticalRenderingPass: true, expansionWidth: outlineWidth, backgroundColor: dxScene.BackgroundColor);

                _disposables.Add(_horizontalExpandPostProcess);
                _disposables.Add(_verticalExpandPostProcess);

                var expandPostProcesses = new PostProcessBase[]
                {
                    _horizontalExpandPostProcess,
                    _verticalExpandPostProcess
                };

                // To execute the post processes we need to rendering steps:
                // 1) PreparePostProcessingRenderingStep that creates required RenderTargets and ShaderResourceViews and sets that to the RenderPostProcessingRenderingStep
                // 2) RenderPostProcessingRenderingStep that actually executed all the post-processes

                // Because we will execute post-processes before the standard scene rendering,
                // we also need to make sure that the Destination buffer is correctly set (see _prepareExandObjectsPostProcessingRenderingStep.BeforeRunningStep)
                // and that the DepthStencilView is reset after the post-processes are rendered (see _expandObjectsPostProcessesRenderingSteps.AfterRunningStep).

                // First create the RenderPostProcessingRenderingStep because it is needed in the constructor of the PreparePostProcessingRenderingStep
                _expandObjectsPostProcessesRenderingSteps = new RenderPostProcessingRenderingStep("Expand objects rendering step", expandPostProcesses);
                _expandObjectsPostProcessesRenderingSteps.AfterRunningStep += delegate(object sender, RenderingEventArgs args)
                {
                    // Post-processes are usually executed at the end of rendering process and work on 2D textures so they do not require DepthStencil.
                    // The CurrentBackBuffer / Description and RenderTargetView are already correct because they are sent by PreparePostProcessingRenderingStep,
                    // but we need to set the _savedDepthStencilView and SupersamplingCount.
                    args.RenderingContext.SetBackBuffer(args.RenderingContext.CurrentBackBuffer,
                                                        args.RenderingContext.CurrentBackBufferDescription,
                                                        args.RenderingContext.CurrentRenderTargetView,
                                                        _savedDepthStencilView,
                                                        dxScene.SupersamplingCount,
                                                        bindNewRenderTargetsToDeviceContext: true);
                };

                _disposables.Add(_expandObjectsPostProcessesRenderingSteps);


                _prepareExpandObjectsPostProcessingRenderingStep = new PreparePostProcessingRenderingStep(_expandObjectsPostProcessesRenderingSteps, "Prepare expand post process");
                _prepareExpandObjectsPostProcessingRenderingStep.BeforeRunningStep += delegate(object sender, RenderingEventArgs args)
                {
                    // Because after the post-processes are executed we will continue with rendering the scene,
                    // we need to set the DestinationBackBuffer (there will be the final result of the post-processes)
                    // to the currently used BackBuffer. If this is not done, then RenderingContext.FinalBackBuffer is used as destination back buffer.
                    _prepareExpandObjectsPostProcessingRenderingStep.SetCustomDestinationBackBuffer(args.RenderingContext.CurrentBackBuffer,
                                                                                                    args.RenderingContext.CurrentBackBufferDescription,
                                                                                                    args.RenderingContext.CurrentRenderTargetView);

                    // Save CurrentDepthStencilView
                    _savedDepthStencilView = args.RenderingContext.CurrentDepthStencilView;
                };

                _disposables.Add(_prepareExpandObjectsPostProcessingRenderingStep);
            }
            else
            {
                _horizontalExpandPostProcess.ExpansionWidth = outlineWidth;
                _verticalExpandPostProcess.ExpansionWidth   = outlineWidth;
            }


            if (!dxScene.RenderingSteps.Contains(_prepareExpandObjectsPostProcessingRenderingStep))
            {
                dxScene.RenderingSteps.AddAfter(_renderObjectOutlinesRenderingStep, _prepareExpandObjectsPostProcessingRenderingStep);
                dxScene.RenderingSteps.AddAfter(_prepareExpandObjectsPostProcessingRenderingStep, _expandObjectsPostProcessesRenderingSteps);
            }
        }