Unity根据相机的背景色标记变化为明显的颜色并投影

发布时间:2026/9/6 3:00:01

Unity根据相机的背景色标记变化为明显的颜色并投影
1 结构2 CameraDisplay DrawingOverlay 为下面类型3 CanvasProjector 可以复制上面的Canvas然后删除其他的这样就不用修改位置比例等相关参数4 游戏对象参数设置Main Camera脚本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; [Header(投影端RawImage)] public RawImage projectorRaw; private WebCamTexture _camTex; private Texture2D _drawTex; private int _w, _h; private bool _isDraw; private ListVector2 _curPoints new ListVector2(); private ListStroke _history new ListStroke(); private readonly Color[] _candColors { Color.black,Color.white,Color.red,Color.green, Color.blue,Color.yellow,Color.cyan,Color.magenta }; [Serializable] class Stroke { public ListVector2 pts new ListVector2(); 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; 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 ListVector2(_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(ListVector2 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(ListVector2 pts) { DictionaryColor, int vote new DictionaryColor, 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(); } }ProjectorCameraCanvasCanvasProjector效果

相关新闻

【会员专享数据】2006-2022年我国省份级别的各类建设用地面积数据 (工业用地/居住用地等十几个指标)

【会员专享数据】2006-2022年我国省份级别的各类建设用地面积数据 (工业用地/居住用地等十几个指标)

2026/9/6 3:00:01

《中国城市建设统计年鉴》中细致地统计了我国城市市政公用设施建设与发展情况,在之前的文章中,我们分享过基于2000-2024年《中国城市统计年鉴》整理的2006—2024年我国省份级别的市政设施水平相关指标。本次我们分享的是从2006—2024年《中国城市建设统计…

手脚冰凉就是阳虚?巡途医云AI体质测评,辨明真实原因

手脚冰凉就是阳虚?巡途医云AI体质测评,辨明真实原因

2026/9/6 3:00:01

气温稍微下降就手脚冰凉,很多人遇到这种情况直接判定自己属于阳虚体质,随即大量食用羊肉、生姜开展温补。但并不是所有怕冷手脚凉都属于阳虚范畴。气血生成不足、肝气运行不畅、体内湿气阻滞,同样会造成四肢发凉。如果体质判断出错&#xff0…

GPT都能写代码了,转行AI还有意义吗?我被无监督学习打醒

GPT都能写代码了,转行AI还有意义吗?我被无监督学习打醒

2026/9/6 3:00:01

GPT都能写代码了,转行AI还有意义吗?我被无监督学习打醒 去年公司内部转岗,我交了申请表从后端开发切到AI组,报到那天刚好赶上GPT-4的代码能力刷屏。团队里几个老员工笑着说:“现在AI自己就能写代码,你转过来学这个还有什么用?”我当时也懵了,后来花了整整一年才明白:真正让我…

绩效管理系统解决方案:中大型企业如何搭建高效、透明、可持续的绩效管理体系?

绩效管理系统解决方案:中大型企业如何搭建高效、透明、可持续的绩效管理体系?

2026/9/6 6:10:08

对于中大型企业来说,绩效管理真正难的并不是“怎么打分”,而是如何把集团战略传导到部门和个人,同时适配不同业务线的考核方式,并让管理者持续跟进目标、及时反馈调整。除此之外,HR还要处理大量Excel汇总、数据核算和流…

Harness-of-Harness:多日自主软件开发与持续改进的AI Agent框架

Harness-of-Harness:多日自主软件开发与持续改进的AI Agent框架

2026/9/6 6:10:08

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

酒店智能化设计方案:从客控体验到运营降本的系统规划

酒店智能化设计方案:从客控体验到运营降本的系统规划

2026/9/6 6:10:08

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

视觉SLAM主控选型指南:RK3588/RK3576/RK3568分级方案全解析

视觉SLAM主控选型指南:RK3588/RK3576/RK3568分级方案全解析

2026/9/6 6:10:08

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

视频剪辑结尾设计全流程:从关键帧到导出设置

视频剪辑结尾设计全流程:从关键帧到导出设置

2026/9/6 6:10:08

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

同人动画制作全流程解析:从MLP案例学习2D动画技术实践

同人动画制作全流程解析:从MLP案例学习2D动画技术实践

2026/9/6 6:00:08

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

2026/9/6 1:19:56

本文首发于“生态学者”!从“湿地面积”到“土壤碳密度”:为什么需要重新认识潮汐湿地蓝碳变化?潮汐湿地位于陆地与海洋的交汇地带,包括红树林、盐沼和潮滩,是全球重要的蓝碳生态系统。其土壤能够长期储存大量有机碳&a…

adb抓包

adb抓包

2026/9/6 1:19:56

前言 本文介绍如何通过 tcpdump 在 Android 手机上抓取网络数据包,并在电脑端使用 Wireshark 进行分析。适用于需要排查 App 网络请求、分析接口调用或调试网络问题的开发与测试场景。1. 手机要有 root 权限2. 下载 tcpdump3. adb push C:\Users\zhangkuixun\Downlo…

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

2026/9/6 1:19:56

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战 在云原生基础设施中,容器镜像体积直接决定了服务的部署速度与弹性扩容敏捷度。对于传统的 Go / Java 微服务,镜像体积通常被严格控制在 50MB 到 200MB 以内,拉取镜像只…

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

2026/9/6 1:19:56

本文首发于“生态学者”!从“湿地面积”到“土壤碳密度”:为什么需要重新认识潮汐湿地蓝碳变化?潮汐湿地位于陆地与海洋的交汇地带,包括红树林、盐沼和潮滩,是全球重要的蓝碳生态系统。其土壤能够长期储存大量有机碳&a…

adb抓包

adb抓包

2026/9/6 1:19:56

前言 本文介绍如何通过 tcpdump 在 Android 手机上抓取网络数据包,并在电脑端使用 Wireshark 进行分析。适用于需要排查 App 网络请求、分析接口调用或调试网络问题的开发与测试场景。1. 手机要有 root 权限2. 下载 tcpdump3. adb push C:\Users\zhangkuixun\Downlo…

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

2026/9/6 1:19:56

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战 在云原生基础设施中,容器镜像体积直接决定了服务的部署速度与弹性扩容敏捷度。对于传统的 Go / Java 微服务,镜像体积通常被严格控制在 50MB 到 200MB 以内,拉取镜像只…

远程协作的工作台整理

远程协作的工作台整理

2026/9/3 6:56:24

远程协作的工作台整理远程协作的核心不是再加一个工具,而是让交接信息足够完整。异步任务要写明目标、输入位置、完成标准和需要决策的人。 工作台的最小配置 将日程、待办、代码和沟通入口收拢到少数固定位置;通知按紧急程度分层。工作台不需要模仿办公…

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

2026/9/4 7:42:10

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

2026/9/5 23:14:13

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…