完整工程地址:ChineseBrush.zip_u3d毛笔字,unity毛笔字-C#文档类资源-CSDN下载

注:自行使用脚本时,可能出现不生效的情况,需将RawImage的Anchors设置为中心位置。或者更改Start中的脚本,将前两行的raw.rectTransform.sizeDelta.x更改为raw.rectTransform.rect.width,y值同理。

2021.4.22修复设置毛笔颜色不生效,添加撤销(按键R)和清空(按键C)功能;重新上传资源。

2020.4.2更新因Canvas位置带来的偏差。

2018.12.7更新因RawImage位置大小变化引起的错位现象。

画图方法参考这位大神。

先上效果图:

技术实现代码:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Random = UnityEngine.Random;public class Painting : MonoBehaviour
{private RenderTexture texRender;   //画布public Material mat;     //给定的shader新建材质public Texture brushTypeTexture;   //画笔纹理,半透明private Camera mainCamera;private float brushScale = 0.5f;public Color brushColor = Color.black;public RawImage raw;                   //使用UGUI的RawImage显示,方便进行添加UI,将pivot设为(0.5,0.5)private float lastDistance;private Vector3[] PositionArray = new Vector3[3];private int a = 0;private Vector3[] PositionArray1 = new Vector3[4];private int b = 0;private float[] speedArray = new float[4];private int s = 0;[SerializeField]private int num = 50; //画的两点之间插件点的个数[SerializeField]private float widthPower = 0.5f; //关联粗细Vector2 rawMousePosition;            //raw图片的左下角对应鼠标位置float rawWidth;                               //raw图片宽度float rawHeight;                              //raw图片长度[SerializeField]private const int maxCancleStep = 5;  //最大撤销的步骤(越大越耗费内存)[SerializeField]private Stack<RenderTexture> savedList = new Stack<RenderTexture>(maxCancleStep);void Start(){//raw图片鼠标位置,宽度计算rawWidth = raw.rectTransform.sizeDelta.x;rawHeight = raw.rectTransform.sizeDelta.y;Vector2 rawanchorPositon = new Vector2(raw.rectTransform.anchoredPosition.x - raw.rectTransform.sizeDelta.x / 2.0f, raw.rectTransform.anchoredPosition.y - raw.rectTransform.sizeDelta.y / 2.0f);//计算Canvas位置偏差Canvas canvas=raw.canvas;Vector2 canvasOffset=RectTransformUtility.WorldToScreenPoint(Camera.main,canvas.transform.position) - canvas.GetComponent<RectTransform>().sizeDelta/2;//最终鼠标相对画布的位置rawMousePosition = rawanchorPositon + new Vector2(Screen.width / 2.0f, Screen.height / 2.0f) + canvasOffset;texRender = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGB32);Clear(texRender);}Vector3 startPosition = Vector3.zero;Vector3 endPosition = Vector3.zero;void Update(){if (Input.GetMouseButtonDown(0)){SaveTexture();}if (Input.GetMouseButton(0)){OnMouseMove(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));}if (Input.GetMouseButtonUp(0)){OnMouseUp();}if (Input.GetKeyDown(KeyCode.R)){CanclePaint();}if (Input.GetKeyDown(KeyCode.C)){OnClickClear();}DrawImage();}[SerializeField] private RawImage saveImage;void SaveTexture(){RenderTexture newRenderTexture = new RenderTexture(texRender);Graphics.Blit(texRender,newRenderTexture);savedList.Push(newRenderTexture);}void CanclePaint(){if (savedList.Count > 0){texRender.Release();texRender = savedList.Pop();}}void OnMouseUp(){startPosition = Vector3.zero;//brushScale = 0.5f;a = 0;b = 0;s = 0;}//设置画笔宽度float SetScale(float distance){float Scale = 0;if (distance < 100){Scale = 0.8f - 0.005f * distance;}else{Scale = 0.425f - 0.00125f * distance;}if (Scale <= 0.05f){Scale = 0.05f;}return Scale * widthPower;}void OnMouseMove(Vector3 pos){if (startPosition == Vector3.zero){startPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0);}endPosition = pos;float distance = Vector3.Distance(startPosition, endPosition);brushScale = SetScale(distance);ThreeOrderBézierCurse(pos, distance, 4.5f);startPosition = endPosition;lastDistance = distance;}void Clear(RenderTexture destTexture){Graphics.SetRenderTarget(destTexture);GL.PushMatrix();GL.Clear(true, true, Color.white);GL.PopMatrix();}void DrawBrush(RenderTexture destTexture, int x, int y, Texture sourceTexture, Color color, float scale){DrawBrush(destTexture, new Rect(x, y, sourceTexture.width, sourceTexture.height), sourceTexture, color, scale);}void DrawBrush(RenderTexture destTexture, Rect destRect, Texture sourceTexture, Color color, float scale){//增加鼠标位置根据raw图片位置换算。float left = (destRect.xMin-rawMousePosition.x)*Screen.width/rawWidth - destRect.width * scale / 2.0f;float right = (destRect.xMin - rawMousePosition.x) * Screen.width / rawWidth + destRect.width * scale / 2.0f;float top = (destRect.yMin - rawMousePosition.y) *Screen.height / rawHeight - destRect.height * scale / 2.0f;float bottom = (destRect.yMin - rawMousePosition.y) * Screen.height / rawHeight + destRect.height * scale / 2.0f;Graphics.SetRenderTarget(destTexture);GL.PushMatrix();GL.LoadOrtho();mat.SetTexture("_MainTex", brushTypeTexture);mat.SetColor("_Color", color);mat.SetPass(0);GL.Begin(GL.QUADS);GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(left / Screen.width, top / Screen.height, 0);GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(right / Screen.width, top / Screen.height, 0);GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(right / Screen.width, bottom / Screen.height, 0);GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(left / Screen.width, bottom / Screen.height, 0);GL.End();GL.PopMatrix();}bool bshow = true;void DrawImage(){raw.texture = texRender;}public void OnClickClear(){Clear(texRender);savedList.Clear();}//二阶贝塞尔曲线 效果不好,改用下面三阶public void TwoOrderBézierCurse(Vector3 pos, float distance){PositionArray[a] = pos;a++;if (a == 3){for (int index = 0; index < num; index++){Vector3 middle = (PositionArray[0] + PositionArray[2]) / 2;PositionArray[1] = (PositionArray[1] - middle) / 2 + middle;float t = (1.0f / num) * index / 2;Vector3 target = Mathf.Pow(1 - t, 2) * PositionArray[0] + 2 * (1 - t) * t * PositionArray[1] +Mathf.Pow(t, 2) * PositionArray[2];float deltaSpeed = (float)(distance - lastDistance) / num;DrawBrush(texRender, (int)target.x, (int)target.y, brushTypeTexture, brushColor, SetScale(lastDistance + (deltaSpeed * index)));}PositionArray[0] = PositionArray[1];PositionArray[1] = PositionArray[2];a = 2;}else{DrawBrush(texRender, (int)endPosition.x, (int)endPosition.y, brushTypeTexture,brushColor, brushScale);}}//三阶贝塞尔曲线,获取连续4个点坐标,通过调整中间2点坐标,画出部分(我使用了num/1.5实现画出部分曲线)来使曲线平滑;通过速度控制曲线宽度。private void ThreeOrderBézierCurse(Vector3 pos, float distance, float targetPosOffset){//记录坐标PositionArray1[b] = pos;b++;//记录速度speedArray[s] = distance;s++;if (b == 4){Vector3 temp1 = PositionArray1[1];Vector3 temp2 = PositionArray1[2];//修改中间两点坐标Vector3 middle = (PositionArray1[0] + PositionArray1[2]) / 2;PositionArray1[1] = (PositionArray1[1] - middle) * 1.5f + middle;middle = (temp1 + PositionArray1[3]) / 2;PositionArray1[2] = (PositionArray1[2] - middle) * 2.1f + middle;for (int index1 = 0; index1 < num / 1.5f; index1++){float t1 = (1.0f / num) * index1;Vector3 target = Mathf.Pow(1 - t1, 3) * PositionArray1[0] +3 * PositionArray1[1] * t1 * Mathf.Pow(1 - t1, 2) +3 * PositionArray1[2] * t1 * t1 * (1 - t1) + PositionArray1[3] * Mathf.Pow(t1, 3);//float deltaspeed = (float)(distance - lastDistance) / num;//获取速度差值(存在问题,参考)float deltaspeed = (float)(speedArray[3] - speedArray[0]) / num;//float randomOffset = Random.Range(-1/(speedArray[0] + (deltaspeed * index1)), 1 / (speedArray[0] + (deltaspeed * index1)));//模拟毛刺效果float randomOffset = Random.Range(-targetPosOffset, targetPosOffset);DrawBrush(texRender, (int)(target.x + randomOffset), (int)(target.y + randomOffset), brushTypeTexture, brushColor, SetScale(speedArray[0] + (deltaspeed * index1)));}PositionArray1[0] = temp1;PositionArray1[1] = temp2;PositionArray1[2] = PositionArray1[3];speedArray[0] = speedArray[1];speedArray[1] = speedArray[2];speedArray[2] = speedArray[3];b = 3;s = 3;}else{DrawBrush(texRender, (int)endPosition.x, (int)endPosition.y, brushTypeTexture,brushColor, brushScale);}}
}

Unity实现写毛笔字相关推荐

  1. 【Unity 手写PBR】补充:多光源 阴影 视差 自发光

    写在前面 [Unity 手写PBR]Build-in管线:实现直接光部分 [Unity 手写PBR]Build-in管线:实现间接光部分 这里再快速补充一下剩下的部分,多光源.阴影.视差贴图应用和自发 ...

  2. [Unity3D]用C#在unity里面写一个简单的红绿灯

    [Unity3D]用C#在unity里面写一个简单的红绿灯 00.成果展示 01.创建可变颜色的小球 02.编写代码 R1.cs R2.cs G1.cs G2.cs Y1.cs Y2.cs 03.自己 ...

  3. 学写毛笔字前要了解的

    学写毛笔字之前,首先得准备必备工具,那就是古人称之为"文房四宝"的笔.墨.纸.砚. 第一节 毛笔 一. 毛笔的种类 在挑选毛笔之前,我们先要了解一下毛笔的种类和性能. 毛笔的种类很 ...

  4. [Unity实战]一个简单的unity手写摇杆[入门级][手写demo][开箱可用]

    一个简单的unity手写摇杆 1.摇杆是什么 2.常见的unity摇杆插件 3.如何做一个简单摇杆(代码) 4.效果展示 5.github 1.摇杆是什么 固定移动摇杆的意思指固定一个摇杆的贴图,操作 ...

  5. canvas 手写毛笔字效果

    <!doctype html> <html lang="en"> <head><meta charset="UTF-8" ...

  6. 基于unity自己写光追。

    前几天写的博客好像访问量多了不少. 最近听到裁员的消息挺多的.但是貌似搞unity的找工作还行. 毕竟从事这个的相对少一点.前些年互联网大热,大家都一窝蜂的去学java,后台,前端这个那个的.你看现在 ...

  7. 【Unity自己写框架】FairyGUI UI框架(一)

    笔者之前沉迷游戏无法自拔,但是现在之前玩的游戏也不太爱玩了,发现下班到睡觉之前有2-3个小时空闲,仿佛发现了一笔宝贵的财富不能浪费. 笔者从事手游工作也有两年的时间了,主要做的是逻辑和SDK方面的工作 ...

  8. unity绘制管道_在Unity里写一个纯手动的渲染管线(一)

    随着Unity3D 2018的面世,Scriptable Rendering Pipeline,也就是可编程渲染管线这项新技术变得家喻户晓.官方在推出这项技术的时候,着重强调了他的各种优点,而笔者总结 ...

  9. 【Unity 手写PBR】Build-in管线:实现直接光部分

    写在前面 前期积累: GAMES101作业7提高-实现微表面模型你需要了解的知识 [技术美术图形部分]PBR直接光部分:Disney原则的BRDF和次表面散射模型 [技术美术图形部分]PBR全局光照: ...

最新文章

  1. 评审过程中,A小组发现了5个缺陷,B小组发现了9个缺陷,他们发现的缺陷中有3个是相同的。请问:还有多少个潜在的缺陷没有发现?
  2. JVM内存管理学习总结(一)
  3. linux下防火墙加白名单
  4. mvvm 耗时加载进度条_ZK的实际应用:MVVM –加载和渲染数据
  5. 用CSS伪类制作一个不断旋转的八卦图?
  6. 阿里P8架构师谈:分布式架构设计12精讲
  7. MFC鼠标OnMouseHover使用
  8. 03 CSS听课笔记
  9. [转]使用debugger调试JavaScript脚本
  10. 注解、路径、 Log4J、<settings>标签
  11. frame.origin和frame.size的心得
  12. java笔_JAVA笔试题(基础一)
  13. FreeMarker 日期转换
  14. Object型转list,jsonObject型转list方法,亲测可用
  15. Jump Desktop for Mac(远程桌面控制工具)怎么链接windows电脑
  16. python seo 外链_用python实现超级外链发布系统
  17. 大一计算机系挂科,大一挂科和大三挂科,哪个后果更严重?实际差距不是一星半点...
  18. 【手工】空投箱手工制作
  19. android人脸识别——HowOld测测你的年龄和性别
  20. 国外大学php课程,美国大学课程视频

热门文章

  1. 20220912深圳市梧桐山桃花源看植物
  2. ThinkPad T430 开机ThinkVantage指纹软件自启动
  3. 如何将设计CAD图纸文件打印成纸质的图纸,并居中显示?
  4. 给女生的幽默搞笑短信
  5. Foxmail新建文件夹隐藏
  6. iPhone更新iOS 16.3出现应用卡死、闪退的问题怎么办?
  7. MSP 现场服务管理
  8. css中首字母下沉_CSS中的经典版式效果:基本首字母大写和首字下沉
  9. 全套源码丨Cocos Creator 轻松玩转 3D 策略对战游戏,创意拉满!
  10. html标签数学标记语言MathML简介、工具及兼容