在 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 视频的主要内容,如果未能解决你的问题,请参考以下文章
使用 C#、DirectX 和 XNA 播放音频和最终用户的要求