ShaderLab:可互动水面(基于RenderTexture,实时生成动态扰动)

本篇文章是一个基于URP的可交互水面系统,实现了实时渲染水面扰动,使用了三缓冲RT交换用于实现水面波纹扩散效果。

上一篇文章实现了在无扰动理想情况下的程序化生成海面顶点数据,然而这种水面是在理想情况下,现实中水面不会平静也不会只有波浪,还有对系统施加外力情况下的水面扰动。比如,石子被投入水中、小船在水面航行等。这种情况下就不仅仅需要程序化计算顶点位置,还需要计算扰动并叠加。

代码分享:https://pan.baidu.com/s/1-VApXIrHxRjCthW2OYUMmA?pwd=9527https://pan.baidu.com/s/1-VApXIrHxRjCthW2OYUMmA?pwd=9527代码学习、参考:神奇的小阿飞https://space.bilibili.com/31839293/?spm_id_from=333.788.upinfo.detail.clickhttps://www.bilibili.com/video/BV1Xh411D7z7/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=e72728138018176c10ef41188bea0c35https://www.bilibili.com/video/BV1Xh411D7z7/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=e72728138018176c10ef41188bea0c35

效果预览

代码分析

本项目包含四个文件:

  • DrawShader.shader: 用于绘制鼠标输入点的水波扰动逻辑,以及扰动叠加逻辑
  • RippleShader.shader: 用于处理水波扩散逻辑
  • WaterShader.shader: 用于计算水质感
  • Ripple.cs: 用于处理输入相关逻辑以及shader脚本调用逻辑

DrawShader.shader

着色器功能:在一张已有纹理上绘制一个可扩散的圆形涟漪效果。在_SourceTex上叠加一个"环形渐变"的亮度ripple,位置和半径由_Pos控制。

结构体:appdata------顶点着色器输入结构,v2f------顶点输出结构。

核心公式(一个平滑的环形函数):

float ripple = pow(saturate(0.05 - abs(length(uv - _Pos.xy) - _Pos.z)), 2.0);

叠加到原图:

return SAMPLE_TEXTURE2D(_SourceTex, sampler_SourceTex, uv).r + ripple * 0.8;

SAMPLE_TEXTURE2D(_SourceTex, sampler_SourceTex, uv).r采样源图并返回r通道值。

RippleShader.shader

着色器功能:二维波动方程(水波/高度场)离散求解器。

使用经典Ping-Pong Simulation双缓冲模拟,_CurrentTex:当前帧,_PreviousTex:上一帧,输出:下一帧

cs 复制代码
half4 frag(v2f i) : SV_Target
{
    float2 e = _CurrentTex_TexelSize.xy;
    float2 uv = i.uv;

    // 这里的 up/down/left/right 是相对于当前像素的四个邻居,而 center 是当前像素在上一帧的值
    float up = SAMPLE_TEXTURE2D(_CurrentTex, sampler_CurrentTex, uv + float2(0, e.y)).r;
    float down = SAMPLE_TEXTURE2D(_CurrentTex, sampler_CurrentTex, uv - float2(0, e.y)).r;
    float left = SAMPLE_TEXTURE2D(_CurrentTex, sampler_CurrentTex, uv - float2(e.x, 0)).r;
    float right = SAMPLE_TEXTURE2D(_CurrentTex, sampler_CurrentTex, uv + float2(e.x, 0)).r;
    float center = SAMPLE_TEXTURE2D(_PreviousTex, sampler_PreviousTex, uv).r;
 
    float c = (up + down + left + right) * 0.5f - center;
    c *= 0.99f;
    return c;
}

对当前像素的当前帧进行四领域采样,以及前一帧的中心像素采样,核心更新公式:

float c = (up + down + left + right) * 0.5f - center;

因为在波的传输过程中是有能量损失的,所以计算出来的下一帧波的高度应该有一定的损失程度,这里按每次损失1%,即c *= 0.99。

WaterShader.shader

着色器功能:用高度图(_RippleTex)驱动几何+法线+反射/折射,渲染动态水面。它做了三件事情:

  • 顶点位移(水面波形):水面真实的"起伏"
  • 法线重建(光照基础):根据高度图采样计算法线
  • 光照(真实感来源):反射 + 折射 + 菲涅尔 + 高光

数据流:RippleTex(高度图) -> 顶点位移 -> 法线计算 -> 光照计算 -> 最终水面

在顶点着色器阶段不能使用函数SAMPLE_TEXTURE2D函数读取贴图,因为在这种情况下需要计算贴图的minmap,但是顶点阶段不能用自动LOD,所以会报错。这时候可以用SAMPLE_TEXTURE2D_LOD指定其minmap等级然后获取贴图。

这个shader就是普通的画面表现shader。

Ripple.cs

在Unity中实现一个基于高度场的水波模拟+鼠标交互扰动,实际上是一个在纹理上做物理模拟的系统,而不是直接操作网格。

使用三张RT做模拟,PreviousRT、CurrentRT、TempRT

包含功能:

  • 鼠标点击产生扰动
  • 每帧做波动传播
  • 把结果给水材质显示
相关推荐
爱看书的小沐1 天前
【小沐学GIS】基于C++渲染三维飞行仿真Flight Simulation(OpenGL )第十三期
c++·qt·webgl·opengl·飞行仿真·flight
郑寿昌3 天前
UE5与UE6在Lumen和Nanite的差异解析
游戏引擎·图形渲染·着色器
魔士于安3 天前
Unity完整小球迷宫项目
前端·unity·游戏引擎·贴图·模型
threelab3 天前
Three.js 动态旋转同心圆着色器 | 三维可视化效果
开发语言·javascript·着色器
魔士于安4 天前
Unity 超市总动员 超市收银台 超市货架 超市购物手推车 超市常见商品
游戏·unity·游戏引擎·贴图·模型
♡すぎ♡4 天前
ShaderLab:海面——顶点变换,程序化生成无需贴图
计算机图形学·opengl·着色器
魔士于安5 天前
Unity windows 同步 异步 打开文件文件夹工具
游戏·unity·游戏引擎·贴图·模型
魔士于安5 天前
unity lowpoly 风格 城市 建筑 道路 交通标志
游戏·unity·游戏引擎·贴图·模型
魔士于安6 天前
Unity UI图片 复活节UI,卡通风格
游戏·ui·unity·游戏引擎·材质·贴图