这节教程的结构如下:
作者生成镜像的方法比D3D11龙书的那个方法方便,刚开始我是不懂作者的方法的,现在终于看懂了。
看懂这个教程的前提技术是:一,学会D3D11的渲染到纹理技术,D3D11教程十四之RenderToTexture(RTT技术)
二,学会D3D11的投影纹理技术,D3D11教程三十之ProjectiveTexturing(投影纹理)
一,水的实现。
说说三个ShaderClass的作用:
(1),ColorShaderClass用来渲染普通的3D场景
(2),RefractionShaderClass用来将3D场景在某个透明平面的折射物渲染(到一个纹理上),其实跟ColorShaderClass差不多,就多了一个裁剪面(透明平面,折射面),毕竟折射生成的物体是位于折射面下面的。
,,裁剪面在 D3D11教程十六之ClipPlane(裁剪面)已经讲了,尤其注意SV_ClipDistance0关键字.
(3),WaterShaderClass利用折射纹理(承载折射物的2D纹理资源)和反射纹理(承载反射物的2D纹理资源) 以及利用相应的纹理作用来生成水体折射和反射的现象,注意水体流动的效果是通过纹理移动技术(Texture Translation),而水体波纹是通过NormalMap
放出三个Shader文件的代码:
ColorShader.fx
Texture2D ShaderTexture:register(t0); //纹理资源
SamplerState SampleType:register(s0); //采样方式
//VertexShader
cbuffer CBMatrix:register(b0)
{
matrix World;
matrix View;
matrix Proj;
matrix WorldInvTranspose;
};
cbuffer CBLight:register(b1)
{
float4 AmbientColor;
float4 DiffuseColor;
float3 LightDirection;
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 LightFactor; //灯光因子
float4 color = {0.0f,0.0f,0.0f,0.0f}; //最终输出的颜色
//第一,获取采样颜色
TexColor = ShaderTexture.Sample(SampleType, outa.Tex);
color = AmbientColor;
//第二,求出灯光因子
float3 InvLightDir = -LightDirection;
LightFactor = saturate(dot(InvLightDir,outa.W_Normal));
//第三,求出灯光照射颜色
if (LightFactor>0)
{
color += LightFactor*DiffuseColor; //saturate(float1*float4)
}
color = saturate(color);
//第四,用灯光颜色调节纹理颜色
color = color*TexColor;
return color;
}
Refraction.fx
Texture2D ShaderTexture:register(t0); //纹理资源
SamplerState SampleType:register(s0); //采样方式
//VertexShader
cbuffer CBMatrix:register(b0)
{
matrix World;
matrix View;
matrix Proj;
matrix WorldInvTranspose;
};
cbuffer CBLight:register(b1)
{
float4 AmbientColor;
float4 DiffuseColor;
float3 LightDirection;
float pad;
}
cbuffer CBClipPlane:register(b2)
{
float4 clipPlane;
}
struct VertexIn
{
float3 Pos:POSITION;
float2 Tex:TEXCOORD0; //多重纹理可以用其它数字
float3 Normal:NORMAL;
};
struct VertexOut
{
float4 Pos:SV_POSITION;
float2 Tex:TEXCOORD0;
float3 W_Normal:NORMAL; //世界空间的法线
float Clip: ; //裁剪值,小于0的被裁剪
};
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;
//根据裁剪面求出裁剪值,得分清楚离散乘法和点乘
outa.Clip = dot(mul(float4(ina.Pos, 1.0f), World), clipPlane);
return outa;
}
float4 PS(VertexOut outa) : SV_Target
{
float4 TexColor; //采集的纹理颜色
float LightFactor; //灯光因子
float4 color ; //最终输出的颜色
color = AmbientColor;
//第一,获取采样颜色
TexColor = ShaderTexture.Sample(SampleType, outa.Tex);
color = TexColor;
//第二,求出灯光因子
float3 InvLightDir = -LightDirection;
LightFactor = saturate(dot(InvLightDir,outa.W_Normal));
//第三,求出灯光照射颜色
if (LightFactor>0)
{
color += LightFactor*DiffuseColor; //saturate(float1*float4)
}
color = saturate(color);
//第四,用灯光颜色调节纹理颜色
color = color*TexColor;
return color;
}
Water.fx
Texture2D ReflectionTexture:register(t0); //反射纹理资源,打印有3D模型的纹理
Texture2D RefractionTexture:register(t1); //纹理
Texture2D NormalTexture:register(t2); //法线贴图
SamplerState SampleType:register(s0); //采样方式
//VertexShader
cbuffer CBMatrix:register(b0)
{
matrix WorldMatrix;
matrix ViewMatrix;
matrix ProjMatrix;
};
cbuffer CBReflectMatrix:register(b1)
{
matrix ReflectMatrix;
}
cbuffer CBWater : register(b2)
{
float waterTranslation; //水体移动系数
float reflectRefratScale; //反射 折射缩放系数
float2 pad; //填充系数
}
struct VertexIn
{
float3 Pos:POSITION;
float2 Tex:TEXCOORD0; //多重纹理可以用其它数字
};
struct VertexOut
{
float4 Pos:SV_POSITION;
float2 Tex:TEXCOORD0;
float4 ReflectPostion:TEXCOORD1;
float4 RefractionPostion:TEXCOORD2;
};
VertexOut VS(VertexIn ina)
{
VertexOut outa;
matrix ReflectProjWorldMatrix;
matrix ViewProjWorldMatrix;
//第一,将镜面(反射)变换到CVV空间
outa.Pos = mul(float4(ina.Pos,1.0f), WorldMatrix); //移动到y=-1.5的面
outa.Pos = mul(outa.Pos, ViewMatrix);
outa.Pos = mul(outa.Pos, ProjMatrix);
//第二,计算纹理
outa.Tex= ina.Tex;
/*第三,计算反射的坐标*/
//计算反色的变换矩阵 ReflectProjWorldMatrix =WorldMatrix*ReflectMatrix* ProjMatrix;
ReflectProjWorldMatrix = mul(WorldMatrix, ReflectMatrix);
ReflectProjWorldMatrix = mul(ReflectProjWorldMatrix,ProjMatrix);
//变换输入的顶点到反射现象下的投影位置(CVV空间),变换的位置坐标将在PixelShader里用来推到我们投影反射纹理所在的地方
outa.ReflectPostion= mul(float4(ina.Pos, 1.0f), ReflectProjWorldMatrix);
/*第四,计算折射的坐标*/
ViewProjWorldMatrix = mul(WorldMatrix, ViewMatrix);
ViewProjWorldMatrix = mul(ViewProjWorldMatrix, ProjMatrix);
outa.RefractionPostion = mul(float4(ina.Pos, 1.0f), ViewProjWorldMatrix);
return outa;
}
float4 PS(VertexOut outa) : SV_Target
{
float2 ReflectTex; //反射纹理坐标
float2 RefractTex; //折射纹理坐标
float4 ReflectColor; //反射纹理像素颜色
float4 RefractColor; //折射纹理像素颜色
float4 NormalMap; //法线纹理提取值
float3 Normal; //法线纹理像素值所指的法向量
float4 color = {0.0f,0.0f,0.0f,0.0f}; //最终输出的颜色
//第一,移动基础纹理坐标,其实就是每个像素提取的法向量不断改变
outa.Tex.y += waterTranslation;
//第二,计算反射纹理D3D11齐次裁剪空间(-w=<x=<w,-w=<y=<w,0=<z<=w)的三角形中像素坐标对应的纹理值
ReflectTex.x = (outa.ReflectPostion.x /outa.ReflectPostion.w /2.0f) + 0.5f;
ReflectTex.y= (-outa.ReflectPostion.y/ outa.ReflectPostion.w /2.0f) + 0.5f;
//第三,计算折射纹理D3D11齐次裁剪空间(-w=<x=<w,-w=<y=<w,0=<z<=w)的三角形中像素坐标对应的纹理值
RefractTex.x =( outa.RefractionPostion.x /outa.RefractionPostion.w /2.0f) + 0.5f;
RefractTex.y= (-outa.RefractionPostion.y/ outa.RefractionPostion.w /2.0f) + 0.5f;
//第四,获取法线纹理的上面的法向量(这个的切线空间和世界空间一致,因为为与Y平面平行的水面的像素的法向量)
NormalMap = NormalTexture.Sample(SampleType, outa.Tex);
Normal = NormalMap.xyz*2.0f - 1.0f; //别忘记这一步
//第五,通过重定位纹理采样坐标来模拟水体波纹效果
ReflectTex = ReflectTex+(Normal.xy*reflectRefratScale);
RefractTex = RefractTex+(Normal.xy*reflectRefratScale);
//第六,对反射纹理和折射纹理进行采样
ReflectColor= ReflectionTexture.Sample(SampleType, ReflectTex);
RefractColor= RefractionTexture.Sample(SampleType, RefractTex);
//第七,在反射纹理像素颜色和折射纹理颜色中进行插值(就像ID3D11BlendState那样的混合)
color = lerp(ReflectColor, RefractColor, 0.5f);
return color;
}
还有一点就是: 法向量的XY分量调节水波纹这里我其实也不清楚,以后想清楚回来补充。
程序运行图:
我的源代码链接如下: