如何使用SharpDX应用桶形失真(镜头校正)?

时间:2015-05-27 07:21:54

标签: c# shader sharpdx distortion

我已经制作了一个小应用程序来截取来自任何窗口游戏的截图并将其发送到iPhone以创建虚拟现实应用程序,如oculus rift(有关详细信息,请参阅https://github.com/gagagu/VR-Streamer-Windows-Server)。 使用SharpDX捕获图像,一切正常。 现在我想实现像镜头校正(桶形失真)这样的,我正在寻找实现它的最快方法。我正在寻找许多关于桶形失真信息的网站,我认为最快的方法是使用着色器,但我对sharpdx很新(并且不了解着色器)我不知道如何实现我的代码的着色器。大多数教程将着色器应用于对象(如多维数据集),但不应用于捕获的图像,因此我不知道如何操作。

       [STAThread]
    public System.Drawing.Bitmap Capture()
    {
        isInCapture = true;

        try
        {

            // init
            bool captureDone = false;
            bitmap = new System.Drawing.Bitmap(captureRect.Width, captureRect.Height, PixelFormat.Format32bppArgb);

            // the capture needs some time
            for (int i = 0; !captureDone; i++)
            {
                try
                {
                    //capture
                    duplicatedOutput.AcquireNextFrame(-1, out duplicateFrameInformation, out screenResource);
                    // only for wait
                    if (i > 0)
                    {
                        using (var screenTexture2D = screenResource.QueryInterface<Texture2D>())
                            device.ImmediateContext.CopyResource(screenTexture2D, screenTexture);

                        mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, MapFlags.None);
                        mapDest = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, captureRect.Width, captureRect.Height),
                                                              ImageLockMode.WriteOnly, bitmap.PixelFormat);

                        sourcePtr = mapSource.DataPointer;
                        destPtr = mapDest.Scan0;

                        // set x position offset to rect.x
                        int rowPitch = mapSource.RowPitch - offsetX;
                        // set pointer to y position
                        sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch * captureRect.Y);

                        for (int y = 0; y < captureRect.Height; y++) // needs to speed up!!
                        {
                            // set pointer to x position
                            sourcePtr = IntPtr.Add(sourcePtr, offsetX);

                            // copy pixel to bmp
                            Utilities.CopyMemory(destPtr, sourcePtr, pWidth);

                            // incement pointert to next line
                            sourcePtr = IntPtr.Add(sourcePtr, rowPitch);
                            destPtr = IntPtr.Add(destPtr, mapDest.Stride);
                        }

                        bitmap.UnlockBits(mapDest);
                        device.ImmediateContext.UnmapSubresource(screenTexture, 0);

                        captureDone = true;
                    }

                    screenResource.Dispose();
                    duplicatedOutput.ReleaseFrame();

                }
                catch//(Exception ex)  //                    catch (SharpDXException e)
                {
                    //if (e.ResultCode.Code != SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
                    //{
                    //   // throw e;
                    //}

                    return new Bitmap(captureRect.Width, captureRect.Height, PixelFormat.Format32bppArgb);
                }
            }

        }
        catch 
        {
            return new Bitmap(captureRect.Width, captureRect.Height, PixelFormat.Format32bppArgb);
        }

        isInCapture = false;
        return bitmap;
    }

从愿意帮助的人那里得到一点启动助手真的很棒。 我在inet上找到了一些着色器,但它是为opengl(https://github.com/dghost/glslRiftDistort/tree/master/libovr-0.4.x/glsl110)编写的。我可以使用也用于directx(sharpdx)吗?

感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

现在我自己从未使用过DirectX,但我认为您需要使用HLSL而不是GLSL(尽管它应该非常相似)。这个想法是你必须加载你的&#34;截图&#34;到纹理缓冲区,作为片段着色器(像素着色器)的输入。碎片着色器看似容易理解,它只是一段代码(用GLSL或HLSL编写)看起来非常像C的一个子集,其中添加了一些数学函数(主要是向量和矩阵操作)对于要渲染的每个像素。

代码应该相当简单,您将获取当前像素位置,将桶形失真变换应用于其坐标,然后在屏幕截图纹理中查找该坐标。转型看起来应该是这样的:

vec2 uv;

/// Barrel Distortion ///

float d=length(uv);
float z = sqrt(1.0 - d * d);
float r = atan(d, z) / 3.14159;
float phi = atan(uv.y, uv.x);

uv = vec2(r*cos(phi)+.5,r*sin(phi)+.5);

Here's a shadertoy link if you wanna play with it and figure out how it works

我不知道HLSL如何处理纹理过滤(当你使用坐标的浮点值时你会得到哪个像素),但是我把钱花在了双线性过滤上,这可能会给人一种不愉快的感觉像素输出你的输出。一旦失真工作,你就必须看better filtering methods。不应该是太复杂,熟悉HLSL语法,找到如何将截图加载到DirectX中的纹理并开始滚动。

编辑:我说桶形失真,但代码实际上是鱼眼效果。当然两者都非常相同,桶形失真仅在一个轴上。我相信你需要的是鱼眼效果,但如果我没有弄错的话,它是通常用于HMD的。