从本篇文章开始,将带着大家一起写代码,我不会直接贴出成品代码,而是会把写代码的历程以及遇到的问题、如何解决这些问题都记录在文章里面,当然,同一个问题的解决方案可能会有很多,甚至有更好更高效的方式是我所没有想到的,大家也可以在评论区一起讨论一下。
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);
}
}
效果如下,感觉还是不错的~
这样我们就简单实现了绘制的功能,下一章我们来实现滑动条调节画笔的大小~