Unity实现在白板上绘画涂鸦
来源:互联网 发布:袁立 知乎 编辑:程序博客网 时间:2024/05/01 22:47
前言
- 有段时间没有更新博客了,不知道应该写些什么,太简单感觉没有记录的必要,太难自己都没能理解,不知道如何下手。回归初心,记录自己想记录的东西。
- 需要实现一个白板绘画的功能,可以使用LineRenderer或者GL,但是都被我舍弃了,我想同时实现笔刷功能,以上两种方法都不合适,于是我选择了用材质渲染到RenderTexture上,用来记录绘画的痕迹。
- 之前已经在ue4中,实现了一个类似的功能,现在准备在unity上画在一个白板上,如果想在3D物体上涂鸦,就参考之前的博客:UE4快速实现涂鸦功能
思路
有之前的demo作为参考,我们基本上已经确定了实现白板绘画的可能性。我们需要做的就是利用
Graphics.Blit函数,将笔刷纹理、颜色绘制到一张RenderTexture保存下来,并重复利用,就能完整保存下来自己的绘画痕迹。
笔刷Shader
除了上面的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); }
注意事项
- 如果你在update获取的鼠标移动过快,两个点的距离太大会导致绘画不连续,这里就需要插值绘制,我这里的做法不太严谨,有需要可以自己重新写插值算法。
//插点 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; }
- 因为我们使用到了RenderTexture,unity好像会将RenderTexture缓存下来以便下次的快速调用,但是这就有一个新的问题,每次我们重新运行的时候,RenderTexture可能还会保留上次的内容,这时候,我们就可以在最开始的时候,将RenderTexture的内容全部清除掉。
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 file="Test.cs" company="Codingworks Game Development">// Copyright (c) codingworks. All rights reserved.// </copyright>// <author> codingworks </author>// <email> coding2233@163.com </email>// <time> #CREATETIME# </time>//-----------------------------------------------------------------------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; } /// <summary> /// 绘画 /// </summary> /// <param name="point">鼠标的位置</param> 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); }}
截图展示
未完成
- 橡皮擦还没做,最好参照笔刷shader,再单独写一个橡皮擦的shader
- 颜色如果能做成一个面板,能随意选择颜色
- 本来就只是一个demo,就不要求太多…
总结
- 作为一个程序员,绘画和写字就不要吐槽了
- 整篇博客下来,自己都有一种不知所云的感觉,思维太飘了
- 有兴趣的同学,直接看整个工程吧
源码链接
https://github.com/coding2233/UnityPaint
阅读全文
0 0
- Unity实现在白板上绘画涂鸦
- unity在模型上绘画
- [Unity插件]Unity的画图绘画插件在人物角色物体身上涂鸦绘画
- Unity实现在3D模型上涂鸦
- android 实现在照片上涂鸦
- 最近买了一块白板, 以后就在上面涂鸦
- android:在照片上绘制涂鸦
- Android在ImageView上做一些涂鸦
- 关于在unity上实现音频播放
- 白板上的黑点
- 白板应用的普及正好适合我的绘画软件
- 第5部份:在显示区域上绘画
- 30.因为绘画,我在豆瓣上认识了老婆
- Android中在屏幕上涂鸦的例子
- Android中在屏幕上涂鸦的例子
- Android中在屏幕上涂鸦的例子
- android涂鸦程序(在图像上绘制)
- 以Imageview为画布,在其上涂鸦
- 《深入理解mybatis原理》 MyBatis的二级缓存的设计原理
- Codeforces897B Chtholly's request
- BZOJ1269 [AHOI2006]文本编辑器editor 【82行splay】
- 约瑟夫环问题
- zookeeper下载安装
- Unity实现在白板上绘画涂鸦
- gitignore常见配置
- search-insert-position/search-in-rotated-sorted-array/search-in-rotated-sorted-array ii
- 善用 ApacheAB 和 VisualVM, 做开发阶段代码性能调优
- Ubuntu 16.04找不到语言支持的解决办法以及安装搜狗输入法
- Hibernate的学习之路二十二(一对多的级联删除)
- 如果创建工程的时候没有勾选Unit Test选项,如何创建单元测试 正确删除test target
- win 10 取消用户帐户控制
- 发现智能变革力量 GTIC AWARDS 2018年度评选正式启动!