NVidia 3D Video utilisant DirectX11 et SlimDX en C#
Bonne journée, J'essaie d'afficher une vidéo stéréo en temps réel en utilisant nvidia 3DVision et deux caméras IP. Je suis totalement nouveau à DirectX, mais j'ai essayé de travailler à travers quelques tutoriels et d'autres questions sur ce site et d'autres. Pour l'instant, je montre deux bitmaps statiques pour les yeux de gauche et de droite. Ceux-ci seront remplacés par des bitmaps de mes caméras une fois que j'aurai fait fonctionner cette partie de mon programme. Cette question <!-Nv_stereo_image_signature et DirectX 10/11 (NVidia 3D Vision) a cela m'a beaucoup aidé, mais je me bats toujours pour que mon programme fonctionne comme il se doit. Ce que je constate, c'est que mes volets obturateurs commencent à fonctionner comme ils le devraient, mais seule l'image pour l'oeil droit est affichée, tandis que l'oeil gauche reste vide (sauf pour le curseur de la souris).
Voici mon code pour générer les images stéréo:
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;
}
}
}
j'ai essayé diverses méthodes pour copier le Texture2D de l'image stéréo y compris la signature (3840x1081) au backbuffer, mais aucune des méthodes que j'ai essayées n'affiche les deux images... Toute aide ou commentaire sera très apprécié, Ryan
4 réponses
si vous utilisez DirectX11.1 est une option, il y a une façon beaucoup plus facile d'activer les caractéristiques stéréoscopiques, sans avoir à compter sur la magie byte de nVidia. En gros, vous créez un SwapChan1
au lieu de SwapChain
, alors c'est aussi simple que de créer Stereo
pour Vrai.
jetez un oeil à cette post je l'ai fait, il vous montre comment créer un son Stéréo à la swapChain. Le code est un portage vers C# de l'échantillon stéréo de MS. Alors vous aurez deux cibles de rendu et c'est beaucoup plus simple. Avant de rendu:
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]
}
où ITarget est une interface pour une classe donnant accès au backbuffer, aux rendertargets, etc. C'est ça, DirectX s'occupera de tout. Espérons que cette aide.
essayez de créer le backbufer avec width = 1920 et non 3840. étirez chaque image à la moitié de la taille en largeur et mettez-les côte à côte.
je me souviens avoir vu cette même question en cherchant il y a quelques jours sur les forums des développeurs Nvidia. Malheureusement, les forums sont en baisse en raison d'une récente attaque de pirates. Je me souviens que L'opération sur ce thread était capable de le faire fonctionner avec DX11 et Slimdx en utilisant le hack de signature. Vous n'utilisez pas la méthode stretchRectangle its était quelque chose comme createResuroseRegion() ou mais pas que exactement Je ne peux pas me rappeler. Il peut s'agir de ces méthodes: CopyResource () ou CopySubresourceRegion() trouvé dans ce fil similaire sur la pile de flux. copier la Texture vers la Texture
est-ce que vous restituez l'image en continu ou au moins quelques fois? Je faisais la même chose avec DX9 et j'ai dû dire à DX de rendre 3 images avant que le pilote ne le reconnaisse comme une vision 3D. Tes lunettes ont donné un coup de pied? Est votre backbuffer = (largeur*2), (Hauteur+1) et faites-vous écrit le backbuffer comme suit:
_________________________
| | |
| img1 | img2 |
| | |
--------------------------
|_______signature________| where this last row = 1 pix tall