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().

daniele3004
  • 13,072
  • 12
  • 67
  • 75
diolo
  • 13
  • 4
  • did you tried to replicate same in xaml? did you see as expected or you see blank? – pushpraj Jun 24 '14 at 07:03
  • Yes i tried to replicate in Xaml and i see result as expected; only with code behind i get empty image – diolo Jun 24 '14 at 07:26
  • is it possible for you to post the xaml or a full working sample which demonstrate the issue? – pushpraj Jun 24 '14 at 07: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. – diolo Jun 24 '14 at 07:55

1 Answers1

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();
Community
  • 1
  • 1
Shivam cv
  • 526
  • 5
  • 16
  • Hi, thank you for answer. I can't understand what is wrong in my method. I used this tutorial http://www.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. – diolo Jun 24 '14 at 08:47
  • 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 – Shivam cv Jun 24 '14 at 08:48
  • 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? – diolo Jun 24 '14 at 09:37
  • 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. – Shivam cv Jun 24 '14 at 10:16