博客文章

Unity中涂鸦和任意擦除效果的实现

作者: Andy.      时间: 2021-12-15 22:38:33

最近又玩翡翠大师觉得非常有趣,特别是在将翡翠擦出来的时候的感觉。所以打算也来实现一个。

模型从3D Game Kit - Environment Pack这个里面取。把模型拖出来,重新创建一个材质球和一个SurfaceShader。修改Surface Shader里面的属性,添加法线贴图:

    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _NormalMap ("NormalMap", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }

在surf函数里面对法线进行采样并赋值:

o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));

应用新的材质球后的显示效果:

应用我们的shader后.png

里面的贴图是土黄土黄的,稍微做一点儿修改,给改成绿色的。实现步骤:

1、叠加一个色值,让这块石头变成绿色的,看起来比较像一块翡翠。

2、增加一张遮罩贴图,约定遮罩贴图R通道为1的时候,石头显示比较暗的颜色。R通道为0的时候显示为翠绿。在最终的颜色输出的时候进行处理。

3、让这块石头旋转起来。

4、在Update里面进行检测,当鼠标点击在石头上面的时候,通过射线获得当前点击的贴图位置,将遮罩贴图对应的位置R通道修改为0。


点击鼠标在石头上面擦上一笔,最后的效果:

image.png

效果达成。变暗可以用模糊效果或者美术直接出一张粗糙的贴图就有一种擦拭的效果了。


这样就可以得到效果了。

由上面得到C#代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test2 : MonoBehaviour
{
    public Texture2D BrushTex;
    private int brushRadius = 200;

    void Update()
    {
        transform.Rotate(Vector3.up, Time.deltaTime * (float)10.0);
        if (Input.GetMouseButton(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray, out RaycastHit raycast, 200f))
            {
                MeshCollider meshCollider = raycast.transform.GetComponent<MeshCollider>();
                Vector2 mousePos = raycast.textureCoord;
                mousePos.x = (mousePos.x + 1f) * 0.5f;
                mousePos.y = (mousePos.y + 1f) * 0.5f;
                mousePos.x *= BrushTex.width;
                mousePos.y *= BrushTex.height;

                Brush(mousePos);
            }
        }
    }

    public void Brush(Vector2 mousePos)
    {
        Rect rectBorderCheck = new Rect(0, 0, BrushTex.width, BrushTex.height);
        mousePos -= new Vector2(BrushTex.width / 2, BrushTex.height / 2);
        int centerX = Mathf.FloorToInt(mousePos.x);
        int enterY = Mathf.FloorToInt(mousePos.y);

        Vector2 tempPos = new Vector2(0, 0);
        for (int i = (int)(mousePos.x - brushRadius); i < mousePos.x + brushRadius; i++)
        {
            for (int j = (int)(mousePos.y - brushRadius); j < mousePos.y + brushRadius; j++)
            {
                tempPos.x = i;
                tempPos.y = j;
                if (rectBorderCheck.Contains(tempPos) && Vector2.Distance(mousePos, tempPos) < brushRadius)
                {
                    BrushTex.SetPixel(centerX + i, enterY + j, Color.green);
                }
            }
        }

        BrushTex.Apply();
    }
}

对应的SurfaceShader代码:

Shader "Custom/Test2"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _NormalMap ("NormalMap", 2D) = "bump" {}
        _MaskTex ("MaskTex", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _Diffuse("Diffuse", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows finalcolor:final

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _NormalMap;
        sampler2D _MaskTex;
        fixed4 _Diffuse;

        struct Input
        {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb * _Diffuse.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
            o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
            
        }

        void final(Input IN, SurfaceOutputStandard o, inout fixed4 color)
        {
            fixed4 maskColor = tex2D(_MaskTex, IN.uv_MainTex);
            color.rgb *= (1 - maskColor.r * 0.6);
        }
        ENDCG
    }
    FallBack "Diffuse"
}