private Model3D ConvertVisualToModel3D(Visual visual, int dpi, ref double z)
        {
            Model3D model  = null;
            var     bounds = VisualTreeHelper.GetContentBounds(visual);

            if (visual is Viewport3D viewport3D)
            {
                bounds = new Rect(viewport3D.RenderSize);
            }

            if (this.includeEmptyVisuals)
            {
                bounds.Union(VisualTreeHelper.GetDescendantBounds(visual));
            }

            if (!bounds.IsEmpty && bounds.Width > 0 && bounds.Height > 0)
            {
                var mesh = new MeshGeometry3D();
                mesh.Positions.Add(new Point3D(bounds.Left, bounds.Top, z));
                mesh.Positions.Add(new Point3D(bounds.Right, bounds.Top, z));
                mesh.Positions.Add(new Point3D(bounds.Right, bounds.Bottom, z));
                mesh.Positions.Add(new Point3D(bounds.Left, bounds.Bottom, z));
                mesh.TextureCoordinates.Add(new Point(0, 0));
                mesh.TextureCoordinates.Add(new Point(1, 0));
                mesh.TextureCoordinates.Add(new Point(1, 1));
                mesh.TextureCoordinates.Add(new Point(0, 1));
                mesh.Normals.Add(new Vector3D(0, 0, 1));
                mesh.Normals.Add(new Vector3D(0, 0, 1));
                mesh.Normals.Add(new Vector3D(0, 0, 1));
                mesh.Normals.Add(new Vector3D(0, 0, 1));
                mesh.TriangleIndices = new Int32Collection(new int[] { 0, 1, 2, 2, 3, 0 });
                mesh.FreezeIfPossible();

                var brush    = this.MakeBrushFromVisual(visual, bounds, dpi);
                var material = new DiffuseMaterial(brush);
                material.FreezeIfPossible();

                model = new GeometryModel3D(mesh, material);
                ((GeometryModel3D)model).BackMaterial = material;

                z -= 1;
            }

            var childrenCount = VisualTreeHelper.GetChildrenCount(visual);

            if (childrenCount > 0)
            {
                var group = new Model3DGroup();

                if (model != null)
                {
                    group.Children.Add(model);
                }

                for (var i = 0; i < childrenCount; i++)
                {
                    if (VisualTreeHelper.GetChild(visual, i) is Visual childVisual)
                    {
                        var childModel = this.ConvertVisualToModel3D(childVisual, dpi, ref z);
                        if (childModel != null)
                        {
                            group.Children.Add(childModel);
                        }
                    }
                }

                model = group;
            }

            if (model != null)
            {
                var transform = VisualTreeHelper.GetTransform(visual);
                var matrix    = transform?.Value ?? Matrix.Identity;
                var offset    = VisualTreeHelper.GetOffset(visual);
                matrix.Translate(offset.X, offset.Y);

                if (!matrix.IsIdentity)
                {
                    var         matrix3D    = new Matrix3D(matrix.M11, matrix.M12, 0, 0, matrix.M21, matrix.M22, 0, 0, 0, 0, 1, 0, matrix.OffsetX, matrix.OffsetY, 0, 1);
                    Transform3D transform3D = new MatrixTransform3D(matrix3D);
                    transform3D.FreezeIfPossible();
                    model.Transform = transform3D;
                }

                model.FreezeIfPossible();
            }

            return(model);
        }