有之前的demo作为参考,我们基本上已经确定了实现白板绘画的可能性。我们需要做的就是利用
Graphics.Blit函数,将笔刷纹理、颜色绘制到一张RenderTexture保存下来,并重复利用,就能完整保存下来自己的绘画痕迹。
除了上面的Graphics.Blit
函数,最核心的就是这个shader了,里面就是将之前的Texture与最新的笔刷已经纹理再混合成一张新的图片。注释写得比较随意,看看就好。
Shader "Unlit/PaintBrush"
{
Properties
{
//之前的Texture
_MainTex ("Texture", 2D) = "white" {}
//笔刷纹理
_BrushTex("Brush Texture",2D)= "white" {}
//笔刷颜色
_Color("Color",Color)=(1,1,1,1)
//最新绘制笔刷的位置
_UV("UV",Vector)=(0,0,0,0)
//笔刷的大小
_Size("Size",Range(1,1000))=1
}
SubShader
{
Tags { "RenderType"="Transparent" }
LOD 100
//开启深度测试 关闭剔除...
ZTest Always Cull Off ZWrite Off Fog{ Mode Off }
//半透明混合
Blend SrcAlpha OneMinusSrcAlpha
//Blend One DstColor
Pass
{
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;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BrushTex;
fixed4 _UV;
float _Size;
fixed4 _Color;
v2f vert (appdata v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
//将笔刷的中心移动到整个纹理的中心
float size = _Size;
float2 uv = i.uv + (0.5f/size);
//计算动态的绘画的位置
uv = uv - _UV.xy;
//放大uv->缩小纹理
uv *= size;
fixed4 col = tex2D(_BrushTex,uv);
//去掉原来的颜色
//我这里基本上都是取rng图片做的笔刷
col.rgb = 1;
//*上笔刷的颜色
col *= _Color;
return col;
}
ENDCG
}
}
}
我们在一个白板上去画线,比在模型上用射线取模型uv的值应该更好理解了,我们只需要获取鼠标的位置计算与屏幕宽高的占比就是对应了图片的uv值。
//画点
private void Paint(Vector2 point)
{
if (point.x < 0 || point.x > _screenWidth || point.y < 0 || point.y > _screenHeight)
return;
Vector2 uv = new Vector2(point.x / (float)_screenWidth,
point.y / (float)_screenHeight);
_paintBrushMat.SetVector("_UV", uv);
Graphics.Blit(_renderTex, _renderTex, _paintBrushMat);
}
//插点
private void LerpPaint(Vector2 point)
{
Paint(point);
if (_lastPoint == Vector2.zero)
{
_lastPoint = point;
return;
}
float dis = Vector2.Distance(point, _lastPoint);
if (dis > _brushLerpSize)
{
Vector2 dir = (point - _lastPoint).normalized;
int num = (int)(dis / _brushLerpSize);
for (int i = 0; i < num; i++)
{
Vector2 newPoint = _lastPoint + dir * (i + 1) * _brushLerpSize;
Paint(newPoint);
}
}
_lastPoint = point;
}
Shader "Unlit/ClearBrush"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
ZTest Always Cull Off ZWrite Off Fog{ Mode Off }
Blend One DstColor
Pass
{
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;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
col = 0;
return col;
}
ENDCG
}
}
}
//-----------------------------------------------------------------------
//
// Copyright (c) codingworks. All rights reserved.
//
// codingworks
// [email protected]
//
//-----------------------------------------------------------------------
using UnityEngine;
using UnityEngine.UI;
public class Paint : MonoBehaviour
{
private Vector2 _lastPoint;
[SerializeField] private Material _clearBrushMat;
[SerializeField] private Material _paintBrushMat;
private RenderTexture _renderTex;
private int ScreenWidth, ScreenHeight;
[SerializeField] private RawImage _rawImage;
private float _paintLerpSize;
// Use this for initialization
private void Start()
{
ScreenWidth = Screen.width;
ScreenHeight = Screen.height;
var brushSize = _paintBrushMat.GetFloat("_Size");
float brushTexWidth = _paintBrushMat.GetTexture("_BrushTex").width;
_paintLerpSize = brushTexWidth / brushSize;
_renderTex = RenderTexture.GetTemporary(ScreenWidth, ScreenHeight, 24);
Graphics.Blit(null, _renderTex, _clearBrushMat);
_rawImage.texture = _renderTex;
}
// Update is called once per frame
private void Update()
{
if (_renderTex && _paintBrushMat)
{
if (Input.GetMouseButton(0))
LerpPaint(Input.mousePosition);
if (Input.GetMouseButtonUp(0))
_lastPoint = Vector2.zero;
}
}
private void LerpPaint(Vector2 point)
{
Paint(point);
if (_lastPoint == Vector2.zero)
{
_lastPoint = point;
return;
}
var dis = Vector2.Distance(point, _lastPoint);
if (dis > _paintLerpSize)
{
var dir = (point - _lastPoint).normalized;
var num = (int) (dis / _paintLerpSize);
for (var i = 0; i < num; i++)
{
var newPoint = _lastPoint + dir * (i + 1) * _paintLerpSize;
Paint(newPoint);
}
}
_lastPoint = point;
}
///
/// 绘画
///
/// 鼠标的位置
private void Paint(Vector2 point)
{
if (point.x < 0 || point.x > ScreenWidth || point.y < 0 || point.y > ScreenHeight)
return;
var uv = new Vector2(point.x / ScreenWidth,
point.y / ScreenHeight);
_paintBrushMat.SetVector("_UV", uv);
Graphics.Blit(_renderTex, _renderTex, _paintBrushMat);
}
}
https://github.com/coding2233/UnityPaint