使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻

文章目录

一、前言

嗨,大家好,我是新发。昨天写了篇文章:《使用Unity ShaderGraph实现在模型上涂鸦的效果,那么,纹个手吧》
这个原理,可以触类旁通,比如刮刮乐的刮卡效果,也可以利用这个原理来实现,今天就教大家如何使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果。

本文最终效果如下:
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻
本文Demo工程已上传到CodeChina(最近GitHub貌似有问题,经常连不上),感兴趣的同学可自行下载学习。
CodeChina地址:https://codechina.csdn.net/linxinfa/UnityShaderGraphScratchTicket

注意,我使用的Unity版本是2020.2.7f1c1ShaderGraph版本是Version 10.3.2 - March 01, 2021,如果你使用的版本比我的版本低,则可能运行我的Demo工程会有问题。
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻

二、原理

原理就是利用Alpha通道,Alpha0时透明,我们可以使用一张RenderTexture作为Alpha通道图,与UI的主贴图的Alpha通道图相乘即可。
根据鼠标刮的位置,在RenderTexture上对应的位置画笔刷印记即可。

三、实操

1、RenderTexture

首先,创建一个Render Texture
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻
设置一下Render Texture的尺寸和格式。
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻

2、笔刷图案

photoshop做两张图,一张纯黑色的方图(用于初始化填充Render Texture),一张笔刷图,简单起见,笔刷图案我就用一个白点。
如下:
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻

3、写脚本:ScratchUI.cs

开始写代码,就一个脚本:ScratchUI.cs。代码的注释我写得比较清晰了,大家应该能看懂。
代码如下:

// ScratchUI.cs

using UnityEngine;
using UnityEngine.EventSystems;


/// <summary>
/// 刮刮乐UI
/// </summary>
public class ScratchUI : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
    /// <summary>
    /// 绘制的目标图片
    /// </summary>
    public RenderTexture renderTexture;
    /// <summary>
    /// 笔刷
    /// </summary>
    public Texture brushTexture;

    /// <summary>
    /// 空白图
    /// </summary>
    public Texture blankTexture;

    /// <summary>
    /// mask的RectTransform
    /// </summary>
    public RectTransform rectTransform;
    /// <summary>
    /// 画布
    /// </summary>
    public Canvas canvas;

    private bool m_isMove = false;

    private void Start()
    {
        DrawBlank();
    }

    /// <summary>
    /// 初始化RenderTexture
    /// </summary>
    private void DrawBlank()
    {
        // 激活rt
        RenderTexture.active = renderTexture;
        // 保存当前状态
        GL.PushMatrix();
        // 设置矩阵
        GL.LoadPixelMatrix(0, renderTexture.width, renderTexture.height, 0);

        // 绘制贴图
        Rect rect = new Rect(0, 0, renderTexture.width, renderTexture.height);
        Graphics.DrawTexture(rect, blankTexture);

        // 弹出改变
        GL.PopMatrix();

        RenderTexture.active = null;
    }

    /// <summary>
    /// 在RenderTexture的(x,y)坐标处画笔刷图案
    /// </summary>
    /// <param name="x">Graphics坐标系下的x</param>
    /// <param name="y">Graphics坐标系下的y</param>
    private void Draw(int x, int y)
    {
        // 激活rt
        RenderTexture.active = renderTexture;
        // 保存当前状态
        GL.PushMatrix();
        // 设置矩阵
        GL.LoadPixelMatrix(0, renderTexture.width, renderTexture.height, 0);


        // 绘制笔刷图案
        x -= (int)(brushTexture.width * 0.5f);
        y -= (int)(brushTexture.height * 0.5f);
        Rect rect = new Rect(x, y, brushTexture.width, brushTexture.height);
        Graphics.DrawTexture(rect, brushTexture);

        // 弹出改变
        GL.PopMatrix();

        RenderTexture.active = null;
    }

    /// <summary>
    /// 按下
    /// </summary>
    public void OnPointerDown(PointerEventData data)
    {
        m_isMove = true;
    }

    /// <summary>
    /// 抬起
    /// </summary>
    public void OnPointerUp(PointerEventData data)
    {
        m_isMove = false;
    }

    private void Update()
    {
        if (m_isMove)
        {
            onm ouseMove(Input.mousePosition);
        }
    }

    /// <summary>
    /// 刮卡
    /// </summary>
    /// <param name="position">刮卡的屏幕坐标</param>
    private void onm ouseMove(Vector2 position)
    {
        // 获取刮的位置的ui局部坐标
        var uiLocalPos = ScreenPosToUiLocalPos(position, rectTransform, canvas.worldCamera);
        // 将局部坐标转化为uv坐标
        var uvX = (rectTransform.sizeDelta.x / 2f + uiLocalPos.x) / rectTransform.sizeDelta.x;
        var uvY = (rectTransform.sizeDelta.y / 2f + uiLocalPos.y) / rectTransform.sizeDelta.y;
        // 将uv坐标转化为Graphics坐标
        var x = (int)(uvX * renderTexture.width);
        // 注意,uv坐标系和Graphics坐标系的y轴方向相反
        var y = (int)(renderTexture.height - uvY * renderTexture.height);

        Draw(x, y);
    }

    /// <summary>
    /// 将屏幕坐标抓话为目标RectTransform的局部坐标
    /// </summary>
    /// <param name="screenPos">屏幕坐标</param>
    /// <param name="transform">目标RectTransform</param>
    /// <param name="cam">摄像机</param>
    /// <returns>ui局部坐标</returns>
    private Vector2 ScreenPosToUiLocalPos(Vector3 screenPos, RectTransform transform, Camera cam)
    {
        Vector2 uiLocalPos;

        if (RectTransformUtility.ScreenPointToLocalPointInRectangle(transform, screenPos, cam, out uiLocalPos))
        {
            return uiLocalPos;
        }
        return Vector2.zero;
    }
}

4、ShaderGraph

创建一个Unlit ShaderGraph,实现UI主贴图的Alpha通道与和RenderTexture的相乘。
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻
注意Graph Settings设置SurfaceTransparentBlend设置为Alpha
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻
暴露出两个变量,方便在材质球中设置参数。
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻

5、UI制作

准备刮刮乐的UI图片,导入Unity中。
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻
拼成界面,如下。mask层就是要被刮掉的层。
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻

6、材质球

创建一个材质球ScratchMaterial,使用上面做的ShaderGraph,给材质球赋值贴图。
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻
最后将材质赋给maskMaterial
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻

7、挂脚本

ScratchUI.cs脚本挂到mask上,并设置好参数。
RenderTexture:用于Alpha通道图;
Brush Texture:笔刷图案,一个白点;
Blank Texture:一张纯黑色的空白图;
RectTransformmaskRectTransform,用于坐标转换;
Canvas:用于坐标转换。
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻

四、运行测试

运行Unity,测试效果如下:
使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻

上一篇:untiy command buffer——实现render texture


下一篇:Unity C# Texture图像高校传输到C++ dll的方法