在 C# 中使用 DirectX11 和 SlimDX 的 Nvidia 3d 视频

Posted

技术标签:

【中文标题】在 C# 中使用 DirectX11 和 SlimDX 的 Nvidia 3d 视频【英文标题】:Nvidia 3d Video using DirectX11 and SlimDX in C# 【发布时间】:2012-06-30 19:36:05 【问题描述】:

美好的一天, 我正在尝试使用 nvidia 3DVision 和两个 IP 摄像机显示实时立体视频。我对 DirectX 完全陌生,但尝试在此站点和其他站点上完成一些教程和其他问题。现在,我正在为左眼和右眼显示两个静态位图。一旦我的程序的这部分工作正常,这些将被我相机中的位图所取代。 这个问题NV_STEREO_IMAGE_SIGNATURE and DirectX 10/11 (nVidia 3D Vision) 帮助了我很多,但我仍在努力让我的程序正常工作。我发现我的快门眼镜开始正常工作,但只显示右眼的图像,而左眼保持空白(鼠标光标除外)。

这是我生成立体图像的代码:

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;
     


我尝试了多种方法将立体图像的 Texture2D 包括签名 (3840x1081) 复制到后缓冲区,但我尝试过的方法都没有显示这两个图像... 任何帮助或 cmets 将不胜感激, 瑞恩

【问题讨论】:

我已尝试返回 Direct3D 9(以便我可以使用stretchrect),但现在我无法让程序以全屏模式运行。一旦我设置了presentparams.Windowed = false,当我创建我的交换链时程序就会崩溃。我收到以下错误:D3DERR_INVALIDCALL (-2005530516)。如果有帮助的话,我正在使用带有内置 3D 发射器的戴尔 XPS17 笔记本电脑...... 好的,所以我已经设法使用 SlimDX 和 Direct3D 9 让它工作。我只使用我的 presentparams 创建一个设备,而不创建交换链(这导致我的程序在尝试以全屏模式启动)。我认为创建设备时需要交换链,但似乎不需要。现在,我将坚持使用 Direct3D 9 并让我的程序的其余部分正常工作(连接两个摄像头并同步所有内容等)。让它在 Direct3D11 中工作仍然很好,但这需要等待。 在主循环中你有 ResourceRegion Bottom = 1080 和 Right = 1920 不应该有 right = 1920*2? 【参考方案1】:

如果可以选择使用 DirectX11.1,则有一种更简单的方法来启用立体功能,而无需依赖 nVidia 的字节向导。基本上,您创建一个SwapChan1 而不是常规的SwapChain,然后就像将Stereo 设置为True 一样简单。

看看我做的这个post,它向你展示了如何创建立体声交换链。该代码是对 MS 自己的立体声示例的 C# 的移植。然后你将有两个渲染目标,它更简单。在渲染之前,您必须:

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]

其中 ITarget 是类的接口,提供对后台缓冲区、渲染目标等的访问。 就是这样,DirectX 将处理一切。希望这会有所帮助。

【讨论】:

【参考方案2】:

尝试创建宽度 = 1920 而不是 3840 的后备缓冲区。 将每张图片拉伸到宽度的一半并将它们并排放置。

【讨论】:

感谢您的建议。正如我在上面的 cmets 中提到的,我已经让它与 D3D 9 一起使用。我的后缓冲区设置为宽度 = 1920,但我遇到的问题是在 D3D 10 和 11 中没有与 StretchRectangle() 等效的函数。如何做您在没有 StretchRectangle() 的情况下将 3840 像素宽的图像拉伸到 1920 像素宽的后备缓冲区?我尝试使用 CopySubResourceRegion(),但只能指定源大小,但无法正常工作...【参考方案3】:

我记得几天前在 Nvidia 开发者论坛上搜索时看到了同样的问题。不幸的是,由于最近的黑客攻击,论坛已关闭。我记得那个线程上的 OP 能够使用签名 hack 让它与 DX11 和 Slimdx 一起工作。您不使用stretchRectangle 方法,它类似于createResuroseRegion(),但不是我不记得了。可能是这些方法 CopyResource() 或 CopySubresourceRegion() 在堆栈溢出的类似线程中找到。 Copy Texture to Texture

【讨论】:

【参考方案4】:

您是连续渲染图像还是至少渲染几次?我在 DX9 中做同样的事情,必须告诉 DX 在驱动程序识别为 3D 视觉之前渲染 3 帧。你的眼镜开了吗?您的后备缓冲区是否 = (width*2), (Height+1) 并且您是否像这样编写后备缓冲区:

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

【讨论】:

嗨。是的,我正在连续渲染。正如我在上面的 cmets 中提到的,我让它与 DX9 一起工作。我现在也运行了两台摄像机,它运行良好。我使用的中间缓冲区看起来像你上面描述的,但是我使用 StretchRectangle() 写这个的后缓冲区只有 1920x1080,而不是宽度和高度 + 1 的两倍。我认为 nvidia 驱动程序会立即检测到它,但它需要一段时间后,红外发射器打开,眼镜开始工作。在这个阶段我不会再费心去尝试让它在 DX11 中工作。

以上是关于在 C# 中使用 DirectX11 和 SlimDX 的 Nvidia 3d 视频的主要内容,如果未能解决你的问题,请参考以下文章

挂钩/覆盖 DirectX 游戏?

使用 C#、DirectX 和 XNA 播放音频和最终用户的要求

带有 Directx 11 的 WPF

在 C# 中使用 DirectX 进行无缝(循环)音频播放

如何从使用directx c# visual studio播放的视频中获取和处理每一帧(位图图像​​)

C# Directx App 在 Release 中崩溃