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

包含功能:

  • 鼠标点击产生扰动
  • 每帧做波动传播
  • 把结果给水材质显示
相关推荐
灵境(虚幻知音)5 小时前
WebGL 不支持>1px的线绘制,那 Cesium 的带宽度的线是怎么画出来的?
webgl·cesium·着色器
Rina GO1 天前
PS怎么把文字贴在圆柱体上?2种实用方法教程附不规则物体贴图
人工智能·贴图·ps插件·不规则物体贴图·文字贴图·ai贴图
又吹风_Bassy2 天前
WhatsCanvas教程-第三章:画笔 Paint 详解
canvas·opengl·skia·2d渲染库·nanovg·whatscanvas·2d图形库
黄一一9112432 天前
告别手动敲字,本地截图 OCR,识别完直接复制!开源截图识别工具
windows·ocr·开源软件·贴图
又吹风_Bassy2 天前
WhatsCanvas教程-第二章:基础图形绘制
canvas·opengl·skia·2d渲染库·nanovg·whatscanvas·2d图形库
郝学胜-神的一滴2 天前
CMake 047:解锁安装阶段自定义操作,告别配置阶段提前执行坑
运维·服务器·c++·游戏引擎·图形渲染·opengl
sunphp开发者3 天前
明灯智慧地图导览系统制作教程
vue·php·uniapp·js·贴图
唯_ww3 天前
3ds max导入场景材质
3dsmax·贴图
令狐掌门6 天前
OpenGL是个状态机,如何理解
opengl
2601_9628601510 天前
AI生成3D模型后还能继续编辑、贴图和绑定吗?从网格、材质到自动绑骨的处理流程
人工智能·3d·贴图