Как применить искажение бочки (коррекция объектива) с SharpDX?
Я сделал небольшое приложение для захвата скриншотов из любой оконной игры и отправки его на 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 ответ
Теперь я сам никогда не использовал 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);
Вот ссылка на шейдертой, если вы хотите поиграть с ней и выяснить, как она работает
Я понятия не имею, как HLSL справляется с фильтрацией текстур (какой пиксель вы получите при использовании значений с плавающей запятой для координат), но я бы вложил свои деньги в билинейную фильтрацию, которая вполне может дать неприятную пикселизацию вашему выводу. Вам нужно будет взглянуть на лучшие методы фильтрации, как только вы получите искажение. Не должно быть ничего сложного, ознакомьтесь с синтаксисом HLSL, найдите, как загрузить свой скриншот в текстуру в DirectX и приступайте к работе.
Редактировать: я сказал, что искажение ствола, но код на самом деле для эффекта "рыбий глаз". Конечно, оба в значительной степени идентичны, искажение ствола происходит только по одной оси. Я считаю, что вам нужен эффект "рыбий глаз", это то, что обычно используется для HMD, если я не ошибаюсь.