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

包含功能:

  • 鼠标点击产生扰动
  • 每帧做波动传播
  • 把结果给水材质显示
相关推荐
CAD开发小慧3 天前
CAE软件开发:Coreform为何用ACIS处理CAD几何?
算法·架构·计算机图形学
ly_Enhs3 天前
从曲面细分到计算着色器:我的 Vulkan 实验记录
着色器·vulkan图形渲染c/c++
扶尔魔ocy4 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行并进行态势标绘(四)
qt·opengl·osg
:mnong5 天前
Web 三维可视化技术对比:ParaView Web vs 本项目 wasm + Three.js
前端·webgl·opengl·opencl·科学可视化
刻BITTER5 天前
LocalLow 乱码文件排查 Intel INSC着色器缓存文件
缓存·哈希算法·着色器
扶尔魔ocy6 天前
【qt openGL入门笔记】OSG实现显示飞机模型飞行的点迹(二)
qt·opengl·osg
扶尔魔ocy6 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行(三)
opengl·osg
扶尔魔ocy7 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行
qt·opengl·osg
扶尔魔ocy11 天前
【qt openGL入门笔记】FPS相机
qt·opengl
扶尔魔ocy11 天前
qt openGL入门笔记】FPS相机和轨道相机
qt·opengl