在Unity中实现画图/字帖功能
前段时间总是加班,也没啥心情和精力去研究新东西,总结一下自己之前做的字帖的功能
先上效果图:
文章分为几部分:
(一) 画图板实现原理
(二) 画图具体实现过程中的核心点
(三) 在画图板的基础上 演变为字帖的思路
· 画图板实现原理
画图板功能一定要有两个东西:一个画布,一个画笔。
然后你需要知道Unity中有这样一个函数:
public static void Blit (Texture source, RenderTexture dest, Material mat) ;
这个函数的官方解释是:“Copies source texture into destination render texture with a shader”
我个人的理解就是: 把source贴图上的信息,通过一个材质上的shader里的处理方法,赋给dest贴图。
这就是画板的实现原理的支撑,Material 是画笔,RawImage 是画布。
具体点说,获取 RawImage 组件上的 RenderTexture 作为 source贴图,同时也作为dest贴图,然后用自己的Material去对RenderTexture做处理,处理结果还是保存回这个RenderTexture。
Graphics.Blit(m_renderTex, m_renderTex, brushMat);
材质的处理逻辑是写在shader上的。
那么接下来的问题变成了如下 :
1.Shader 如何知道你的“落笔位置”
2.Shader 如何把你画的东西 画到 RawImage 上
· 画图具体实现过程中的核心点
1. Shader 如何知道你的“落笔位置”
大家都知道shader中计算的坐标是贴图的uv坐标
所以如何知道你的落笔位置呢?这就需要一系列比较恶心的换算了~
(1) 得到画板中心在屏幕中的中心位置
(2) 得到鼠标/手指触碰位置在屏幕中的位置
(3) 计算鼠标/手指触碰位置 与 画板中心的相对位置
(4) 计算鼠标/手指触碰位置 相对于贴图的uv坐标
试了很多次的代码,满满干货 ~ 拿去拿去 (如果父物体及以上的层级有缩放,这里可能还需要修正的参数,这里就不写了)
Vector2 GetUV(Vector2 brushPos){//获取图片在屏幕中的像素位置Vector2 rawImagePos = Vector2.zero;//判断所在画布的渲染方式,不同渲染方式的位置计算方式不同switch (m_renderMode){case RenderMode.ScreenSpaceOverlay:rawImagePos = rawImage.rectTransform.position;break;default:rawImagePos = m_uiCamera.WorldToScreenPoint(rawImage.rectTransform.position);break;}//换算鼠标在图片中心点的像素位置Vector2 pos = brushPos - rawImagePos;//换算鼠标在图片中UV坐标Vector2 uv = new Vector2(pos.x / m_rawImageSizeX + 0.5f, pos.y / m_rawImageSizeY + 0.5f);return uv;}
2. Shader 如何把你画的东西 画到 RawImage 上
经过1中的一些列的换算,我们知道了落笔位置对应图板的贴图的uv坐标了,
下一步就是把uv对应像素及周边的像素填上你想要的颜色,喏~ 一个点就画完了。
然后是如何画线呢? 你会说:简单,点多了就是线了啊,每帧去打点不就ok了~ 这时候就出问题了,如果画的太快,一帧的时间过去你的手已经画出去了好远。那么就是一些不连续的点,而不是完整的线。
所以,我们需要把当前点和上一个点存起来,两点之间做填充。
我这里用的方式是:将两个点为圆心的两个圆填满,再将将以两个点连线为对称轴,长为两点之间距离,圆直径为宽的矩形填满。(自己算法 不一定好~ 各位大神有高招欢迎讨论~ )
具体步骤看一下代码:
Shader "Hidden/DrawWord"
{Properties{_Tex("Texture" , 2D) = "white" {}_Size("Size", float) = 0_Color("Color" , color) = (1,1,1,1)_UV("UV" , vector) = (0,0,0,0)_LastUV("LastUV" , vector) = (0,0,0,0)}SubShader{ZTest Always Cull Off ZWrite Off Fog{ Mode Off }Blend SrcAlpha OneMinusSrcAlphaPass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};v2f vert(appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = v.uv;return o;}sampler2D _Tex;float _Size;fixed4 _UV;fixed4 _LastUV;fixed4 _Color;fixed4 frag(v2f i) : SV_Target{fixed4 col = tex2D(_Tex, i.uv); float a = _UV.x;float b = _UV.y;float c = _LastUV.x;float d = _LastUV.y;float AA = d - b;float BB = a - c;float CC = b * c - a * d;float x = i.uv.x;float y = i.uv.y;float sqrDic1 = (x - a) * (x - a) + (y - b) * (y - b);float sqrDic2 = (x - c) * (x - c) + (y - d) * (y - d);float sqrDic11 = (AA * x + BB * y + CC) * (AA * x + BB * y + CC) / (AA * AA + BB * BB);float sqrDic22 = (x - (a + c) / 2) * (x - (a + c) / 2) + (y - (b + d) / 2) * (y - (b + d) / 2);float sqrDicStand1 = _Size/10000 * _Size/10000;float sqrDicStand2 = ((a - c) * (a - c) + (b - d) * (b - d)) / 4;//判断当前像素是否在被画的范围之内if (sqrDic1 < sqrDicStand1 || sqrDic2 < sqrDicStand1 || (sqrDic11 < sqrDicStand1 && sqrDic22 < sqrDicStand2)){col = _Color;}return col;}ENDCG}}
}
好嘞~ 核心代码就这些啦,项目贴在最下面。
画图的Demo 项目地址:https://github.com/PatrickBoomBoom/board.git
· 在画图板的基础上 演变为字帖的思路
这些已经在公司的项目中实现了,不太方便贴出来,就口述一下吧 ~
画图的思路是:找到操作的uv坐标,然后去改变贴图颜色,想做字帖的话在画图的基础上再加两个步骤:
1. 规定可涂色的范围:
准备一张有透明通道的写好的字的图片,shader中判断像素是否在写字的范围内的时候同时判断是否在预先准备好的字(或者对应笔画)的图片中,那么就只有(你画的 && 属于字内的)像素才会被填色;
2. 规定下笔起点、转折点、终点、方向:
配好一个汉字的各种配置,然后用一个Gameobject来充当画笔输入位置,规定这个obj只能沿着笔画走,如果写对了,跳转下一笔,如果写错重写。
在Unity中实现画图/字帖功能相关推荐
- Unity中实现放大镜的功能
一个项目中需要实现一个放大镜的功能,对图片或者模型的某一个部分进行局部的放大,但是不能够改变原图片或者原模型的大小和样式,大致效果如下图所示: 相对来说还是比较简单的,借助Unity的UGUI和Ren ...
- 直播笔记 | Unity中射线检测详解
本文首发于洪流学堂微信公众号. 洪流学堂,学Unity快人几步 你好,我是郑洪智,你的技术探路者. 这周三我们直播剖析了Unity中射线检测的功能,以下是直播内容精华部分笔记. 完整录播:https: ...
- unity中Avatar换装实现(三)之美
前言 最近学习了Unity中Avatar换装功能实现,参考了网上的几篇文章,总结了一个Demo.Unity的换装实现参考网上的教程,总体有两种实现,一种是官方Demo给出的合并Mesh实现, 还有一种 ...
- 眼睛慢慢眯成一条线的人都是实力很强劲的,教你在Unity中通过BlendShape来实现角色面部表情过渡切换(Animation)
文章目录 一.前言 二.BlendShape(动画师.MAYA) 三.Unity控制BlendShapes 1.SkinnedMeshRenderer控制BlendShape 2.Animation控 ...
- unity如何实现砍树功能?以及如何性能优化?
Unity中实现砍树功能 要在Unity中实现砍树功能,您需要遵循以下几个步骤: 创建一个脚本来处理砍树功能.您可以通过在项目窗口中右键单击并选择Create > C# Script来创建一个新 ...
- 【Unity3D Shader编程】之五 圣诞夜篇 Unity中Shader的三种形态对比 混合操作合辑
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...
- Unity中实现涂鸦和橡皮擦功能
一.目的 1.想知道:Unity中实现涂鸦和橡皮擦功能 二.参考 1.Unity 实现橡皮擦效果 https://www.cnblogs.com/lzzhentou/p/11634696.html 总 ...
- 【Unity】在Unity中实现扫描二维码 生成二维码功能
在Unity中使用二维码扫描功能需要我们在Unty中导入扫描库 下载地址:https://github.com/micjahn/ZXing.Net/releases 然后编写扫描脚本: 先在脚本上添加 ...
- 小功能⭐️Unity中利用材质自发光实现物体闪烁效果
文章目录 本文基于VDer的文章<Unity中利用材质自发光实现物体闪烁效果>延伸开发 在实现了具有一个Material的物体闪烁发光之后,延伸开发了具有多个Material的自闪烁效果, ...
最新文章
- 【Linux入门连载三】Linux常用的基本命令
- 关于keil编译cortex-m3纯汇编时为什么问题使用align地址问题
- 上传Text文档并转换为PDF
- JavaScript知识(二)
- android各版本市场占有率报告,你用的是哪个版本 Android系统报告:果冻豆市占率升至62%...
- 局部临时表 全局临时表 表变量
- jQuery左侧图片右侧文字滑动切换代码
- win10添加桌面图标到开始屏幕
- 网页设计html流水效果图,15例简单常用网页设计效果代码
- JAVA写入与读取GPX文件工具类
- 广西南宁机器人比赛_广西南宁中小学生机器人竞赛精彩纷呈
- 华为机试真题 C++ 实现【竖直四子棋】
- Socket通信实例详解
- 最速降线求解的数学模型
- 用天球星座测量地球表面经纬度的方法
- 计算机通信网络设备调试员(三级 高级),计算机通信网络设备调试员国家职业标准.doc...
- android中高级面试题,Android高级工程师必看系列
- 2022危险化学品生产单位安全生产管理人员考题及在线模拟考试
- 高博14讲:第七讲中g20报错
- 海外IT工程师工作福利揭秘
热门文章
- 张宇考研数学闭关修炼【解析分册】
- 当初我要是这么学操作系统就好了(附思维导图)
- Orleans 2.0 官方文档 —— 3.1 核心概念 - 什么是grain
- 微软输入法正则bug
- 基于Ubuntu20.04 GTX960m搭建cudacunn
- 语音群呼促进企业营销大力推广
- matlab小波神经网络,MATLAB 小波神经网络预测求助大神
- 「可口可乐 + Zion」7天上线小程序是如何做到的?
- Ambassador系列-09-AuthService认证服务
- 关于Explaining and harnessing adversarial examples的理解