Directx11教程二十一之Water(水的实现)

这节教程的结构如下:




作者生成镜像的方法比D3D11龙书的那个方法方便,刚开始我是不懂作者的方法的,现在终于看懂了。

看懂这个教程的前提技术是:一,学会D3D11的渲染到纹理技术,D3D11教程十四之RenderToTexture(RTT技术)

                                                    二,学会D3D11的投影纹理技术,D3D11教程三十之ProjectiveTexturing(投影纹理)

                                                     


一,水的实现。

水的实现由几个技术点: 1,RTT技术
                                           2,投影纹理技术 
                                           3,纹理移动技术
                                           4,NormalMap(法线贴图)技术

这里我们在 D3D11教程十九之PlannarReflection(基于RTT技术和投影纹理技术)已经实现了如何实现水面的反射的,那么下面我们就讲一下如何实现水面(镜面)的折射。

折射,本质上就是透过水面看到水面下的物体,我们实现水面的折射,也跟 D3D11教程十九之PlannarReflection教程一样分为两步,

第一步,在观察相机下将水面的物体渲染到一张2D纹理上,形成折射纹理。(注意此时观察相机和投影相机是相同的,具体概念参考 D3D11教程三十之ProjectiveTexturing(投影纹理)。

第二步,渲染水面时,利用投影相机生成的相机变换矩阵(这里投影相机和观察相机是同一个相机)将水面的顶点坐标变换为折射纹理的纹理坐标系,具体概念参考 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分量调节水波纹这里我其实也不清楚,以后想清楚回来补充。


程序运行图:



我的源代码链接如下:

点击打开链接


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值