体积雾(dx9)
1.什么是体积雾?
uniform float2 g_fogParam; // x: fogStart y: fogEnd
uniform float3 g_fogColor; // FogColor
// vertexInput
struct VS_INPUT
{
float3 Position : POSITION0;
float2 Texcoord : TEXCOORD0;
};
if( depth > fogBack)
dis = fogBack - fogFront;
else if( depth > fogFront)
dis = depth - fogFront;
float3 rgb = tex2D(RGBSampler, In.Texcoord);
float3 color = rgb + factor * ( g_fogColor - rgb);
这个问题通过图片来解答再合适不过了,下面是本文利用体积雾做的一个结果
所谓体积雾:顾名思义就是被限制了形状的雾,本文表述如何通过ImageProcess(图象处理)的方式实现体积雾。
2.常规雾原理
雾效最终体现在雾颜色与场景色的混合上。决定雾的浓度的关键就在这个混合因子上。下面我们给出公式.
float4 finalColor = factor * fogColor + ( 1 - factor) * sceneColor; ( !提取公因式,减少计算量)
= factor( fogColor - sceneColor) + sceneColor; ( factor>=0 && factor<=1)
(雾化公式) finalColor :混合后颜色
sceneColor:场景象素色
factor :混合因子
3.体积雾原理
雾效关键在于如何求得每个场景可见象素点的混合因子!
float factor = fogDepth / ( fogEnd - fogStart);
也就是视点与目标象素点的距离上雾所占的距离 fogDepth。根据Z Buffer,我们可以获取每帧每个象素到视点的距离。
于是,我们同样可以通过渲染FogVolume的正面与反面的深度到两张贴图上。这样我们就可以根据当前场景深度信息以及前面的两张贴图,获得FogDepth,
4.实现步骤
1.首先渲染所有场景物体, 此处我们需要FrameBuffer, 与 Z Buffer信息
2.渲染所有物模型的背面的深度到一张纹理上
3.渲染所有雾模型的正面的尝试到一张纹理上
4.利用场景的ZBuffer信息, 雾的两张深度纹理, 雾参数 处理 场景的FrameBuffer
5.具体步骤
1.获取场景的ZBuffer, 与 FrameBuffer
因为dx9无法直接获取场景的ZBuffer信息,所以我们要采用别的方式,
1.把所有的场景多渲染一次,把深度信息渲染到一张纹理上;
2.使用MultiRenderTarget 渲染场景的同时把深度信息保存到一张纹理上
下面我们只介绍MultiRenderTarget的方式。
MultiRenderTarget 是 Ps 2.0 即支持的渲染方式。其实现方法比较简单,只需更改PS( pixel shader)的返回值
例(这里使用使用两个RenderTarget):
struct PS_OUTPUT
{
float4 Target0 : COLOR0;
float4 Target1 : COLOR1;
};
{
float4 Target0 : COLOR0;
float4 Target1 : COLOR1;
};
硬件支持的RenderTarget数可以通过 D3DCaps.NumSimultaneousRTs 查看, 一般显卡:4
代码部分:
// 设置多RenderTarget
g_pDevice->SetRenderTarget( 0, m_pMainRenderTarget);
g_pDevice->SetRenderTarget( 1, m_pDepthTexture->GetSurface());
g_pDevice->SetRenderTarget( 0, m_pMainRenderTarget);
g_pDevice->SetRenderTarget( 1, m_pDepthTexture->GetSurface());
// 渲染调用代码
g_pDevice->SetRenderTarget( 0, m_pMainRenderTarget);
g_pDevice->SetRenderTarget( 1, NULL);
g_pDevice->SetRenderTarget( 1, NULL);
注意:使用MultiRenderTarget 不能开启反锯齿
// 拷贝FrameBuffer到一张动态纹理上
IDirect3DSurface9* pSurface = NULL;
g_pDevice->GetRenderTarget( 0, &pSurface);
g_pDevice->GetRenderTarget( 0, &pSurface);
// copy back buffer into refraction map texture
g_pDevice->StretchRect( pSurface, 0, m_pTextureSrc->GetSurface(), 0, D3DTEXF_NONE);
g_pDevice->StretchRect( pSurface, 0, m_pTextureSrc->GetSurface(), 0, D3DTEXF_NONE);
SAFE_RELEASE( pSurface);
2.渲染所有物模型的背面的深度到一张纹理上
3.渲染所有雾模型的正面的尝试到一张纹理上
3.渲染所有雾模型的正面的尝试到一张纹理上
4最后利用所有的数据更改FrameBuffer
Shader 代码:
uniform float2 g_fogParam; // x: fogStart y: fogEnd
uniform float3 g_fogColor; // FogColor
// vertexInput
struct VS_INPUT
{
float3 Position : POSITION0;
float2 Texcoord : TEXCOORD0;
};
// Vertex OutPut
struct VS_OUTPUT
{
float4 Position : POSITION;
float2 Texcoord : TEXCOORD1;
};
struct VS_OUTPUT
{
float4 Position : POSITION;
float2 Texcoord : TEXCOORD1;
};
// Vertex Shader
VS_OUTPUT VS(VS_INPUT In)
{
VS_OUTPUT Out;
Out.Position = float4(In.Position, 1.0f);
Out.Texcoord = In.Texcoord;
VS_OUTPUT VS(VS_INPUT In)
{
VS_OUTPUT Out;
Out.Position = float4(In.Position, 1.0f);
Out.Texcoord = In.Texcoord;
return Out;
}
}
texture texSrc;
texture texSceneDepth;
texture texFogFront;
texture texFogBack;
texture texSceneDepth;
texture texFogFront;
texture texFogBack;
// 纹理采样
sampler2D RGBSampler = sampler_state
{
texture = <texSrc>;
};
sampler2D RGBSampler = sampler_state
{
texture = <texSrc>;
};
sampler2D DepthSampler = sampler_state
{
texture = <texSceneDepth>;
};
{
texture = <texSceneDepth>;
};
sampler2D FogFrontSampler = sampler_state
{
texture = <texFogFront>;
};
{
texture = <texFogFront>;
};
sampler2D FogBackSampler = sampler_state
{
texture = <texFogBack>;
};
{
texture = <texFogBack>;
};
// Pixel Shader
float4 PS(VS_OUTPUT In) : COLOR
{
float depth = tex2D( DepthSampler, In.Texcoord).r;
float fogFront = tex2D( FogFrontSampler, In.Texcoord).r;
float fogBack = tex2D( FogBackSampler, In.Texcoord).r;
float4 PS(VS_OUTPUT In) : COLOR
{
float depth = tex2D( DepthSampler, In.Texcoord).r;
float fogFront = tex2D( FogFrontSampler, In.Texcoord).r;
float fogBack = tex2D( FogBackSampler, In.Texcoord).r;
float dis = 0.f;
if( depth > fogBack)
dis = fogBack - fogFront;
else if( depth > fogFront)
dis = depth - fogFront;
float factor = max(( dis - g_fogParam.x), 0.f) / ( g_fogParam.y - g_fogParam.x);
float3 rgb = tex2D(RGBSampler, In.Texcoord);
float3 color = rgb + factor * ( g_fogColor - rgb);
return float4( color, 1.f);
}
}
technique technique0
{
pass p0
{
VertexShader = compile vs_3_0 VS();
PixelShader = compile ps_3_0 PS();
}
}
{
pass p0
{
VertexShader = compile vs_3_0 VS();
PixelShader = compile ps_3_0 PS();
}
}
另:为了减少判断量, 需把所有的图片 Clear为黑色
Please help to write shaders for volumetric fog. I have an error. But I do not know where.
float4x4 g_wvp;
//depth.vsh m_pFogShaderDepth
void vs_depth( inout float4 pos : POSITION ,
out float depth : TEXCOORD0 )
{
pos = mul( pos, g_wvp );
depth = pos.z;
}
//depth.psh m_pFogShaderDepth
float4 ps_depth( in float depth : TEXCOORD0 ) : COLOR
{
return float4( depth, 0.f, 0.f, 0.f );
}
sampler2D s_fog_back;
sampler2D s_fog_front;
sampler2D s_scene_depth;
//fog.ps m_pPixShader
float4 Main_ps( in float2 tex : TEXCOORD0 ) : COLOR
{
float fog_back = tex2D( s_fog_back , tex ).r;
float fog_front = tex2D( s_fog_front , tex ).r;
float scene_depth = tex2D( s_scene_depth , tex ).r;
float k = fog_back - fog_front;
k -= fog_back - clamp( scene_depth, 0, fog_back );
return float4(0.5f,0.5f,0.5f, k*0.5f );
}
Code C++
p_d3d_Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
p_d3d_Device->SetTexture(0,m_pEndFogTexture);
//********************************
D3DXMATRIX V;
TheCamera.getViewMatrix(&V);
D3DXMATRIX matRes;
//matRes=mxWorld*mxView*mxProj;
matRes=mxWorld*V*mxProj;
VertShaderConstTable->SetMatrix(p_d3d_Device, MatrixHandle, &matRes);
//------------------------------
p_d3d_Device->SetPixelShader( NULL );
//reverse the culling order to get the back side
p_d3d_Device->SetRenderState( D3DRS_ZFUNC , D3DCMP_GREATER );
p_d3d_Device->Clear(NULL, NULL, D3DCLEAR_TARGET| D3DCLEAR_ZBUFFER , D3DCOLOR_ARGB(0, 0, 0, 0), 0.0, 0);
p_d3d_Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
p_d3d_Device->SetTexture(0,m_pEndFogTexture);
p_d3d_Device->SetVertexShader( m_pFogShaderDepth );
for( DWORD i=0; i<g_dwNumMaterials; i++ )
{
m_pMeshFloor->DrawSubset( i );
}
p_d3d_Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
p_d3d_Device->SetTexture(1,m_pStartFogTexture);
p_d3d_Device->SetVertexShader( m_pFogShaderDepth );
for( DWORD i=0; i<g_dwNumMaterials; i++ )
{
m_pMeshFloor->DrawSubset( i );
}
p_d3d_Device->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
p_d3d_Device->SetVertexShader( NULL);
p_d3d_Device->SetPixelShader( m_pPixShader );
for( DWORD i=0; i<g_dwNumMaterials; i++ )
{
m_pMeshFloor->DrawSubset( i );
}
//---------------------------------
p_d3d_Device->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE);
//**********************************
p_d3d_Device->EndScene ();
p_d3d_Device->Present (NULL, NULL, NULL, NULL);
as a result I have displayed a blue cube, textured very wrong, on a gray background. Read the article Russian programmer http://timai-ru.blogspot.com/
float4x4 g_wvp;
//depth.vsh m_pFogShaderDepth
void vs_depth( inout float4 pos : POSITION ,
out float depth : TEXCOORD0 )
{
pos = mul( pos, g_wvp );
depth = pos.z;
}
//depth.psh m_pFogShaderDepth
float4 ps_depth( in float depth : TEXCOORD0 ) : COLOR
{
return float4( depth, 0.f, 0.f, 0.f );
}
sampler2D s_fog_back;
sampler2D s_fog_front;
sampler2D s_scene_depth;
//fog.ps m_pPixShader
float4 Main_ps( in float2 tex : TEXCOORD0 ) : COLOR
{
float fog_back = tex2D( s_fog_back , tex ).r;
float fog_front = tex2D( s_fog_front , tex ).r;
float scene_depth = tex2D( s_scene_depth , tex ).r;
float k = fog_back - fog_front;
k -= fog_back - clamp( scene_depth, 0, fog_back );
return float4(0.5f,0.5f,0.5f, k*0.5f );
}
Code C++
p_d3d_Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
p_d3d_Device->SetTexture(0,m_pEndFogTexture);
//********************************
D3DXMATRIX V;
TheCamera.getViewMatrix(&V);
D3DXMATRIX matRes;
//matRes=mxWorld*mxView*mxProj;
matRes=mxWorld*V*mxProj;
VertShaderConstTable->SetMatrix(p_d3d_Device, MatrixHandle, &matRes);
//------------------------------
p_d3d_Device->SetPixelShader( NULL );
//reverse the culling order to get the back side
p_d3d_Device->SetRenderState( D3DRS_ZFUNC , D3DCMP_GREATER );
p_d3d_Device->Clear(NULL, NULL, D3DCLEAR_TARGET| D3DCLEAR_ZBUFFER , D3DCOLOR_ARGB(0, 0, 0, 0), 0.0, 0);
p_d3d_Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
p_d3d_Device->SetTexture(0,m_pEndFogTexture);
p_d3d_Device->SetVertexShader( m_pFogShaderDepth );
for( DWORD i=0; i<g_dwNumMaterials; i++ )
{
m_pMeshFloor->DrawSubset( i );
}
p_d3d_Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
p_d3d_Device->SetTexture(1,m_pStartFogTexture);
p_d3d_Device->SetVertexShader( m_pFogShaderDepth );
for( DWORD i=0; i<g_dwNumMaterials; i++ )
{
m_pMeshFloor->DrawSubset( i );
}
p_d3d_Device->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
p_d3d_Device->SetVertexShader( NULL);
p_d3d_Device->SetPixelShader( m_pPixShader );
for( DWORD i=0; i<g_dwNumMaterials; i++ )
{
m_pMeshFloor->DrawSubset( i );
}
//---------------------------------
p_d3d_Device->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE);
//**********************************
p_d3d_Device->EndScene ();
p_d3d_Device->Present (NULL, NULL, NULL, NULL);
as a result I have displayed a blue cube, textured very wrong, on a gray background. Read the article Russian programmer http://timai-ru.blogspot.com/