Good day, I am trying to display a real-time stereo video using nvidia 3DVision and two IP cameras. I am totally new to DirectX, but have tried to work through some tutorials and other questions on this and other sites. For now, I am displaying two static bitmaps for left and right eyes. These will be replaced by bitmaps from my cameras once I have got this part of my program working. This question NV_STEREO_IMAGE_SIGNATURE and DirectX 10/11 (nVidia 3D Vision) has helped me quite a bit, but I am still struggling to get my program working as it should. What I am finding is that my shutter glasses start working as they should, but only the image for the right eye gets displayed, while the left eye remains blank (except for the mouse cursor).
Here is my code for generating the stereo images:
using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using System.Drawing; using System.Drawing.Imaging; using System.IO; using SlimDX; using SlimDX.Direct3D11; using SlimDX.Windows; using SlimDX.DXGI; using Device = SlimDX.Direct3D11.Device; // Make sure we use DX11 using Resource = SlimDX.Direct3D11.Resource; namespace SlimDxTest2 { static class Program { private static Device device; // DirectX11 Device private static int Count; // Just to make sure things are being updated // The NVSTEREO header. static byte[] stereo_data = new byte[] {0x4e, 0x56, 0x33, 0x44, //NVSTEREO_IMAGE_SIGNATURE = 0x4433564e; 0x00, 0x0F, 0x00, 0x00, //Screen width * 2 = 1920*2 = 3840 = 0x00000F00; 0x38, 0x04, 0x00, 0x00, //Screen height = 1080 = 0x00000438; 0x20, 0x00, 0x00, 0x00, //dwBPP = 32 = 0x00000020; 0x02, 0x00, 0x00, 0x00}; //dwFlags = SIH_SCALE_TO_FIT = 0x00000002 [STAThread] static void Main() { Bitmap left_im = new Bitmap("Blue.png"); // Read in Bitmaps Bitmap right_im = new Bitmap("Red.png"); // Device creation var form = new RenderForm("Stereo test") { ClientSize = new Size(1920, 1080) }; var desc = new SwapChainDescription() { BufferCount = 1, ModeDescription = new ModeDescription(1920, 1080, new Rational(120, 1), Format.R8G8B8A8_UNorm), IsWindowed = false, //true, OutputHandle = form.Handle, SampleDescription = new SampleDescription(1, 0), SwapEffect = SwapEffect.Discard, Usage = Usage.RenderTargetOutput }; SwapChain swapChain; Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.Debug, desc, out device, out swapChain); RenderTargetView renderTarget; // create a view of our render target, which is the backbuffer of the swap chain we just created using (var resource = Resource.FromSwapChain<Texture2D>(swapChain, 0)) renderTarget = new RenderTargetView(device, resource); var context = device.ImmediateContext; // set up a viewport var viewport = new Viewport(0.0f, 0.0f, form.ClientSize.Width, form.ClientSize.Height); context.OutputMerger.SetTargets(renderTarget); context.Rasterizer.SetViewports(viewport); // prevent DXGI handling of alt+enter, which doesn't work properly with Winforms using (var factory = swapChain.GetParent<Factory>()) factory.SetWindowAssociation(form.Handle, WindowAssociationFlags.IgnoreAll); form.KeyDown += (o, e) => // handle alt+enter ourselves { if (e.Alt && e.KeyCode == Keys.Enter) swapChain.IsFullScreen = !swapChain.IsFullScreen; }; form.KeyDown += (o, e) => // Alt + X -> Exit Program { if (e.Alt && e.KeyCode == Keys.X) { form.Close(); } }; context.ClearRenderTargetView(renderTarget, Color.Green); // Fill Screen with specified colour Texture2DDescription stereoDesc = new Texture2DDescription() { ArraySize = 1, Width = 3840, Height = 1081, BindFlags = BindFlags.None, CpuAccessFlags = CpuAccessFlags.Write, Format = SlimDX.DXGI.Format.R8G8B8A8_UNorm, OptionFlags = ResourceOptionFlags.None, Usage = ResourceUsage.Staging, MipLevels = 1, SampleDescription = new SampleDescription(1, 0) }; // Main Loop MessagePump.Run(form, () => { Texture2D texture_stereo = Make3D(left_im, right_im); // Create Texture from two bitmaps in memory ResourceRegion stereoSrcBox = new ResourceRegion { Front = 0, Back = 1, Top = 0, Bottom = 1080, Left = 0, Right = 1920 }; context.CopySubresourceRegion(texture_stereo, 0, stereoSrcBox, renderTarget.Resource, 0, 0, 0, 0); texture_stereo.Dispose(); swapChain.Present(0, PresentFlags.None); }); // Dispose resources swapChain.IsFullScreen = false; // Required before swapchain dispose device.Dispose(); swapChain.Dispose(); renderTarget.Dispose(); } static Texture2D Make3D(Bitmap leftBmp, Bitmap rightBmp) { var context = device.ImmediateContext; Bitmap left2 = leftBmp.Clone(new RectangleF(0, 0, leftBmp.Width, leftBmp.Height), PixelFormat.Format32bppArgb); // Change bmp to 32bit ARGB Bitmap right2 = rightBmp.Clone(new RectangleF(0, 0, rightBmp.Width, rightBmp.Height), PixelFormat.Format32bppArgb); // Show FrameCount on screen: (To test) Graphics left_graph = Graphics.FromImage(left2); left_graph.DrawString("Frame: " + Count.ToString(), new System.Drawing.Font("Arial", 16), Brushes.Black, new PointF(100, 100)); left_graph.Dispose(); Graphics right_graph = Graphics.FromImage(right2); right_graph.DrawString("Frame: " + Count.ToString(), new System.Drawing.Font("Arial", 16), Brushes.Black, new PointF(200, 200)); right_graph.Dispose(); Count++; Texture2DDescription desc2d = new Texture2DDescription() { ArraySize = 1, Width = 1920, Height = 1080, BindFlags = BindFlags.None, CpuAccessFlags = CpuAccessFlags.Write, Format = SlimDX.DXGI.Format.R8G8B8A8_UNorm, OptionFlags = ResourceOptionFlags.None, Usage = ResourceUsage.Staging, MipLevels = 1, SampleDescription = new SampleDescription(1, 0) }; Texture2D leftText2 = new Texture2D(device, desc2d); // Texture2D for each bmp Texture2D rightText2 = new Texture2D(device, desc2d); Rectangle rect = new Rectangle(0, 0, left2.Width, left2.Height); BitmapData leftData = left2.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); IntPtr left_ptr = leftData.Scan0; int left_num_bytes = Math.Abs(leftData.Stride) * leftData.Height; byte[] left_bytes = new byte[left_num_bytes]; byte[] left_bytes2 = new byte[left_num_bytes]; System.Runtime.InteropServices.Marshal.Copy(left_ptr, left_bytes, 0, left_num_bytes); // Get Byte array from bitmap left2.UnlockBits(leftData); DataBox box1 = context.MapSubresource(leftText2, 0, MapMode.Write, SlimDX.Direct3D11.MapFlags.None); box1.Data.Write(left_bytes, 0, left_bytes.Length); context.UnmapSubresource(leftText2, 0); BitmapData rightData = right2.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); IntPtr right_ptr = rightData.Scan0; int right_num_bytes = Math.Abs(rightData.Stride) * rightData.Height; byte[] right_bytes = new byte[right_num_bytes]; System.Runtime.InteropServices.Marshal.Copy(right_ptr, right_bytes, 0, right_num_bytes); // Get Byte array from bitmap right2.UnlockBits(rightData); DataBox box2 = context.MapSubresource(rightText2, 0, MapMode.Write, SlimDX.Direct3D11.MapFlags.None); box2.Data.Write(right_bytes, 0, right_bytes.Length); context.UnmapSubresource(rightText2, 0); Texture2DDescription stereoDesc = new Texture2DDescription() { ArraySize = 1, Width = 3840, Height = 1081, BindFlags = BindFlags.None, CpuAccessFlags = CpuAccessFlags.Write, Format = SlimDX.DXGI.Format.R8G8B8A8_UNorm, OptionFlags = ResourceOptionFlags.None, Usage = ResourceUsage.Staging, MipLevels = 1, SampleDescription = new SampleDescription(1, 0) }; Texture2D stereoTexture = new Texture2D(device, stereoDesc); // Texture2D to contain stereo images and Nvidia 3DVision Signature // Identify the source texture region to copy (all of it) ResourceRegion stereoSrcBox = new ResourceRegion { Front = 0, Back = 1, Top = 0, Bottom = 1080, Left = 0, Right = 1920 }; // Copy it to the stereo texture context.CopySubresourceRegion(leftText2, 0, stereoSrcBox, stereoTexture, 0, 0, 0, 0); context.CopySubresourceRegion(rightText2, 0, stereoSrcBox, stereoTexture, 0, 1920, 0, 0); // Offset by 1920 pixels // Open the staging texture for reading and go to last row DataBox box = context.MapSubresource(stereoTexture, 0, MapMode.Write, SlimDX.Direct3D11.MapFlags.None); box.Data.Seek(stereoTexture.Description.Width * (stereoTexture.Description.Height - 1) * 4, System.IO.SeekOrigin.Begin); box.Data.Write(stereo_data, 0, stereo_data.Length); // Write the NVSTEREO header context.UnmapSubresource(stereoTexture, 0); left2.Dispose(); leftText2.Dispose(); right2.Dispose(); rightText2.Dispose(); return stereoTexture; } }
}
I have tried various methods of copying the Texture2D of the stereo image including signature (3840x1081) to the backbuffer, but none of the methods I have tried display both images... Any help or comments will be much appreciated, Ryan