using UnityEngine; using Shapes; namespace AibisDream.MiniGame.Language { /// /// 复古噪点叠加渲染器 - 在指定区域上方绘制复古显示器效果 /// 使用 Shapes 库绘制,与粒子系统使用相同的渲染方式 /// [ExecuteAlways] public class NoiseOverlayRenderer : ImmediateModeShapeDrawer { [Header("区域设置")] [Tooltip("效果覆盖的世界空间区域中心")] public Vector3 areaCenter = Vector3.zero; [Tooltip("效果覆盖的区域大小")] public Vector2 areaSize = new Vector2(10f, 10f); [Header("噪点效果")] [Tooltip("启用噪点效果")] public bool enableNoise = true; [Range(0f, 1f)] [Tooltip("噪点强度")] public float noiseIntensity = 0.15f; [Range(1, 100)] [Tooltip("噪点密度(点数)")] public int noiseDensity = 50; [Tooltip("噪点颜色")] public Color noiseColor = new Color(1f, 1f, 1f, 0.3f); [Range(0.001f, 0.05f)] [Tooltip("噪点大小")] public float noiseSize = 0.02f; [Header("扫描线效果")] [Tooltip("启用扫描线")] public bool enableScanlines = true; [Range(0f, 1f)] [Tooltip("扫描线强度")] public float scanlineIntensity = 0.1f; [Range(10, 200)] [Tooltip("扫描线数量")] public int scanlineCount = 60; [Tooltip("扫描线颜色")] public Color scanlineColor = new Color(0f, 0f, 0f, 0.15f); [Range(0.001f, 0.02f)] [Tooltip("扫描线厚度")] public float scanlineThickness = 0.005f; [Header("闪烁效果")] [Tooltip("启用闪烁")] public bool enableFlicker = true; [Range(0f, 0.5f)] [Tooltip("闪烁强度")] public float flickerIntensity = 0.05f; [Range(1f, 30f)] [Tooltip("闪烁频率")] public float flickerFrequency = 10f; [Header("边框暗角")] [Tooltip("启用边框暗角")] public bool enableVignette = true; [Range(0f, 1f)] [Tooltip("暗角强度")] public float vignetteIntensity = 0.3f; [Tooltip("暗角颜色")] public Color vignetteColor = new Color(0f, 0f, 0f, 0.5f); [Header("颜色失真")] [Tooltip("启用色差")] public bool enableChromaticAberration = false; [Range(0f, 0.05f)] [Tooltip("色差偏移量")] public float chromaticOffset = 0.01f; // 内部状态 private float noiseTimer = 0f; private float flickerValue = 1f; private System.Random noiseRandom; private void OnEnable() { noiseRandom = new System.Random(42); } private void Update() { // 更新噪点动画 noiseTimer += Time.deltaTime * 20f; if (noiseTimer > 1000f) noiseTimer = 0f; // 更新闪烁 if (enableFlicker) { flickerValue = 1f - flickerIntensity * Mathf.PerlinNoise(Time.time * flickerFrequency, 0f); } else { flickerValue = 1f; } // 重新生成噪点种子 noiseRandom = new System.Random((int)(Time.time * 60f) % 10000); } public override void DrawShapes(Camera cam) { if (!enabled) return; using (Draw.Command(cam)) { Draw.BlendMode = ShapesBlendMode.Transparent; Draw.ZTest = UnityEngine.Rendering.CompareFunction.Always; // 绘制暗角 if (enableVignette) { DrawVignette(); } // 绘制扫描线 if (enableScanlines) { DrawScanlines(); } // 绘制噪点 if (enableNoise) { DrawNoise(); } } } private void DrawNoise() { float halfWidth = areaSize.x / 2f; float halfHeight = areaSize.y / 2f; // 根据闪烁调整噪点数量 int actualDensity = Mathf.RoundToInt(noiseDensity * noiseIntensity * flickerValue); for (int i = 0; i < actualDensity; i++) { // 使用随机数生成器获取位置 float x = areaCenter.x + (float)(noiseRandom.NextDouble() * 2 - 1) * halfWidth; float y = areaCenter.y + (float)(noiseRandom.NextDouble() * 2 - 1) * halfHeight; float z = areaCenter.z; // 随机亮度 float brightness = (float)noiseRandom.NextDouble(); Color pointColor = noiseColor; pointColor.a = noiseColor.a * brightness * flickerValue; // 随机大小 float size = noiseSize * (0.5f + (float)noiseRandom.NextDouble()); Draw.Disc(new Vector3(x, y, z), size, pointColor); } } private void DrawScanlines() { float halfWidth = areaSize.x / 2f; float halfHeight = areaSize.y / 2f; // 计算扫描线间距 float spacing = areaSize.y / scanlineCount; Draw.LineGeometry = LineGeometry.Flat2D; Draw.Thickness = scanlineThickness; Color lineColor = scanlineColor; lineColor.a = scanlineColor.a * scanlineIntensity * flickerValue; for (int i = 0; i < scanlineCount; i++) { float y = areaCenter.y - halfHeight + spacing * i + spacing * 0.5f; // 添加轻微的闪烁变化 float lineAlpha = lineColor.a * (0.8f + 0.2f * Mathf.Sin(y * 10f + Time.time * 5f)); Color finalColor = lineColor; finalColor.a = lineAlpha; Vector3 start = new Vector3(areaCenter.x - halfWidth, y, areaCenter.z); Vector3 end = new Vector3(areaCenter.x + halfWidth, y, areaCenter.z); Draw.Line(start, end, finalColor); } } private void DrawVignette() { float halfWidth = areaSize.x / 2f; float halfHeight = areaSize.y / 2f; // 绘制四个角落的暗角渐变(使用多个半透明矩形模拟) int steps = 8; for (int i = 0; i < steps; i++) { float t = (float)i / steps; float alpha = vignetteColor.a * vignetteIntensity * (1f - t) * flickerValue; Color cornerColor = vignetteColor; cornerColor.a = alpha * 0.3f; float cornerSize = Mathf.Lerp(areaSize.x * 0.3f, 0.1f, t); float offset = cornerSize * 0.5f; // 四个角落 Vector3[] corners = new Vector3[] { new Vector3(areaCenter.x - halfWidth + offset, areaCenter.y + halfHeight - offset, areaCenter.z), new Vector3(areaCenter.x + halfWidth - offset, areaCenter.y + halfHeight - offset, areaCenter.z), new Vector3(areaCenter.x - halfWidth + offset, areaCenter.y - halfHeight + offset, areaCenter.z), new Vector3(areaCenter.x + halfWidth - offset, areaCenter.y - halfHeight + offset, areaCenter.z) }; foreach (var corner in corners) { Draw.Disc(corner, cornerSize, cornerColor); } } } /// /// 设置覆盖区域(可以从 LanguageParticleManager 获取 Canvas 区域) /// public void SetArea(Vector3 center, Vector2 size) { areaCenter = center; areaSize = size; } /// /// 设置效果强度(0-1) /// public void SetIntensity(float intensity) { intensity = Mathf.Clamp01(intensity); noiseIntensity = intensity * 0.15f; scanlineIntensity = intensity * 0.1f; flickerIntensity = intensity * 0.05f; vignetteIntensity = intensity * 0.3f; } private void OnDrawGizmosSelected() { // 在编辑器中显示效果区域 Gizmos.color = new Color(0f, 1f, 0f, 0.3f); Gizmos.DrawWireCube(areaCenter, new Vector3(areaSize.x, areaSize.y, 0.01f)); } } }