这个教程建立在
关于ShadowMappingAndTransparency的一个经典的例子,就是有关于如何投影树的叶子在地面形成影子的,程序的结构如下:
树由树躯干和树叶两部分组成,树叶的纹理如下面图所示:
一般来说纹理像素的alpha值默认为1.0f, 但像这样的树叶纹理,感觉绿色网格部分的像素alpha值不等于1.0f.
树干的纹理如下:
按照一般情况下的渲染出的结果如图所示:
这里可以明显的看出两个问题:
一,树叶是成片成片的,很难看
二,影子也是成片成片的,按现实中来看,树叶的阴影应该存在光亮的间隙。
第一个问题呢,我们可以通过在绘制树叶时,在PixelShader使用HLSL指令中的Clip或者discard,只要判断纹理像素的alpha值的范围,也就是LeafColor.a<0.8,就能剔除掉那些透明的像素,shader代码如下所示:
ColorShader.fx
Texture2D BaseTexture:register(t0); //基础纹理
SamplerState WrapSampleType:register(s0); //采样方式
//VertexShader
cbuffer CBMatrix:register(b0)
{
matrix World;
matrix View;
matrix Proj;
matrix WorldInvTranspose;
};
cbuffer CBLight:register(b1)
{
float4 DiffuseColor;
float4 AmbientColor;
float3 ParallelDir;
float pad; //填充系数
}
struct VertexIn
{
float3 Pos:POSITION;
float2 Tex:TEXCOORD0; //多重纹理可以用其它数字
float3 Normal:NORMAL;
};
struct VertexOut
{
float4 Pos:SV_POSITION;
float2 Tex:TEXCOORD0;
float3 W_Normal:NORMAL; //世界空间的法线
};
VertexOut VS(VertexIn ina)
{
VertexOut outa;
//将坐标变换到观察相机下的齐次裁剪空间
outa.Pos = mul(float4(ina.Pos, 1.0f), World);
outa.Pos = mul(outa.Pos, View);
outa.Pos = mul(outa.Pos, Proj);
//将顶点法向量由局部坐标变换到世界坐标
outa.W_Normal = mul(ina.Normal, (float3x3)WorldInvTranspose); //此事世界逆转置矩阵的第四行本来就没啥用
//对世界空间的顶点法向量进行规格化
outa.W_Normal = normalize(outa.W_Normal);
//获取纹理坐标
outa.Tex = ina.Tex;
return outa;
}
float4 PS(VertexOut outa) : SV_Target
{
float4 TexColor; //采集基础纹理颜色
float DiffuseFactor;
float4 DiffuseLight;
float4 color = { 0.0f,0.0f,0.0f,0.0f }; //最终输出的颜色
//获取基础纹理的采样颜色
TexColor = BaseTexture.Sample(WrapSampleType, outa.Tex);
//如果纹理图像素alpha是小于0.8的,则丢弃像素,这个主要是为了绘制树叶准备的,是a而不是r
if (TexColor.a <0.8)
{
discard;
}
//不管有没有遮挡,都应该具备环境光,注意环境光不生成阴影,这里仅仅是漫反射光生成阴影
color = AmbientColor;
//规格化漫反射光的的方向
float3 DiffuseDir = normalize(ParallelDir);
//求漫反射光的反光向
float3 InvseDiffuseDir = -DiffuseDir;
//求出漫反射因子[0.0,1.0]
DiffuseFactor = saturate(dot(InvseDiffuseDir,outa.W_Normal));
//求出漫射光
DiffuseLight = DiffuseFactor*DiffuseColor;
//颜色加上漫反射光
color += DiffuseLight;
color = saturate(color);
color = color*TexColor;
return color;
}
程序运行图如下:
这时候第一个问题解决了,下面来解决阴影的问题,其实阴影的问题也很简单,我们在前面教程知道DepthShader是用来渲染得到ShadowMap,我们可以在DepthShader改进,增加剔除树叶纹理中透明的像素的功能,也就是不让透明纹理像素对应的树叶的顶点的高度进入ShadowMap, 为了和DepthShader区别起来,我们命名为TransparentDepthShader,下面给出代码:
TransparentDepthShader.fx
Texture2D BaseTexture:register(t0); //基础纹理
SamplerState WrapSampleType:register(s0); //采样方式
//VertexShader
cbuffer CBMatrix:register(b0)
{
matrix World;
matrix View;
matrix Proj;
};
struct VertexIn
{
float3 Pos:POSITION;
float2 Tex:TEXCOORD0; //多重纹理可以用其它数字
float3 Normal:NORMAL;
};
struct VertexOut
{
float4 Pos:SV_POSITION;
float4 ProjPos:POSITION;
float2 Tex:TEXCOORD0;
};
VertexOut VS(VertexIn ina)
{
VertexOut outa;
//将坐标变换齐次裁剪空间
outa.Pos = mul(float4(ina.Pos,1.0f), World);
outa.Pos = mul(outa.Pos, View);
outa.Pos = mul(outa.Pos, Proj);
outa.ProjPos = outa.Pos;
outa.Tex = ina.Tex;
return outa;
}
float4 PS(VertexOut outa) : SV_Target
{
float4 TexColor;
//获取基础纹理的采样颜色
TexColor = BaseTexture.Sample(WrapSampleType, outa.Tex);
//如果TexColor的alpha值小于0.8,则丢弃像素,这样ShadowMap对应位置的深度依然为1.0,也就是没有阴影,(感觉用clip函数也行)
if (TexColor.a < 0.8f)
{
discard;
}
return TexColor;
}
最后的程序运行图为:
这样在显示效果我们就用了较少的面数的树叶做出较多面数的树叶的效果,当然最后要注意的是我们用的光源类型为太阳,也就是平行光,感觉大自然树的阴影更多的是太阳光成的阴影吧。
下面放出我的源代码链接:
http://download.csdn.net/detail/qq_29523119/9674772