【问题标题】:How to apply barrel distortion (lens correction) with SharpDX?如何使用 SharpDX 应用桶形失真(镜头校正)?
【发布时间】:2015-05-27 07:21:54
【问题描述】:

我制作了一个小应用程序,用于从任何窗口游戏中截取屏幕截图并将其发送到 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) 吗?

感谢您的帮助!

【问题讨论】:

  • 基本上,您将着色器应用于覆盖整个屏幕的矩形......在该着色器中,对于每个纹素,您应用偏移量(我现在无法帮助您进行计算,但应该不难)到纹理的 UV,具体取决于 XY 位置。

标签: c# shader sharpdx distortion


【解决方案1】:

现在我自己从未使用过 DirectX,但我想您需要使用 HLSL 而不是 GLSL(不过应该非常相似)。这个想法是您必须将“屏幕截图”加载到纹理缓冲区中,作为片段着色器(像素着色器)的输入。片段着色器看似容易理解,它只是一段代码(用 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 常用的效果。

【讨论】:

  • 您好,感谢您的帮助。我的主要问题是我不知道如何将着色器应用于加载的位图(请参阅我的代码)。我没有许多游戏那样的绘图循环。我只有一个捕获循环,抓取图像并一次又一次地发送。抓取后,我必须将着色器应用于图像,但如何?谢谢大家
  • 如果您使用的是 Visual Studio,this 似乎详细解释了如何在您的项目中编译和使用着色器。如果您不使用 Visual Studio,则必须了解如何自己编译它(可能使用 this),并且该文章仍然可以帮助您应用着色器。
  • 您好,很抱歉回复晚了,但是很长时间以来病得很重。我找到了一个包含所需计算的 shader.fx,我想将它应用到背景中的图像(上图),因此程序不应该提供视觉输出,因为图像将通过网络发送。我真的很感谢你的帮助,但你的回复让我更加困惑。
  • 哈哈,对不起,如果我对你没有意义。所以最后你没有设法解决你的问题?我上面发布的关于将着色器直接应用于位图的链接对您不起作用?
猜你喜欢
  • 2012-09-19
  • 1970-01-01
  • 2014-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-12
  • 1970-01-01
相关资源
最近更新 更多