Unity标记固定颜色及投影标记

1 结构

2 CameraDisplay和DrawingOverlay为Raw Image

3 ProjectorCanvas可以直接复制上面的Canvas,然后删除其他不用的。

最后留下的只是渲染原来的标记。

4 游戏组件设置

Main Camera

ProjectorCamera

Canvas

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

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

    [Header("投影端RawImage")]
    public RawImage projectorRaw;

    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 Color _currentColor = Color.red;

    [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;

        if (projectorRaw != null)
            projectorRaw.texture = _drawTex;
    }

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

        // 键盘快捷键切换画笔颜色(与按钮功能一致)
        if (Input.GetKeyDown(KeyCode.R)) SetRedColor();
        if (Input.GetKeyDown(KeyCode.G)) SetGreenColor();
        if (Input.GetKeyDown(KeyCode.B)) SetBlueColor();

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

        DealMouse();
        RenderDraw();
    }

    // ========== 三个颜色切换方法,供UI按钮点击调用 ==========
    public void SetRedColor()
    {
        _currentColor = Color.red;
    }

    public void SetGreenColor()
    {
        _currentColor = Color.green;
    }

    public void SetBlueColor()
    {
        _currentColor = Color.blue;
    }

    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;
            }
            // 使用当前选中的颜色创建新笔画,默认红色
            _history.Add(new Stroke { pts = new List<Vector2>(_curPoints), col = _currentColor });
            _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, _currentColor);
        _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);
                }
            }
    }

    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();
    }
}

ProjectorCanvas

ProjectorCanvas下面的DrawingOverlay直接复制上在的

效果,主屏可以显示而且投影可以显示标记。

相关推荐
云雨巫山4 小时前
Unity 游戏开发性能优化全景手册
unity·智能手机·性能优化·游戏引擎
maybeyaluokenai4 小时前
unity build in渲染管线概述
unity·图形渲染
云雨巫山4 小时前
Unity 性能优化 · 图解全景手册
unity·性能优化·游戏引擎
Madokaly19 小时前
DOTween的Vector3Plugin
unity
一梭键盘任平生1 天前
Shader入门笔记2
unity
点心的游戏开发世界2 天前
Unity C# 脚本学习笔记:命名空间与 using
学习·unity·c#
点心的游戏开发世界2 天前
Unity C# 脚本学习笔记:泛型
学习·unity·c#
微三云生态系统架构师-彭丹3 天前
任务卷轴积分系统架构设计:从任务状态机到合规风控的完整实现
unity·系统架构·游戏引擎
XR技术研习社3 天前
从 PICO 文档看未来短期内的 Unity 版本选择
unity·游戏引擎·xr·vr