2013-12-22 4 views
0

Я пытаюсь сделать передачу рендеринга HDR в своем 3D-приложении.XNA и HLSL: Пример всей текстуры для средней яркости

Я понимаю, что для того, чтобы получить средний свет на сцене, вам нужно уменьшить выходной результат рендеринга до текстуры 1x1. На данный момент я боюсь.

Я установил цель рендеринга разрешения 1x1, к которой я собираюсь сделать предыдущий вывод рендеринга. Я попытался вывести результат на эту цель рендеринга, используя простой вызов SpriteBatch Draw и использование целевого прямоугольника. Однако было слишком много надежд на то, что я обнаружил бы что-то, о чем никто не думал, результат этого не был на самом деле целым сценами, сбрасываемыми на 1x1 текстуру, кажется, что рисовался только верхний левый пиксель, независимо от того, насколько я играл с целевыми прямоугольниками или перегрузками масштаба.

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

Я пришел через этот учебник: http://www.xnainfo.com/content.php?content=28

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

Ничего из того, что я до сих пор не смог создать жизнеспособный выход. Текстура с пониженной выборкой отображается в угол моего экрана для целей отладки.

Я изменил код HLSL, чтобы выглядеть следующим образом:

pixelShaderStruct vertShader(vertexShaderStruct input) 
{ 
    pixelShaderStruct output; 

    output.position = float4(input.pos, 1); 
    output.texCoord = input.texCoord + 0.5f; 

    return output; 
}; 

float4 PixelShaderFunction(pixelShaderStruct input) : COLOR0 
{ 
    float4 color = 0; 
    float2 position = input.texCoord; 

    for(int x = 0; x < 4; x++) 
    { 
     for (int y = 0; y < 4; y++) 
     { 
      color += tex2D(getscene, position + float2(offsets[x], offsets[y])); 
     } 
    } 

    color /= 16; 

    return color; 
} 

Эта специфическая линия здесь, где я полагаю, что я делаю ошибку:

color += tex2D(getscene, position + float2(offsets[x], offsets[y])); 

Я никогда не правильно понял, как значения texCoord, используемые в выборке tex2D, работают. При создании эффекта размытия движения мне приходилось пересылать такие значения бесконечности, которые я боялся, что он будет округлен до нуля, чтобы произвести нормальный эффект, в то время как в других случаях пересылка больших значений, таких как 30 и 50, была необходима для производят эффекты, которые занимают, возможно, треть экрана.

Так или иначе, мой вопрос:

Учитывая квад экрана (так, плоская поверхность), как я увеличить или изменить texCoord значения иметь сетку пикселей, равномерно распространиться, отобранную через него?

Я попытался с помощью:

color += tex2D(getscene, position + float2(offsets[x] * (1/maxX), offsets[y] * (1/maxY))); 

Где Maxx и MAXY являются разрешение экрана,

color += tex2D(getscene, position + float2(offsets[x] * x, offsets[y] * y)); 

... и другие "снимки в темноте", и все результаты оказались быть то же: конечный результирующий пиксель, похоже, идентичен таковому в середине моего экрана, как будто это единственный выбор.

Как это решить?

Также, как работают эти координаты текстуры? Где (0,0)? Какой максимум?

Спасибо всем заранее.

ответ

1

Я решил проблему.

Я полагал, что использование дюжины renderTargets, для которых половина разрешения каждого шага будет дорогостоящим, но я был неправ.

На среднем процессоре в 2013 году nVidia GTX 560 стоимость рендеринга на 10 целей рендеринга не была заметна, а конкретные номера: от 230 FPS производительность снизилась до 220 FPS.

Решение следует. Предполагается, что вы уже обработали всю вашу сцену и отобразили ее в renderTarget, что в моем случае - «renderOutput».

Во-первых, я объявляю массив RenderTarget:

public RenderTarget2D[] HDRsampling; 

Далее я вычислить, сколько целей мне нужно будет в моей нагрузке() метод, который вызывается между обновлением меню и игры петли обновления (переходное состояние для загрузки игровых активов не требуется в меню), и инициализировать их должным образом:

int counter = 0; 
    int downX = Game1.maxX; 
    int downY = Game1.maxY; 

    do 
    { 
     downX /= 2; 
     downY /= 2; 
     counter++; 

    } while (downX > 1 && downY > 1); 

    HDRsampling = new RenderTarget2D[counter]; 
    downX = Game1.maxX/2; 
    downY = Game1.maxY/2; 

    for (int i = 0; i < counter; i++) 
    { 
     HDRsampling[i] = new RenderTarget2D(Game1.graphics.GraphicsDevice, downX, downY); 
     downX /= 2; 
     downY /= 2; 
    } 

и, наконец, код C# рендеринга выглядит следующим образом:

 if (settings.HDRpass) 
     { //HDR Rendering passes 
      //Uses Hardware bilinear downsampling method to obtain 1x1 texture as scene average 

      Game1.graphics.GraphicsDevice.SetRenderTarget(HDRsampling[0]); 
      Game1.graphics.GraphicsDevice.Clear(ClearOptions.Target, Color.Black, 0, 0); 
      downsampler.Parameters["maxX"].SetValue(HDRsampling[0].Width); 
      downsampler.Parameters["maxY"].SetValue(HDRsampling[0].Height); 
      downsampler.Parameters["scene"].SetValue(renderOutput); 
      downsampler.CurrentTechnique.Passes[0].Apply(); 
      quad.Render(); 

      for (int i = 1; i < HDRsampling.Length; i++) 
      { //Downsample the scene texture repeadetly until last HDRSampling target, which should be 1x1 pixel 
       Game1.graphics.GraphicsDevice.SetRenderTarget(HDRsampling[i]); 
       Game1.graphics.GraphicsDevice.Clear(ClearOptions.Target, Color.Black, 0, 0); 
       downsampler.Parameters["maxX"].SetValue(HDRsampling[i].Width); 
       downsampler.Parameters["maxY"].SetValue(HDRsampling[i].Height); 
       downsampler.Parameters["scene"].SetValue(HDRsampling[i-1]); 
       downsampler.CurrentTechnique.Passes[0].Apply(); 
       quad.Render(); 

      } 
      //assign the 1x1 pixel 
      downsample1x1 = HDRsampling[HDRsampling.Length - 1]; 

      Game1.graphics.GraphicsDevice.SetRenderTarget(extract); 
      //switch out rendertarget so we can send the 1x1 sample to the shader. 
      bloom.Parameters["downSample1x1"].SetValue(downsample1x1); 

     } 

Получает текстуру downSample1x1, которая позже используется в последнем проходе финального шейдера.

Шейдер код для фактического субдискретизации является скелетным проста:

texture2D scene; 

sampler getscene = sampler_state 
{ 
    texture = <scene>; 
    MinFilter = linear; 
    MagFilter = linear; 
    MipFilter = point; 
    MaxAnisotropy = 1; 
    AddressU = CLAMP; 
    AddressV = CLAMP; 
}; 

float maxX, maxY; 

struct vertexShaderStruct 
{ 
    float3 pos  : POSITION0; 
    float2 texCoord : TEXCOORD0; 
}; 

struct pixelShaderStruct 
{ 
    float4 position : POSITION0; 
    float2 texCoord : TEXCOORD0; 
}; 

pixelShaderStruct vertShader(vertexShaderStruct input) 
{ 
    pixelShaderStruct output; 

    float2 offset = float2 (0.5/maxX, 0.5/maxY); 
    output.position = float4(input.pos, 1); 
    output.texCoord = input.texCoord + offset; 

    return output; 
}; 

float4 PixelShaderFunction(pixelShaderStruct input) : COLOR0 
{ 
    return tex2D(getscene, input.texCoord); 
} 

technique Sample 
{ 
    pass P1 
    { 
     VertexShader = compile vs_2_0 vertShader(); 
     PixelShader = compile ps_2_0 PixelShaderFunction(); 
    } 
} 

Как вы реализуете сцену в среднем светимость до вас, я все еще экспериментирует со всем этим, но я надеюсь, что это помогает кто-то там !

0

Урок, который вы связали, является хорошей демонстрацией того, как делать Tonemapping, но это звучит для меня так же, как вы не выполнили повторную сэмплинг-выборку, необходимую для получения изображения 1x1.

Вы не можете просто взять изображение с высоким разрешением (1920x1080, скажем) взять 16 произвольных пикселей, добавить их и разделить на 16 и назвать это вашей яркостью.

Вам необходимо многократно уменьшать исходное изображение до меньших и меньших текстур, обычно пополам в каждом измерении на каждом этапе. Каждый пиксель результирующего понижающего сэмпла представляет собой среднее значение 2x2 сетки пикселей на предыдущей текстуре (это обрабатывается билинейной выборкой). В конце концов вы получите изображение 1x1, которое является средним значением цвета для всего исходного изображения 1920x1080, и из него вы можете рассчитать среднюю яркость исходного изображения.

Без повторной дискретизации выбор яркости будет очень шумным, так как его вход - всего лишь 16 из ~ 2M пикселей исходного изображения. Чтобы получить правильную и плавную яркость, каждый пиксель в исходном изображении должен внести свой вклад.

+0

Это имеет смысл, на самом деле я собирался об идеальном решении, как это, но как только я решаю фактическую выборку. Как вы предлагаете мне решить это? – Eudaimonium

Смежные вопросы