Fog
接下来讨论的几种effects都与texture cubes不相关,但是可以帮助学习更多不一样的图形学技术。第一个要讨论的是fog,随着object到camera的距离不断增加,这种effect会让object逐渐模糊直到完全消失在背景色中。
可以使用一种颜色,距离camera的一段距离来模块fog effect,这段距离用于表示从多远的距离开始产生fog,以及fog能够延伸到多远直到object的颜色完全由fog的颜色占满了。要把最终的颜色应用到一个object上,首先要确定颜色中fog所占的比例以及正常光照所占的比例。与environment mapping一样,该过程也是使用线性插值,可以使用下面的公式来得到用于插值计算的scale值:
其中,V是从camera到表面的view vector。然后就可以使用下面的方法插值计算最终的颜色:
在pixel shader中可以使用任何光照模型计算litColor值,最后再执行flog插值。列表8.3中列出了一个小型的flog effect的代码。
列表8.3 Abbreviated Fog Effect
#include "include\\Common.fxh"
/************* Resources *************/
cbuffer CBufferPerFrame
{
float4 AmbientColor : AMBIENT <
string UIName = "Ambient Light";
string UIWidget = "Color";
> = {1.0f, 1.0f, 1.0f, 0.0f};
float4 LightColor : COLOR <
string Object = "LightColor0";
string UIName = "Light Color";
string UIWidget = "Color";
> = {1.0f, 1.0f, 1.0f, 1.0f};
float3 LightDirection : DIRECTION <
string Object = "DirectionalLight0";
string UIName = "Light Direction";
string Space = "World";
> = {0.0f, 0.0f, -1.0f};
float3 FogColor <
string UIName = "Fog Color";
string UIWidget = "Color";
> = {0.5f, 0.5f, 0.5f};
float FogStart = { 20.0f };
float FogRange = { 40.0f };
float3 CameraPosition : CAMERAPOSITION < string UIWidget="None"; >;
}
cbuffer CBufferPerObject
{
float4x4 WorldViewProjection : WORLDVIEWPROJECTION < string UIWidget="None"; >;
float4x4 World : WORLD < string UIWidget="None"; >;
float4 SpecularColor : SPECULAR <
string UIName = "Specular Color";
string UIWidget = "Color";
> = {1.0f, 1.0f, 1.0f, 1.0f};
float SpecularPower : SPECULARPOWER <
string UIName = "Specular Power";
string UIWidget = "slider";
float UIMin = 1.0;
float UIMax = 255.0;
float UIStep = 1.0;
> = {25.0f};
}
Texture2D ColorTexture <
string ResourceName = "default_color.dds";
string UIName = "Color Texture";
string ResourceType = "2D";
>;
SamplerState ColorSampler
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
RasterizerState DisableCulling
{
CullMode = NONE;
};
/************* Data Structures *************/
struct VS_INPUT
{
float4 ObjectPosition : POSITION;
float2 TextureCoordinate : TEXCOORD;
float3 Normal : NORMAL;
};
struct VS_OUTPUT
{
float4 Position : SV_Position;
float3 Normal : NORMAL;
float2 TextureCoordinate : TEXCOORD0;
float3 LightDirection : TEXCOORD1;
float3 ViewDirection : TEXCOORD2;
float FogAmount: TEXCOORD3;
};
/************* Utility Functions *************/
float get_fog_amount(float3 viewDirection, float fogStart, float fogRange)
{
return saturate((length(viewDirection) - fogStart) / (fogRange));
}
/************* Vertex Shader *************/
VS_OUTPUT vertex_shader(VS_INPUT IN, uniform bool fogEnabled)
{
VS_OUTPUT OUT = (VS_OUTPUT)0;
OUT.Position = mul(IN.ObjectPosition, WorldViewProjection);
OUT.TextureCoordinate = get_corrected_texture_coordinate(IN.TextureCoordinate);
OUT.Normal = normalize(mul(float4(IN.Normal, 0), World).xyz);
OUT.LightDirection = normalize(-LightDirection);
float3 worldPosition = mul(IN.ObjectPosition, World).xyz;
float3 viewDirection = CameraPosition - worldPosition;
OUT.ViewDirection = normalize(viewDirection);
if (fogEnabled)
{
OUT.FogAmount = get_fog_amount(viewDirection, FogStart, FogRange);
}
return OUT;
}
/************* Pixel Shader *************/
float4 pixel_shader(VS_OUTPUT IN, uniform bool fogEnabled) : SV_Target
{
float4 OUT = (float4)0;
float3 normal = normalize(IN.Normal);
float3 viewDirection = normalize(IN.ViewDirection);
float4 color = ColorTexture.Sample(ColorSampler, IN.TextureCoordinate);
float3 ambient = get_vector_color_contribution(AmbientColor, color.rgb);
LIGHT_CONTRIBUTION_DATA lightContributionData;
lightContributionData.Color = color;
lightContributionData.Normal = normal;
lightContributionData.ViewDirection = viewDirection;
lightContributionData.LightDirection = float4(IN.LightDirection, 1);
lightContributionData.SpecularColor = SpecularColor;
lightContributionData.SpecularPower = SpecularPower;
lightContributionData.LightColor = LightColor;
float3 light_contribution = get_light_contribution(lightContributionData);
OUT.rgb = ambient + light_contribution;
OUT.a = 1.0f;
if (fogEnabled)
{
OUT.rgb = lerp(OUT.rgb, FogColor, IN.FogAmount);
}
return OUT;
}
technique10 fogEnabled
{
pass p0
{
SetVertexShader(CompileShader(vs_4_0, vertex_shader(true)));
SetGeometryShader(NULL);
SetPixelShader(CompileShader(ps_4_0, pixel_shader(true)));
SetRasterizerState(DisableCulling);
}
}
technique10 fogDisabled
{
pass p0
{
SetVertexShader(CompileShader(vs_4_0, vertex_shader(false)));
SetGeometryShader(NULL);
SetPixelShader(CompileShader(ps_4_0, pixel_shader(false)));
SetRasterizerState(DisableCulling);
}
}
Fog Preamble
CBufferPerFrame部分新增了FogColor,FogStart,FogRange变量。VS_OUPUT结构体中包含了一个用于表示fog插值的float类型变量FogAmout。其中FogStart和FogRange的值由CPU端的应用程序指定
Fog Vertex and Pixel Shader
在vertex shader和pixel shader中都包含了uniform参数fogEnabled,这两个fogEnabled都由fogEnabled和fogDisable techniques设定。如果设置为允许fog,vertex shader就会调用通用函数get_fog_amount()计算FogAmount值。该函数get_fog_amount()应该放到Common.fxh文件中,以便其他的effects调用。
Pixel shader主要是计算object的光照颜色,再把光照颜色与fog颜色进行插值得到最终的pixel颜色值。
Fog Output
图8.7是把fog effect应用到一个sphere上的输出结果,fog的start值为5.0,range值为10.0,颜色为灰色(0.5, 0.5, 0.5)。其中有一个directional light和一个中等强度(强度值为0.5)的specular highlight照射到sphere上。最上面的图片表示sphere到camera的距离比fog开始生效的距离更近,因此,fog的颜色对sphere没有影响。中间的图,以及最下面的图表示随着sphere object距离camera越来越远,fog color的作用就越发明显。
8.7 Fog.fx applied to a sphere with a texture of Earth, with a fog start of 5.0, a fog
range of 10.0, and a gray fog color. The three images show the object at progressively farther
distances from the camera (top to bottom). (Original texture from Reto Stöckli, NASA Earth
Observatory. Additional texturing by Nick Zuccarello, Florida Interactive Entertainment
Academy.)