wpfwebserverviewport3drendertargetbitmap

wpf RenderTargetBitmap of Viewport3D on a web server (off-screen)


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?


Solution

  • 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.