永远也不完美的程序

不断学习,不断实践,不断的重构……

常用链接

统计

积分与排名

好友链接

最新评论

[转]HLSL实现Layer Fog

What’s Layer Fog

这个问题通过图片来解答再合适不过了,下面是本文利用layerFog做的一个结果。

所谓layer fog,顾名思义就是被限制在某一层的雾,本文的目的就是描述如何实现了被限制在一定高度范围内的雾效。

1         Thanks to Programmable Pipeline

雾效增强场景真实感也不是一天半天了,但无论opengl还是d3d,无论linear(线性衰减)、exp(指数衰减)还是exp2(指数平方衰减),以往实现的雾效都是全局的,要么就都加雾效,要么都不加。在固定渲染流水线的统治时代,想做到“山谷烟雾弥漫,峰顶明月清风是相当复杂而啰嗦的事。

感谢那些大牛公司和它们的图形精英,他们在享受创造的乐趣之余,想到了我们,给了我们参与创作的机会。可编程流水线的出现,让我们在做类似layer fog这类事情的时候可以直奔主题。

闲话少说,本文不介绍Programmable Pipeline,也不介绍vspsHLSLeffect,相关知识可以参考Directx9 SDK,下面来开始介绍Layer fog

1         Theory

我们知道雾效最终体现在一个颜色的融合因子上,根据这个融合因子的大小,可以确定雾化程度,如果融合因子为factor,雾的颜色为fogColor,场景点本身的颜色为sceneColor则最终雾化后的颜色finalColor应为:

       finalColor = sceneColor+factor*(fogColor-sceneColor)                (0<=factor<=1)

    该融合因子体现了颜色混合中雾的权重,假设雾的浓度函数为fuction(x,y,z), 以视线进入雾层为起点fStart,实现离开雾层(或到达场景物体表面)为结束点fEnd,factor实际上是function(x,y,z)在从fStartfEnd这段路径上的积分,如下:

本文的重点是描述layer fog的实现思想,所以采用了最简单的雾效方程,认为在雾层范围内,雾的浓度保持常数不变。则公式变为:

distance(fStart,fEnd)是求两点之间距离的函数,在实际计算中,雾层定义在Y方向,此式往往可用以下公式表示

其中abs函数是取绝对值函数,θ角是射线与XOZ平面的夹角。

下面针对具体情况进行说明。

   

如图所示,layerFog有雾顶(y坐标为fFogTop),雾底(fFogEnd,雾在fFogEndfFogTop之间存在,需要保证fFogTop>fFogEnd

由于layerfog的照相机位置存在三种情况

l         Camera.y>fFogTop

l         fFogEnd<Camera.y<fFogTop

l         fFogEnd<Camera.y

场景点ScenePoint位置也存在三种情况

l         ScenePoint.y>fFogTop

l         fFogEnd< ScenePoint.y<fFogTop

l         fFogEnd< ScenePoint.y

所以,实际上共有9种组合情况,每种的处理方法有所不同,实际上说白了就一句话“合法范围内积分,超出雾层范围之外不进行积分“,本着这个原则针对每种情况的不同确定积分上下限。

1 Code

本文采用Effect实现该算法,其主要代码如下:

 

texture g_MeshTexture;              // 纹理

 

float4x4 g_matWorld;                  // 物体的世界变换矩阵,由应用程序输入

float4x4 g_matWorldViewProj;    // World * View * Projection matrix,由应用程序输入

float4   g_FogParameter;//.x=fogHeight   .y = fogEnd  .z = fogRange,由应用程序输入

float4   g_vCamera;      //摄像机位置,由应用程序输入

float4   g_FogColor;     //雾颜色,由应用程序输入

 

//--------------------------------------------------------------------------------------

// 纹理采样器

//--------------------------------------------------------------------------------------

sampler MeshTextureSampler =

sampler_state

{

    Texture = <g_MeshTexture>;

    MipFilter = LINEAR;

    MinFilter = LINEAR;

    MagFilter = LINEAR;

};

 

 

//--------------------------------------------------------------------------------------

// 顶点着色器输入结构体

//--------------------------------------------------------------------------------------

 

struct VS_INPUT

{

    float4 Position   : POSITION;   // 顶点位置

    float4 Diffuse    : COLOR0;     // 顶点颜色

    float2 TextureUV  : TEXCOORD0;  // 纹理坐标

};

//--------------------------------------------------------------------------------------

// 顶点着色器输出结构体

//--------------------------------------------------------------------------------------

 

struct VS_OUTPUT

{

    float4 Position   : POSITION;   // 顶点位置

    float2 TextureUV  : TEXCOORD0;  // 纹理坐标

     float4 FogVal     : COLOR0;      //雾化因子,仅使用x分量

};

 

//--------------------------------------------------------------------------------------

// 顶点着色器处理程序

//--------------------------------------------------------------------------------------

VS_OUTPUT RenderSceneVS( const VS_INPUT Input)

{

         float4 clpPos, camPos, worldPos;

         float fDistance;

        

         // 初始化输出

         VS_OUTPUT ut = (VS_OUTPUT) 0;

        

         // 计算顶点剪切空间的坐标

         clpPos = mul(Input.Position, g_matWorldViewProj);

         Out.Position = clpPos;

        

         // 输出纹理坐标

         Out.TextureUV.xy = Input.TextureUV.xy;

        

         // 获得雾化参数

         float fFogTop   = g_FogParameter.x;

         float fFogEnd   = g_FogParameter.y;

         float fFogRange = g_FogParameter.z;

        

        

         // 计算顶点在世界坐标系中的位置

         worldPos = mul(Input.Position, g_matWorld);

        

         // 计算顶点和观测者之间的位置

         fDistance = distance(worldPos, g_vCamera);

        

         // factor = 1/sinθ * fDensityFog ,其中fDensityFog = 1/fFogRange;

         // 该值就是最后与deltaY相乘的系数,在一起计算,可以节省一次除法运算。

        

         float factor =fDistance/(fFogRange*(worldPos.y - g_vCamera.y));

      

         //fDeltaY 是经过雾层的线段在Y方向的距离,下面是分情况却定fDeltaY的代码 

         float fDeltaY ;

         if(g_vCamera.y > fFogTop)

              {

                            if (worldPos.y > fFogTop) //

                            {

                                 fDeltaY = 0.0f;

                            }

                            else

                            {

                                 if( worldPos.y > fFogEnd)//fFogEnd< worldPos.y <fFogTop

                                 {

                                     fDeltaY = fFogTop - worldPos.y;

                                 }

                                 else                      //worldPos.y< fFogEnd

                                 {

                                     fDeltaY = fFogTop - fFogEnd;

                                 }

                            }

              }

              else

              {

                       if( g_vCamera.y > fFogEnd)

                       {

                                     if (worldPos.y > fFogTop)

                                     {

                                         

                                          fDeltaY =fFogTop - g_vCamera.y;

                                     }

                                     else

                                     {

                                          if( worldPos.y > fFogEnd)//fFogEnd< worldPos.y <fFogTop

                                          {

                                               fDeltaY = worldPos.y - g_vCamera.y;

                                          }

                                          else                        //worldPos.y< fFogEnd

                                          {

                                               fDeltaY = fFogEnd -g_vCamera.y;

                                          }

                                     }

                       }

                       else//g_vCamera.y < fFogEnd

                       {

                                 if (worldPos.y > fFogTop)

                                     {

                                          fDeltaY = fFogTop - fFogEnd;

                                     }

                                     else

                                     {

                                          if( worldPos.y > fFogEnd) //fFogEnd< worldPos.y <fFogTop

                                          {

                                               fDeltaY =  worldPos.y - fFogEnd;

 

                                          }

                                          else                     //worldPos.y< fFogEnd

                                          {

                                               fDeltaY = 0.0f;

                                          }

                                     }

                       }

              }

         Out.FogVal.x = abs(factor*fDeltaY);

         return Out;

}

 

 

//--------------------------------------------------------------------------------------

// 象素着色器输出结构体

//--------------------------------------------------------------------------------------

struct PS_OUTPUT

{

    float4 RGBColor : COLOR0;  // 象素颜色 

};

struct PS_IUTPUT

{

    float2 TextureUV  : TEXCOORD0;  // 顶点纹理坐标

    float4  FogVal    : COLOR0;      //雾化系数

};

//--------------------------------------------------------------------------------------

// This shader outputs the pixel's color by modulating the texture's

//       color with diffuse material color

//--------------------------------------------------------------------------------------

PS_OUTPUT RenderScenePS( const PS_IUTPUT In)

{

  PS_OUTPUT Output;

 

  //获得纹理颜色

  Output.RGBColor = tex2D(MeshTextureSampler, In.TextureUV);

 

  //颜色混合

  float f = In.FogVal.x;

  Output.RGBColor = lerp(Output.RGBColor,g_FogColor,f);

  return Output;

}

posted on 2008-08-27 17:18 狂烂球 阅读(756) 评论(0)  编辑 收藏 引用 所属分类: 图形编程


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理