这节来实现最简单的平面反射,先看看本届教程的结构吧:
这个教程理解原理前得学会两项技术:一,渲染到纹理技术(RTT) D3D11教程十四之RenderToTexture(RTT技术)
二,投影纹理技术 D3D11教程三十之ProjectiveTexturing(投影纹理)
第一,平面反射的简介和实现。
这节平面反射的方式跟D3D11龙书的方法不一样,作者并没有用上depthBuffer和stencilBuffer那种方法,作者用了求相机关于反射平面的反射点的相机变换矩阵。
上图:
方法思路是这样的:
第一步, 求出观察相机关于反射平面的对称的相机,这个对称面的相机其实就是D3D11教程三十之ProjectiveTexturing(投影纹理) 那个教程所说的投影相机, 利用RTT技术所得到的2D纹理为反射纹理,反射纹理的像素是在反射面上的,此时反射面相当于视窗口(ViewPort),这里要注意的是反射纹理的初始颜色设为黑色以便不影响到反射面的混合。
第二步,利用投影相机变换矩阵,求出 反射面在投影相机下的纹理坐标。这里用的是投影技术。
//产生反射相机矩阵,height为相机,计算与投影面对称下的相机的位置的相机变换矩阵 构建 y= height为反射面,针对Y轴平面进行反射的
void RenderReflect(float height);
void CameraClass::RenderReflect(float height)
{
//上向量,位置向量,观察向量
XMVECTOR Up, Postion, LookAt;
float yaw, pitch, roll; //欧拉角
//设置上向量
Up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
//设置相机的位置,与原来的相机位置是关于反射面对称的
Postion = XMVectorSet(mPostionX, -mPostionY+2* height, mPostionZ,1.0f);
//设置相机默认情况下看哪的
LookAt = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f);
//设置 yaw(Y轴), pitch(X轴), roll(Z轴),为弧度值
pitch = mRotationX*0.0174532925f;
yaw = mRotationY*0.0174532925f;
roll = mRotationZ*0.0174532925f;
//创建旋转矩阵,并旋转局部空间相机的Up向量和LookAt向量
XMMATRIX RotateMatrix = XMMatrixRotationRollPitchYaw(pitch, yaw, roll);
LookAt = XMVector3TransformCoord(LookAt, RotateMatrix);
Up = XMVector3TransformCoord(Up, RotateMatrix);
//将相机从局部模型空间变为世界空间
LookAt = LookAt + Postion;
mReflecMatirx= XMMatrixLookAtLH(Postion, LookAt, Up);
}
这里非常奇特的是,作者先把3D立方体渲染到一个与屏幕大小的自创建的纹理上,就是RenderToTexture(可参考)
D3D11教程十四之RenderToTexture,然后作者在把反射面和这个纹理混合在一起,最终得到一个有反射立方体的反射面
shader代码如下:
Texture2D ShaderTexture:register(t0); //纹理资源
Texture2D ReflectionTexture:register(t1); //反射纹理资源,打印有3D模型的纹理
SamplerState SampleType:register(s0); //采样方式
//VertexShader
cbuffer CBMatrix:register(b0)
{
matrix WorldMatrix;
matrix ViewMatrix;
matrix ProjMatrix;
};
cbuffer CBReflectMatrix:register(b1)
{
matrix ReflectMatrix;
}
struct VertexIn
{
float3 Pos:POSITION;
float2 Tex:TEXCOORD0; //多重纹理可以用其它数字
};
struct VertexOut
{
float4 Pos:SV_POSITION;
float2 Tex:TEXCOORD0;
float4 ReflectPostion:TEXCOORD1;
};
VertexOut VS(VertexIn ina)
{
VertexOut outa;
matrix ReflectProjWorldMatrix;
//将镜面(反射)变换到齐次裁剪空间
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);
return outa;
}
float4 PS(VertexOut outa) : SV_Target
{
float4 TexColor; //(反射面)基础纹理颜色
float4 ReflectTextureColor; //(承载反射3D模型)纹理的颜色
float2 ReflectTex;
float4 color = {0.0f,0.0f,0.0f,0.0f}; //最终输出的颜色
//第一,获取反射面的采样颜色
TexColor = ShaderTexture.Sample(SampleType, outa.Tex);
//第二,计算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;
//第三,使用投影纹理坐标系从反射纹理中采集像素值
ReflectTextureColor = ReflectionTexture.Sample(SampleType, ReflectTex);
//第四,在底面基础纹理颜色和反射纹理进行线性插值
color = lerp(TexColor, ReflectTextureColor, 0.6f);
return color;
}</span>
我先在还是看不到原理是怎么样的,等以后看懂在回来修改吧,不过我还是推荐D3D11龙书那种用stencilbuffer方法的,通俗易懂,过几天我在做个D3D11
那种方法的
程序运行图:
另外在说下:HLSL的lerp的作用
lerp(x, y, s):return Returns x + s(y - x); s范围[0, 1]
也就是 (1-s)*x+sy,跟ID3D11BlendState在某些方面有异曲同工之妙
我的源代码链接如下: