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

包含功能:

  • 鼠标点击产生扰动
  • 每帧做波动传播
  • 把结果给水材质显示
相关推荐
CG_MAGIC3 天前
3ds Max材质编辑器:精简模式与Slate模式对比
3d·编辑器·材质·贴图·uv·建模教程
玉夏3 天前
【Shader基础】UV 与纹理采样 Part1
unity·着色器·uv
魔士于安4 天前
Shader forge技术美术专用
游戏·unity·游戏引擎·贴图·技术美术·模型
郝学胜-神的一滴5 天前
[简化版 GAMES 101] 计算机图形学 13:从光栅化到着色——赋予三维像素光影灵魂
c++·计算机视觉·unity·godot·图形渲染·opengl·unreal
Strugglingler5 天前
【Qt,OpenGL, RHI,Wayland 等概念梳理】
qt·opengl·wayland·rhi·x11·egl·glx
魔士于安5 天前
unity 音乐会场景 unity2022
游戏·unity·游戏引擎·贴图·模型
auccy6 天前
Unity Sprite 添加法线贴图
unity·贴图·normal
mxwin6 天前
次世代角色 PBR 贴图制作 + Unity URP 接入 极简流程图
unity·流程图·贴图·shader
mxwin6 天前
Unity URP 法线贴图如何生成 用什么工具创建
unity·游戏引擎·贴图
BugShare6 天前
把「贴图 + OCR + 翻译 + 长截图 + 录屏」做到极致的截图软件—PixPin
ocr·贴图