MY BLOG DIRECTORY:
YivanLee:专题概述及目录zhuanlan.zhihu.comINTRODUCTION:
前段时间在毛大的专栏里看到他总结了十种模糊算法(原文链接,我打算在材质编辑器里重写一些常用的。
因为效果有很多种,出于程序的优雅性考虑,我准备用面向对象的方式在材质编辑器里完成这些效果。为了完成这一目的我们需要对UE做一些了解和改造。
UnrealEngine4.25的CustomNode加了包含usf的功能(本来就是几行代码就能搞定的,Epic官方就是不加!),可以在这里把自己的ush文件包含进来,但是我觉得还是太不优雅了。所以我还是使用我以前修改引擎的方法:文章链接
这样就能优雅得把头文件搞成一个Node了。
对于shader而言我们把数据封装到Struct里其实到最后也会展开,但是这很方便维护不是吗!一个效果就一个对象,对象里自带Init和Render方法接口,比以前那种面向过程式的思维写出来的神仙shader代码好多了不是吗,可惜的是目前这种写法不是所有平台都支持比如移动端,这样写的话直接就崩溃了。
MAIN CONTENT:
首先准备一张原图
我这里截图出来也是为了方便和后面的效果做对比。
【1】Directional Blur
代码如下:
//DirectionalBlur Obj;
//Obj.Init(SceneTex, SceneTexSampler, float2(0.5, 0.5), 5, 0.01);
//return Obj.Render(uv);
struct DirectionalBlur
{
Texture2D SceneTex;
SamplerState SceneTexSampler;
float2 BlurCenter;
int Times;
float BlurSize;
void Init(
in Texture2D InSceneTex,
in SamplerState InSceneTexSampler,
in float2 InBlurCenter,
in int InTimes,
in float InBlurSize
)
{
SceneTex = InSceneTex;
SceneTexSampler = InSceneTexSampler;
BlurCenter = InBlurCenter;
Times = InTimes;
BlurSize = InBlurSize;
}
float3 Render(float2 uv)
{
float3 RetColor = float3(0, 0, 0);
float2 BlurVector = (BlurCenter - uv) * BlurSize;
for(int i = 0; i < Times; i++)
{
RetColor += SceneTex.SampleLevel(SceneTexSampler, uv, 0).rgb;
uv += BlurVector;
}
RetColor /= Times;
return RetColor;
}
};
径向模糊根据一个向量对采样进行偏移,把采样结果加起来然后平均一下即可。
需要注意的是,这里其实只执行了一次模糊迭代,如果想要增加迭代次数需要做一个乒乓缓冲把结果在RT上反复画。
DirectionalBlur怼在一个Pass里也是可以的(就是费),直接增加Times即可。
【2】Box Blur
BoxBlur需要乒乓缓冲来回画,在上一帧的基础上再次模糊,不然就会出很多晶格。
//BoxBlur Obj;
//Obj.Init(SceneTex, SceneTexSampler, 0.01);
//return Obj.Render(uv);
struct BoxBlur
{
Texture2D SceneTex;
SamplerState SceneTexSampler;
float2 r;
float2 UVOffset[9];
void Init(
in Texture2D InSceneTex,
in SamplerState InSceneTexSampler,
in float2 InRadius
)
{
SceneTex = InSceneTex;
SceneTexSampler = InSceneTexSampler;
r = InRadius;
UVOffset[0] = float2(-r.x, r.y);
UVOffset[1] = float2(0, r.y);
UVOffset[2] = float2(r.x, r.y);
UVOffset[3] = float2(-r.x, 0);
UVOffset[4] = float2(0, 0);
UVOffset[5] = float2(r.x, 0);
UVOffset[6] = float2(-r.x, -r.y);
UVOffset[7] = float2(0, -r.y);
UVOffset[8] = float2(r.x, -r.y);
}
float3 Render(float2 uv)
{
float3 RetColor = float3(0, 0, 0);
RetColor += SceneTex.SampleLevel(SceneTexSampler, uv + UVOffset[0], 0).rgb;
RetColor += SceneTex.SampleLevel(SceneTexSampler, uv + UVOffset[1], 0).rgb;
RetColor += SceneTex.SampleLevel(SceneTexSampler, uv + UVOffset[2], 0).rgb;
RetColor += SceneTex.SampleLevel(SceneTexSampler, uv + UVOffset[3], 0).rgb;
RetColor += SceneTex.SampleLevel(SceneTexSampler, uv + UVOffset[4], 0).rgb;
RetColor += SceneTex.SampleLevel(SceneTexSampler, uv + UVOffset[5], 0).rgb;
RetColor += SceneTex.SampleLevel(SceneTexSampler, uv + UVOffset[6], 0).rgb;
RetColor += SceneTex.SampleLevel(SceneTexSampler, uv + UVOffset[7], 0).rgb;
RetColor += SceneTex.SampleLevel(SceneTexSampler, uv + UVOffset[8], 0).rgb;
return RetColor / 9;
}
};
BP:
【3】GaussBlur
高斯模糊就是把卷积核的权重按照高斯分布计算即可
#define PI 3.1415927
float Gaussian(float2 pos)
{
float sigma = 1.5;
float x = pos.x;
float y = pos.y;
return 1 / (2 * PI * sigma * sigma) * exp(-(x * x + y * y) / (2 * sigma * sigma));
}
高斯模糊的卷积核可以事先离线算好
const float r = 0.002;
float4 BlurData_00 = Texture2DSampleLevel(NoiseResult, NoiseResultSampler, UVAndScreenUV.xy + float2(0.0, 0), 0).rgba;
float Alpha = BlurData_00.a;
BlurData_00 = Gaussian(float2(0.0, 0.0)) * BlurData_00;
float4 BlurData_01 = Gaussian(float2( r, 0)) * Texture2DSampleLevel(NoiseResult, NoiseResultSampler, UVAndScreenUV.xy + float2( r, 0), 0).rgba;
float4 BlurData_02 = Gaussian(float2(-r, 0)) * Texture2DSampleLevel(NoiseResult, NoiseResultSampler, UVAndScreenUV.xy + float2(-r, 0), 0).rgba;
float4 BlurData_03 = Gaussian(float2(0, r)) * Texture2DSampleLevel(NoiseResult, NoiseResultSampler, UVAndScreenUV.xy + float2(0, r), 0).rgba;
float4 BlurData_04 = Gaussian(float2(0, -r)) * Texture2DSampleLevel(NoiseResult, NoiseResultSampler, UVAndScreenUV.xy + float2(0, -r), 0).rgba;
float4 BlurData_05 = Gaussian(float2(-r, -r)) * Texture2DSampleLevel(NoiseResult, NoiseResultSampler, UVAndScreenUV.xy + float2(-r, -r), 0).rgba;
float4 BlurData_06 = Gaussian(float2(r, -r)) * Texture2DSampleLevel(NoiseResult, NoiseResultSampler, UVAndScreenUV.xy + float2(r, -r), 0).rgba;
float4 BlurData_07 = Gaussian(float2(-r, r)) * Texture2DSampleLevel(NoiseResult, NoiseResultSampler, UVAndScreenUV.xy + float2(-r, r), 0).rgba;
float4 BlurData_08 = Gaussian(float2(r, r)) * Texture2DSampleLevel(NoiseResult, NoiseResultSampler, UVAndScreenUV.xy + float2(r, r), 0).rgba;
OutColor.rgb = BlurData_00 + BlurData_01 + BlurData_02 + BlurData_03 + BlurData_04 + BlurData_05 + BlurData_06 + BlurData_07 + BlurData_08;
OutColor.a = Alpha;
SUMMARY AND OUTLOOK:
没事写着玩儿,在shader里写结构体还是有点意思,但是现在的shader语言的确有点落后。希望这么low的文章不要被大佬看到。
NEXT:
todo...
By YivanLee 2020/4/30