静态毛玻璃渲染效果
最近项目需要优化毛玻璃渲染效果,之前是使用的线性Shader来实现的。需求是使用高斯模糊来实现,然后做出了第一版纯使用Shader来实现,发现效率不佳,便得以引申,使用了Unity的Graphics 来进行图像处理。下面来介绍实现思想:
1.准备工作,需要实现一个高斯模糊毛玻璃的Shader(网上有很多),并且创建一个对应的材质球(在代码处理图片的时候会用到)
2. 通过RenderTexture来将需要毛玻璃化的场景或者界面渲染一张图(注意这里需要理清逻辑,盖在毛玻璃上层的界面如果跟毛玻璃是一个整体,可先隐藏然后处理完毕后再显示)。
float scale = 0.4f;
int mScreenx = (int)FairyGUI.StageCamera.main.pixelWidth;
int mScreeny = (int)FairyGUI.StageCamera.main.pixelHeight;
var rt = RenderTexture.GetTemporary((int)(mScreenx * scale), (int)(mScreeny * scale), 16, RenderTextureFormat.ARGB32);
//这里实例化材质
materialBlur = GameObject.Instantiate(Resources.Load<Material>("Material/GaussianBlur"), gameObject.transform) as Material;
FairyGUI.StageCamera.main.targetTexture = rt;
FairyGUI.StageCamera.main.RenderDontRestore();
FairyGUI.StageCamera.main.targetTexture = null;
for (int i = 0; i < iteration; i++)
{
Graphics.Blit(rt, rt2, materialBlur, 0);
Graphics.Blit(rt2, rt, materialBlur, 1);
}
float _x = screenPos.x;
float _y = screenPos.y;
NTexture tex = new NTexture(rt, new Rect(_x, _y, mScreenx * scale, mScreeny * scale));
graphics.texture = tex;
以上就是核心代码了
一下是Shader代码
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Windplay/UI/Gaussian Blur" {
Properties {
[HideInInspector]_MainTex ("Base (RGB)", 2D) = "white" {}
_BlurSize ("Blur Size", Float) = 4.0
_TextureSize ("_TextureSize",Range(0.0001,0.004)) = 0.00208
_Weight("Weight",Range(1,100)) = 100
_BlurRadius ("_BlurRadius",Range(1,30) ) = 17
}
SubShader {
Tags {"Queue" = "Transparent+200" "RenderType" = "Opaque" }
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _MainTex_TexelSize;
float _BlurSize;
float _Weight;
float _TextureSize;
int _BlurRadius;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float4 texuv:TEXCOORD0;
};
v2f vertBlurVertical(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
#if UNITY_UV_STARTS_AT_TOP
float scale = -1.0;
#else
loat scale = 1.0;
#endif
//o.vertex = UnityObjectToClipPos(v.vertex);
o.texuv.xy = (float2(o.pos.x, o.pos.y * scale) + o.pos.w) * 0.5;
o.texuv.zw = o.pos.zw;
//o.texuv = v.texcoord;
return o;
}
v2f vertBlurHorizontal(appdata v) {
//v2f o;
//o.pos = UnityObjectToClipPos(v.vertex);
//o.texuv = v.texcoord;
//return o;
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
#if UNITY_UV_STARTS_AT_TOP
float scale = -1.0;
#else
loat scale = 1.0;
#endif
//o.vertex = UnityObjectToClipPos(v.vertex);
o.texuv.xy = (float2(o.pos.x, o.pos.y * scale) + o.pos.w) * 0.5;
o.texuv.zw = o.pos.zw;
//o.texuv = v.texcoord;
return o;
}
float4 GetGaussBlurColor( float2 uv )
{
//算出一个像素的空间
float space = 1.0/_TextureSize;
//参考正态分布曲线图,可以知道 3σ 距离以外的点,权重已经微不足道了。
//反推即可知道当模糊半径为r时,取σ为 r/3 是一个比较合适的取值。
float rho = (float)_BlurRadius * space / 3.0;
//---权重总和
float weightTotal = _BlurRadius * _BlurRadius * 2 * _Weight;
//--------
float4 colorTmp = float4(0,0,0,0);
float weight = _Weight/weightTotal;
float blurdadius = _BlurRadius/1.5;
for( int x = -blurdadius ; x <= blurdadius ; x++ )
{
for( int y = -blurdadius ; y <= blurdadius ; y++ )
{
float4 color = tex2D(_MainTex,uv + float2(x * _TextureSize,y * _TextureSize));
color = color * weight;
colorTmp += color;
}
}
return colorTmp;
}
fixed4 fragBlur(v2f i) : SV_Target {
return GetGaussBlurColor(i.texuv);
}
ENDCG
ZTest Always Cull Off ZWrite Off
Pass {
NAME "GAUSSIAN_BLUR_VERTICAL"
CGPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur
ENDCG
}
Pass {
NAME "GAUSSIAN_BLUR_HORIZONTAL"
CGPROGRAM
#pragma vertex vertBlurHorizontal
#pragma fragment fragBlur
ENDCG
}
}
FallBack "Diffuse"
}