其他分享
首页 > 其他分享> > WPF-3D-Z-buffering 导致的遮盖物体不渲染问题

WPF-3D-Z-buffering 导致的遮盖物体不渲染问题

作者:互联网

WPF-3D-Z-buffering 导致的遮盖物体不渲染问题

问题描述:

3D渲染时存在一个Z-buffering的问题,如果后面的物体被前面的物体遮盖,则后面的物体被遮盖的部分不会被渲染,即使前面的物体是有透明度的,后面的物体也不会透过来。

这个立方体的所有面都是透明的,但是我们可以看到,其中有一条边线不完整,这就是因为WPF认为那个面被前面的面遮盖了,导致被遮盖的部分没有被渲染,所以前面的面是透明的也看不到后面的边线。

原因解释:

Here's a simple overview. Imagine two quads with diffuse, partially transparent material, one over the other. When the first object to be rendered is behind the next object, both objects are successfully rendered, and you get your intended blending.

Camera --> 2 1

In the case where the first object is in front of a later object, the later object does not get rendered/blended, because the Z-Buffer was marked to ignore everything behind the first one.

Camera -> 1 2

解决思路:

动态调整Z轴,因为Z是根据你添加Model3D顺序决定的,所以每次调整角度的时候动态调整Model3Ds的顺序,将前面的面放在前面,即BringToFront();

如何确定Model3Ds顺序?

对于长方形来说,我们通过摄像机位置对于长方形中心的距离来判断物体对于摄像机的Z轴顺序。

Code:

            foreach (var m in ModelVisual3Ds)
            {
                var rect3D =
                    WorldTransform.TransformBounds(
                        m.Transform.TransformBounds(
                           m.Model.Bounds
                        )
                    );

                var center = new Point3D(rect3D.X + rect3D.SizeX / 2, rect3D.Y + rect3D.SizeY / 2, rect3D.Z + rect3D.SizeZ / 2);

                double distance = Point3D.Subtract(CameraPosition, center).Length;
                list.Add(new ModelVisual3DDistance(distance, m));
            }
            list.Sort(new DistanceComparer(SortDirection.FarToNear));
            ModelVisual3Ds.Clear();
            foreach (ModelVisual3DDistance d in list)
            {
                ModelVisual3Ds.Add(d.modelVisual3D);
            }

完整:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Media3D;

namespace _3DTools
{
    // original class taken from
    // https://web.archive.org/web/20071201135137/http://blogs.msdn.com/pantal/archive/2007/07/23/sorting-for-wpf-3d-transparency.aspx
    public static class SceneSortingHelper
    {
        /// <summary>
        /// Sort Modelgroups in farthest to closest order, to enable transparency
        /// Should be applied whenever the scene is significantly re-oriented
        /// </summary>
        public static void AlphaSort(Point3D CameraPosition,
            System.Collections.Generic.List<System.Windows.Media.Media3D.ModelUIElement3D> ModelVisual3Ds, Transform3D WorldTransform)
        {
            ArrayList list = new ArrayList();
            foreach (var m in ModelVisual3Ds)
            {
                var rect3D =
                    WorldTransform.TransformBounds(
                        m.Transform.TransformBounds(
                           m.Model.Bounds
                        )
                    );

                var center = new Point3D(rect3D.X + rect3D.SizeX / 2, rect3D.Y + rect3D.SizeY / 2, rect3D.Z + rect3D.SizeZ / 2);

                double distance = Point3D.Subtract(CameraPosition, center).Length;
                list.Add(new ModelVisual3DDistance(distance, m));
            }
            list.Sort(new DistanceComparer(SortDirection.FarToNear));
            ModelVisual3Ds.Clear();
            foreach (ModelVisual3DDistance d in list)
            {
                ModelVisual3Ds.Add(d.modelVisual3D);
            }
        }

        private class ModelVisual3DDistance
        {
            public ModelVisual3DDistance(double distance, ModelUIElement3D modelVisual3D)
            {
                this.distance = distance;
                this.modelVisual3D = modelVisual3D;
            }

            public double distance;
            public ModelUIElement3D modelVisual3D;
        }

        private enum SortDirection
        {
            NearToFar,
            FarToNear
        }

        private class DistanceComparer : IComparer
        {
            public DistanceComparer(SortDirection sortDirection)
            {
                _sortDirection = sortDirection;
            }

            int IComparer.Compare(Object o1, Object o2)
            {
                double x1 = ((ModelVisual3DDistance)o1).distance;
                double x2 = ((ModelVisual3DDistance)o2).distance;

                if (_sortDirection == SortDirection.FarToNear)
                {
                    if (x1 > x2)
                        return -1;
                    else
                    if (x1 < x2)
                        return 1;
                    else
                        return 0;
                }
                else
                {
                    if (x1 > x2)
                        return 1;
                    else
                    if (x1 < x2)
                        return -1;
                    else
                        return 0;
                }
            }

            private SortDirection _sortDirection;
        }
    }

    public static class Viewport3DExtension
    {
        public static void AlphaSortModels(this System.Windows.Controls.Viewport3D viewport3D)
        {
            var projectionCamera = viewport3D.Camera as System.Windows.Media.Media3D.ProjectionCamera;

            if (projectionCamera != null)
            {
                var modelVisual3Ds = new System.Collections.Generic.List<System.Windows.Media.Media3D.ModelUIElement3D>();

                foreach (var c in viewport3D.Children)
                {
                    var m = c as System.Windows.Media.Media3D.ContainerUIElement3D;

                    if (m != null)
                    {
                        foreach (var item in m.Children)
                        {
                            modelVisual3Ds.Add(item as System.Windows.Media.Media3D.ModelUIElement3D);
                        }

                        var position = projectionCamera.Transform.Transform(projectionCamera.Position);
                        // note:
                        //  the following method works well most of the time but sometimes the sort is wrong as the sort is simply based on model bounds,
                        //  to get rid of artifacts we would need something like binary space partitioning
                        SceneSortingHelper.AlphaSort(
                            position,
                            modelVisual3Ds,
                            new System.Windows.Media.Media3D.Transform3DGroup()
                        );

                        m.Children.Clear();

                        foreach (var item in modelVisual3Ds)
                            m.Children.Add(item);

                        break;
                    }
                }
            }
        }
    }
}

参考:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/1bc97750-d5c0-4137-b16f-3ad8c901ea92/strange-wpf-3d-opacity-behavior
https://forums.autodesk.com/t5/3ds-max-shading-lighting-and/zdepth-and-opacity/td-p/7330790
https://stackoverflow.com/questions/48924505/how-to-handle-diffuse-material-transparency-in-wpf-3d
http://csharphelper.com/blog/2014/10/perform-hit-testing-in-a-3d-program-that-uses-wpf-xaml-and-c/
https://docs.microsoft.com/en-us/archive/msdn-magazine/2007/october/foundations-3d-text-in-wpf
https://xoax.net/blog/rendering-transparent-3d-surfaces-in-wpf-with-c/
https://docs.microsoft.com/en-us/dotnet/desktop/wpf/graphics-multimedia/how-to-animate-material-properties-in-a-3-d-scene?view=netframeworkdesktop-4.8

标签:distance,System,rect3D,var,new,WPF,buffering,ModelVisual3DDistance,3D
来源: https://www.cnblogs.com/swobble/p/14607576.html