質問

I'm creating a Viewport3D (CurViewport3D) off-screen and fill it with only one plane (for testing purpose).

Via RenderTargetBitmap and BitmapEncoder a MemoryStream of the content of this viewport (as jpg) is created. The whole functionality is put in a library (dll) because I would like to use this library on a web server to create an image there to be shown on a website.

The code below is reduced to the basics but it is working.

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Shapes;
using System.Windows.Controls;
using System.Windows.Threading;

namespace RenderTargetImage_Test
{
    public class RenderTargetImage01
    {
        public System.IO.MemoryStream stream = new System.IO.MemoryStream();
        Int32 width = 100;
        Int32 height = 100;
        Viewport3D CurViewport3D;

        public RenderTargetImage01()
        {
            CurViewport3D = new Viewport3D();
            Model3DGroup myModel3DGroup = new Model3DGroup();
            ModelVisual3D myModelVisual3D = new ModelVisual3D(); 

            PerspectiveCamera myPCamera = new PerspectiveCamera();
            myPCamera.Position = new Point3D(3500, 1500, -5000);
            myPCamera.LookDirection = new Vector3D(-0.5, 0, 1);
            myPCamera.FieldOfView = 70;
            CurViewport3D.Camera = myPCamera;

            AmbientLight myAmbientLight = new AmbientLight();
            myAmbientLight.Color = Colors.White;
            myModel3DGroup.Children.Add(myAmbientLight);

            MeshGeometry3D meshS2 = new MeshGeometry3D();
            GeometryModel3D modelS2 = new GeometryModel3D();

            meshS2.Positions.Add(new Point3D(500, 500, -500));
            meshS2.Positions.Add(new Point3D(500, 3000, -500));
            meshS2.Positions.Add(new Point3D(3000, 500, -500));
            meshS2.Positions.Add(new Point3D(3000, 3000, -500));
            meshS2.TriangleIndices.Add(0);
            meshS2.TriangleIndices.Add(1);
            meshS2.TriangleIndices.Add(2);
            meshS2.TriangleIndices.Add(3);
            meshS2.TriangleIndices.Add(2);
            meshS2.TriangleIndices.Add(1);
            meshS2.Normals.Add(new Vector3D(0, 0, 1));
            meshS2.Normals.Add(new Vector3D(0, 0, 1));
            meshS2.Normals.Add(new Vector3D(0, 0, 1));
            meshS2.Normals.Add(new Vector3D(0, 0, 1));
            meshS2.Normals.Add(new Vector3D(0, 0, 1));
            meshS2.Normals.Add(new Vector3D(0, 0, 1));
            meshS2.TextureCoordinates.Add(new System.Windows.Point(0, 0));
            meshS2.TextureCoordinates.Add(new System.Windows.Point(1, 0));
            meshS2.TextureCoordinates.Add(new System.Windows.Point(0, 1));
            meshS2.TextureCoordinates.Add(new System.Windows.Point(1, 1));

            modelS2.Geometry = meshS2;
            DiffuseMaterial DiffMS2 = new DiffuseMaterial(System.Windows.Media.Brushes.Red);
            modelS2.Material = DiffMS2;
            myModel3DGroup.Children.Add(modelS2);
            myModelVisual3D.Content = myModel3DGroup;
            CurViewport3D.Children.Add(myModelVisual3D);

            // render
            RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
            CurViewport3D.Width = width;
            CurViewport3D.Height = height;
            CurViewport3D.Measure(new System.Windows.Size(width, height));
            CurViewport3D.Arrange(new Rect(0, 0, width, height));
            CurViewport3D.Dispatcher.Invoke(((Action)(() => renderTargetBitmap.Render(CurViewport3D))), DispatcherPriority.Render);
            BitmapEncoder bitmapEncoder = new PngBitmapEncoder();
            bitmapEncoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
            bitmapEncoder.Save(stream);
        }
    }
}

Here comes the code of the website:

<body>
    <form id="form1" runat="server">
    <div>
    </div>
    </form>
</body>

and the code behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Windows.Shapes;

using System.Windows.Media;
using System.Windows.Media.Imaging;

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

using RenderTargetImage_Test;

public partial class Default2 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        RenderTargetImage01 rti = new RenderTargetImage01();

        rti.stream.WriteTo(Response.OutputStream);
        Response.ContentType = "image/png";
        Response.Flush();
        Response.End();
    }
}

So simple!

Now the problem: Locally everything works fine. The image is shown in the browser - perfect. Once I try to run it on the web server (Microsoft Server 2008) the image shown in the browser is empty, no content. No error.

Does anyone know how to make it run on the web server?

役に立ちましたか?

解決

The answer is very simple: WPF 3D rendering does not work in Session 0 Isolation that applies to non interactive users under WIndows 2008 server and above ( Or Vista/Seven in clients ). I did not find any real solution to the same problem, since all of these needs to have a logged on user on the server ( even a remote user ) hosting the WPF drawing in a Session different from 0. So as a really drastical solution I used OpenGL to render the same: since Opengl can render in a pure software way, it is not influenced by session 0, anf it works like a charm.

他のヒント

I tried DirectX9 (through SharpDX) and cannot initialize the 3d support in session 0 isolation. After some investigation, I found out that this is only working with DirectX 11: http://msdn.microsoft.com/en-us/library/windows/desktop/hh404562%28v=vs.85%29.aspx#use_direct3d_in_session_0_processes

Unfortunately, this only works on Windows 8 and Windows Server 2012.

Well It seems to be an issue with the fact that the Viewport is Off-Screen while rendering. This I believe Results in the Viewport never actually being rendered to the screen. Now since it works locally, the problem may lie in the graphics hardware on the server. While Locally you may have the hardware to render everything that is called, the server is probably rendering everything via software. The software will just negate anything that is not view-able to save on timing and resources. While the Graphics hardware will still render everything and store it in a buffer until it becomes view-able.

If that is not the case then I am going to have to say it has to do with the memory stream that you have running. Is there any 100% reason you have to use the memory stream? If not try the solution without it and see if that fixes the issue.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top