Рендеринг в расширенном динамическом диапазоне в XNA. Часть 2

Проход яркости.

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

Здесь код фильтра яркости:

float2 SourceDimensions = 1; 
float2 TargetDimensions; 
float Exposure = 0.6f; 
float Threshold = 0.7f; 
static const float3 LUMINANCE = float3(0.299f, 0.587f, 0.114f); 
texture2D SourceTexture; 
sampler2D SourceTextureSampler = sampler_state 
{ 
 Texture = <SourceTexture>; 
 MinFilter = POINT; 
 MagFilter = POINT; 
 MipFilter = POINT; 
 MaxAnisotropy = 1; 
 AddressU = CLAMP; 
 AddressV = CLAMP; 
}; 
texture2D LuminanceTexture; 
sampler2D LuminanceTextureSampler = sampler_state 
{ 
 Texture = <LuminanceTexture>; 
 MinFilter = POINT; 
 MagFilter = POINT; 
 MipFilter = POINT; 
 MaxAnisotropy = 1; 
 AddressU = CLAMP; 
 AddressV = CLAMP; 
}; 
struct VS_OUTPUT 
{ 
 float4 Position : POSITION; 
 float2 TexCoord : TEXCOORD0; 
}; 
VS_OUTPUT Common_VS(float4 Position : POSITION, 
 float2 TexCoord : TEXCOORD0) 
{ 
 VS_OUTPUT OUT; 
 OUT.Position = Position; 
 OUT.TexCoord = TexCoord + (0.5f / TargetDimensions); 
 return OUT; 
} 
float4 BrightPass_PS(float2 texCoord : TEXCOORD0) : COLOR0 
{ 
 float4 color = tex2D(SourceTextureSampler, texCoord); 
 float4 lc = tex2D(LuminanceTextureSampler, float2(0.5, 0.5)); 
 float lum = dot(color.rgb, LUMINANCE); 
 float scaleLum = (lum * Exposure) / lc.r; 
 color.rgb *= (scaleLum * (1 + (scaleLum / (lc.g * lc.g)))) 
 / (1 + scaleLum); 
 color.rgb -= Threshold; 
 color.rgb = max(color.rgb, 0.0f); 
 return color; 
} 
technique BrightPass 
{ 
 pass p0 
 { 
 VertexShader = compile vs_1_1 Common_VS(); 
 PixelShader = compile ps_2_0 BrightPass_PS(); 
 ZEnable = false; 
 ZWriteEnable = false; 
 AlphaBlendEnable = false; 
 AlphaTestEnable = false; 
 StencilEnable = false; 
 } 
}
public sealed class BrightPass : PostProcessEffect 
{ 
 private Effect _Effect = null; 
 private EffectParameter Parameter_TargetDimensions = null; 
 private EffectParameter Parameter_SourceDimensions = null; 
 private EffectParameter Parameter_SourceTexture = null; 
 private EffectParameter Parameter_LuminanceTexture = null; 
 private Vector2 TargetDimensions; 
 private Vector2 SourceDimensions; 
 public BrightPass(GraphicsDevice graphicsDevice, 
 ContentManager content) 
 : base(graphicsDevice) 
 { 
 _Effect = content.Load<Effect>("Effects/BrightPass"); 
 Parameter_TargetDimensions = 
 _Effect.Parameters["TargetDimensions"]; 
 Parameter_SourceDimensions = 
 _Effect.Parameters["SourceDimensions"]; 
 Parameter_SourceTexture = 
 _Effect.Parameters["SourceTexture"]; 
 Parameter_LuminanceTexture = 
 _Effect.Parameters["LuminanceTexture"]; 
 } 
 private void SetSourceTexture(Texture2D value) 
 { 
 Parameter_SourceTexture.SetValue(value); 
 if (value == null) 
 { 
 SourceDimensions.X = 1; 
 SourceDimensions.Y = 1; 
 } 
 else 
 { 
 SourceDimensions.X = value.Width; 
 SourceDimensions.Y = value.Height; 
 } 
 Parameter_SourceDimensions.SetValue(SourceDimensions); 
 } 
 public Texture2D LuminanceTexture 
 { 
 get 
 { 
 return Parameter_LuminanceTexture.GetValueTexture2D(); 
 } 
 set 
 { 
 Parameter_LuminanceTexture.SetValue(value); 
 } 
 } 
 public float Exposure 
 { 
 get 
 { 
 return _Effect.Parameters["Exposure"].GetValueSingle(); 
 } 
 set 
 { 
 _Effect.Parameters["Exposure"].SetValue(value); 
 } 
 } 
 public float Threshold 
 { 
 get 
 { 
 return _Effect.Parameters["Threshold"].GetValueSingle(); 
 } 
 set 
 { 
 _Effect.Parameters["Threshold"].SetValue(value); 
 } 
 } 
 public override void PostProcess(Texture2D sourceTexture, 
 RenderTarget2D result) 
 { 
 SetSourceTexture(sourceTexture); 
 GetTargetDimensions(result, out TargetDimensions); 
 Parameter_TargetDimensions.SetValue(TargetDimensions); 
 _GraphicsDevice.SetRenderTarget(0, result); 
 _GraphicsDevice.Clear(Color.Black); 
 DrawQuad(_Effect); 
 _GraphicsDevice.SetRenderTarget(0, null); 
 } 
}

Свечение с Гауссовским размытием.

(Если эффект свечения не необходим, можно пропустить этот шаг).

Зная более яркие области буфера сцены после «прохода яркости», можно улучшить визуальное изображение, применив эффект свечения, для этого просто примените горизонтальное и вертикальное размытие на текстуру, полученную после «яркого прохода».

Код:

float Offsets[9]; 
float Weights[9]; 
texture2D SourceTexture; 
sampler2D SourceTextureSampler = sampler_state 
{ 
 Texture = <SourceTexture>; 
 MinFilter = POINT; 
 MagFilter = POINT; 
 MipFilter = POINT; 
 MaxAnisotropy = 1; 
 AddressU = CLAMP; 
 AddressV = CLAMP; 
}; 
struct VS_OUTPUT 
{ 
 float4 Position : POSITION; 
 float2 TexCoord : TEXCOORD0; 
}; 
VS_OUTPUT Common_VS(float4 Position : POSITION, 
 float2 TexCoord : TEXCOORD0) 
{ 
 VS_OUTPUT OUT; 
 OUT.Position = Position; 
 OUT.TexCoord = TexCoord; 
 return OUT; 
} 
float4 GaussianBlurH(float2 texCoord : TEXCOORD0) : COLOR 
{ 
 float4 color = {0.0f, 0.0f, 0.0f, 0.0f}; 
 for(int i = 0; i < 9; i++) 
 { 
 float2 t = texCoord + float2(Offsets[i], 0.0f); 
 color += (tex2D(SourceTextureSampler, t) 
 * Weights[i]); 
 } 
 return float4( color.rgb, 1.0f ); 
} 
float4 GaussianBlurV(float2 texCoord : TEXCOORD0) : COLOR 
{ 
 float4 color = {0.0f, 0.0f, 0.0f, 0.0f}; 
 for(int i = 0; i < 9; i++) 
 { 
 float2 t = texCoord + float2(Offsets[i], 0.0f); 
 color += (tex2D(SourceTextureSampler, t)) 
 * Weights[i]); 
 } 
 return float4( color.rgb, 1.0f ); 
} 
int ShaderIndex = 0; 
PixelShader PS[] = 
{ 
 compile ps_2_0 GaussianBlurH(), 
 compile ps_2_0 GaussianBlurV() 
}; 
technique GaussianBlur9x9 
{ 
 pass p0 
 { 
 VertexShader = compile vs_1_1 Common_VS(); 
 PixelShader = (PS[ShaderIndex]); 
 } 
}
public sealed class GaussianBlur9x9 : PostProcessEffect 
{ 
 private Effect Effect = null; 
 EffectParameter Parameter_SourceTexture = null; 
 EffectParameter Parameter_Offsets = null; 
 EffectParameter Parameter_Weights = null; 
 EffectParameter Parameter_ShaderIndex = null; 
 private float _StandardDeviation = 0.8f; 
 private float _Amplitude = 0.4f; 
 private bool _Horizontal = true; 
 private Vector2 TargetDimensions = new Vector2(); 
 private bool _DirtyWeights = true; 
 private float[] Weights = new float[9]; 
 private float[] Offsets = new float[9]; 
 public bool Horizontal 
 { 
 get 
 { 
 return _Horizontal; 
 } 
 set 
 { 
 _Horizontal = value; 
 Parameter_ShaderIndex.SetValue(_Horizontal ? 0 : 1); 
 } 
 } 
 public float StandardDeviation 
 { 
 get 
 { 
 return _StandardDeviation; 
 } 
 set 
 { 
 if (_StandardDeviation != value) 
 { 
 _DirtyWeights = true; 
 } 
 _StandardDeviation = value; 
 } 
 } 
 public float Amplitude 
 { 
 get 
 { 
 return _Amplitude; 
 } 
 set 
 { 
 if (_Amplitude != value) 
 { 
 _DirtyWeights = true; 
 } 
 _Amplitude = value; 
 } 
 } 
 public GaussianBlur9x9(GraphicsDevice graphicsDevice, 
 ContentManager content) 
 : base(graphicsDevice) 
 { 
 Effect = content.Load<Effect>("Effects/GaussianBlur9x9"); 
 Parameter_SourceTexture = Effect.Parameters["SourceTexture"]; 
 Parameter_Offsets = Effect.Parameters["Offsets"]; 
 Parameter_Weights = Effect.Parameters["Weights"]; 
 Parameter_ShaderIndex = Effect.Parameters["ShaderIndex"]; 
 } 
 public override void PostProcess(Texture2D sourceTexture, 
 RenderTarget2D result) 
 { 
 Parameter_SourceTexture.SetValue(sourceTexture); 
 GetTargetDimensions(result, out TargetDimensions); 
 if (_DirtyWeights) 
 { 
 if (_Horizontal) 
 { 
 Gaussian.Fill(Offsets, 1.0f / TargetDimensions.X, 
 Weights, 0, _StandardDeviation, _Amplitude); 
 } 
 else 
 { 
 Gaussian.Fill(Offsets, 1.0f / TargetDimensions.Y, 
 Weights, 0, _StandardDeviation, _Amplitude); 
 } 
 Parameter_Weights.SetValue(Weights); 
 Parameter_Offsets.SetValue(Offsets); 
 _DirtyWeights = false; 
 } 
 _GraphicsDevice.SetRenderTarget(0, result); 
 _GraphicsDevice.Clear(Color.Black); 
 DrawQuad(Effect); 
 _GraphicsDevice.SetRenderTarget(0, null); 
 } 
}

Карта тона.

Карта тона используется для отображения одного множества цветов на другое. Здесь карта тона используется для интерполирования от высокого цвета интенсивности до более низкого цвета интенсивности, чтобы сделать возможным рендеринг: устройства отображения не могут отображать цвета со значениями выше, чем 1.0, или меньше, чем 0.0, для решения этой проблемы используется карта тона. Эффект карты тона берет среднюю яркость сигнала яркости сцены и приводит цвета, слишком яркие или слишком темные к визуализируемым цветам. Также увеличивается общий контраст, что делает тёмные области более яркими.


Текстура сцены.

Текстура сцены после применения карты тона.

Код для «карты тона».

texture2D SourceTexture; 
sampler2D SourceTextureSampler = sampler_state 
{ 
 Texture = <SourceTexture>; 
 MinFilter = POINT; 
 MagFilter = POINT; 
 MipFilter = POINT; 
 MaxAnisotropy = 1; 
 AddressU = CLAMP; 
 AddressV = CLAMP; 
}; 
texture2D LuminanceTexture; 
sampler2D LuminanceTextureSampler = sampler_state 
{ 
 Texture = <LuminanceTexture>; 
 MinFilter = POINT; 
 MagFilter = POINT; 
 MipFilter = POINT; 
 MaxAnisotropy = 1; 
 AddressU = CLAMP; 
 AddressV = CLAMP; 
}; 
float Exposure = 1.0; 
struct VS_OUTPUT 
{ 
 float4 Position : POSITION; 
 float2 TexCoord : TEXCOORD0; 
}; 
VS_OUTPUT Common_VS(float4 Position : POSITION, 
 float2 TexCoord : TEXCOORD0) 
{ 
 VS_OUTPUT OUT; 
 OUT.Position = Position; 
 OUT.TexCoord = TexCoord; 
 return OUT; 
} 
float4 Tonemap_PS(float2 texcoord : TEXCOORD0) : COLOR0 
{ 
 float4 final = tex2D(SourceTextureSampler, texcoord); 
 float4 l = tex2D(LuminanceTextureSampler, float2(0.5f, 0.5f)); 
 float Lp = (Exposure / l.r) * max(final.r, max(final.g, final.b)); 
 float LmSqr = (l.g * l.g) * (l.g * l.g); 
 float toneScalar = ( Lp * ( 1.0f + ( Lp / ( LmSqr ) ) ) ) 
 / ( 1.0f + Lp ); 
 final.rgb *= toneScalar; 
 return final; 
} 
technique ToneMapping 
{ 
 pass p0 
 { 
 VertexShader = compile vs_1_1 Common_VS(); 
 PixelShader = compile ps_2_0 Tonemap_PS(); 
 } 
}
public sealed class ToneMapping : PostProcessEffect 
{ 
 Effect _Effect = null; 
 //EffectParameter Parameter_TargetDimensions = null; 
 //EffectParameter Parameter_SourceDimensions = null; 
 EffectParameter Parameter_SourceTexture = null; 
 EffectParameter Parameter_LuminanceTexture = null; 
 private Vector2 TargetDimensions; 
 private Vector2 SourceDimensions; 
 public ToneMapping(GraphicsDevice graphicsDevice, 
 ContentManager content) 
 : base(graphicsDevice) 
 { 
 _Effect = 
 content.Load<Effect>("Effects/ToneMapping"); 
 Parameter_SourceTexture = 
 _Effect.Parameters["SourceTexture"]; 
 Parameter_LuminanceTexture = 
 _Effect.Parameters["LuminanceTexture"]; 
 } 
 private void SetSourceTexture(Texture2D value) 
 { 
 Parameter_SourceTexture.SetValue(value); 
 if (value == null) 
 { 
 SourceDimensions.X = 1; 
 SourceDimensions.Y = 1; 
 } 
 else 
 { 
 SourceDimensions.X = value.Width; 
 SourceDimensions.Y = value.Height; 
 } 
 } 
 public Texture2D LuminanceTexture 
 { 
 get 
 { 
 return Parameter_LuminanceTexture.GetValueTexture2D(); 
 } 
 set 
 { 
 Parameter_LuminanceTexture.SetValue(value); 
 } 
 } 
 public float Exposure 
 { 
 get 
 { 
 return _Effect.Parameters["Exposure"].GetValueSingle(); 
 } 
 set 
 { 
 _Effect.Parameters["Exposure"].SetValue(value); 
 } 
 } 
 public override void PostProcess(Texture2D sourceTexture, 
 RenderTarget2D result) 
 { 
 SetSourceTexture(sourceTexture); 
 GetTargetDimensions(result, out TargetDimensions); 
 //Parameter_TargetDimensions.SetValue(TargetDimensions); 
 _GraphicsDevice.SetRenderTarget(0, result); 
 _GraphicsDevice.Clear(Color.Black); 
 DrawQuad(_Effect); 
 _GraphicsDevice.SetRenderTarget(0, null); 
 } 
}
Реклама
Запись опубликована в рубрике Uncategorized. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s