1.什么是体积雾?
这个问题通过图片来解答再合适不过了,下面是本文利用体积雾做的一个结果
所谓体积雾:顾名思义就是被限制了形状的雾,本文表述如何通过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;
};
硬件支持的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, NULL);
注意:使用MultiRenderTarget 不能开启反锯齿
// 拷贝FrameBuffer到一张动态纹理上
IDirect3DSurface9* pSurface = NULL;
g_pDevice->GetRenderTarget( 0, &pSurface);
//
copy back buffer into refraction map texture
g_pDevice->StretchRect( pSurface, 0, m_pTextureSrc->GetSurface(), 0, D3DTEXF_NONE);
SAFE_RELEASE( pSurface);
2.渲染所有物模型的背面的深度到一张纹理上
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;
};
// Vertex Shader
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;
// 纹理采样
sampler2D RGBSampler = sampler_state
{
texture = <texSrc>;
};
sampler2D DepthSampler = sampler_state
{
texture = <texSceneDepth>;
};
sampler2D FogFrontSampler = sampler_state
{
texture = <texFogFront>;
};
sampler2D FogBackSampler = sampler_state
{
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;
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();
}
}
另:为了减少判断量, 需把所有的图片 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/