Unity实现写毛笔字

完整工程地址: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);
        }
 
    }
}

  • 19
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 66
    评论
嵌入式软件开发的CPU性能优化涉及到多核高并发的问题。随着科技的进步和嵌入式系统的不断发展,单核处理能力已经无法满足需求,因此多核技术应运而生。 多核技术指的是将多个CPU核心集成到一个芯片上,每个核心都可以独立地执行任务。通过将任务分配给不同的核心并发执行,可以提升系统的整体性能。在嵌入式软件开发中,利用多核技术进行性能优化主要有以下几个方面。 首先是任务划分和并行化。将系统中的任务划分为不同的子任务,并分配给不同的核心进行执行。通过合理的任务分配和并行化设计,可以最大程度地利用多核处理器的计算能力。 其次是任务调度和同步。在多核系统中,不同的核心可能要同时访问共享资源,因此需要进行任务调度和同步。通过合理的调度策略和同步机制,可以避免资源冲突和竞争状况,提高并发执行的效率。 另外,还可以采用并行算法和数据结构。在嵌入式软件开发中,选择适合并行执行的算法和数据结构,可以进一步提高多核处理器的性能。例如,可以采用并行搜索算法、并行排序算法等,以及适合多核并发访问的数据结构,如并发队列、并发哈希表等。 此外,还可以通过优化指令和数据流,减少数据的移动和拷贝操作。多核处理器的性能瓶颈之一是数据在不同核心之间的传输和拷贝。通过优化指令和数据流,可以尽量减少数据的移动和拷贝操作,提高多核处理器的数据访问效率。 综上所述,嵌入式软件开发的CPU性能优化多核高并发可以通过任务划分和并行化、任务调度和同步、并行算法和数据结构、优化指令和数据流等方式来实现。这些技术的应用可以提高系统的整体性能,适应日益复杂和高并发的嵌入式应用需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 66
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值