2012-06-30 9 views
13

Buen día, Estoy tratando de mostrar un video estéreo en tiempo real usando nvidia 3DVision y dos cámaras IP. Soy totalmente nuevo en DirectX, pero he intentado trabajar con algunos tutoriales y otras preguntas en este y otros sitios. Por ahora, estoy mostrando dos bitmaps estáticos para los ojos izquierdo y derecho. Estos serán reemplazados por mapas de bits de mis cámaras una vez que tenga esta parte de mi programa funcionando. Esta pregunta NV_STEREO_IMAGE_SIGNATURE and DirectX 10/11 (nVidia 3D Vision) me ha ayudado bastante, pero todavía estoy luchando para que mi programa funcione como debería. Lo que estoy descubriendo es que mis gafas de obturador comienzan a funcionar como deberían, pero solo se muestra la imagen del ojo derecho, mientras que el ojo izquierdo permanece en blanco (excepto el cursor del mouse).Nvidia 3d Video usando DirectX11 y SlimDX en C#

Aquí está mi código para generar las imágenes en estéreo:

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; 
    } 

} 

}

he intentado varios métodos para copiar el Texture2D de la imagen estéreo, incluyendo la firma (3840x1081) a la backbuffer, pero ninguno de los métodos que he intentado mostrar tanto imágenes ... Cualquier ayuda o comentarios serán muy apreciados, Ryan

+0

He intentado volver a Direct3D 9 (para poder usar stretchrect), pero ahora estoy teniendo problemas para ejecutar el programa en modo de pantalla completa. Tan pronto como establezco presentparams.Windowed = false, el programa falla cuando creo mi swapchain. Me sale el siguiente error: D3DERR_INVALIDCALL (-2005530516). Si ayuda en absoluto, estoy usando una laptop Dell XPS17 con un transmisor 3D incorporado ... –

+0

Ok, así que he logrado hacerlo funcionar usando SlimDX y Direct3D 9. Solo creo un dispositivo usando mis parámetros presentes, y no creo una cadena de intercambio (que causaba que mi programa fallara al intentar iniciar en modo de pantalla completa). Pensé que se necesitaba una cadena de intercambio al crear un dispositivo, pero parece que no. Por ahora, me atengo a Direct3D 9 y hago funcionar el resto de mi programa (conectando las dos cámaras y sincronizando todo, etc.). Todavía será bueno hacerlo funcionar en Direct3D11, pero eso tendrá que esperar. –

+0

En el bucle principal, tiene ResourceRegion Bottom = 1080 y Right = 1920 ¿no debería haber right = 1920 * 2? –

Respuesta

0

Trate de crear la backbufer con width = 1920 y 3840. no estire cada imagen a la mitad del ancho y colóquelas una al lado de la otra.

+0

Gracias por el consejo. Como mencioné en los comentarios anteriores, lo tengo trabajando con D3D 9. Mi backbuffer está configurado con width = 1920, pero el problema que tengo es que no hay una función equivalente a StretchRectangle() en D3D 10 y 11. ¿Cómo funciona? estira la imagen de 3840 píxeles de ancho a un backbuffer de 1920 píxeles de ancho sin StretchRectangle()? Intenté usar CopySubResourceRegion(), pero solo se puede especificar el tamaño de fuente y no lo hago funcionar ... –

0

Recuerdo haber visto esta misma pregunta al buscar hace unos días en los foros de desarrolladores de Nvidia. Lamentablemente, los foros están caídos debido a un reciente ataque de piratas informáticos. Recuerdo que el OP en ese hilo fue capaz de hacerlo funcionar con DX11 y Slimdx usando el truco de la firma. No usas el método stretchRectangle, es algo así como createResuroseRegion() o no es exactamente lo que no puedo recordar. Pueden ser estos métodos CopyResource() o CopySubresourceRegion() encontrados en este hilo similar en stack over flow. Copy Texture to Texture

0

También está representando la imagen continuamente o al menos algunas veces? Estaba haciendo lo mismo en DX9 y tuve que decirle a DX que renderizara 3 cuadros antes de que el conductor lo reconociera como visión 3D. ¿Se pusieron las gafas? Es el backbuffer = (ancho x 2), (Altura + 1) y está escribiendo la backbuffer así:

_________________________ 
|   |   |  
| img1  |  img2 | 
|   |   | 
-------------------------- 
|_______signature________| where this last row = 1 pix tall 
+0

Hola. Sí, estoy representando continuamente. Como mencioné en mis comentarios anteriores, lo conseguí trabajando con DX9. Ahora también tengo las dos cámaras funcionando y funciona muy bien. El buffer intermedio con el que trabajo parece como se describe arriba, pero el backbuffer al que escribo esto usando StretchRectangle() es solo 1920x1080, y no el doble del ancho y alto + 1. Creo que el controlador nvidia lo detecta de inmediato, pero se necesita un tiempo para que el transmisor IR se encienda y las gafas comiencen a funcionar. No voy a molestarme más en esta etapa para intentar hacer que funcione en DX11. –

1

Si se utiliza DirectX11.1 es una opción, hay una manera mucho más fácil de activar funciones estereoscópicas , sin tener que depender de la magia de byte de nVidia. Básicamente, crea un SwapChan1 en lugar de un SwapChain normal, luego es tan simple como establecer Stereo en True.

Eche un vistazo a este post que hice, le muestra cómo crear un Stereo swapChain. El código es una migración a C# de la propia muestra estéreo de MS. Entonces tendrás dos objetivos de renderizado y es mucho más simple. Antes de dictar lo que tienes que:

void RenderEye(bool rightEye, ITarget target) 
{ 
    RenderTargetView currentTarget = rightEye ? target.RenderTargetViewRight : target.RenderTargetView; 
    context.OutputMerger.SetTargets(target.DepthStencilView, currentTarget); 
    [clean color/depth] 
    [render scene] 
    [repeat for each eye] 
} 

donde iTarget es una interfaz para una clase que proporciona el acceso a la backbuffer, rendertargets, etc. Eso es todo, DirectX se hará cargo de todo. Espero que esto ayude.