如何在WPF 3D中处理漫反射材质透明度?

时间:2018-02-22 10:02:02

标签: c# wpf 3d

我有一个场景,其中多个模型具有平面和具有透明度的漫反射材料。

这些模型有时因相机角度的变化而重叠,然后这些模型无法正确渲染,如下图所示,其中蓝色透明面隐藏了红色立方体的一部分:

blue transparent face hides part of the red cube

经过一些研究后我发现需要进行深度排序才能在所有情况下正确渲染模型。

那里是否有任何库已经处理了这个问题,例如通过实现Newell的算法还是有人知道另一种解决方案?

1 个答案:

答案 0 :(得分:0)

我发现了一种基于模型边界框的排序算法,该模型边界框源自一个非常旧的Microsoft博客,不再可以访问。

以下是包含一些代码更改的类,可直接传递ModelVisual3D个实例:

// 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.ModelVisual3D> ModelVisual3Ds, Transform3D WorldTransform)
    {
        ArrayList list = new ArrayList();
        foreach (var m in ModelVisual3Ds)
        {
            var location =
                WorldTransform.TransformBounds(
                    m.Transform.TransformBounds(
                        m.Content.Transform.TransformBounds(
                            m.Content.Bounds
                        )
                    )
                ).Location;

            double distance = Point3D.Subtract(CameraPosition, location).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, ModelVisual3D modelVisual3D)
        {
            this.distance = distance;
            this.modelVisual3D = modelVisual3D;
        }

        public double distance;
        public ModelVisual3D 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;
    }
}

我正在使用上述类与以下Viewport3D扩展方法相关...

namespace Viewport3DExtensions
{
    public static class Implementation
    {
        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.ModelVisual3D>();

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

                    if (m != null)
                        modelVisual3Ds.Add(m);
                }

                // 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 artefacts we would need something like binary space partioning
                SceneHelper.SceneSortingHelper.AlphaSort(
                    projectionCamera.Position,
                    modelVisual3Ds,
                    new System.Windows.Media.Media3D.Transform3DGroup()
                );

                viewport3D.Children.Clear();

                foreach (var c in modelVisual3Ds)
                    viewport3D.Children.Add(c);
            }
        }
    }
}

...喜欢这样

System.Windows.Controls.Viewport3D scene;  
...
Scene.AlphaSortModels();

现在看起来很不错:

Transparency displayed correctly

相关问题