一、实现效果:

  1. 类似刮刮乐的擦除效果
  2. 支持多笔擦除(一次擦不干净)

二、所用技术点:

  1. RenderTexture
  2. Shader

三、实现原理:

一个相机单独渲染笔刷轨迹到RenderTexture上,在通过RenderTexture中的笔刷路径修改原图中对uv的像素点的alpha值实现透明或者半透明

1. Camera渲染到RenderTexture上:

a. 在场景中新建Camera并将ClearFlag设置为Don't Clear,目的是将渲染的物体连成轨迹。
b. 设置渲染层,只渲染笔刷(笔刷是一个球),笔刷根据鼠标位置移动即可。
c. 调整相机位置,使得要擦除的区域在整个视锥体内,也可以设置成正交投影。
d. 新建RenderTexture并挂载到相机上,相机设置为非激活状态(通过代码代码Camera.Render())进行渲染控制。因为只记录路径,所以只创建一个R8RenderTexture就可以
ps:需要关闭相机的垂直同步(MSAA),否则会将RenderTexture翻转渲染。
设置效果如下图:

2. 通过RenderTexture修改原图片透明度(shader实现)

a. 通过相机的矩阵将RenderTexture变换到像素坐标系
b. 修改对应uv的原像素点的alpha
shader代码:

Shader "Learning/guacaipiao"
{Properties{_MainTex ("Texture", 2D) = "white" {}// 相机渲染的RenderTexture_BlitTex ("BlitTexture", 2D) = "white" {}}SubShader{Tags{"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}Cull OffPass{Tags{"LightMode" = "ForwardBase"}// 开启alpah混合Blend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"#include "UnityCG.cginc"sampler2D _MainTex;float4 _MainTex_ST;sampler2D _BlitTex;struct a2v{float4 vertex : POSITION;float4 texcoord : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float3 worldPos : TEXCOORD1;float2 uv : TEXCOORD2;float4 paintPos : TEXCOORD3;};// 相机的投影矩阵// C#中通过SetMatrix传入// material.SetMatrix("paintCameraVP", camera.nonJitteredProjectionMatrix * camera.worldToCameraMatrix);float4x4 paintCameraVP;v2f vert(a2v v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);// 下面三行是通过投影矩阵将顶点变换到像素坐标系中([0, 1])float4 paintPos = mul(paintCameraVP, mul(unity_ObjectToWorld, v.vertex));paintPos /= paintPos.w; // 除以w分量,如果是相机正交投影可以省略o.paintPos.xy = paintPos.xy * 0.5 + 0.5; // 将[-1, 1] 变换到 [0, 1]return o;}fixed4 frag(v2f i) : SV_TARGET0{fixed4 texcolor = tex2D(_MainTex,i.uv);// 划过的轨迹r值为1,所以1 - r作为原图片的alpha值输出float mask = tex2D(_BlitTex, i.paintPos).r;return fixed4(texcolor.rgb, 1 - mask);}ENDCG}}
}

C#代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class GuaCaiPiaoSub : MonoBehaviour {public Camera rtCamera;public Transform brush;RenderTexture renderTexture;public Material renderMaterial;void Start () {renderTexture = rtCamera.targetTexture;renderMaterial.SetTexture("BlitTex", renderTexture);renderMaterial.SetMatrix("paintCameraVP", rtCamera.nonJitteredProjectionMatrix * rtCamera.worldToCameraMatrix);}void OnMouseDrag(){Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hitInfo;if (Physics.Raycast(ray, out hitInfo)){brush.position = hitInfo.point;rtCamera.Render();}}
}

到这一步的实现效果:

很容易看出,滑动慢的时候可以连成一条线,但是快速滑动时候就变成了分开的点了。为避免这种情况出现就是把相邻两帧的点连接起来,再进行渲染。下面就要说要优化效果相关的了。


四、效果优化

1. 笔刷改用LineRenderer

记录上一帧鼠标的位置,跟当前帧连线,绘制好LineRenderer后在进行渲染,这样就算两帧的点间隔大,也可以绘制两点的连线。另外可以通过调整LineRenderder宽度来调整笔刷大小。效果如下:

只需要改C#代码,代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class GuaCaiPiaoSub : MonoBehaviour {public Camera rtCamera;public LineRenderer lineBrush;RenderTexture renderTexture;public Material renderMaterial;void Start () {renderTexture = rtCamera.targetTexture;renderMaterial.SetTexture("BlitTex", renderTexture);renderMaterial.SetMatrix("paintCameraVP", rtCamera.nonJitteredProjectionMatrix * rtCamera.worldToCameraMatrix);}Vector3 prePos = Vector3.one * 10000;Vector3[] linePosArr = new Vector3[2];void OnMouseDrag(){Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hitInfo;if (Physics.Raycast(ray, out hitInfo)){if (prePos == Vector3.one * 10000) {prePos = hitInfo.point;}lineBrush.positionCount = 2;linePosArr[0] = prePos;linePosArr[1] = hitInfo.point;lineBrush.SetPositions(linePosArr);lineBrush.startWidth = 1f;lineBrush.endWidth = 1f;rtCamera.Render();prePos = hitInfo.point;}}void OnMouseUp(){prePos = Vector3.one * 10000;}
}

其中void OnMouseUp(){ prePos = Vector3.one * 10000; }是为了防止下次绘画时,跟上一帧点关联。

至此,基本的效果已经完成,大体已经可以满足刮彩票效果的需求。
但是需求是不断改变的,如果想要擦玻璃的效果,同一个地方擦多次才能擦得干净,这就需要下面的做法了。例如文章开头的效果。

2. 多次擦除

多次擦除首先想到叠加,但是渲染的r值只有10,这样如何做到叠加呢,这时候就需要另外两张RenderTexture来做混合:CurrentRTPrevirousRT分别是当前帧渲染的RT和上一帧渲染的RT,求出茶之后,将差值和要渲染的RenderTexture进行混合,然后作为最终应用到物体上。
实现混合需要使用一个接口:Graphics.Blit();,具体使用方式可以看unity的api
混合的shader代码:

Shader "Learning/blit"
{Properties{_BrushStrength ("BrushStrength", int) = 1}SubShader{Cull Off ZWrite Off ZTest Always// 设置混合模式Blend One OnePass{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 _CurrentRT; // 当前帧rtsampler2D _PrevirousRT; // 上一帧rtint _BrushStrength; // 笔刷强度 (需要几次擦干净)fixed4 frag (v2f i) : SV_Target{// 计算两帧的差值,输出的的值跟物体的rt进行混合fixed4 cur = tex2D(_CurrentRT, i.uv);fixed4 pre = tex2D(_PrevirousRT, i.uv);float r = step(0.5, cur.r - pre.r); // cg的内置setp函数 大于0.5为1,小于0.5为0return fixed4(r / _BrushStrength, 0, 0, 1);}ENDCG}}
}

C#代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class GuaCaiPiao : MonoBehaviour {public Camera rtCamera;RenderTexture renderTexture;public RenderTexture currentRT;public RenderTexture previrousRT;public Material blitMaterial;public Material renderMaterial;public LineRenderer lineBrush;[Range(1.0f, 5.0f)]public float brushWidth = 1.0f;void Start () {renderTexture = rtCamera.targetTexture;renderMaterial.SetTexture("BlitTex", renderTexture);renderMaterial.SetMatrix("paintCameraVP", rtCamera.nonJitteredProjectionMatrix * rtCamera.worldToCameraMatrix);blitMaterial.SetTexture("_CurrentRT", currentRT);blitMaterial.SetTexture("_PrevirousRT", previrousRT);}void OnMouseDown(){// 每次按钮要清空两张rtrtCamera.clearFlags = CameraClearFlags.Color;rtCamera.backgroundColor = Color.black;rtCamera.targetTexture = previrousRT;rtCamera.Render();rtCamera.targetTexture = currentRT;rtCamera.Render();rtCamera.clearFlags = CameraClearFlags.Nothing;}Vector3 prePos = Vector3.one * 10000;Vector3[] linePosArr = new Vector3[2];void OnMouseDrag(){Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hitInfo;if (Physics.Raycast(ray, out hitInfo)){if (prePos == Vector3.one * 10000) {prePos = hitInfo.point;}lineBrush.positionCount = 2;linePosArr[0] = prePos;linePosArr[1] = hitInfo.point;lineBrush.SetPositions(linePosArr);lineBrush.startWidth = brushWidth;lineBrush.endWidth = brushWidth;rtCamera.Render();// 将当前帧和上一帧差值混合到 renderTexture,具体混合的实现看shader// 混合的计算方式为 blitMaterial 上的 shader 中的计算Graphics.Blit(currentRT, renderTexture, blitMaterial);// 上一帧的rt替换为当前帧渲染的rt,为下一帧计算做准备Graphics.Blit(currentRT, previrousRT);prePos = hitInfo.point;}}void OnMouseUp(){prePos = Vector3.one * 10000;}void OnGUI(){if (GUI.Button(new Rect(0, 0, 80, 30), "RESET")){lineBrush.positionCount = 2;linePosArr[0] = Vector3.one * 10000;linePosArr[1] = Vector3.one * 10000;lineBrush.SetPositions(linePosArr);rtCamera.clearFlags = CameraClearFlags.Color;rtCamera.backgroundColor = Color.black;rtCamera.targetTexture = renderTexture;rtCamera.Render();rtCamera.targetTexture = previrousRT;rtCamera.Render();rtCamera.targetTexture = currentRT;rtCamera.Render();rtCamera.clearFlags = CameraClearFlags.Nothing;}}
}

实现效果及混合的演示:

左边黑色是为了观察混合后的RenderTexture的实时演示,右边为具体最终效果。具体需要擦除几次变干净调整blitMaterialBrushStrength属性。

以上为本片博客整体内容,主要应用为 3D 物体,UI可以类比进行实现。
具体项目可以看我的 github工程
喜欢shader的朋友可以看我的GitHub中ShaderProject

Unity RenderTexture实现 刮彩票、橡皮擦、擦除效果(3D物体)相关推荐

  1. GJM:Unity开发HTC vive 三、远处拖动3D物体 【转载】

    腾讯GAD  窗间风月 原文URL : http://gad.qq.com/article/detail/7192223 版权所有,禁止匿名转载:禁止商业使用:禁止个人使用. 在项目中会需要拖动远处的 ...

  2. java 渐变橡皮擦_HTML5 实现橡皮擦的擦除效果

    声明:本文为原创文章,如需转载,请注明来源WAxes,谢谢! 最近项目刚好用到这种效果,也就是有点像刮刮卡一样,在移动设备上,把某张图片刮掉显示出另一张图片.效果图如下:  DEMO请戳右:DEMO ...

  3. HTML5 实现橡皮擦的擦除效果

    最近项目刚好用到这种效果,也就是有点像刮刮卡一样,在移动设备上,把某张图片刮掉显示出另一张图片.效果图如下: 扫描下方二维码查看DEMO演示 这种在网上还是挺常见的,本来就想直接网上找个demo套用下 ...

  4. 【Axure教程】橡皮擦的擦除效果——刮奖原型

    橡皮擦的擦除效果是系统常见的效果,在可以画图编辑的系统中或者是在抽奖刮奖的系统中非常常见.所以今天和大家分享在Axure中如何制作橡皮刷的效果,我们会议刮奖原型为案例,教大家制作出一个刮奖效果的高保真 ...

  5. 【自定义控件】Android仿刮刮乐|刮刮卡|橡皮擦效果

    背景:需要实线一个类似刮刮乐的擦一擦效果,要求是在图片上覆盖半透明蒙层,蒙层支持手势擦除(类似橡皮擦). 思路:使用自定义View在onDraw时进行绘制,绘制模式选择混合模式(叠加变透明). 示例: ...

  6. 【Unity3D小功能】Unity3D中实现UI擦除效果、刮刮卡功能

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦. 一.前言 ...

  7. 【你问我答】unity实现一个刮刮乐效果

    文章目录 一.前言 二.原理 三.实操 1.安装环境 2. 渲染纹理RenderTexture 3.写脚本:ScratchUI.cs 4.ShaderGraph 5. UI制作 6.材质球 7.挂脚本 ...

  8. Unity中实现涂鸦和橡皮擦功能

    一.目的 1.想知道:Unity中实现涂鸦和橡皮擦功能 二.参考 1.Unity 实现橡皮擦效果 https://www.cnblogs.com/lzzhentou/p/11634696.html 总 ...

  9. 简单的Canvas刮刮乐带动画效果的实例

    今天做了个刮刮乐领奖品的小活动,感觉挺有用的,整理在这儿记录下 上个效果图先: 加了个简单的 css 动画效果 下面贴上主要代码: index.html <!DOCTYPE html> & ...

  10. Unity URP世界空间后处理扫描圈效果Shader

    实现原理 见这篇文章Unity Shader-深度相关知识总结与效果实现(LinearDepth,Reverse Z,世界坐标重建,软粒子,高度雾,运动模糊,扫描线效果)_puppet_master的 ...

最新文章

  1. 各大网站屏蔽搜索引擎抓取分析
  2. 阿米洛键盘取消win_【机械键盘】2020年双十二那些值得购买的机械键盘推荐
  3. IEEE发布2022年科技趋势全球调研:人工智能和机器学习、云计算及5G将成为下一年最重要的技术...
  4. oracle11g中SQL优化(SQL TUNING)新特性之Adaptive Cursor Sharing (ACS)
  5. async/await工作机制探究--NodeJS
  6. User breakpoint called from code at 0x771064f4
  7. mysql 错误提示_Mysql必读mysql常见的错误提示问题处理小结
  8. arcgis图层叠加不匹配
  9. Android Vendor Test Suite (VTS) 作用及测试方法
  10. 数据挖掘常用聚类算法性能比较
  11. 关于数据库设计是否需要加入(建立)外键
  12. pdf论文中查看使用的字体
  13. git 创建邮箱 用户名_初次安装git配置用户名和邮箱及密钥
  14. AAAI-19录用论文
  15. 修改植物大战僵尸游戏存档相关知识
  16. 中国显示器粘合胶市场趋势报告、技术动态创新及市场预测
  17. Javascript验证信用卡号、信用卡类型(最全最新)
  18. Android 12.0 锁屏页面禁止下拉状态栏
  19. Yii中CGridView单元格组件和数据提供者的使用
  20. 默纳克调试说明书_默纳克NICE调试说明书修改版

热门文章

  1. php union用法,php编程SQL语句union all的使用编程
  2. Windows 下使用PDH 获取CPU 使用率
  3. 概率笔记4——重要公式
  4. 2017第九届中国(上海)国际先进复合材料及应用展览会(AM China中国新材料展)会刊(参展商名录)
  5. div3 C. Dominant Piranha
  6. CC2530采集重力加速度MMA7455的值
  7. 开关电源电路组成及常见各模块电路分析
  8. linux安装Ice3.7 c++
  9. Android摇一摇领红包
  10. 联想小新14风扇一直很响怎么办