HDR,这里有一篇写得不错的:http://www.zwqxin.com/archives/shaderglsl/review-high-dynamic-range.html
原理可以参考上面的博文~那就进入代码。
首先我们看看Luminance部分,也就是计算平均亮度的部分。
LPDIRECT3DPIXELSHADER9 g_pLumDispPS = NULL; // PShader used to display the debug panel
LPDIRECT3DPIXELSHADER9 g_pLum1PS = NULL; // PShader that does a 2x2 downsample and convert to greyscale
LPD3DXCONSTANTTABLE g_pLum1PSConsts = NULL; // Interface to set the sampling points for the above PS
LPDIRECT3DPIXELSHADER9 g_pLum3x3DSPS = NULL; // The PS that does each 3x3 downsample operation
LPD3DXCONSTANTTABLE g_pLum3x3DSPSConsts = NULL; // Interface for the above PS
static const DWORD g_dwLumTextures = 6; // How many luminance textures we're creating
// Be careful when changing this value, higher than 6 might
// require you to implement code that creates an additional
// depth-stencil buffer due to the luminance textures dimensions
// being greater than that of the default depth-stencil buffer.
LPDIRECT3DTEXTURE9 g_pTexLuminance[ g_dwLumTextures ]; // An array of the downsampled luminance textures
D3DFORMAT g_fmtHDR = D3DFMT_UNKNOWN; // Should be either G32R32F or G16R16F depending on the hardware
上面的是用到的比较重要的变量。
下面将逐个对函数代码进行阅读。
//--------------------------------------------------------------------------------------
// CreateResources( )
//
// DESC:
// This function creates the necessary texture chain for downsampling the
// initial HDR texture to a 1x1 luminance texture.
函数描述。
//[ 2 ] CREATE HDR RENDER TARGETS
//-------------------------------
int iTextureSize = 1;
for( int i = 0; i < Luminance::g_dwLumTextures; i++ )
{
// Create this element in the array
V( pDevice->CreateTexture( iTextureSize, iTextureSize, 1,
D3DUSAGE_RENDERTARGET, Luminance::g_fmtHDR,
D3DPOOL_DEFAULT, &Luminance::g_pTexLuminance[i], NULL ) );
// Increment for the next texture
iTextureSize *= 3;
}
创建计算平均亮度所需要的纹理。
V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"Shader Code\\Luminance.psh" ) );
V( D3DXCompileShaderFromFile(str, NULL, NULL, "LuminanceDisplay", "ps_2_0", 0, &pCode, NULL, NULL ) );
编译亮度展示着色器。
V( pDevice->CreatePixelShader( reinterpret_cast< DWORD* >( pCode->GetBufferPointer() ),
&Luminance::g_pLumDispPS ) );
。。。。。接下来照葫芦画瓢,创建两个减取样着色器,一个用作转换为灰阶,一个计算平均值来进行减采样。这个函数就完成任务了。
下一个函数~
//--------------------------------------------------------------------------------------
// MeasureLuminance( )
//
// DESC:
// This is the core function for this particular part of the application, it's
// job is to take the previously rendered (in the 'HDRScene' namespace) HDR
// image and compute the overall luminance for the scene. This is done by
// repeatedly downsampling the image until it is only 1x1 in size. Doing it
// this way (pixel shaders and render targets) keeps as much of the work on
// the GPU as possible, consequently avoiding any resource transfers, locking
// and modification.
HDRScene::GetOutputTexture( &pSourceTex );
pDestTex = Luminance::g_pTexLuminance[ Luminance::g_dwLumTextures - 1 ];
pDevice->SetRenderTarget( 0, pDestSurf );
pDevice->SetTexture( 0, pSourceTex );
pDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
pDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
首先得到整个场景作为源纹理,并得到最后一个亮度纹理作为输出。
//[ 2 ] RENDER AND DOWNSAMPLE THE HDR TEXTURE
// TO THE LUMINANCE MAP.
//-------------------------------------------
// Set which shader we're going to use. g_pLum1PS corresponds
// to the 'GreyScaleDownSample' entry point in 'Luminance.psh'.
pDevice->SetPixelShader( Luminance::g_pLum1PS );
// We need to compute the sampling offsets used for this pass.
// A 2x2 sampling pattern is used, so we need to generate 4 offsets.
D3DXVECTOR4 offsets[4];
// Find the dimensions for the source data
D3DSURFACE_DESC srcDesc;
pSourceTex->GetLevelDesc( 0, &srcDesc );
// Because the source and destination are NOT the same sizes, we
// need to provide offsets to correctly map between them.
float sU = ( 1.0f / static_cast< float >( srcDesc.Width ) );
float sV = ( 1.0f / static_cast< float >( srcDesc.Height ) );
// The last two components (z,w) are unused. This makes for simpler code, but if
// constant-storage is limited then it is possible to pack 4 offsets into 2 float4's
offsets[0] = D3DXVECTOR4( -0.5f * sU, 0.5f * sV, 0.0f, 0.0f );
//省略其他offset设置
// Set the offsets to the constant table
Luminance::g_pLum1PSConsts->SetVectorArray( pDevice, "tcLumOffsets", offsets, 4 );
// With everything configured we can now render the first, initial, pass
// to the luminance textures.
RenderToTexture( pDevice );
这一个过程每计算一次平均亮度只需要进行一次,因为转换为灰阶只需要进行一次。下面先看看RenderToTexture再回来继续下面的亮度计算。
//--------------------------------------------------------------------------------------
// RenderToTexture( )
//
// DESC:
// A simple utility function that draws, as a TL Quad, one texture to another
// such that a pixel shader (configured before this function is called) can
// operate on the texture. Used by MeasureLuminance() to perform the
// downsampling and filtering.
// To correctly map from texels->pixels we offset the coordinates
// by -0.5f:
float fWidth = static_cast< float >( desc.Width ) - 0.5f;
float fHeight = static_cast< float >( desc.Height ) - 0.5f;
// Now we can actually assemble the screen-space geometry
Luminance::TLVertex v[4];
v[0].p = D3DXVECTOR4( -0.5f, -0.5f, 0.0f, 1.0f );
v[0].t = D3DXVECTOR2( 0.0f, 0.0f );
v[1].p = D3DXVECTOR4( fWidth, -0.5f, 0.0f, 1.0f );
v[1].t = D3DXVECTOR2( 1.0f, 0.0f );
v[2].p = D3DXVECTOR4( -0.5f, fHeight, 0.0f, 1.0f );
v[2].t = D3DXVECTOR2( 0.0f, 1.0f );
v[3].p = D3DXVECTOR4( fWidth, fHeight, 0.0f, 1.0f );
v[3].t = D3DXVECTOR2( 1.0f, 1.0f );
// Configure the device and render..
pDev->SetVertexShader( NULL );
pDev->SetFVF( Luminance::FVF_TLVERTEX );
pDev->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, v, sizeof( Luminance::TLVertex ) );
渲染代码挺简单,注意texel和pixel之间的坐标转换就好了。下面回到MeasureLuminance。
//[ 3 ] SCALE EACH RENDER TARGET DOWN
// The results ("dest") of each pass feeds into the next ("src")
//-------------------------------------------------------------------
for( int i = ( Luminance::g_dwLumTextures - 1 ); i > 0; i-- )
{
// Configure the render targets for this iteration
pSourceTex = Luminance::g_pTexLuminance[ i ];
pDestTex = Luminance::g_pTexLuminance[ i - 1 ];
if( FAILED( pDestTex->GetSurfaceLevel( 0, &pDestSurf ) ) )
{
//省略
}
pDevice->SetRenderTarget( 0, pDestSurf );
pDevice->SetTexture( 0, pSourceTex );
//省略
// Because each of these textures is a factor of 3
// different in dimension, we use a 3x3 set of sampling
// points to downscale.
D3DSURFACE_DESC srcTexDesc;
pSourceTex->GetLevelDesc( 0, &srcTexDesc );
// Create the 3x3 grid of offsets
D3DXVECTOR4 DSoffsets[9];
int idx = 0;
for( int x = -1; x < 2; x++ )
{
for( int y = -1; y < 2; y++ )
{
DSoffsets[idx++] = D3DXVECTOR4(
static_cast< float >( x ) / static_cast< float >( srcTexDesc.Width ),
static_cast< float >( y ) / static_cast< float >( srcTexDesc.Height ),
0.0f, //unused
0.0f //unused
);
}
}
// Set them to the current pixel shader
pDevice->SetPixelShader( Luminance::g_pLum3x3DSPS );
Luminance::g_pLum3x3DSPSConsts->SetVectorArray( pDevice, "tcDSOffsets", DSoffsets, 9 );
// Render the display to this texture
RenderToTexture( pDevice );
//省略
}
接下来是DisplayLuminance函数
//--------------------------------------------------------------------------------------
// DisplayLuminance( )
//
// DESC:
// This function is for presentation purposes only - and isn't a *required*
// part of the HDR rendering pipeline. It draws the 6 stages of the luminance
// calculation to the appropriate part of the screen.
所以我就不说了。。。Next
//--------------------------------------------------------------------------------------
// GetLuminanceTexture( )
//
// DESC:
// The final 1x1 luminance texture created by the MeasureLuminance() function
// is required as an input into the final image composition. Because of this
// it is necessary for other parts of the application to have access to this
// particular texture.
这个函数是输出最后的1X1结果
HRESULT GetLuminanceTexture( IDirect3DTexture9** pTex )
{
// [ 0 ] ERASE ANY DATA IN THE INPUT
//----------------------------------
SAFE_RELEASE( *pTex );
// [ 1 ] COPY THE PRIVATE REFERENCE
//---------------------------------
*pTex = Luminance::g_pTexLuminance[ 0 ];
// [ 2 ] INCREMENT THE REFERENCE COUNT..
//--------------------------------------
( *pTex )->AddRef();
return S_OK;
}
这样我们就完成了HDR三部分的其中之一(Tone Mapping,另外还有Bloom和最终的合成)。