1

I have a viewport3D that rotate an image. I have to construct it in code behind. If I put my viewport in a grid everything works fine. But if I try to render it using a RenderTargetBitmap some problem arise and I get always an empty image.

This is my code:

Viewport3D vp = new Viewport3D();
PerspectiveCamera came = new PerspectiveCamera();
came.Position = new Point3D(0,0,4);
vp.Camera = came;
ModelVisual3D light = new ModelVisual3D();
AmbientLight ambLight = new AmbientLight(Colors.White);
light.Content = ambLight;
vp.Children.Add(light);

Viewport2DVisual3D v2d = new Viewport2DVisual3D();
MeshGeometry3D geom3d = new MeshGeometry3D();
geom3d.Positions = new Point3DCollection() {new Point3D(-1,1,0), new Point3D(-1,-1,0), new Point3D(1,-1,0), new Point3D(1,1,0)};    
geom3d.TextureCoordinates = new PointCollection(){new Point(0,0), new Point(0,1), new Point(1,1), new Point(1,0)};
geom3d.TriangleIndices = new Int32Collection(){0,1,2,0,2,3};
v2d.Geometry=geom3d;
DiffuseMaterial mat = new DiffuseMaterial(new SolidColorBrush(Colors.Black));
Viewport2DVisual3D.SetIsVisualHostMaterial(mat, true);
v2d.Material = mat;
v2d.Transform = new RotateTransform3D( new AxisAngleRotation3D( new Vector3D(0, 1, 0), 45));

Image img = new Image();
BitmapImage bmpRend = new BitmapImage();
bmpRend.BeginInit();
bmpRend.UriSource = new Uri(@"/WpfProva;component/Images/06_09_2012_09_57_46.jpg", UriKind.Relative);
bmpRend.EndInit();
img.Source = bmpRend; //cam.CAM1_Image_act; 
img.Width=200;
img.Height=200;
//Button btn = new Button();
//btn.Content = "Bottone 3D";
//btn.Width = 50;
//btn.Height = 50;
v2d.Visual = img;
vp.Children.Add(v2d);

//grdContainer.Children.Add(vp);

int wd = 500; //(int)vp.ActualWidth;
int ht = 500; //(int)vp.ActualHeight;

vp.Width = wd;
vp.Height = ht;
vp.Measure(new Size(wd, ht));
vp.Arrange(new Rect(0, 0, wd, ht));

//Viewbox viewbox = new Viewbox();
//viewbox.Child = vp; //control to render
//viewbox.Measure(new System.Windows.Size(wd/20, ht/20));
//viewbox.Arrange(new Rect(0, 0, wd/5, ht/5));
//viewbox.UpdateLayout();

RenderTargetBitmap bmpRender = new RenderTargetBitmap(wd, ht, 300, 300, PixelFormats.Pbgra32);
bmpRender.Render(vp);

using (FileStream outStream = new FileStream(@"C:\mycanvas.png", FileMode.Create))
{
    PngBitmapEncoder enc = new PngBitmapEncoder();
    enc.Frames.Add(BitmapFrame.Create(bmpRender));

    enc.Save(outStream);
}

I call this function after InitializeComponent().

4
  • did you tried to replicate same in xaml? did you see as expected or you see blank? Commented Jun 24, 2014 at 7:03
  • Yes i tried to replicate in Xaml and i see result as expected; only with code behind i get empty image Commented Jun 24, 2014 at 7:26
  • is it possible for you to post the xaml or a full working sample which demonstrate the issue? Commented Jun 24, 2014 at 7:32
  • xaml is trivial, only an empty window generated by VS2013 with "Add->New Window" command (it contains only an empty Grid tag). In the code behind only the InitializeComponents() call and a call to this code i posted.No other functions. You can try it in whatever empty sample project. Obviously adding an image to the resources of the project. Commented Jun 24, 2014 at 7:55

1 Answer 1

1

You have to add it to visual and let it render then only you can user RenderTargetBitmap.

so in xaml add viewport3d

 <Grid x:Name="baseGd">
    <Viewport3D x:Name="vp"/>

    <Button Content="Click" Click="Render_click" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    <Button Content="save" Click="Save_click" HorizontalAlignment="Center" VerticalAlignment="Top"/>
</Grid>

then in render_click render it

    private void Render_click(object sender, RoutedEventArgs e)
    {

        PerspectiveCamera came = new PerspectiveCamera();
        came.Position = new Point3D(0, 0, 4);
        vp.Camera = came;
        ModelVisual3D light = new ModelVisual3D();
        AmbientLight ambLight = new AmbientLight(Colors.White);
        light.Content = ambLight;
        vp.Children.Add(light);

        Viewport2DVisual3D v2d = new Viewport2DVisual3D();
        MeshGeometry3D geom3d = new MeshGeometry3D();
        geom3d.Positions = new Point3DCollection() { new Point3D(-1, 1, 0), new Point3D(-1, -1, 0), new Point3D(1, -1, 0), new Point3D(1, 1, 0) };
        geom3d.TextureCoordinates = new PointCollection() { new Point(0, 0), new Point(0, 1), new Point(1, 1), new Point(1, 0) };
        geom3d.TriangleIndices = new Int32Collection() { 0, 1, 2, 0, 2, 3 };
        v2d.Geometry = geom3d;
        DiffuseMaterial mat = new DiffuseMaterial(new SolidColorBrush(Colors.Black));
        Viewport2DVisual3D.SetIsVisualHostMaterial(mat, true);
        v2d.Material = mat;
        v2d.Transform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 45));

        Image img = new Image();
        BitmapImage bmpRend = new BitmapImage();
        bmpRend.BeginInit();
        bmpRend.UriSource = new Uri(@"image-1.jpg", UriKind.Relative);
        bmpRend.EndInit();
        img.Source = bmpRend;
        img.Width = 200;
        img.Height = 200;

        img.Source = bmpRend;
        v2d.Visual = img;
        vp.Children.Add(v2d);

        int wd = 500; 
        int ht = 500; 

        vp.Width = wd;
        vp.Height = ht;
        vp.Measure(new Size(wd, ht));
        vp.Arrange(new Rect(0, 0, wd, ht));

    }

and save it

 private void Save_click(object sender, RoutedEventArgs e)
    {
        RenderTargetBitmap rtb = new RenderTargetBitmap((int)vp.ActualWidth, (int)vp.ActualHeight, 96, 96, PixelFormats.Pbgra32);
        rtb.Render(vp);
        PngBitmapEncoder png = new PngBitmapEncoder();

        png.Frames.Add(BitmapFrame.Create(rtb));

        string tempFilename = @"g:\mycanvas1.png";

        using (Stream stm = File.Create(tempFilename))
        {
            png.Save(stm);
        }
    }

also follow this link for more help.

Workaround : add this in render_click

   DispatcherTimer tmr = new DispatcherTimer();
        tmr.Interval = TimeSpan.FromMilliseconds(10);
        tmr.Tick += (snd,ee) =>
            {
        Save_click(null,null);
        tmr.Stop();
            };
        tmr.Start();
Sign up to request clarification or add additional context in comments.

4 Comments

Hi, thank you for answer. I can't understand what is wrong in my method. I used this tutorial ericsink.com/wpf3d/3_Bitmap.html. In your link the issue was quality in the result image, my problem is that i can't get any image other than empty image.
you are not adding it to UI and its not rendered at all. so Add vp in UI and you can collapse it after saving it
Only last question about your code: if i put your Save_click function content inside Render_click function after already existing code (i don't want a save button because image must be produced automatically) then image is not correctly generated.Why?
when render_click function completes, then only the UI is actually rendered. so I can give you a workaround for this, check Update. remember Its a work around, not recommended.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.