unity 生成标记根据背景色变色为明显的颜色

设置

两个是RawImage一个是显示相机,一个是显示标记

两个的层级关系不能错,不然相机显示的会挡住标记。

CameraDrawingAutoColor.cs

cs 复制代码
    using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

public class CameraDrawingAutoColor : MonoBehaviour
{
    [Header("底层显示摄像头")]
    public RawImage CamRaw;
    [Header("顶层绘图面板(同尺寸全屏)")]
    public RawImage DrawRaw;

    private WebCamTexture _camTex;
    private Texture2D _drawTex;
    private int _w, _h;

    private bool _isDraw;
    private List<Vector2> _curPoints = new List<Vector2>();
    private List<Stroke> _history = new List<Stroke>();

    private readonly Color[] _candColors =
    {
        Color.black,Color.white,Color.red,Color.green,
        Color.blue,Color.yellow,Color.cyan,Color.magenta
    };

    [Serializable]
    class Stroke
    {
        public List<Vector2> pts = new List<Vector2>();
        public Color col;
    }

    void Start()
    {
        // 初始化相机
        var device = WebCamTexture.devices[0];
        _camTex = new WebCamTexture(device.name, 1280, 720);
        _camTex.Play();
        CamRaw.texture = _camTex;

        // 初始化顶层绘图纹理
        Invoke(nameof(InitDrawTex), 0.5f);
    }

    void InitDrawTex()
    {
        _w = _camTex.width;
        _h = _camTex.height;
        _drawTex = new Texture2D(_w, _h, TextureFormat.RGBA32, false);
        _drawTex.wrapMode = TextureWrapMode.Clamp;
        ClearDrawTex();
        DrawRaw.texture = _drawTex;
    }

    void Update()
    {
        if (_drawTex == null) return;

        DealMouse();
        RenderDraw();

        // 快捷键
        if (Input.GetKeyDown(KeyCode.C)) ClearAll();
        if (Input.GetKeyDown(KeyCode.S)) SaveImg();
    }

    void DealMouse()
    {
        // 按下
        if (Input.GetMouseButtonDown(0))
        {
            _isDraw = true;
            _curPoints.Clear();
            _curPoints.Add(GetMouseTexPos());
        }
        // 拖动
        if (Input.GetMouseButton(0) && _isDraw)
        {
            var p = GetMouseTexPos();
            if (Vector2.Distance(p, _curPoints.Last()) > 2f)
                _curPoints.Add(p);
        }
        // 抬起
        if (Input.GetMouseButtonUp(0) && _isDraw)
        {
            _isDraw = false;
            if (_curPoints.Count < 2)
            {
                _curPoints.Clear();
                return;
            }
            // 自动选对比色
            Color bestCol = CalcStrokeColor(_curPoints);
            _history.Add(new Stroke { pts = new List<Vector2>(_curPoints), col = bestCol });
            _curPoints.Clear();
        }
    }

    // 鼠标屏幕坐标 → 纹理像素坐标
    Vector2 GetMouseTexPos()
    {
        RectTransform rect = DrawRaw.rectTransform;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(rect, Input.mousePosition, null, out Vector2 local);

        float x = Mathf.Lerp(0, _w, (local.x / rect.rect.width) + 0.5f);
        float y = Mathf.Lerp(0, _h, (local.y / rect.rect.height) + 0.5f);

        x = Mathf.Clamp(x, 0, _w - 1);
        y = Mathf.Clamp(y, 0, _h - 1);
        return new Vector2(x, y);
    }

    // 渲染所有笔画
    void RenderDraw()
    {
        ClearDrawTex();

        // 历史正式线条
        foreach (var s in _history)
            DrawLineList(s.pts, s.col);

        // 当前临时灰色线
        if (_isDraw && _curPoints.Count > 1)
            DrawLineList(_curPoints, Color.red);

        _drawTex.Apply();
    }

    void DrawLineList(List<Vector2> pts, Color col)
    {
        for (int i = 0; i < pts.Count - 1; i++)
            DrawBresenham(pts[i], pts[i + 1], col, 3);
    }

    // 画线
    void DrawBresenham(Vector2 p1, Vector2 p2, Color col, int size)
    {
        int x0 = (int)p1.x, y0 = (int)p1.y;
        int x1 = (int)p2.x, y1 = (int)p2.y;

        int dx = Mathf.Abs(x1 - x0);
        int dy = Mathf.Abs(y1 - y0);
        int sx = x0 < x1 ? 1 : -1;
        int sy = y0 < y1 ? 1 : -1;
        int err = dx - dy;

        while (true)
        {
            DrawPoint(x0, y0, col, size);
            if (x0 == x1 && y0 == y1) break;
            int e2 = 2 * err;
            if (e2 > -dy) { err -= dy; x0 += sx; }
            if (e2 < dx) { err += dx; y0 += sy; }
        }
    }

    void DrawPoint(int x, int y, Color col, int r)
    {
        for (int dx = -r; dx <= r; dx++)
            for (int dy = -r; dy <= r; dy++)
            {
                if (dx * dx + dy * dy <= r * r)
                {
                    int px = x + dx;
                    int py = y + dy;
                    if (px >= 0 && px < _w && py >= 0 && py < _h)
                        _drawTex.SetPixel(px, py, col);
                }
            }
    }

    // 计算整条笔画最佳对比色
    Color CalcStrokeColor(List<Vector2> pts)
    {
        Dictionary<Color, int> vote = new Dictionary<Color, int>();
        foreach (var p in pts)
        {
            Color c = GetBestColor((int)p.x, (int)p.y);
            if (vote.ContainsKey(c)) vote[c]++;
            else vote[c] = 1;
        }
        return vote.OrderByDescending(k => k.Value).First().Key;
    }

    Color GetBestColor(int x, int y)
    {
        int win = 15;
        int x1 = Mathf.Max(0, x - win);
        int y1 = Mathf.Max(0, y - win);
        int x2 = Mathf.Min(_w - 1, x + win);
        int y2 = Mathf.Min(_h - 1, y + win);

        Color avg = Color.black;
        float r = 0, g = 0, b = 0;
        int cnt = 0;

        for (int yy = y1; yy <= y2; yy++)
            for (int xx = x1; xx <= x2; xx++)
            {
                Color c = _camTex.GetPixel(xx, yy);
                r += c.r; g += c.g; b += c.b; cnt++;
            }

        avg = new Color(r / cnt, g / cnt, b / cnt);
        float maxDis = -999;
        Color best = Color.red;

        foreach (var cc in _candColors)
        {
            float d = Mathf.Pow(cc.r - avg.r, 2) + Mathf.Pow(cc.g - avg.g, 2) + Mathf.Pow(cc.b - avg.b, 2);
            if (d > maxDis)
            {
                maxDis = d;
                best = cc;
            }
        }
        return best;
    }

    void ClearDrawTex()
    {
        if (_drawTex == null) return;
        Color[] clear = new Color[_w * _h];
        for (int i = 0; i < clear.Length; i++)
            clear[i] = Color.clear;
        _drawTex.SetPixels(clear);
    }

    void ClearAll()
    {
        _history.Clear();
        _curPoints.Clear();
        _isDraw = false;
        ClearDrawTex();
    }

    void SaveImg()
    {
        RenderTexture rt = new RenderTexture(_w, _h, 24);
        rt.Create();
        CamRaw.texture = _camTex;
        DrawRaw.texture = _drawTex;

        Texture2D saveTex = new Texture2D(_w, _h);
        saveTex.ReadPixels(new Rect(0, 0, _w, _h), 0, 0);
        saveTex.Apply();

        byte[] png = saveTex.EncodeToPNG();
        string path = Application.persistentDataPath + "/cam_draw.png";
        System.IO.File.WriteAllBytes(path, png);
        Debug.Log("保存成功:" + path);
    }

    void OnDestroy()
    {
        if (_camTex != null) _camTex.Stop();
    }
}
相关推荐
叶帆15 天前
【YFIOs】用C#开发硬件之设备上云
开发语言·unity·c#
久数君15 天前
AI三维建模工具“造形家”:地理场景三维化的高效解决方案
unity·glb·ai算法·ai三维建模工具·地图框选·造形家·城市建筑模型
会思考的猴子15 天前
Unity VFX 属性 Postion 和 TargetPostion
unity
hai31524754315 天前
九章编程法 · 猜数字游戏 (GW-BASIC 重构版) *
人工智能·microsoft·游戏引擎·游戏程序
心前阳光16 天前
Unity资源导入之自动化资源导入
unity·自动化·游戏引擎
心前阳光16 天前
Unity之2021.3.45f2c1发布安卓程序遇到的问题
android·unity·游戏引擎
纪纯16 天前
PicoVR Unity Integration SDK 3.4 常用交互API
unity·游戏引擎·vr·pico
龙智DevSecOps解决方案16 天前
3A 游戏优化技术栈:如何打通引擎级分析工具与 DevOps 持续集成管线?
unity·性能优化·游戏开发·技术美术·perforce·unrealengine
葛兰岱尔16 天前
从 SolidWorks 到 Three.js,从 Inventor 到 Unity——制造业CAD模型“几何-语义一体化“转换,不再是天方夜谭!
开发语言·javascript·unity
鼎艺创新科技16 天前
三维电子沙盘中OSGB倾斜摄影数据的加载与渲染
游戏引擎·cocos2d