Unity CRT显示器效果实现

Unity 实现CRT显示器(老旧电视机)后处理效果(扫描线、鬼影等)


后处理全部放在片元着色器中操作,着重解析一下这一部分,完整代码放在最后~

· 效果展示

效果展示

1. Low Resolution 低分辨率

1
2
3
4
5
6
7
8
9
10
11
if (isLowResolution)
{
RenderTexture target = RenderTexture.GetTemporary(source.width / lowResolutionMultiple, source.height / lowResolutionMultiple);
Graphics.Blit(source, target);
Graphics.Blit(target, destination, material);
RenderTexture.ReleaseTemporary(target);
}
else
{
Graphics.Blit(source, destination, material);
}
  • 最后渲染的时候将摄像机图像渲染到一张分辨率低的RenderTexture上再进行后处理
  • lowResolutionMultiple 降低分辨率的倍数

2. Screen Jump 屏幕跳变

1
2
3
float2 uv = i.uv;
// Jump Noise
uv.y = frac(uv.y + _ScreenJumpLevel * _ScreenJumpOnOff);
  • uv在y轴上进行偏移
  • frac方法保证结果在[0,1]

Screen Jump 屏幕跳变



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Screen Jump
material.SetIntege(_ScreenJumpOnOff,isScreenJump ? 1 : 0);
screenJumpTimer -= 0.01f;
if (screenJumpTimer <= 0)
{
if (Random.Range(0, 100) < screenJumpFrequency)
{
material.SetFloat(_ScreenJumpLevel, Random.Range(screenJumpMinLevel, screenJumpMaxLevel));
screenJumpTimer = screenJumpLength;
}
else
{
material.SetFloat(_ScreenJumpLevel, 0);
}
}
  • screenJumpFrequency为跳屏频率(触发百分比)
  • screenJumpTimer为计时器,screenJumpLength为跳屏持续时间,screenJumpOffset为偏移量

Screen Jump 跳屏



3. Frickering 屏闪

1
2
3
4
// Frickering
float flickeringNoise = GetRandom(_Time.y);
float flickeringMask = pow(abs(sin(i.uv.y * _FlickeringCycle + _Timey)), 10);
uv.x = uv.x + (flickeringNoise * flickeringMask * _FlickeringStrength * _FlickeringOnOff);
  • _Times是场景开始加载经历的时间,四个分量分别为time/20,time,time*2,time*3
  • flickeringNoise以时间为种子计算得到一个[0,1]的噪声
  • flickeringMask通过将正弦函数输出取绝对值后求10次方得到较为尖锐的波形
  • 噪声与掩模的乘积修改uv.x使之在水平方向扭曲

Frickering 屏闪



4. Slippage 滑移错位

1
2
3
4
5
// Slippage
float scrollSpeed = _Time.x * _SlippageScrollSpeed;
float slippageMask = pow(abs(sin(i.uv.y * _SlippageInterval +scrollSpeed)), _SlippageSize);
float stepMask = round(sin(i.uv.y * _SlippageInterval + scrollSpeed - 1));
uv.x = uv.x + (slippageMask * stepMask * _SlippageStrength * _SlippageNoiseOnOff) * _SlippageOnOff;
  • slippageMask滑动掩码使用正弦函数创建周期性波形,频率由_SlippageInterval控制并随时间滚动,_SlippageSize作为指数调整波形形状
  • stepMask步进掩码使用round函数生成离散掩码,控制滑动的步进效果

Slippage 滑移错位



5. Chromatic Aberration 色差

1
2
3
4
5
// Chromatic Aberration
float red = tex2D(_MainTex, float2(uv.x - _ChromaticAberrationStrength *_ChromaticAberrationOnOff, uv.y)).r;
float green = tex2D(_MainTex, float2(uv.x, uv.y)).g;
float blue = tex2D(_MainTex, float2(uv.x + _ChromaticAberrationStrength *_ChromaticAberrationOnOff, uv.y)).b;
float4 color = float4(red, green, blue, 1);
  • 对R和B色彩通道进行左右偏移模拟色差效果

Chromatic Aberration 色差



6. Multiple Ghost 鬼影

1
2
3
float4 ghost1 = tex2D(_MainTex, uv - float2(1, 0) *_MultipleGhostStrength * _MultipleGhostOnOff);
float4 ghost2 = tex2D(_MainTex, uv - float2(1, 0) * 2 *_MultipleGhostStrength * _MultipleGhostOnOff);
color = color * 0.8 + ghost1 * 0.15 + ghost2 * 0.05;
  • ghost1保存纹理坐标在水平方向向左偏移_MultipleGhostStrength * _MultipleGhostOnOff量的采样结果
  • ghost2偏移量是ghost1的两倍,形成多重鬼影
  • 颜色混合时 原始颜色:第一个鬼影:第二个鬼影 = 0.8:0.15:0.05,保持颜色的归一化

Multiple Ghost 鬼影



7. Scanline 扫描线

1
2
3
4
5
// Scanline
float scanline = sin((i.uv.y + _Time.x) * _ScanlineFrequency) * 0.05;
color -= scanline * _ScanlineOnOff;
if (pow(sin(uv.y + _Time.y * 2), 200) * _ScanlineNoiseOnOff >= 0.999)
color *= GetRandom(uv.y);
  • 每一行像素颜色加上或减去一个偏移量使亮度按照正弦函数波形变化
  • _ScanlineFrequency 如果只使用i.uv.y的量作为正弦函数的自变量,其范围在[0,1]会导致相邻行的颜色偏移差别太小,乘上一个较大的值使得自变量离散影响扫描线密度以获得较好的效果
  • 注意_Time是跟随帧变化的,因此每一帧中所有像素经过着色器处理时使用的_Time值相同
  • 通过$sin(x^{300})$这样较为尖锐的波形,控制噪声间隔一定时间触发,且自变量为uv.y让噪声从上到下步进

Scanline 扫描线



8. Monochrome 单色

1
2
// Monochorme
color.rgb += (0.299f * color.r + 0.587f * color.g + 0.114f * color.b) * _MonochromeOnOff;
  • 调整灰度使画面成为单色画面,权重是根据人眼对不同颜色敏感度的差异来选择

Monochrome 单色



9. Film Dirt 胶片噪点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Film Dirt
float filmDirtTime = _Time.x;
float2 centeredUV = -1.0 + 2.0 * uv;
float2 noiseLookupCoords = centeredUV + filmDirtTime * 1000;
float3 noiseSample =
tex2D(_FilmDirtTex, 0.1 * noiseLookupCoords.xy).xyz +
tex2D(_FilmDirtTex, 0.01 * noiseLookupCoords.xy).xyz +
tex2D(_FilmDirtTex, 0.004 * noiseLookupCoords.xy).xyz;
float threshold = 0.6;
float smoothRadius = 0.1;
float mul1 = smoothstep(threshold - smoothRadius, threshold + smoothRadius, noiseSample.x);
float mul2 = smoothstep(threshold - smoothRadius, threshold + smoothRadius, noiseSample.y);
float mul3 = smoothstep(threshold - smoothRadius, threshold + smoothRadius, noiseSample.z);

float seed = tex2D(_FilmDirtTex, float2(filmDirtTime * 0.35, filmDirtTime)).x;
float result = clamp(0, 1, seed + 0.7);
result += 0.06 * EaseIn(19.2, 19.4, filmDirtTime);

float band = 0.05;
if (_FilmDirtOnOff == 1)
{
if (0.3 < seed && 0.3 + band > seed)
color *= mul1 * result;
else if (0.6 < seed && 0.6 + band > seed)
color *= mul2 * result;
else if (0.9 < seed && 0.9 + band > seed)
color *= mul3 * result;
}
  • centeredUV 将UV坐标转换到[-1,1]区间,中心对齐
  • noiseLookupCoords 计算噪声纹理的采样坐标,给一个较大的乘数使之随时间有显著变化
  • noiseSample 从纹理中三次进行采样并累加采样结果,不同的缩放因子创建多层次的噪声效果
  • smoothstep插值函数,基于噪声纹理的三个通道返回一个基于三次方程的平滑过渡值
  • seed 从噪声纹理中获取一个基于时间的随机值,用于产生动画效果中的随机变化
  • resultseed 进行调整,确保其在[0,1],然后再添加一个基于时间的平滑过渡增强动画动态性
  • band 影响触发噪点频率,根据随机到的seed对画面进行不同的处理

Film Dirt 胶片噪点



10. Decal 印花

1
2
3
// Decal
float4 decal = tex2D(_DecalTex, (i.decaluv - _DecalTexPos) * _DecalTexScale) * _DecalTexOnOff;
color = color * (1 - decal.a) + decal;
  • i.decaluv - _DecalTexPos 将印花纹理移动到正确的位置
  • (1 - decal.a) 计算出印花影响下的原始颜色的残留量,印花的 Alpha 值越大,原始颜色被保留的越少,color * (1 - decal.a) 对原始颜色进行衰减


· 完整代码

CRT.shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
Shader "CRTShader"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_DecalTex("Decal Texture", 2D) = "white" {}
_NoiseTex("Noise Texture", 2D) = "white" {}
}

SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100

Pass
{
CGPROGRAM
#include "UnityCG.cginc"
#pragma enable_d3d11_debug_symbols
#pragma vertex vert
#pragma fragment frag

struct a2v
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};

struct v2f
{
float2 uv : TEXCOORD0;
float2 decaluv : TEXCOORD1;
float4 vertex : SV_POSITION;
};

sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainTex_TexelSize;

// Screen Jump
int _ScreenJumpOnOff;
float _ScreenJumpOffset;

// Frickering
int _FlickeringOnOff;
float _FlickeringStrength;
float _FlickeringCycle;

// Slippage
int _SlippageOnOff;
float _SlippageNoiseOnOff;
float _SlippageStrength;
float _SlippageSize;
float _SlippageInterval;
float _SlippageScrollSpeed;

// Chromatic Aberration
int _ChromaticAberrationOnOff;
float _ChromaticAberrationStrength;

// Multiple Ghost
int _MultipleGhostOnOff;
float _MultipleGhostStrength;

// Scanline
int _ScanlineOnOff;
float _ScanlineFrequency;
int _ScanlineNoiseOnOff;

// Monochorme
int _MonochromeOnOff;

// Film Dirt
int _FilmDirtOnOff;
sampler2D _FilmDirtTex;
float4 _FilmDirtTex_ST;

// Decal
int _DecalTexOnOff;
float2 _DecalTexPos;
float2 _DecalTexScale;
sampler2D _DecalTex;
float4 _DecalTex_ST;

float GetRandom(float value);
float EaseIn(float t0, float t1, float t);

v2f vert(a2v v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.decaluv = TRANSFORM_TEX(v.uv, _DecalTex);
return o;
}

fixed4 frag(v2f i) : SV_Target
{
float2 uv = i.uv;

// Screen Jump
uv.y = frac(uv.y + _ScreenJumpOffset * _ScreenJumpOnOff);

// Frickering
float flickeringNoise = GetRandom(_Time.y);
float flickeringMask = pow(abs(sin(i.uv.y * _FlickeringCycle + _Time.y)), 10);
uv.x = uv.x + (flickeringNoise * flickeringMask * _FlickeringStrength * _FlickeringOnOff);

// Slippage
float scrollSpeed = _Time.x * _SlippageScrollSpeed;
float slippageMask = pow(abs(sin(i.uv.y * _SlippageInterval + scrollSpeed)), _SlippageSize);
float stepMask = round(sin(i.uv.y * _SlippageInterval + scrollSpeed - 1));
uv.x = uv.x + (slippageMask * stepMask * _SlippageStrength * _SlippageNoiseOnOff) * _SlippageOnOff;

// Chromatic Aberration
float red = tex2D(_MainTex, float2(uv.x - _ChromaticAberrationStrength * _ChromaticAberrationOnOff, uv.y)).r;
float green = tex2D(_MainTex, float2(uv.x, uv.y)).g;
float blue = tex2D(_MainTex, float2(uv.x + _ChromaticAberrationStrength * _ChromaticAberrationOnOff, uv.y)).b;
float4 color = float4(red, green, blue, 1);

// Multiple Ghost
float4 ghost1 = tex2D(_MainTex, uv - float2(1, 0) * _MultipleGhostStrength * _MultipleGhostOnOff);
float4 ghost2 = tex2D(_MainTex, uv - float2(1, 0) * 2 * _MultipleGhostStrength * _MultipleGhostOnOff);
color = color * 0.8 + ghost1 * 0.15 + ghost2 * 0.05;

// Decal
float4 decal = tex2D(_DecalTex, (i.decaluv - _DecalTexPos) * _DecalTexScale) * _DecalTexOnOff;
color = color * (1 - decal.a) + decal;

// Scanline
float scanline = sin((i.uv.y + _Time.x) * _ScanlineFrequency) * 0.05;
color -= scanline * _ScanlineOnOff;
if (pow(sin(uv.y + _Time.y * 2), 300) * _ScanlineNoiseOnOff >= 0.999)
color *= GetRandom(uv.y);

// Monochorme
color.rgb += (0.299f * color.r + 0.587f * color.g + 0.114f * color.b) * _MonochromeOnOff;

// Film Dirt
float filmDirtTime = _Time.x;
float2 centeredUV = -1.0 + 2.0 * uv;
float2 noiseLookupCoords = centeredUV + filmDirtTime * 1000;
float3 noiseSample =
tex2D(_FilmDirtTex, 0.1 * noiseLookupCoords.xy).xyz +
tex2D(_FilmDirtTex, 0.01 * noiseLookupCoords.xy).xyz +
tex2D(_FilmDirtTex, 0.004 * noiseLookupCoords.xy).xyz;
float threshold = 0.6;
float smoothRadius = 0.1;
float mul1 = smoothstep(threshold - smoothRadius, threshold + smoothRadius, noiseSample.x);
float mul2 = smoothstep(threshold - smoothRadius, threshold + smoothRadius, noiseSample.y);
float mul3 = smoothstep(threshold - smoothRadius, threshold + smoothRadius, noiseSample.z);

float seed = tex2D(_FilmDirtTex, float2(filmDirtTime * 0.35, filmDirtTime)).x;
float result = clamp(0, 1, seed + 0.7);
result += 0.06 * EaseIn(19.2, 19.4, filmDirtTime);

float band = 0.05;
if (_FilmDirtOnOff == 1)
{
if (0.3 < seed && 0.3 + band > seed)
color *= mul1 * result;
else if (0.6 < seed && 0.6 + band > seed)
color *= mul2 * result;
else if (0.9 < seed && 0.9 + band > seed)
color *= mul3 * result;
}

return color;
}

float GetRandom(float x)
{
return frac(sin(dot(x, float2(12.9898, 78.233))) * 43758.5453);
}

float EaseIn(float t0, float t1, float t)
{
return 2.0 * smoothstep(t0, 2.0 * t1 - t0, t);
}
ENDCG
}
}
}

CRTEffect.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode, ImageEffectAllowedInSceneView]
public class CRTEffect : MonoBehaviour
{
public Material material;

[Header("Low Resolution")]
public bool isLowResolution;
public int lowResolutionMultiple = 2;
[Header("Screen Jump")]
public bool isScreenJump;
public int screenJumpFrequency = 1;
public float screenJumpMinLength = 0.01f;
public float screenJumpMaxLength = 0.2f;
public float screenJumpMinOffset = 0.1f;
public float screenJumpMaxOffset = 0.9f;
private float screenJumpTimer;
[Header("Frickering")]
public bool isFrickering;
public float flickeringStrength = 0.002f;
public float flickeringCycle = 50f;
[Header("Slippage")]
public bool isSlippage;
public bool isSlippageNoise;
public float slippageStrength = 0.005f;
public float slippageSize = 11f;
public float slippageInterval = 1f;
public float slippageScrollSpeed = 33f;
[Header("Chromatic Aberration")]
public bool isChromaticAberration;
public float chromaticAberrationStrength = 0.005f;
[Header("Multiple Ghost")]
public bool isMultipleGhostStrength;
public float multipleGhostStrength = 0.01f;
[Header("Scanline")]
public bool isScanline;
public bool isScanlineNoise;
public float scanlineFrequency = 700.0f;
[Header("Monochorme")]
public bool isMonochorme;
[Header("Film Dirt")]
public bool isFilmDirt;
public Texture2D filmDirtTex;
[Header("Decal")]
public bool isDecal;
public Vector2 decalTexPos;
public Vector2 decalTexScale;
public Texture2D decalTex;

#region Shader Properties
// Screen Jump
private int _ScreenJumpOnOff;
private int _ScreenJumpOffset;
// Frickering
private int _FlickeringOnOff;
private int _FlickeringStrength;
private int _FlickeringCycle;
// Slippage
private int _SlippageOnOff;
private int _SlippageNoiseOnOff;
private int _SlippageStrength;
private int _SlippageSize;
private int _SlippageInterval;
private int _SlippageScrollSpeed;
// Chromatic Aberration
private int _ChromaticAberrationOnOff;
private int _ChromaticAberrationStrength;
// Multiple Ghost
private int _MultipleGhostOnOff;
private int _MultipleGhostStrength;
// Scanline
private int _ScanlineOnOff;
private int _ScanlineNoiseOnOff;
private int _ScanlineFrequency;
// Monochorme
private int _MonochromeOnOff;
// Film Dirt
private int _FilmDirtOnOff;
private int _FilmDirtTex;
// Decal
private int _DecalTexOnOff;
private int _DecalTexPos;
private int _DecalTexScale;
private int _DecalTex;
#endregion

void Start()
{
// Screen Jump
_ScreenJumpOnOff = Shader.PropertyToID("_ScreenJumpOnOff");
_ScreenJumpOffset = Shader.PropertyToID("_ScreenJumpOffset");
// Frickering
_FlickeringOnOff = Shader.PropertyToID("_FlickeringOnOff");
_FlickeringStrength = Shader.PropertyToID("_FlickeringStrength");
_FlickeringCycle = Shader.PropertyToID("_FlickeringCycle");
// Slippage
_SlippageOnOff = Shader.PropertyToID("_SlippageOnOff");
_SlippageNoiseOnOff = Shader.PropertyToID("_SlippageNoiseOnOff");
_SlippageStrength = Shader.PropertyToID("_SlippageStrength");
_SlippageSize = Shader.PropertyToID("_SlippageSize");
_SlippageInterval = Shader.PropertyToID("_SlippageInterval");
_SlippageScrollSpeed = Shader.PropertyToID("_SlippageScrollSpeed");
// Chromatic Aberration
_ChromaticAberrationOnOff = Shader.PropertyToID("_ChromaticAberrationOnOff");
_ChromaticAberrationStrength = Shader.PropertyToID("_ChromaticAberrationStrength");
// Multiple Ghost
_MultipleGhostOnOff = Shader.PropertyToID("_MultipleGhostOnOff");
_MultipleGhostStrength = Shader.PropertyToID("_MultipleGhostStrength");
// Scanline
_ScanlineOnOff = Shader.PropertyToID("_ScanlineOnOff");
_ScanlineNoiseOnOff = Shader.PropertyToID("_ScanlineNoiseOnOff");
_ScanlineFrequency = Shader.PropertyToID("_ScanlineFrequency");
// Monochorme
_MonochromeOnOff = Shader.PropertyToID("_MonochromeOnOff");
// Film Dirt
_FilmDirtOnOff = Shader.PropertyToID("_FilmDirtOnOff");
_FilmDirtTex = Shader.PropertyToID("_FilmDirtTex");
// Decal
_DecalTexOnOff = Shader.PropertyToID("_DecalTexOnOff");
_DecalTexPos = Shader.PropertyToID("_DecalTexPos");
_DecalTexScale = Shader.PropertyToID("_DecalTexScale");
_DecalTex = Shader.PropertyToID("_DecalTex");
}

private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
// Screen Jump
material.SetInteger(_ScreenJumpOnOff, isScreenJump ? 1 : 0);
screenJumpTimer -= 0.01f;
if (screenJumpTimer <= 0)
{
if (Random.Range(0, 5000) < screenJumpFrequency)
{
material.SetFloat(_ScreenJumpOffset, Random.Range(screenJumpMinOffset, screenJumpMaxOffset));
screenJumpTimer = Random.Range(screenJumpMinLength, screenJumpMaxLength);
}
else
{
material.SetFloat(_ScreenJumpOffset, 0);
}
}

// Frickering
material.SetInteger(_FlickeringOnOff, isFrickering ? 1 : 0);
material.SetFloat(_FlickeringStrength, flickeringStrength);
material.SetFloat(_FlickeringCycle, flickeringCycle);

// Slippage
material.SetInteger(_SlippageOnOff, isSlippage ? 1 : 0);
material.SetFloat(_SlippageNoiseOnOff, isSlippageNoise ? Random.Range(0f, 1f) : 1f);
material.SetFloat(_SlippageStrength, slippageStrength);
material.SetFloat(_SlippageSize, slippageSize);
material.SetFloat(_SlippageInterval, slippageInterval);
material.SetFloat(_SlippageScrollSpeed, slippageScrollSpeed);

// Chromatic Aberration
material.SetInteger(_ChromaticAberrationOnOff, isChromaticAberration ? 1 : 0);
material.SetFloat(_ChromaticAberrationStrength, chromaticAberrationStrength);

// Multiple Ghost
material.SetInteger(_MultipleGhostOnOff, isMultipleGhostStrength ? 1 : 0);
material.SetFloat(_MultipleGhostStrength, multipleGhostStrength);

// Scanline
material.SetInteger(_ScanlineOnOff, isScanline ? 1 : 0);
material.SetInteger(_ScanlineNoiseOnOff, isScanlineNoise ? 1 : 0);
material.SetFloat(_ScanlineFrequency, scanlineFrequency);

// Monochorme
material.SetInteger(_MonochromeOnOff, isMonochorme ? 1 : 0);

// Film Dirt
material.SetInteger(_FilmDirtOnOff, isFilmDirt ? 1 : 0);
material.SetTexture(_FilmDirtTex, filmDirtTex);

// Decal
material.SetInteger(_DecalTexOnOff, isDecal ? 1 : 0);
material.SetVector(_DecalTexPos, decalTexPos);
material.SetVector(_DecalTexScale, decalTexScale);
material.SetTexture(_DecalTex, decalTex);

if (isLowResolution)
{
RenderTexture target = RenderTexture.GetTemporary(source.width / lowResolutionMultiple, source.height / lowResolutionMultiple);
Graphics.Blit(source, target);
Graphics.Blit(target, destination, material);
RenderTexture.ReleaseTemporary(target);
}
else
{
Graphics.Blit(source, destination, material);
}
}
}


完结撒花~

Vacation pid:121373760


Unity CRT显示器效果实现
https://baifabaiquan.cn/2024/08/19/UnityCRT后处理效果实现/
作者
白发败犬
发布于
2024年8月19日
许可协议