unity製作刮刮樂效果

Zerone羽發表於2018-01-25
網上看過了很多刮刮樂的文章,自己參考了一些,也改良了一些方法,比如說改良了畫的每個點不連續的情況。具體效果如下:
 

做出這種效果,其實挺簡單,主要就是利用unity的render texture加上自己寫的遮罩shader。
我們首先設定兩個攝像機,一個是專門渲染render texture用的,讓它只能看到筆刷圖層,命名為brushCamera,並且要設為dont clear模式:
 


 

然後我們要建立一個筆刷預設體,這個筆刷預設體主要實現筆刷效果:
 

下面的實現思路就是:當按下滑鼠時,我們就克隆一個筆刷,這樣就形成了塗畫的效果。
然後我們寫一個遮罩shader,shader中需要兩張圖,一張是遮罩的圖片(就是圖中的藍色圖片),另一張是用於剔除遮罩的圖片,我們將渲染出的rendertexture作為剔除遮罩的圖片。這樣就完成了刮刮樂效果。

其中,我們要注意幾個問題
1、由於當滑鼠快速滑動時,可能會產生每個點不連續的情況,這裡我們用了貝塞爾平滑方法進行處理。
2、大量克隆筆刷,會非常消耗效能,這裡我們採用建立物件池方法的方法解決這個問題。
下面是主要的程式碼:
c#程式碼:

[C#] 純文字檢視 複製程式碼
?
 
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
 
public class DrawMask : MonoBehaviour {
    public float radius = 0.5f;//半徑
    public GameObject brush;
    bool startDraw = false;
    bool twoPoints = false;
    Vector2 lastPos;//最後一個點
    Vector2 penultPos;//倒數第二個點
    List<GameObject> brushesPool = new List<GameObject>(),activeBrushes = new List<GameObject>();//筆刷物件池
 
    public delegate void DrawHandler(Vector2 pos);
    public event DrawHandler onStartDraw;
    public event DrawHandler onEndDraw;
    public event DrawHandler drawing;
    // Use this for initialization
    void Start () {
    }
     
    // Update is called once per frame
    void Update () {
         GetInput();
 
    }
 
    void GetInput()
    {
        if (Input.GetMouseButtonDown(0))
        {
            startDraw = true;
            if (onStartDraw != null)
            {
                onStartDraw(VectorTransfer(Input.mousePosition));
            }
            penultPos = Input.mousePosition;
        }
        else if (Input.GetMouseButton(0))
        {
            if (twoPoints && Vector2.Distance(Input.mousePosition,lastPos) > 0.5f)//如果兩次記錄的滑鼠座標距離大於一定的距離,開始記錄滑鼠的點
            {
                Vector2 pos = Input.mousePosition;
                float dis = Vector2.Distance(lastPos, pos);
                int segments = (int)(dis / radius);//計算出平滑的段數
                segments = segments < 1 ? 1 : segments;
                Vector2[] points = Beizier(penultPos, lastPos, pos, segments);//進行貝塞爾平滑
                for (int i = 0; i < points.Length; i++)
                {
                    InstanceBrush(VectorTransfer(points[i]));
                }
                if (drawing != null)
                {
                    drawing(VectorTransfer(Input.mousePosition));
                }
                lastPos = pos;
                penultPos = points[points.Length - 2];
            }
            else
            {
                twoPoints = true;
                lastPos = Input.mousePosition;
            }
        }
        else if (Input.GetMouseButtonUp(0))
        {
            if (onEndDraw != null)
            {
                onEndDraw(VectorTransfer(Input.mousePosition));
            }
            startDraw = false;
            twoPoints = false;
        }
    }
 
    private void OnPostRender()
    {
        InitBrushes();
    }
 
    void InitBrushes()
    {
        for (int i = 0; i < activeBrushes.Count; i++)
        {
            activeBrushes[i].SetActive(false);
            brushesPool.Add(activeBrushes[i]);
        }
        activeBrushes.Clear();
    }
 
    void InstanceBrush(Vector2 pos)
    {
        GameObject brushClone;
        if (brushesPool.Count > 0)
        {
            brushClone = brushesPool[brushesPool.Count - 1];
            brushesPool.RemoveAt(brushesPool.Count - 1);
        }
        else
        {
            brushClone = Instantiate(brush, pos, Quaternion.identity);
        }
        brushClone.transform.position = pos;
 
        brushClone.transform.localScale = Vector3.one * radius;
        brushClone.SetActive(true);
        activeBrushes.Add(brushClone);
    }
 
    /// <summary>
    /// 貝塞爾平滑
    /// </summary>
    /// <param name="start">起點</param>
    /// <param name="mid">中點</param>
    /// <param name="end">終點</param>
    /// <param name="segments">段數</param>
    /// <returns></returns>
    public Vector2[] Beizier(Vector2 start,Vector2 mid, Vector2 end,int segments)
    {
        float d = 1f / segments;
        Vector2[] points = new Vector2[segments - 1];
        for (int i = 0; i < points.Length; i++)
        {
            float t = d * (i + 1);
            points[i] = (1 - t) * (1 - t) * mid + 2 * t * (1 - t) * start + t * t * end;
        }
        List<Vector2> rps = new List<Vector2>();
        rps.Add(mid);
        rps.AddRange(points);
        rps.Add(end);
        return rps.ToArray();
    }
 
    Vector2 VectorTransfer(Vector2 point)
    {
        return Camera.main.ScreenToWorldPoint(new Vector3(point.x, point.y, 0));
    }
}



遮罩shader:
[C] 純文字檢視 複製程式碼
?
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
 
Shader "Custom/MaskShader" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        //_MainTex ("Albedo (RGB)", 2D) = "white" {}
        _MaskTex("Mask Texture",2D) = "white"{}
        _Mask("Mask",2D) = "white"{}
 
    }
    SubShader {
        Tags{"RenderType" = "Transparent" "Queue" = "Transparent"}
        pass
        {
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"
 
            struct v2f
            {
                float4 pos:POSITION;
                float2 uv:TEXCOORD1;
            };
 
            //sampler2D _MainTex;
            sampler2D _MaskTex;
            sampler2D _Mask;
 
            v2f vert(appdata_base v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;
                return o;
            }
 
            float4 frag(v2f i):COLOR
            {
                //float4 mainColor = tex2D(_MainTex,i.uv);
                float4 maskTexColor = tex2D(_MaskTex,i.uv);
                float4 maskColor = tex2D(_Mask,i.uv);
                maskTexColor.a = 1 - maskColor.a;
                return maskTexColor;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

相關文章