HDR需要对源场景进行处理,因此在渲染初始源场景的时候,也要创建一个纹理,并且将场景渲染到这个纹理上,然后进行HDR处理。这个例子中,上述工作由HDRScene完成。关于渲染到纹理,这篇博文讲得很清楚:http://www.cnblogs.com/graphics/archive/2011/04/23/2024294.html
那么到此为止,我们明白了如何进行计算平均亮度,如何进行Bloom,如何渲染场景到纹理上。那么接下来我们就应该将这些东西组合起来,完成HDR的渲染。
让我们直接进入RenderScene函数:
if( SUCCEEDED( pd3dDevice->BeginScene() ) )
{
// RENDER THE COMPLETE SCENE
//--------------------------
// The first part of each frame is to actually render the "core"
// resources - those that would be required for an HDR-based pipeline.
// HDRScene creates an unprocessed, raw, image to work with.
HDRScene::RenderScene( pd3dDevice );
// Luminance attempts to calculate what sort of tone/mapping should
// be done as part of the post-processing step.
Luminance::MeasureLuminance( pd3dDevice );
// The post-processing adds the blur to the bright (over-exposed) areas
// of the image.
PostProcess::PerformPostProcessing( pd3dDevice );
上面三个简单的句子就是前面讲过的渲染场景到纹理、计算平均亮度和Bloom。
LPDIRECT3DSURFACE9 pFinalSurf = NULL;
g_pFinalTexture->GetSurfaceLevel( 0, &pFinalSurf );
pd3dDevice->SetRenderTarget( 0, pFinalSurf );
LPDIRECT3DTEXTURE9 pHDRTex = NULL;
HDRScene::GetOutputTexture( &pHDRTex );
LPDIRECT3DTEXTURE9 pLumTex = NULL;
Luminance::GetLuminanceTexture( &pLumTex );
LPDIRECT3DTEXTURE9 pBloomTex = NULL;
PostProcess::GetTexture( &pBloomTex );
pd3dDevice->SetTexture( 0, pHDRTex );
pd3dDevice->SetTexture( 1, pLumTex );
pd3dDevice->SetTexture( 2, pBloomTex );
pd3dDevice->SetPixelShader( g_pFinalPassPS );
D3DSURFACE_DESC d;
pBloomTex->GetLevelDesc( 0, &d );
g_pFinalPassPSConsts->SetFloat( pd3dDevice, "g_rcp_bloom_tex_w", 1.0f / static_cast< float >( d.Width ) );
g_pFinalPassPSConsts->SetFloat( pd3dDevice, "g_rcp_bloom_tex_h", 1.0f / static_cast< float >( d.Height ) );
g_pFinalPassPSConsts->SetFloat( pd3dDevice, "fExposure", g_fExposure );
g_pFinalPassPSConsts->SetFloat( pd3dDevice, "fGaussianScalar", PostProcess::GetGaussianMultiplier() );
DrawHDRTextureToScreen();
//省略
pd3dDevice->SetRenderTarget( 0, pLDRSurface );
然后就是调用最后一个着色器完成合并工作。注意最后一句,将渲染目标还原为原来的LDR平面,其实也没用到。
前面没有看过FX文件,因为其实都比较简单。下面就看一下最后的这个PS完成了什么。
float4 main( in float2 t : TEXCOORD0 ) : COLOR0
{
// Read the HDR value that was computed as part of the original scene
float4 c = tex2D( original_scene, t );
// Read the luminance value, target the centre of the texture which will map to the only pixel in it!
float4 l = tex2D( luminance, float2( 0.5f, 0.5f ) ); //也就是亮度平均值
// Compute the blur value using a bilinear filter
float xWeight = frac( t.x / g_rcp_bloom_tex_w ) - 0.5;
float xDir = xWeight;
xWeight = abs( xWeight );
xDir /= xWeight;
xDir *= g_rcp_bloom_tex_w;
float yWeight = frac( t.y / g_rcp_bloom_tex_h ) - 0.5;
float yDir = yWeight;
yWeight = abs( yWeight );
yDir /= yWeight;
yDir *= g_rcp_bloom_tex_h;
// sample the blur texture for the 4 relevant pixels, weighted accordingly
float4 b = ((1.0f - xWeight) * (1.0f - yWeight)) * tex2D( bloom, t );
b += (xWeight * (1.0f - yWeight)) * tex2D( bloom, t + float2( xDir, 0.0f ) );
b += (yWeight * (1.0f - xWeight)) * tex2D( bloom, t + float2( 0.0f, yDir ) );
b += (xWeight * yWeight) * tex2D( bloom, t + float2( xDir, yDir ) );
// Compute the actual colour:
float4 final = c + 0.25f * b;
// Reinhard's tone mapping equation (See Eqn#3 from
// "Photographic Tone Reproduction for Digital Images" for more details) is:
//
// ( ( Lp ))
// Lp * (1.0f +(---------))
// ( ((Lm * Lm)))
// -------------------------
// 1.0f + Lp
//
// Lp is the luminance at the given point, this is computed using Eqn#2 from the above paper:
//
// exposure
// Lp = -------- * HDRPixelIntensity
// l.r
//
// The exposure ("key" in the above paper) can be used to adjust the overall "balance" of
// the image. "l.r" is the average luminance across the scene, computed via the luminance
// downsampling process. 'HDRPixelIntensity' is the measured brightness of the current pixel
// being processed.
float Lp = (fExposure / l.r) * max( final.r, max( final.g, final.b ) );
// A slight difference is that we have a bloom component in the final image - this is *added* to the
// final result, therefore potentially increasing the maximum luminance across the whole image.
// For a bright area of the display, this factor should be the integral of the bloom distribution
// multipled by the maximum value. The integral of the gaussian distribution between [-1,+1] should
// be AT MOST 1.0; but the sample code adds a scalar to the front of this, making it a good enough
// approximation to the *real* integral.
float LmSqr = (l.g + fGaussianScalar * l.g) * (l.g + fGaussianScalar * l.g);
// Compute Eqn#3:
float toneScalar = ( Lp * ( 1.0f + ( Lp / ( LmSqr ) ) ) ) / ( 1.0f + Lp );
// Tonemap the final outputted pixel:
c = final * toneScalar;
// Return the fully composed colour
c.a = 1.0f;
return c;
}
着色器完成了从纹理取值,并且根据公式计算像素颜色的工作。
DrawHDRTextureToScreen()完成的工作跟以前的同类函数类似,就是将纹理渲染到屏幕上。
到此为止,HDR渲染就差不多了。