Unity实现在白板上绘画涂鸦

前言

  • 有段时间没有更新博客了,不知道应该写些什么,太简单感觉没有记录的必要,太难自己都没能理解,不知道如何下手。回归初心,记录自己想记录的东西。
  • 需要实现一个白板绘画的功能,可以使用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 DstColorPass{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 100ZTest Always Cull Off ZWrite Off Fog{ Mode Off }Blend One DstColorPass{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 texturefixed4 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 initializationprivate 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 frameprivate 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);}
}

截图展示

这里写图片描述

未完成

  1. 橡皮擦还没做,最好参照笔刷shader,再单独写一个橡皮擦的shader
  2. 颜色如果能做成一个面板,能随意选择颜色
  3. 本来就只是一个demo,就不要求太多…

总结

  1. 作为一个程序员,绘画和写字就不要吐槽了
  2. 整篇博客下来,自己都有一种不知所云的感觉,思维太飘了
  3. 有兴趣的同学,直接看整个工程吧

源码链接

https://github.com/coding2233/UnityPaint

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/353754.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

手机怎么使用涂鸦?分享几个手机视频怎么添加涂鸦的妙招

相信大家在给视频进行处理时&#xff0c;都会运用一些视频剪辑工具来给视频增添一些涂鸦等效果&#xff0c;比如把云朵涂鸦成独角兽或者与其相似的图案&#xff0c;让那些观看我们视频的人在视觉上增加了些许乐趣&#xff0c;不会显得视频画面太过于枯燥乏味。 那你们知道这些…

使用Fabric.js库制作一个绘画网站,实现绘画板涂鸦功能

1.学习之前 这个功能是基于vue的&#xff0c;如果你没有学过vue&#xff0c;可能对里面的知识不理解&#xff0c;对于canvas要有一点点的理解&#xff0c;如果没有&#xff0c;建议学习一下 如果实在需要关于Fabricjs写的绘画功能&#xff0c;可以看一下我参考其他人写的文档&a…

视频涂鸦怎么弄?视频涂鸦用什么软件?

随着时代的发展&#xff0c;大众对于“艺术”这个词的诠释也会变得不同。这段时间里&#xff0c;我的朋友在朋友圈、微博发布的视频都带有浓浓的个人风格&#xff0c;但总体上都是依据“涂鸦”这一形式来进行的。通过在视频中绘制涂鸦&#xff0c;来抒发内心的情绪、展示对艺术…

如何用ps做出街头涂鸦效果

制作过程大概几分钟时间即可搞定&#xff0c;我们主要用的是陌鱼社区街头涂鸦喷绘行为艺术效果PS动作&#xff0c;需要的可以搜一下应该可以找到&#xff0c;下面是效果&#xff1a; 01、载入我们下载文件包时面的预设文件&#xff0c;包括笔刷、图案、动作&#xff0c;关闭Ps软…

ROS:坐标管理系统

目录 一、机器人中的坐标变换二、TF功能包2.2TF功能包简介2.2TF坐标变换实现2.3TF案例 三、小海龟跟随实验3.1打开小程序3.2查看当前的TF树3.3坐标相对位置关系可视化1&#xff08;tf_echo&#xff09;3.4坐标相对位置关系可视化2&#xff08;rviz&#xff09; 一、机器人中的坐…

人事管理项目-前端实现

人事管理项目-前端实现 引入Element和Axios开发Login页面配置路由配置请求转发启动前端项目 引入Element和Axios 前端UI使用Element&#xff0c;网络请求则使用Axios&#xff0c;因此首先安装Element和Axios依赖&#xff0c;代码如下&#xff1a; 依赖添加成功后&#xff0c;接…

妖精的尾巴手游快速升级辅助 爱蜂窝自动新手挂机升级

妖精的尾巴手游上线了&#xff0c;是一款特别火的手游&#xff0c;那么如果才能快速升级&#xff0c;迅速提升战斗力呢&#xff0c;主要依靠游戏里面的这些内容。 方式一&#xff1a;主线任务 主线任务是快速升级中速度最快的&#xff0c;能获得大量的经验和等级&#xff0c;后…

妖精的尾巴勇气之旅服务器维护,妖精的尾巴勇气之旅攻略大全 新手攻略开局发展技巧[多图]...

妖精的尾巴勇气之旅怎么玩&#xff0c;作为一个新手玩家来说的话&#xff0c;在开局的时候要做些什么会比较的好&#xff0c;如何能够更好的上手&#xff0c;带来一个比较好的发展&#xff0c;也可以为后续的阶段起到一个比较不错的铺垫呢&#xff1f;下面来了解下&#xff01;…

【妖精的尾巴win7动漫主题】

主题描述&#xff1a;本主题是由主题世界win7主题下载独家原创制作而成 本地下载 迅雷下载 妖精的尾巴win7动漫主题介绍 妖精的尾巴卡通桌面壁纸下载《鼠标右键另存为本地》 妖精的尾巴卡通桌面主界面效果图 妖精的尾巴卡通桌面开始菜单效果图 妖精的尾巴卡通桌面图标效果图 妖…

智能算法实现PID智能车控制系统

目录 第一章 绪论 1.1 智能车概述 1.2 智能PID研究现状 1.3 本文工作 第二章 PID控制简介 第三章 内模PID简介 3.1 内模PID控制 第四章 内模智能PID智能车控制系统设计 4.1 系统设计 4.2 内模控制原理 第五章 系统仿真及结果分析 5.1 系统仿真分析 5.2 控制效…

Linux Shell 实现一键部署mariadb10.11

mariadb MariaDB数据库管理系统是MySQL的一个分支&#xff0c;主要由开源社区在维护&#xff0c;采用GPL授权许可 MariaDB的目的是完全兼容MySQL&#xff0c;包括API和命令行&#xff0c;使之能轻松成为MySQL的代替品。在存储引擎方面&#xff0c;使用XtraDB来代替MySQL的Inno…

简单聊一聊数据库驱动

数据库驱动通常是数据库厂家提供的&#xff0c;他们按照jdbc协议对自家数据库封装了一套可对外调用的API。在应用程序和数据库之间起到了桥接的作用。它是一个软件组件&#xff0c;提供了与特定数据库系统进行通信的接口和功能。 1. 数据库驱动的作用&#xff1a; 连接数据库&…

科研工具-R-META分析与【文献计量分析、贝叶斯、机器学习等】多技术融合实践

Meta分析是针对某一科研问题&#xff0c;根据明确的搜索策略、选择筛选文献标准、采用严格的评价方法&#xff0c;对来源不同的研究成果进行收集、合并及定量统计分析的方法&#xff0c;最早出现于“循证医学”&#xff0c;现已广泛应用于农林生态&#xff0c;资源环境等方面。…

如何查看文件的MD5值?

MD5 什么是MD5&#xff1f; md5是一种信息摘要算法&#xff0c;是计算机广泛使用的杂凑算法之一&#xff08;又译摘要算法、哈希算法&#xff09;&#xff0c;它可以从一个字符串或一个文件中按照一定的规则生成一个特殊的字符串&#xff08;这个特殊的字符串就被称之为摘要&…

查看文件的MD5 值

从网上下载到资源文件后&#xff0c;为了确保下载的文件没有被黑客非法篡改&#xff0c;一般都会校验一下MD5是否与最初上传的版本是否一致。查看两个文件的MD5 值可以判断文件在传输过程中有没有损坏&#xff0c;或者丢失字节。 Windows电脑 window&#xff08;键盘左下角Ctr…

什么是md5

什么是md5? md5是一种不可逆的散列算法 不可逆&#xff1a;从明文可以得到密文&#xff0c;但是从密文不可以得到明文。散列&#xff1a;通过一种函数得到一个映射值&#xff0c;有可能是重复的&#xff01;但重复的概率比较低&#xff0c;例如设置字符大小写字母数字的密码…

简单的MD5查询工具

这是一个MD5查询工具&#xff0c;输入将要加密的字符串&#xff0c;可以查询到其MD5值。 目前提供了32位和16位两种算法。 什么是MD5&#xff1f; Message Digest Algorithm MD5&#xff08;中文名为消息摘要算法第五版&#xff09;为计算机安全领域广泛使用的一种散列函数&a…

一文读懂md5,md5有什么用,什么是md5加盐

md5是一种密码散列函数&#xff0c;在计算机安全领域得到广泛的应用。本文将带大家了解一些md5的知识点&#xff0c;什么是md5&#xff0c;md5有什么用&#xff0c;什么是md5加盐&#xff0c;为什么md5不可逆&#xff0c;为什么md5可能会被解密&#xff1f;帮助大家快速了解md5…

MD5值的简介和查看

MD5值的简介和查看 MD5即Message-Digest Algorithm 5&#xff08;信息-摘要算法第5版&#xff09;&#xff0c;用于确保信息传输完整一致。每个文件都可以用MD5验证程序算出一个固定的MD5码来。 MD5在论坛上、软件发布时经常用&#xff0c;是为了保证文件的正确性&#xff0c…