引擎技术研究之水的渲染

本文介绍了一种实现水面倒影效果的技术方案,通过自定义water类并编写对应的shader,结合法线贴图和反射渲染目标,使得场景中的水体能够真实地反映周围环境。
部署运行你感兴趣的模型镜像

水的表面显示场景物体倒影的效果。这是把场景渲染到水面上来实现,不是简单的渲染,而要经过反射处理,即要反射渲染目标。

法线贴图是可以应用到3D表面的特殊纹理,有时也称凹凸纹理,它包括了每个像素的高度值,内含许多细节的表面信息,能够在平平无奇的物体上,创建出许多种特殊的立体外形。可以把法线贴图想像成与原表面垂直的点,所有点组成另一个不同的表面。对于视觉效果而言,它的效率比原有的表面更高,若在特定位置上应用光源,可以生成精确的光照方向和反射。

现具体讨论一下实现水效果的步骤:

一:设计一继承模型类的water类,并为其设计一shader文件,可在其构造函数里设置相应的shader文件,在water改写模型类的_Rend()函数,函数内对water进行特殊的渲染。

_Rend()具体操作如下:

先使用当前的shader变量设置纹理

m_pShader->SetTexture("BumpMap",m_pBumpmap);

BumpMapshader里与C++的接口

再使用IDirect3DDevice9接口设置四个渲染状态:

SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);

SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );//关闭alpha测试

SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );//打开alpha混合

最后调用模型类的渲染函数 _Render(),即调用Water_Render()

如此递归调用water的渲染函数使water的纹理和渲染状态即时更新,使水的效果随场景的变化而变化。

二:设计一监听器ReflectListener和相应的渲染目标mpReflectTarget

(参考“引擎技术之渲染到目标“)

其对应Execute()函数主要负责摄像机信息的设置,以实现水面的反射功能

先看一下水面反射图:

Execute()要设置摄像机的视点在水下对应位置和方向后进行物体的渲染,渲染后再设置为原来地面的视点和方向。

具体实现如下:

先获取当前摄像机的视点OldEye,获取水面高度WaterH,

则水下的摄像机视点NewEye为:OldEye 2 * OldEye WaterH

化简得:NewEye = 2 * WaterH OldEye

获取当前摄像机的方向 Direction ,则其在水面下方向为 Direction

设置摄像机新的视点和方向后,进行场景物体的渲染:scene->_Render();

渲染后再把摄像机设置为原来的视点和方向。

三:在场景类的渲染函数里,在物体渲染前执行:

渲染目标 mpReflectTarget->Render()

设置water的纹理 water->SetTexture(mpReflectTarget->GetTexture())

四:现讨论water对应的shader文件的编写。 有关shader的基本设置可参考

“引擎技术研究之shader技术“

1 VS_Scene的实现

VSOut OUT = (VSOut)0;

//矩阵变换

inPosition = mul( inPosition, World );

OUT.vPosition = mul( inPosition, View );

OUT.vPosition = mul( OUT.vPosition, Projection );

//计算水对应的屏幕坐标,从投影坐标到纹理坐标转换

float4 vHPos=OUT.vPosition;

vHPos.xy = -vHPos.xy;

float4 screenpos;

screenpos.xyz = (vHPos.xyz + vHPos.w)*0.5f;

screenpos.w = vHPos.w;

OUT.vProjCoord=screenpos;

//计算绕动纹理坐标

float wavescale=0.5;

float2 uv=inPosition.xz*0.01/wavescale;

float2 fTranslation=float2(0,1)*time*0.005;// float2(0,1) 水波流动的方向

OUT.Wave0.xy = uv + fTranslation*2.0;

OUT.Wave1.xy = uv*2.0 + fTranslation*4.0;

OUT.Wave2.xy = uv*4.0+fTranslation*2.0;

OUT.Wave3.xy = uv*8.0+fTranslation;

return OUT;

2 ComputeBumpTexCoord()函数的实现//为凹凸纹理计算纹理坐标

//vBumpTexA等是通过对应的Wave0即纹理坐标读取出来的像素

float3 vBumpTexA = tex2D(BumpSampler, IN.Wave0).xyz;

float3 vBumpTexB = tex2D(BumpSampler, IN.Wave1).xyz;

float3 vBumpTexC = tex2D(BumpSampler, IN.Wave2).xyz;

float3 vBumpTexD = tex2D(BumpSampler, IN.Wave3).xyz;

//vBumpTexA B C D用于计算最终的纹理坐标抖动量,用来采样反射贴图

float3 vBumpTex = 2*(vBumpTexA+vBumpTexB+vBumpTexC+vBumpTexD)/4

-float3(1,1,1);

float3 vReflBump = vBumpTex.xyz * float3(0.1, 0.1, 1.0);

return vReflBump.xy;

3 PS_Scene的实现

//为凹凸纹理计算纹理坐标

float2 bumpTexcoord=ComputeBumpTexCoord(IN);

//计算最终纹理坐标

float2 vProjUV = (IN.vProjCoord.xy/IN.vProjCoord.w);

float2 uv=float2(1,1)-vProjUV;

float4 color= tex2D(ColorSampler, uv + bumpTexcoord ).rgba;

color.a=0.6f;

return color;

效果图如下:

您可能感兴趣的与本文相关的镜像

ComfyUI

ComfyUI

AI应用
ComfyUI

ComfyUI是一款易于上手的工作流设计工具,具有以下特点:基于工作流节点设计,可视化工作流搭建,快速切换工作流,对显存占用小,速度快,支持多种插件,如ADetailer、Controlnet和AnimateDIFF等

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值