当前位置: 首页 > article >正文

Unity开发绘画板——03.简单的实现绘制功能

从本篇文章开始,将带着大家一起写代码,我不会直接贴出成品代码,而是会把写代码的历程以及遇到的问题、如何解决这些问题都记录在文章里面,当然,同一个问题的解决方案可能会有很多,甚至有更好更高效的方式是我所没有想到的,大家也可以在评论区一起讨论一下。

1.监听鼠标输入

要实现绘画的功能,监听鼠标的输入是首先要解决的问题,我们希望按下鼠标左键之后开始在屏幕中绘制,要实现这个也比较简单。

我们先在Scripts文件夹下创建一个新脚本,命名为Painter.cs, 并将其挂载到场景中的Painter节点上,我们将在Painter.cs的Update中实现该逻辑。

public class Painter : MonoBehaviour
{
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
        
            //开始绘制
            var mousePosition = Input.mousePosition; //获取当前鼠标位置
        }
        
        
        if (Input.GetMouseButton(0))
        {
            //绘制
        }

        if (Input.GetMouseUp(0))
        {
            //停止绘制
        }
    }
}

 2.绘制图案

通过Graphics.DrawTexture接口向RenderTexture中绘制图案,这里的图案其实就是我们的笔刷样式了。

例如我们现在有一张圆形的图案:

想要用这个作为笔刷在一张空的RenderTexture上去作画,实现的代码也很简单,在Painter.cs中实现如下方法:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Painter : MonoBehaviour
{
    public RenderTexture renderTexture;
    public Texture2D brushTexture; //笔刷纹理
    public float brushSize = 5f;
    
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            
        }
        
        if (Input.GetMouseButton(0))
        {
            var mousePosition = Input.mousePosition;
            DrawBrush((int)mousePosition.x, (int)mousePosition.y);
        }
    }
    
    

    private void DrawBrush(int x, int y)
    {
        //纹理绘制的位置和大小
        Rect brushRect = new Rect(x, y, brushSize, brushSize);
        //指定渲染目标为renderTexture
        Graphics.SetRenderTarget(renderTexture);
        //保存当前矩阵状态
        GL.PushMatrix();
        //设置像素矩阵
        GL.LoadPixelMatrix(0, renderTexture.width, 0, renderTexture.height);
        //绘制纹理
        Graphics.DrawTexture(brushRect, brushTexture);
        //恢复之前的矩阵状态
        GL.PopMatrix();
        //重置渲染目标
        Graphics.SetRenderTarget(null);
    }

    
}

由于笔刷颜色是白色的,画板背景也是白色,为了让绘制结果能被看到,我们先把背景颜色调成黑色,改变MainCamera的Background即可(改变线条颜色我们之后会讲到),执行效果如下图所示:

可见已经有点感觉了,但是表现是很多间断的点,而不是连续的线,因为我们的绘制是由Update驱动的,所以绘制速度也受Update帧率影响,当鼠标速度快的时候尤其明显。

为了解决这个问题,我们可以考虑在点与点之间进行线性插值,绘制更多的点,这样就会显得连续了。

原理如下图:

从A点到B点之间线性插入若干点。

 为了达到此目的,我们需要从一下几个点入手:

  • 我们期望这些插入点的密度是可以通过参数调节的
  • 我们在绘制当前的位置时需要知道上一次绘制的位置

简单改一下代码:

新增一个previousMousePos字段,用于记录上一次笔刷位置

新增函数 private void DrawLine(Vector2 start, Vector2 end) 用于绘制两点之间的连线

优化后的代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Painter : MonoBehaviour
{
    public RenderTexture renderTexture;
    public Texture2D brushTexture; //笔刷纹理
    public float brushSize = 5f;    //笔刷大小
    public float resolutionMultiplier = 5;  //线性插值密度调节
    
    private Vector2 previousMousePos; //记录上一帧鼠标的位置  
    
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            previousMousePos = Input.mousePosition;
        }
        
        if (Input.GetMouseButton(0))
        {
            var mousePosition = Input.mousePosition;
            DrawLine(previousMousePos, mousePosition);
            previousMousePos = mousePosition;
        }
    }

    private void DrawLine(Vector2 start, Vector2 end)
    {
        float distance = Vector2.Distance(start, end);
        int steps = Mathf.CeilToInt(distance * resolutionMultiplier);
        for (int i = 0; i <= steps; i++)
        {
            float t = i / (float)steps;
            int x = Mathf.FloorToInt(Mathf.Lerp(start.x, end.x, t));
            int y = Mathf.FloorToInt(Mathf.Lerp(start.y, end.y, t));
            DrawBrush(x, y);
        }
    }

    private void DrawBrush(int x, int y)
    {
        Rect brushRect = new Rect(x, y, brushSize, brushSize);
        Graphics.SetRenderTarget(renderTexture);
        GL.PushMatrix();
        GL.LoadPixelMatrix(0, renderTexture.width, 0, renderTexture.height);
        Graphics.DrawTexture(brushRect, brushTexture);
        GL.PopMatrix();
        Graphics.SetRenderTarget(null);
    }

    
}

效果如下,感觉还是不错的~

这样我们就简单实现了绘制的功能,下一章我们来实现滑动条调节画笔的大小~


http://www.kler.cn/news/326517.html

相关文章:

  • 配置ssh后又报错git@github.com: Permission denied (publickey)
  • Linux【基础指令汇总】
  • 论文翻译 | LLaMA-Adapter :具有零初始化注意的语言模型的有效微调
  • SpringBoot+Thymeleaf发票系统
  • 【2025】springboot基于微信小程序记账本的设计与实现(源码+文档+调试+答疑)
  • kafka集群跨双网段及多网段通信问题解决(避免踩坑)
  • 享元(轻量级)模式
  • Spring Boot 进阶- Spring Boot入门程序详解
  • 初始docker以及docker的基本使用!!!
  • Cannon-es.js之Distance Constrait模拟布料
  • 【hot100-java】【合并两个有序链表】
  • MySQL数据库备份详解
  • Ubuntu下安装向日葵:闪退
  • SpirngBoot核心思想之一IOC
  • Leetcode 46 Permutation Leetcode 78 Subsets
  • AndroidStudio依赖报错
  • 力扣(leetcode)每日一题 1014 最佳观光组合
  • Android 开启相机一键拍照,一键录制
  • VirtualService和destinationRule
  • 大数据毕业设计选题推荐-国潮男装微博评论数据分析系统-Hive-Hadoop-Spark
  • Ranger集成CDH6.3.1详细步骤
  • 『功能项目』下载Mongodb【81】
  • C++中string的使用
  • ​​乐​​牛一​面​​​游​​卡​​一​二​​​​面​
  • 什么是IPv6
  • vue中使用jsencrypt加密
  • 超声波清洗机哪家好用又实惠?2024热门超声波清洗机选择推荐!
  • 工作笔记20240927——vscode + jlink调试
  • 数字化AI直播革命:无人直播新纪元,真AI赋能未来!
  • 小程序开发平台源码系统 各行各业适用的小程序开的平台 带完整的安装代码包以及搭建部署教程