Проблемы алгоритма пиксельного масштабирования с шейдером GPU - PullRequest
3 голосов
/ 16 февраля 2011

Так что я работаю над некоторыми пиксельными шейдерами для старых добрых эмуляторов, таких как Super Nintendo. У вас есть классические алгоритмы, такие как HQnx, 2xSaI и т. Д., И они определенно написаны для работы на процессорах и масштабируются ровно в два раза, прежде чем перейти к экрану.

Переходя к фрагментным шейдерам графического процессора, эти алгоритмы можно сделать по сути бесплатно. Я работаю с OpenGL и Cg / GLSL, но этот вопрос должен относиться и к кодировщикам Direct3D / HLSL.

Основная проблема заключается в том, что эти алгоритмы смешиваются с соседними пикселями, используя какой-то алгоритм для выбора цвета. Тем не менее, я нашел эту концепцию довольно сложной для шейдерных языков. Обычно с фрагментными шейдерами вы можете получить координату текстуры с плавающей точкой, которую вы можете использовать для поиска текстур, обычно с GL_LINEAR, используемым в качестве фильтра текстуры. Большинство пиксельных шейдеров используют GL_NEAREST и выполняют сглаживание самостоятельно.

Проблема возникает, если я хочу найти, скажем, точный соседний пиксель. Я видел некоторые реализации, но они иногда вызывают артефакты на экране. Вероятно, из-за возникающих неточностей с плавающей запятой. Я обнаружил, что большинство артефактов просто исчезают при использовании текстур размером с степень двойки, что еще больше укрепляет мою уверенность в наличии неточностей с плавающей запятой. Вот пример фрагмента шейдера в Cg, который показывает проблемы:

struct output
{
   float4 color : COLOR;
};

struct input
{
  float2 video_size;
  float2 texture_size;
  float2 output_size;
};

struct deltas
{
   float2 UL, UR, DL, DR;
};


output main_fragment (float2 tex : TEXCOORD0, uniform input IN, uniform sampler2D s_p : TEXUNIT0)
{
   float2 texsize = IN.texture_size;
   float dx = pow(texsize.x, -1.0) * 0.25;
   float dy = pow(texsize.y, -1.0) * 0.25;
   float3 dt = float3(1.0, 1.0, 1.0);

   deltas VAR = { 
      tex + float2(-dx, -dy),
      tex + float2(dx, -dy),
      tex + float2(-dx, dy),
      tex + float2(dx, dy)
   };

   float3 c00 = tex2D(s_p, VAR.UL).xyz;
   float3 c20 = tex2D(s_p, VAR.UR).xyz;
   float3 c02 = tex2D(s_p, VAR.DL).xyz;
   float3 c22 = tex2D(s_p, VAR.DR).xyz;

   float m1=dot(abs(c00-c22),dt)+0.001;
   float m2=dot(abs(c02-c20),dt)+0.001;

   output OUT;
   OUT.color = float4((m1*(c02+c20)+m2*(c22+c00))/(2.0*(m1+m2)),1.0);
   return OUT;
}

Есть ли какой-нибудь способ убедиться, что мы можем получить данные о цвете с ожидаемого пикселя, а не с другого? Я считаю, что эта проблема возникает, поскольку мы можем запросить пиксель по координате, которая находится между двумя пикселями (если это имеет смысл). Надеюсь, в этих шейдерных языках есть какая-то встроенная функция, которую я пропускаю.

1 Ответ

4 голосов
/ 16 февраля 2011

Конечно, в OpenGL есть несколько способов:

  • Внутри шейдера используйте функцию texelFetch, чтобы сделать выборку текстуры, которая использует целочисленные ненормализованные координаты.Доступный в расширении GL_EXT_gpu_shader4 или OpenGL 3.0.
  • Использовать текстурные прямоугольники (GL_ARB_texture_rectangle, OpenGL 3.2), эта текстурная цель использует ненормализованные координаты, но не допускает мип-карты и ограничивает некоторые режимы переноса.

Ненормализованные координаты - это то, что вы хотите, они находятся в диапазоне [0, 0] x [w, h] /

...