Nvidia 3D Video с использованием DirectX11 и SlimDX на C #

Добрый день, я пытаюсь отобразить стерео-видео в реальном времени с помощью nvidia 3DVision и двух IP-камер. Я совершенно новичок в DirectX, но пытался проработать несколько руководств и ответить на другие вопросы на этом и других сайтах. На данный момент я показываю два статических растровых изображения для левого и правого глаза. Они будут заменены растровыми изображениями с моих камер, как только эта часть моей программы заработает. Этот вопрос NV_STEREO_IMAGE_SIGNATURE и 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), в задний буфер, но ни один из методов, которые я пробовал, не отображал оба изображения ... Любая помощь или комментарии будут очень признательны, Райан


person Ryan Lucke    schedule 30.06.2012    source источник
comment
Я попытался вернуться к Direct3D 9 (чтобы использовать stretchrect), но теперь у меня возникают проблемы с запуском программы в полноэкранном режиме. Как только я устанавливаю presentparams.Windowed = false, программа вылетает, когда я создаю свою цепочку подкачки. Я получаю следующую ошибку: D3DERR_INVALIDCALL (-2005530516). Если это вообще помогает, я использую ноутбук Dell XPS17 со встроенным 3D-передатчиком ...   -  person Ryan Lucke    schedule 05.07.2012
comment
Хорошо, поэтому мне удалось заставить его работать с помощью SlimDX и Direct3D 9. Я создаю устройство только с помощью моих параметров presentparams и не создаю цепочку подкачки (что приводило к сбою моей программы при запуске в полноэкранном режиме). Я думал, что при создании устройства требуется свопчейн, но, похоже, нет. А пока я буду придерживаться Direct3D 9 и заставлю работать остальную часть моей программы (подключу две камеры и все синхронизирую и т. Д.). Было бы неплохо заставить его работать в Direct3D11, но этого придется подождать.   -  person Ryan Lucke    schedule 06.07.2012
comment
В основном цикле у вас ResourceRegion Bottom = 1080 и Right = 1920, разве не должно быть right = 1920 * 2?   -  person Pedro.The.Kid    schedule 04.02.2014


Ответы (4)


Если можно использовать DirectX11.1, существует гораздо более простой способ включить стереоскопические функции, не полагаясь на волшебство байтов nVidia. По сути, вы создаете SwapChan1 вместо обычного SwapChain, тогда это так же просто, как установить Stereo на True.

Взгляните на это сообщение, которое я сделал , он показывает вам, как создать Stereo swapChain. Код представляет собой перенос на C # собственного стереосэмпла MS. Тогда у вас будет две цели рендеринга, и это будет намного проще. Перед рендерингом необходимо:

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. Надеюсь это поможет.

person TheWanderer    schedule 15.07.2013

Попробуйте создать буфер с шириной = 1920, а не 3840. Растяните каждое изображение до половины ширины и поместите их рядом.

person Horonchik    schedule 19.07.2012
comment
Спасибо за совет. Как я упоминал в комментариях выше, у меня он работает с D3D 9. Мой задний буфер имеет ширину = 1920, но у меня проблема в том, что в D3D 10 и 11 нет функции, эквивалентной StretchRectangle (). Как это сделать. вы растягиваете изображение шириной 3840 пикселей до заднего буфера шириной 1920 пикселей без StretchRectangle ()? Я пробовал использовать CopySubResourceRegion (), но можно указать только исходный размер, и я не могу заставить его работать ... - person Ryan Lucke; 19.07.2012

Я помню, как пару дней назад видел точно такой же вопрос во время поиска на форумах разработчиков Nvidia. К сожалению, форумы не работают из-за недавней хакерской атаки. Я помню, что OP в этом потоке смог заставить его работать с DX11 и Slimdx с помощью сигнатурного хака. Вы не используете метод stretchRectangle, это было что-то вроде createResuroseRegion () или, но не совсем то, что я не могу вспомнить. Это могут быть методы CopyResource () или CopySubresourceRegion (), обнаруженные в этом аналогичном потоке при переполнении стека. Копировать текстуру в текстуру

person Alexander Van Atta    schedule 21.07.2012

Также вы визуализируете изображение непрерывно или хотя бы несколько раз? Я проделал то же самое в DX9, и мне пришлось сказать DX отрендерить 3 кадра, прежде чем водитель распознал это как трехмерное зрение. Ваши очки надели? Ваш задний буфер = (ширина * 2), (Высота + 1), и пишете ли вы его так:

_________________________
|           |            |      
|  img1     |     img2   |
|           |            |
--------------------------
|_______signature________| where this last row = 1 pix tall
person Alexander Van Atta    schedule 21.07.2012
comment
Привет. Да, я работаю постоянно. Как я уже упоминал в своих комментариях выше, он работает с DX9. У меня теперь также работают две камеры, и все работает нормально. Промежуточный буфер, с которым я работаю, выглядит так, как вы описали выше, но задний буфер, в который я пишу это с помощью StretchRectangle (), имеет размер только 1920x1080, а не удваивает ширину и высоту +1. Я думаю, что драйвер nvidia обнаруживает его немедленно, но требует некоторое время, чтобы ИК-передатчик включился и очки начали работать. На данном этапе я не буду беспокоиться о том, чтобы заставить его работать в DX11. - person Ryan Lucke; 24.07.2012