由于最近研究一些使用熔岩等粘稠流体模拟技术,需要计算生成一张中间纹理,并用相应的shader实时更新中间纹理中的信息,以实现流体模拟。使用GLSL可以实现,在UnityShader中的实现需要依赖Unity2017新加入的CustomRenderTexture类,来自定义中间纹理,保存需要的信息。这样使用UnityShader同样可以方便的将一些中间信息保存到纹理中,实现更加丰富的模拟效果。
下面现根据官方手册介绍一下新CustomRenderTexture类。边看便翻译的,水平有限,各位凑合着看
https://docs.unity3d.com/2017.2/Documentation/Manual/CustomRenderTextures.html
一、CustomRenderTexture类
CustomRenderTexture是继承自Unity之前版本中的RenderTexture类。RenderTexture的使用需要结合相机,将这个相机所看到的图像渲染到RenderTarget中,这个RenderTarget就是一张RenderTexture类的纹理,这一技术常用语模拟镜面效果(具体应用参考《UnityShader入门精要》一书)。CustomRenderTexture类是对RenderTexture类的一个扩充。我们可以使用Shader来实时地更新纹理,将我们需要的信息保存在纹理中,以方便使用。这一技术可以用来进行很多复杂的模拟,比如:腐蚀、水面产生的涟漪、刚体表面的粘稠流体(血液、熔岩)等等。我们还可以通过脚本,来配置更新纹理使用的Shader的通道、更新区域、以及更新频率等等,来实现许多逼真的模拟效果。
CustomRenderTexture的属性面板展示了RenderTexture已有的许多属性,同时也加入了一些新的独特的属性:
Render Texture:
Custom Texture:
Custom Texture自己独有的属性可以分为三类:
- Material: 指定了更新这个纹理所使用的shader.
- Initialization: 指定初始化时使用的纹理
- Update:用来控制Shader如何更新这张纹理.
Custom Render Texture可以使用Export菜单导出为png格式的或者EXR格式的文件
UpdateZones
默认情况下CustomRenderTexture整体更新,但可以通过设置或者脚本控制选择更新区域列表,而实现纹理的部分更新,这一点对于实现雨滴涟漪的模拟十分有效,比如用UpdateZones来定义雨滴坠落点,在使用一个全局整体更新的pass来实现涟漪(官方给出的示例中正是这样做的)。这同样可以作为一种优化技术使用,比如当你确信并不需要更新整张纹理的全部像素时,只需要指定更新区域即可。
在属性面板上可以对UpDateZones进行配置:
Double Buffered Custom Textures
双重缓存技术
CustomRenderTexture可以使用双重缓存技术,在内部实现上,实际上存在两张纹理,但从开发者的家排毒看他们是一样的。在每一次更新之后,他们的位置将被交换,这样我们可以在更新CustomRenderTexture时使用上一次的结果,这在许多模拟渲染中经常用到。我们可以读取使用上一次已经写入纹理缓存的数据,但我们不能用传统的混合模式来更新其中的值。
性能警告:由于这一技术在内存中实际还拥有一份该纹理的拷贝,在交换时也会影响一定的性能,同时纹理的分辨率大小,交换的频率都可能导致使用双重缓存技术造成性能上的下降。
将CustomRenderTexture连起来
CustomRenderTexture需要使用一个材质(一份Shader实例)来进行更新,这个材质同样可以使用一张纹理作为输入(比如法线纹理,高度纹理等等),这张纹理同样可以是CustomRenderTexture。也就是说我们可以使用一张CustomRenderTexture作为射程或者更新另一张CustomRenderTexture的输入,这样我们就可以使用一个CustomRenderTexture链来完成一些复杂的多步模拟,Unity引擎将会为我们管理好这些纹理更新的先后顺序。
给Custom Render Texture写一个更新Shader
更新CustomRenderTexture的Shader和我们在RenderTexture中使用的2D处理类似,并没有什么特别的不同,官方手册为我们提供了简单的例子:
Shader "CustomRenderTexture/Simple"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_Tex("InputTex", 2D) = "white" {}
}
SubShader
{ Lighting Off Blend One Zero
Pass
{
CGPROGRAM
#include "UnityCustomRenderTexture.cginc"
#pragma vertex CustomRenderTextureVertexShader
#pragma fragment frag
#pragma target 3.0
float4 _Color;
sampler2D _Tex;
float4 frag(v2f_customrendertexture
IN) : COLOR
{
return _Color * tex2D(_Tex, IN.localTexcoord.xy);
}
ENDCG
}
}
}
上面的例子中必须的步骤是:
1、 #include “UnityCustomRenderTexture.cginc”
2、使用内置的顶点着色器函数:CustomRenderTextureVertexShader
3、使用内置的是结构体作为片元着色器的输入: v2f_customrendertexture
除此之外,我们可以自由的编写我们需要的像素着色器函数
下面的例子为我们提供了一个简单的初始化CustomRenderTexture的Shader:
Shader "CustomRenderTexture/CustomTextureInit"
{
Properties
{ _Color ("Color", Color) = (1,1,1,1)
_Tex("InputTex", 2D) = "white" {}
}
SubShader
{ Lighting Off Blend One Zero
Pass
{
CGPROGRAM
#include "UnityCustomRenderTexture.cginc"
#pragma vertex InitCustomRenderTextureVertexShader
#pragma fragment frag
#pragma target 3.0
float4 _Color;
sampler2D _Tex;
float4 frag(v2f_init_customrendertexture IN) : COLOR
{ _Color * tex2D(_Tex, IN.texcoord.xy); }
ENDCG }
}
}
与更新Shader一样,下面三个地方是我们必须做的:
1、包含 “UnityCustomRenderTexture.cginc”文件
2、使用内置顶点着色器函数:InitCustomRenderTextureVertexShader
3、使用内置的片元着色器输入结构:v2f_init_customrendertexture
下面是我们为了方便开发者提供的内置数据结构和相关函数,以及他们的说明
v2f_customrendertexture结构体:
v2f_init_customrendertexture 结构体:
全局变量:
我们可以通过C#脚本来对CustomRenderTexture进行控制
上面属性面板中的多种设置都可以通过脚本来进行控制。有一件事要始终注意的就是:任何根据当前CustomRenderTexture当前状态对CustomRenderTexture的更新请求都是发生在这一帧的开始时,这可以保证所有使用这个纹理作为输入的材质都能获取最新的结果。例如:
customRenderTexture.updateZones = updateZones1;customRenderTexture.Update();customRenderTexture.updateZones = updateZones2;customRenderTexture.Update();
这样的代码将不会向我们书写逻辑一样先对updateZones1更新,在对updateZones2更新,而是对updateZones2调用两次Update()。
这样的规则的好处在于:确保了脚本中任何对CustomRenderTexture的属性配置的改变只会在下一帧的更新时起作用。
翻译水平有限,边看便翻译,大家凑合着看。之后分享官方发布的使用CustomRenderTexture的示例程序WaterSimulation分析