笔刷效果的实现

最终效果:

片元着色器代码:

Shader "Hidden/Brush"
{Properties{_MainTex("MainTex",2D)="white"{}_CenterX("CenterX",int) = 0_CenterY("CenterY",int) = 0_Radius("Radius",int) = 30}SubShader{Tags {"Queue" = "Transparent""RenderType" = "Transparent"}Blend  SrcAlpha OneMinusSrcAlpha// No culling or depthCull Off ZWrite Off ZTest AlwaysPass{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;};v2f vert(appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = v.uv;return o;}sampler2D _MainTex;half _CenterX;half _CenterY;half _Radius;half4 _MainTex_TexelSize;fixed4 frag(v2f i) : SV_Target{fixed4 col;half2 pixelPos = half2(i.uv.x*_MainTex_TexelSize.z,i.uv.y*_MainTex_TexelSize.w);half2 dis = pixelPos - half2(_CenterX,_CenterY);col = tex2D(_MainTex, i.uv);duziclip(col.a - 0.5);if (sqrt(dis.x*dis.x + dis.y*dis.y) < _Radius)col.a = 0.49;          //unity的renderTexture有个问题,就是用透明的RGBA贴图设置的时候,//它会将所有的透明通道信息单独拿出来全部做个乘法叠加,//depth only的渲染机制导致,会分开存储透明信息。return col;}ENDCG}}
}

脚本代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Rendering;public class control : MonoBehaviour
{private Material _Mat;                      //要使用的材质private RectTransform rt;                   //要被擦除的uipublic Texture _Texture;                    //ui绘制的贴图private RenderTexture _After;               //为材质提供的实时贴图private RenderTexture _Before;              //过渡贴图变量void Start(){rt = GetComponent<RectTransform>();                _Mat = new Material(Shader.Find("Hidden/Brush"));rt.GetComponent<Image>().material = _Mat;_After = new RenderTexture((int)rt.rect.width, (int)rt.rect.height, 0, RenderTextureFormat.ARGB32);_Before = new RenderTexture((int)rt.rect.width, (int)rt.rect.height, 0, RenderTextureFormat.ARGB32);Graphics.Blit(_Texture, _After);Graphics.Blit(_Texture, _Before);_Mat.SetTexture("_MainTex", _After);           //初始绘制贴图}private void Update(){if (Input.GetMouseButton(0)){var pos = Vector2.zero;//获取相对于该贴图的像素纹理坐标RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, Input.mousePosition, null, out pos);pos = pos + new Vector2(rt.rect.width, rt.rect.height) / 2;_Mat.SetInt("_CenterX", (int)pos.x);_Mat.SetInt("_CenterY", (int)pos.y);Graphics.Blit(_Before, _After, _Mat);       Graphics.Blit(_After, _Before);}}//绘制成功可以将_After拷贝到texture2D上,通过遍历像素点的透明度来判断贴图是否擦除完成//成功后通过Release()方法来释放_After/_Before空间资源}

使用着色器来实现刷子效果主要要解决的问题以及解决方法:

问题:

  1. 如何使用脚本将鼠标位置映射到要被擦除的贴图的纹理坐标 。
  2. 如何保存下来每一次操作后的贴图以便于下一次在上一次操作的基础上继续操作。
  3. 如何在Shader其中将笔刷范围内的像素值的透明度设置为0,即全透明。
  4. 因为传入Shader中的贴图是RenderTexture类型的,所以如何将Render Texture的某些像素点设置为全透明。

方法:

 var pos = Vector2.zero;//获取相对于该贴图的像素纹理坐标RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, Input.mousePosition, null, out pos);pos = pos + new Vector2(rt.rect.width, rt.rect.height) / 2;

rt就是要被擦出的UI,可以通过 RectTransformUtility.ScreenPointToLocalPointInRectangleAPI来将屏幕坐标系下的鼠标    位置转换为该UI纹理下的像素坐标(即左下角为原点(0,0),右上角为(width,height))。

我们可以在程序初始化时,将RenderTexture类型的_After绑定到刷子Shader的_MainTex属性上,在程序运行中,我们只需要改变_After,刷子Shader对应处理的_MainTex也会发生变化。

 _Mat.SetTexture("_MainTex", _After);           //初始绘制贴图  需要将每次修改过后的_After保留下来以便下次继续在上次操作的基础上继续进行操作。所以我们需要一个RenderTexture类型的过渡变量_Before;Graphics.Blit(_Before, _After, _Mat);Graphics.Blit(_After, _Before);

本来想的时将像素透明度设置为0(col.a=0),就可以让像素透明。但是后来遇到了一个坑如果将col.a设置为0的话,上一次的操作就不会保留下来。也就是说将RenderTexture的像素透明度设置为0,在RenderTexture中存储时透明度并不是简单的存储为0。unity的renderTexture有个问题,透明的RGBA贴图设置的时候,它会将所有的透明通道信息单独拿出来全部做个乘法叠加,depth only的渲染机制导致,会分开存储透明信息。所以这里并不是简单的将像素点透明度设置为0就可以了。可以使用像素剔除的方法,也就是说把低于特定透明度的像素直接丢弃掉。

当我设置RenderTexture的透明度时发现,a=0.5是半透明,a=0||a=1都是不透明。所以我的思路是将我要剔除的区域的透明度设置为0.49,只要把透明度低于0.5的像素点剔除掉就可以实现擦出的效果。

     clip(col.a - 0.5);if (sqrt(dis.x*dis.x + dis.y*dis.y) < _Radius)col.a = 0.49;      //unity的renderTexture有个问题,就是用透明的RGBA贴图设置的时候,//它会将所有的透明通道信息单独拿出来全部做个乘法叠加,//depth only的渲染机制导致,会分开存储透明信息。return col;

更新

**************************************************************************************

上一次实现笔刷效果时,因为时间原因没有完善笔刷中间擦涂不干净的问题

下面是最近更新

**************************************************************************************

问题:

首先,擦除不干净的问题,应该是当鼠标移动过快时,在Update函数中向Shader传送的两个点距离太远,笔刷之擦除鼠标位置点半径范围之内的像素,所以中间区域就漏掉。

解决思路:

直接细分上次鼠标位置和本次鼠标位置,然后根据细分返回的点逐点擦除。

我主要使用直线算法来进行点的细分,也可以使用贝兹曲线。主要是我觉得如果项目不会让帧率很低的话,使用直线算法来进行细分已经可以了,效果差别不大。如果想使用贝兹曲线来生成点的话,也是可以的。

    private Vector2 _LastPos;private void Update(){if (Input.GetMouseButtonDown(0)){_TwoPoints = true;_LastPos = Input.mousePosition;}else if (Input.GetMouseButton(0)){var currentPos = Input.mousePosition;if (Vector2.Distance(_LastPos, currentPos) > 30){int segments = (int)(Vector2.Distance(_LastPos, currentPos) / 10.0F);var points = GetPoints(_LastPos, currentPos, segments);Draw(points);_LastPos = Input.mousePosition;}}if (Input.GetMouseButtonUp(0)){if (PixelDetection())Debug.Log("Complete");elseDebug.Log("No Complete");}}/// <summary>/// 细分/// </summary>/// <param name="lastPos"></param>/// <param name="endPos"></param>/// <param name="segments"></param>/// <returns></returns>public Vector2[] GetPoints(Vector2 lastPos, Vector2 endPos, float segments){//   Debug.Log(segments);Vector2[] points = new Vector2[(int)segments];for (int i = 0; i < (int)segments; i++){points[i] = ((endPos - lastPos) / segments) * i + lastPos;//  Debug.Log(points[i]);}return points;}/// <summary>/// 根据细分出的点来逐个擦除/// </summary>/// <param name="points"></param>public void Draw(Vector2[] points){for (int i = 0; i < points.Length; i++){var pos = Vector2.zero;//获取相对于该贴图的像素纹理坐标RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, points[i], null, out pos);pos = pos + new Vector2(rt.rect.width, rt.rect.height) / 2;_Mat.SetInt("_CenterX", (int)pos.x);_Mat.SetInt("_CenterY", (int)pos.y);Graphics.Blit(_Before, _After, _Mat);Graphics.Blit(_After, _Before);}}/// <summary>/// 像素检测,是否擦除干净/// </summary>/// <returns></returns>public bool PixelDetection(){var currentRT = RenderTexture.active;RenderTexture.active = _After;_TempTexture.ReadPixels(new Rect(0, 0, _After.width, _After.height), 0, 0);RenderTexture.active = currentRT;var number = 0;for (int i = 0; i < _TempTexture.width; i++){for (int j = 0; j < _TempTexture.height; j++){var a = _TempTexture.GetPixel(i,j).a;if (a < 0.9F)number++;}}var count = _TempTexture.width * _TempTexture.height;var percentage = (float)number / (float)count;if (percentage > 0.95)return true;elsereturn false;}

Demo地址:https://github.com/RXBXX/Brush

最后多谢https://gameinstitute.qq.com/community/user/1054734feng,解决了我关于renderTexture透明度存储的问题。希望可以帮到有类似需求的读者。希望在交流学习中成长。

图形学应用_着色器实例—笔刷效果相关推荐

  1. OpenGL ES _ 着色器_片断着色器详解

    OpenGL ES _ 入门_01 OpenGL ES _ 入门_02 OpenGL ES _ 入门_03 OpenGL ES _ 入门_04 OpenGL ES _ 入门_05 OpenGL ES ...

  2. 墨迹笔刷效果怎么制作?

    在观看一些古装电视剧或者电影的时候,经常会在其中看到墨迹笔刷的效果,所以今天就为大家简单介绍一下怎么使用Vegas制作墨迹笔刷效果. 墨迹笔刷开场制作方法: 步骤1:打开视频制作软件,新建三个视频轨道 ...

  3. Unity开发备忘录000006:用Unity标准着色器构建金属材质效果(二)

    按照Unity开发备忘录000005:用Unity标准着色器构建金属材质效果(一)所介绍的方法,我们又做了一个如下的模型渲染. 在此基础上我们再给它加一个高度贴图,其立体感的细节会更加丰富,如下图: ...

  4. 【 Threejs 】- Shader 着色器实例渲染教程

    着色器在threejs中是一个难点,话不多说,先来看看着色器是什么? 如果您已经有使用计算机绘图的经验,您就会知道在这个过程中您先画一个圆,然后画一个矩形.一条线.一些三角形,直到您组成您想要的图像. ...

  5. c语言opengles程序,OpenGL ES _ 着色器_程序

    演示图 你不知道这个东西,请不要看了,请看我的其他文章先了解一下O! 学习目标 掌握着色器程序的执行过程 简单的例子 `` uniform float t; // 时间 uniform mat4 gl ...

  6. 着色器实例 代码+注释 更新中【描边、卡通渲染、法线颜色、贴图动画等等】

    描边着色器 // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader " ...

  7. Unity ShaderLab特效教程 适用于贴图、sprite和ugui的2d着色器实例 代码+详解注释 【将贴图转为马赛克效果】

    如果代码中有什么不清楚请查看以下基础知识 Shader基础知识 unity3d 中 七种坐标知识详解 一个将贴图转为马赛克效果的shader: 万恶的马赛克其实也是一种艺术风格,如果你在开发2d游戏不 ...

  8. webgl 着色器_“着色器”是什么意思? 如何使用HTML5和WebGL创建它们

    webgl 着色器 本文是Microsoft的Web开发技术系列的一部分. 感谢您支持使SitePoint成为可能的合作伙伴. 您可能已经注意到,去年我们第一次谈论了babylon.js ,最近我们发 ...

  9. Unity ShaderLab特效教程 适用于贴图、sprite和ugui的2d着色器实例 代码+详解注释 【锐化效果】

    如果代码中有什么不清楚请查看以下基础知识 Shader基础知识 unity3d 中 七种坐标知识详解 锐化效果 笑狗图 代码 Shader "Custom/BlurBox" {Pr ...

最新文章

  1. Ubuntu下配置Nginx HTTPS
  2. [转载]潜移默化学会WPF(技巧篇)--具有Items元素的控件子项获取(一)
  3. node mysql 多个_使用Node.js处理多个MySQL查询
  4. 【Linux】一步一步学Linux——sudo命令(105)
  5. ant vue 离线文档_超全离线开发手册
  6. UVA10800 Not That Kind of Graph【模拟+绘图】
  7. 年仅30岁!腾讯游戏程序员毛星云意外身故。。。
  8. 在电脑上怎么做报表新手_初学者在电脑上如何制作电子表格
  9. 一个在线QQ客服代码分析
  10. java httpclient 关闭_httpclient 4种关闭连接
  11. CSDN 写作小技巧(3)——学会在 CSDN 中插入超链接
  12. STM32CubeIDE USB Audio声卡 WM8978 + I2S
  13. arm模拟器手机版_在 Android 模拟器上运行 ARM 应用
  14. 使用原生js 监听video 当前播放时间和是否点击了播放或者暂停按钮
  15. 淮师计算机网络题库,淮阴师范学院(淮师)计算机基础一至六章习题
  16. 未能从程序集“mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”中加载类型“System.Coll
  17. Windows系统下Python安装教程
  18. SQLZOO_SELECT within SELECT Tutorial(sql server)
  19. 2021 PostgreSQL 中国技术大会 PPT 下载
  20. 2022-2023 ICPC Brazil Subregional Programming Contest

热门文章

  1. Polya计数法总结 POJ24092154SGU282
  2. WHQL认证环境部署以及测试攻略二之HLK或者HCK软件的安装
  3. ubuntu 安装 opera 浏览器
  4. 比特,字节,字符,字的解释
  5. 分布式缓存 redis 问题(1)
  6. Avada多功能企业主题去授权无限制版本(更新V5.8.2)WordPress主题模板
  7. 百度地图开源库 BMaplib
  8. Facebook发帖的图片标准
  9. 理解Python中的yield
  10. 930页!熬夜整理了一份包含算法+数据结构+大厂面经 高分原创文章的电子书送给你