文章目录
- 思路
- Shader Code - GlowZAlwaysPP.shader
- CSharp Code - GlowPP.cs
- CSharp Code - GlowManager.cs
- 使用方式
- 查看效果
- Project
- References
其实以前老早就想制作这个效果了,但是没有太多的空闲时间
那这次反正项目组需要这个效果,就顺手将 Demo 记录到 Blog
2021/03/03 刚刚好今天早上晨会分享了这个 外发光 的思路
我讲的比较简单的理解化的方式
该效果还是比较简单的,都是非常基础的东西,大神、大佬可以跳过。
思路使用后效处理:
- 先将需要外发光的对象都 draw 到一张 RT(RenderTexture,这里因为颜色统一的,所以 RT 只要 Format为
R8
即可),假设 RT 名为:Mask_RT
- 再将
Mask_RT
Blit 一下,处理高斯模糊,将Mask_RT
高斯模糊后的内容存入到另一张 RT:Blur_RT
- 高斯模糊可以参考我之前的一篇:Unity Shader PostProcessing - 6 - GaussianBlur 高斯模糊+CommandBuffer使用做一些其他的特效
- 再将
Value_RT = Blur_RT - Mask_RT
,就是最终外发光的像素部分了 - 再用一个
FinalCol = _Color * Value_RT
就是最终颜色了
下面列出完整的 CSharp + ShaderLab 的代码
Shader Code - GlowZAlwaysPP.shader// jave.lin 2021/02/25
// references : https://blog.csdn.net/linjf520/article/details/104940213
Shader "Game/PP/GlowZAlwaysPP"
{
CGINCLUDE
#include "UnityCG.cginc"
// pure col
float4 vert_pure_col(float4 vertex : POSITION) : SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
fixed4 frag_pure_col() : SV_Target
{
return 1;
}
// expand
sampler2D _ExpandOrginTex;
float4 _ExpandOrginTex_TexelSize;
float _GlowSize;
struct a2v_expand
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f_expand
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float4 uv01 : TEXCOORD1;
float4 uv23 : TEXCOORD2;
};
v2f_expand vert_expand(a2v_expand v) {
v2f_expand o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
float2 ts = _ExpandOrginTex_TexelSize.xy;
float2 offset1 = float2(1, 0);
float2 offset2 = float2(0, 1);
o.uv01.xy = v.uv + offset1 * ts * _GlowSize; // 左
o.uv01.zw = v.uv + offset1 * -ts * _GlowSize; // 右
o.uv23.xy = v.uv + offset2 * ts * _GlowSize; // 上
o.uv23.zw = v.uv + offset2 * -ts * _GlowSize; // 下
return o;
}
fixed4 frag_expand(v2f_expand i) : SV_Target
{
fixed sum = tex2D(_ExpandOrginTex, i.uv).r;
if (sum == 0)
sum = tex2D(_ExpandOrginTex, i.uv01.xy).r; // 左
if (sum == 0)
sum += tex2D(_ExpandOrginTex, i.uv01.zw).r; // 右
if (sum == 0)
sum += tex2D(_ExpandOrginTex, i.uv23.xy).r; // 上
if (sum == 0)
sum += tex2D(_ExpandOrginTex, i.uv23.zw).r; // 下
//if (sum == 0)
// sum = tex2D(_ExpandOrginTex, float2(i.uv01.xy.x, i.uv23.xy.y)).r; // 左 | 上
//if (sum == 0)
// sum += tex2D(_ExpandOrginTex, float2(i.uv01.zw.x, i.uv23.xy.y)).r; // 右 | 上
//if (sum == 0)
// sum = tex2D(_ExpandOrginTex, float2(i.uv01.xy.x, i.uv23.zw.y)).r; // 左 | 下
//if (sum == 0)
// sum += tex2D(_ExpandOrginTex, float2(i.uv01.zw.x, i.uv23.zw.y)).r; // 右 | 下
return sum != 0 ? 1 : 0;
}
/// blur /
struct a2v_blur
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f_blur
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float4 uv01 : TEXCOORD1;
float4 uv23 : TEXCOORD2;
};
sampler2D _BlurOrginTex;
float4 _BlurOrginTex_TexelSize;
float _BlurSize;
v2f_blur vert_blur_h(a2v_blur v) {
v2f_blur o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
float2 ts = _BlurOrginTex_TexelSize.xy;
float2 offset1 = float2(1, 0);
float2 offset2 = float2(2, 0);
o.uv01.xy = v.uv + offset1 * ts * _BlurSize; // 左1
o.uv01.zw = v.uv + offset1 * -ts * _BlurSize; // 右1
o.uv23.xy = v.uv + offset2 * ts * _BlurSize; // 左2
o.uv23.zw = v.uv + offset2 * -ts * _BlurSize; // 右2
return o;
}
v2f_blur vert_blur_v(a2v_blur v) {
v2f_blur o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
float2 ts = _BlurOrginTex_TexelSize.xy;
float2 offset1 = float2(0, 1);
float2 offset2 = float2(0, 2);
o.uv01.xy = v.uv + offset1 * ts * _BlurSize; // 上1
o.uv01.zw = v.uv + offset1 * -ts * _BlurSize; // 下1
o.uv23.xy = v.uv + offset2 * ts * _BlurSize; // 上2
o.uv23.zw = v.uv + offset2 * -ts * _BlurSize; // 下2
return o;
}
fixed4 frag_blur(v2f_blur i) : SV_Target{
fixed4 sum = tex2D(_BlurOrginTex, i.uv) * 0.4026;
sum += tex2D(_BlurOrginTex, i.uv01.xy) * 0.2442; // 左1 | 上1
sum += tex2D(_BlurOrginTex, i.uv01.zw) * 0.2442; // 右1 | 下1
sum += tex2D(_BlurOrginTex, i.uv23.xy) * 0.0545; // 左2 | 上2
sum += tex2D(_BlurOrginTex, i.uv23.zw) * 0.0545; // 右2 | 下2
return sum;
}
/// final /
sampler2D _MaskTex;
sampler2D _BlurTex;
sampler2D _SrcTex;
fixed4 _GlowColor;
struct a2v_final
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f_final
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f_final vert_final(a2v_final v) {
v2f_final o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag_final(v2f_final i) : SV_Target{
fixed blur_col = tex2D(_BlurTex, i.uv).r;
fixed mask_col = tex2D(_MaskTex, i.uv).r;
fixed value = saturate(blur_col - mask_col);
return saturate(tex2D(_SrcTex, i.uv) + value * _GlowColor);
}
ENDCG
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass // pure col 0
{
ColorMask R
CGPROGRAM
#pragma vertex vert_pure_col
#pragma fragment frag_pure_col
ENDCG
}
Pass // expand 1
{
ColorMask R
CGPROGRAM
#pragma vertex vert_expand
#pragma fragment frag_expand
ENDCG
}
Pass // blur h 2
{
ColorMask R
CGPROGRAM
#pragma vertex vert_blur_h
#pragma fragment frag_blur
ENDCG
}
Pass // blur v 3
{
ColorMask R
CGPROGRAM
#pragma vertex vert_blur_v
#pragma fragment frag_blur
ENDCG
}
Pass // final 4
{
CGPROGRAM
#pragma vertex vert_final
#pragma fragment frag_final
ENDCG
}
}
}
CSharp Code - GlowPP.cs
// jave.lin 2021/02/25
// 绘制外发光后效
// 临时效果,时间关系,未优化
using UnityEngine;
using UnityEngine.Rendering;
public class GlowPP : PostEffectBasic
{
private static int _GlowSize_hash = Shader.PropertyToID("_GlowSize");
private static int _BlurSize_hash = Shader.PropertyToID("_BlurSize");
private static int _GlowColor_hash = Shader.PropertyToID("_GlowColor");
private static int _ExpandOrginTex_hash = Shader.PropertyToID("_ExpandOrginTex");
private static int _BlurTex_hash = Shader.PropertyToID("_BlurTex");
private static int _BlurOrginTex_hash = Shader.PropertyToID("_BlurOrginTex");
private static int _MaskTex_hash = Shader.PropertyToID("_MaskTex");
private static int _SrcTex_hash = Shader.PropertyToID("_SrcTex");
private static int _ZLessTex_hash = Shader.PropertyToID("_ZLessTex");
private static int _ZGreaterTex_hash = Shader.PropertyToID("_ZGreaterTex");
private static int _ExpandTex_hash = Shader.PropertyToID("_ExpandTex");
[Header("ZGreater绘制材质")]
public Material z_greater_draw_mat;
[Header("ZAlways绘制材质")]
public Material z_always_draw_mat;
[Header("DownSample 降采等级")]
public int down_sample_level = 4;
[Range(1, 4)]
[Header("高斯模糊的次数")]
public int iterations = 4;
[Header("模糊边界大小:每次模糊采样纹素距离的缩放因数")]
[Range(0.2f, 3.0f)]
public float blur_size = 0.2f;
[Header("外发光边缘大小")]
[Range(0.0f, 2.0f)]
public float glow_size = 2;
[Header("外发光颜色")]
public Color glow_color = Color.red;
[Header("是否只显示 z greater 部分(暂时无效的参数)")]
public bool only_show_greater_glow = false;
private CommandBuffer cmdBuffer;
private Camera cam;
protected override void Start()
{
base.Start();
cmdBuffer = new CommandBuffer();
cmdBuffer.name = "GlowPPCmdBuffer";
cam = GetComponent();
}
private void OnDestroy()
{
if (cmdBuffer != null)
{
cmdBuffer.Clear();
cmdBuffer.Dispose();
cmdBuffer = null;
}
cam = null;
}
protected override void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (!IsSupported || GlowManager.instance.Count == 0)
{
Graphics.Blit(src, dest);
return;
}
if (z_always_draw_mat == null)
{
Debug.LogError("GlowPP.z_always_draw_mat == null");
Graphics.Blit(src, dest);
return;
}
// clamp down_sample_level
if (down_sample_level 0 ? listPool.Pop() : new List();
go.GetComponentsInChildren(false, list);
GlowElement e = glowElementPool.Count > 0 ? glowElementPool.Pop() : null;
if (ignoreLayer != 0)
{
var count = list.Count;
for (int i = 0; i
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?