开源项目LiquidEffect渲染分析

  • 项目传送门
  • 实现流程
  • 步骤一 创建刚体
  • 步骤二 降采样与模糊处理
    • 降采样分析
      • C#部分
      • 顶点着色器
      • 片元着色器
      • 分析
    • 模糊分析
  • 步骤三 Alpha过滤

项目传送门

GitHub地址

实现流程

一共三个步骤:

  1. 创建若干个圆形Sprite作为“液体粒子”,每个“粒子”都是一个Dynamic的RigidBody2D,并赋予合适的物理材质和2D Collider
  2. 利用Blur着色器将所有的液体粒子图像模糊化并渲染到一张RenderTexture上
  3. 将RenderTexture使用UnlitMod Shader(选择Alpha值在一定范围内的像素渲染成同一颜色)渲染到一个片儿上。

步骤一 创建刚体

作者用了一个比较讨巧的方式来模拟水,使用大量带物理属性的小球来模拟流体的流动,只要“粒子”足够多,足够小,就可以拟态流体
先看看他的场景里都有什么

  • Scene节点下存放平台黑色阻挡物体
  • Effect Camera为特效处理摄像机
    • 设置了Culling Mask,只观察小球
    • 设置了Target Texture,将渲染结果输出到一张RenderTexture
  • MeshWithTextureFromCamera节点为Effect Camera渲染的RT输出
  • MainCamera为主摄像机
    • 设置了Culling Mask,观察阻挡物和MeshWithTextureFromCamera
  • Particles Batch节点下是图中拟态用的蓝色小球,调整好合适的物理属性,摩擦力和弹性如下图:

步骤二 降采样与模糊处理

首先排除其他观测障碍,关掉EffectCamera的HDR和抗锯齿,以便观察结果

先看OnRenderImage做了什么,上代码

// Called by the camera to apply the image effect
void OnRenderImage(RenderTexture source, RenderTexture destination)
{int rtW = source.width / 4;int rtH = source.height / 4;RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);// Copy source to the 4x4 smaller texture.DownSample4x(source, buffer);// Blur the small texturefor (int i = 0; i < iterations; i++){RenderTexture buffer2 = RenderTexture.GetTemporary(rtW, rtH, 0);FourTapCone(buffer, buffer2, i);RenderTexture.ReleaseTemporary(buffer);buffer = buffer2;}Graphics.Blit(buffer, destination);RenderTexture.ReleaseTemporary(buffer);
}

整体模糊处理一共三步

  1. 把本摄像机渲染的source数据降采样到四分之一
  2. 对降采样后对结果进行几次模糊迭代
  3. 把模糊处理的结果输出到destination,也就是屏幕

先看结果再看分析,打开FrameDebug窗口,点击播放,点到这个位置,如下图:

我的iterations迭代次数赋值为7,Draw GL数量为8,是因为第一个为DownSample4x(source, buffer)降采样操作,依次点击Draw GL会发现图片越来越模糊,如下图:

降采样分析

因为作者写法很的很有歧义,很不好懂,这里详细解释一下

C#部分

上代码,先是C#部分


// Downsamples the texture to a quarter resolution.
private void DownSample4x(RenderTexture source, RenderTexture dest)
{float off = 1.0f;Graphics.BlitMultiTap(source, dest, material,new Vector2(-off, -off),new Vector2(-off, off),new Vector2(off, off),new Vector2(off, -off));
}

Graphics.BlitMultiTap函数

  • 第一个参数source为2048x2048尺寸的RenderTexture
  • 第二个参数dest为1/4尺寸的512x512的RenderTexture
  • 第三个参数是可变Vec2数组,官方解释是偏移了多少像素的UV坐标,并且依次填充到Quad方块的纹理单元中

Each vertex of the quad has multiple texture coordinates set up, offset by offsets pixels.

顶点着色器

然后是shader部分,原作者是这么写的

structv2f {float4 pos : SV_POSITION;half2 uv : TEXCOORD0;half2 taps[4] : TEXCOORD1;
};
//顶点着色器
v2f vert( appdata_img v ) {v2f o; o.pos = UnityObjectToClipPos(v.vertex);o.uv = v.texcoord - _BlurOffsets.xy * _MainTex_TexelSize.xy;o.taps[0] = o.uv + _MainTex_TexelSize * _BlurOffsets.xy;o.taps[1] = o.uv - _MainTex_TexelSize * _BlurOffsets.xy;o.taps[2] = o.uv + _MainTex_TexelSize * _BlurOffsets.xy * half2(1,-1);o.taps[3] = o.uv - _MainTex_TexelSize * _BlurOffsets.xy * half2(1,-1);return o;
}
  • _MainTex_TexelSize为Unity默认赋值,其为Vector4(1 / width, 1 / height, width, height)
  • _BlurOffsets为Unity的黑魔法,默认赋值为Graphics.BlitMultiTap函数的Vector2[] offsets的第一个值
  • 顶点着色器里的_MainTex_TexelSize * _BlurOffsets.xy这个函数,Unity做了转化,实际为_MainTex_TexelSize.xy * _BlurOffsets.xy

模糊处理是采用的均值模糊,一个像素的颜色为上下左右的颜色平均值,中心像素为目标像素,他的值是周围四个格子颜色的平均数
顶点着色器他就是想取到像素周围四个顶点的纹理坐标,这里看到作者只用了offset的第一个值,其他传入的参数并没有用到,估计也是参考的,所以我改了一下代码,让参数利用率高一些,如下:

v2f vert( appdata_full v ) {v2f o; o.pos = UnityObjectToClipPos(v.vertex);o.taps[0] = v.texcoord;o.taps[1] = v.texcoord1;o.taps[2] = v.texcoord2;o.taps[3] = v.texcoord3;return o;
}

改了顶点输入结构appdata_full为Unity内置结构,贴出来给大家参考,如下,因为之前传入了四个偏移量,并且已经依次填入顶点数据并且已经算好偏移uv,所以直接赋值就好。(因为顶点到片元数据为差值计算,可以直接赋值在顶点数据里)

struct appdata_full
{float4 vertex : POSITION;float4 tangent : TANGENT;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;float4 texcoord1 : TEXCOORD1;float4 texcoord2 : TEXCOORD2;float4 texcoord3 : TEXCOORD3;
#if defined(SHADER_API_XBOX360)half4 texcoord4 : TEXCOORD4;half4 texcoord5 : TEXCOORD5;
#endiffixed4 color : COLOR;
};

片元着色器

//片元着色器
half4 frag(v2f i) : SV_Target {half4 color = tex2D(_MainTex, i.taps[0]);color += tex2D(_MainTex, i.taps[1]);color += tex2D(_MainTex, i.taps[2]);color += tex2D(_MainTex, i.taps[3]);return color * 0.25;
}
  • _MainTex为传入source图像

这个很好理解,算好周围颜色加起来做平均数

分析

输入为2048 * 2048的全屏RenderTexture,然后创建一个1/4大小的Rt buffer,实际就是创建一个1/4 Rt四边形,在片元着色器采样_MainTex,采样到四边形,则为GPU里的RT buffer镜像,存入真实采样color结果,根据选择的Rt格式为准(譬如float浮点数),此时这个Rt buffer为512 * 512,完成降采样

模糊分析

上代码

    // Performs one blur iteration.
public void FourTapCone(RenderTexture source, RenderTexture dest, int iteration)
{float off = 0.5f + iteration * blurSpread;Graphics.BlitMultiTap(source, dest, material,new Vector2(-off, -off),new Vector2(-off, off),new Vector2(off, off),new Vector2(off, -off));
}

这里使用的shader和降采样使用的是同一shader,所以算纹理坐标是一样的,只不过是改了offsets的偏移值,之前降采样是取上下左右四个值,现在是根据模糊迭代次数取不同偏移量的值,offsets随着迭代次数递增,取越远的位置颜色,blurSpread为预先定义好的模糊系数,可以尝试调整观察结果。执行7次模糊结果如下图,然后将此模糊图像渲染到摄像机目标RT(2048x2048),这时如果设置纹理采样模式为双线性差值采样,模糊效果会更进一步。而且适当的降采样也会对模糊有帮助。

步骤三 Alpha过滤

这步骤思路很简单,就是把上面模糊处理的图像,把模糊的颜色也涂成纯色,这样就把小球周围扩大的模糊颜色链接起来,就变成了水。

  • MeshWithTextureFromCamera为一个Quad方块,使用的材质贴图为刚才的RT
  • 通过shader将Quad渲染出来

上代码,主要看片元着色器

fixed4 frag (v2f i) : SV_Target
{// sample the texturefixed4 col = tex2D(_MainTex, i.texcoord);clip(col.a - _Cutoff);if(col.a < _Stroke) col = _StrokeColor;else col = _Color;return col;
}

操作非常简单,因为模糊处理后,小球中央不透明度最大,随着发散越边缘部分模糊越大,不透明度也越小,所以就可以通过Alpha进行过滤判断

  • clip(col.a - _Cutoff); 将特别微弱的颜色剔除掉
  • if(col.a < _Stroke) col = _StrokeColor; 将剔除后的最边缘部分显示为一个指定颜色
  • else col = _Color; 将剩余部分填充指定颜色

最后,让我们看一个有描边的黄色液体,嘿嘿

Unity开源项目2D流体渲染实现分析相关推荐

  1. 【Unity开源项目精选】ML-Agents:给你的游戏加入AI

    洪流学堂,让你快人几步.你好,我是你的技术探路者郑洪智,你可以叫我大智. 今天给你分享一个Unity开源项目,希望对你有帮助哦! ML-Agents Unity机器学习代理工具包(ML-Agents) ...

  2. 【开源项目----Android OPenGLES渲染YUV视频文件】

    [开源项目----Android OPenGLES渲染YUV视频文件] OpenGLES对YUV渲染相关文章参考

  3. 【Unity开源项目精选】UniRx:Unity中的响应式编程

    洪流学堂,让你快人几步.你好,我是你的技术探路者郑洪智,你可以叫我大智. 本篇文章首发于我的公众号:洪流学堂 今天给你分享一个Unity开源项目,我们一起来看看吧! UniRx是什么? UniRx ( ...

  4. 【Unity开源项目精选】Entitas:Unity DOTS的先行者

    洪流学堂,让你快人几步.你好,我是你的技术探路者郑洪智,你可以叫我大智. Entitas Entitas是Unity官方推出DOTS之前的一个开源ECS框架.不过自从Unity官方启动DOTS以来,E ...

  5. 【Unity开源项目精选】Unity引擎源码的C#部分

    洪流学堂,让你快人几步.你好,我是你的技术探路者郑洪智,你可以叫我大智. 今天给你分享一个Unity开源项目,我们一起来看看吧! Unity引擎源码的C#部分 Unity 引擎和编辑器源代码的 C# ...

  6. 【Unity开源项目精选】xLua:Unity热更新首选

    洪流学堂,让你快人几步.你好,我是你的技术探路者郑洪智,你可以叫我大智. 今天给你分享一个Unity开源项目,我们一起来看看吧! xLua C#下Lua编程支持: xLua为Unity. .Net. ...

  7. 【Unity开源项目精选】AssetStudio:提取Unity游戏的资源

    洪流学堂,让你快人几步.你好,我是你的技术探路者郑洪智,你可以叫我大智. 今天给你分享一个Unity开源项目,我们一起来看看吧! AssetStudio AssetStudio 是一个用于探索.提取和 ...

  8. 【Unity开源项目精选】AirSim

    洪流学堂,让你快人几步.你好,我是你的技术探路者郑洪智,你可以叫我大智. 今天给你分享一个Unity开源项目,希望对你有帮助哦! AirSim AirSim由微软AI研究部门的团队开发,它是一个用于自 ...

  9. Github上有什么好的unity开源项目?

    程序员宝藏库:https://gitee.com/sharetech_lee/CS-Books-Store 直接在GitHub搜关键词「Unity」会返回将近30万个结果,即便是把范围限定在Unity ...

最新文章

  1. Java实用教程笔记 输入、输出流
  2. LINUX符号、快捷键、正则
  3. 【转】[你必须知道的.NET]第二十一回:认识全面的null
  4. halcon11用于C++的HTuple.h头文件,纯手添中文翻译!
  5. Quality Center Issue with CAPICOM.DLL not prop...
  6. field property data type validation error in metadata
  7. 房价必须涨,不涨不正常!因为妈妈又印钱了
  8. Uber无人车事故又有新内情曝光:为竞争盲目冒进,瘾大技术差
  9. c语言qq聊天刷屏代码大全,QQ聊天刷屏脚本 达人分享技巧
  10. 自建rtmp直播和朋友一起异地看电影
  11. Java面试面经大合集(含答案),大厂越来越简单进了,
  12. 什么是计算机剪贴板介绍,剪贴板在哪里,详细教您如何打开电脑剪贴板
  13. 开机黑屏、自检不通过,主板检测卡代码为25问题解决
  14. XP操作系统安装的硬盘空间要求
  15. VMware虚拟机屏幕大小(屏幕分辨率)调整
  16. 点阵图(位图)与矢量图的区别
  17. 立足中国、跻身全球头部代工厂行列,华虹加速蜕变升级
  18. Python 数据分析微专业课程--项目06 城市餐饮店铺选址分析
  19. python安装xgboost的方法
  20. 玉雕工作室php,吴春强玉雕大师—吴春强玉雕工作室

热门文章

  1. 轨道运营管理专业自荐书_城市轨道交通运营服务与管理求职自荐信 (共3篇).doc...
  2. html经典案例pdf,一篇文章学习html【经典案例】
  3. 实现文本diff比较与展示
  4. 汇编 leave popl
  5. Mac技巧 -- 如何快速定位指定路径
  6. 文件I/O:文件流→序列化
  7. 成都盛铭轩:制作主图的方法
  8. 效果差学费贵售后难,VIPKID米雯娟的野心不能只靠“烧钱”营销
  9. pta 地下迷宫探索
  10. RYU控制器基本应用