当前位置:   article > 正文

【游戏开发创新】使用Unity ShaderGraph实现在模型上涂鸦的效果,那么,纹个手吧_unity 纹理采样 涂鸦

unity 纹理采样 涂鸦

一、前言

点关注不迷路,持续输出Unity干货文章。

嗨,大家好,我是新发,之前写了一篇文章:《ShaderGraph使用教程与各种特效案例:Unity2020》
昨天,又有个同学问了我一个问题:
在这里插入图片描述
昨天太忙了,今天趁下班后的业余时间,就来讲讲这个问题吧:
如何使用Unity ShaderGraph实现在模型涂鸦的效果?

本文最终效果如下:
在这里插入图片描述
本文Demo工程已上传到CodeChina(最近GitHub貌似有问题,经常连不上),感兴趣的同学可自行下载学习。
CodeChina地址:https://codechina.csdn.net/linxinfa/UnityShaderGraphGraffiti
注意,我使用的Unity版本是2020.2.7f1c1ShaderGraph版本是Version 10.3.2 - March 01, 2021,如果你使用的版本比我的版本低,则可能运行我的Demo工程会有问题。
在这里插入图片描述

二、思考

首先,我们思考一下,实现这个功能需要解决的问题:
1 我们怎么知道鼠标点击在模型上的具体位置?
2 我们怎么在这个位置画笔刷图案?
3 我们画的图案如何与模型的主贴图融合?

第一个问题,我们可以利用摄像机射线碰撞检测来获取鼠标点击到模型上的具体位置。
用到Camera.ScreenPointToRayPhysics.Raycast接口。

第二个问题,我们可以把鼠标画的点画在一张RenderTexture上,
基于第一个问题答案,我们可以得到射线碰撞检测的RaycastHit,而RaycastHit有一个textureCoord成员变量,这个就是uv坐标,有了uv值,就可以换算出在RenderTexture图上的具体位置,再使用Graphics.DrawTexture接口在RenderTexture上画笔刷图案即可。

第三个问题,两张图片的融合,这就交给ShaderGraph来处理吧。

所以,其实这个功能的核心不是ShaderGraphShaderGraph只是做最后的图片融合处理。
当然,这只是其中一种思路,如果各位大佬有更好的方法,欢迎指出。

三、实操

1、Render Texture

首先,创建一个Render Texture
在这里插入图片描述
设置一下Render Texture的尺寸和格式,尺寸的大小决定我们最后画图案的精度。
在这里插入图片描述
我们可以看到,Render Texture默认是黑乎乎的一张图。
在这里插入图片描述

2、笔刷图案

photoshop做两张图,一张纯黑色的方图(用于初始化填充Render Texture),一张笔刷图,简单起见,笔刷图案我就用一个白点。
如下:
在这里插入图片描述

3、写脚本:DrawOn3D.cs

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

// DrawOn3D.cs

using UnityEngine;

/// <summary>
/// 在3D模型上涂鸦
/// </summary>
public class DrawOn3D : MonoBehaviour
{
    /// <summary>
    /// 绘制的目标图片
    /// </summary>
    public RenderTexture rt;
    /// <summary>
    /// 笔刷
    /// </summary>
    public Texture brushTexture;

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

    /// <summary>
    /// 主摄像机
    /// </summary>
    public Camera cam;

    /// <summary>
    /// 模型
    /// </summary>
    public Transform modelTransform;

    void Start()
    {
        cam = Camera.main;
        DrawBlank();
    }

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

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

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

        RenderTexture.active = null;
    }

	// 在RenderTexture的(x,y)坐标处画笔刷图案
    private void Draw(int x, int y)
    {
        // 激活rt
        RenderTexture.active = rt;
        // 保存当前状态
        GL.PushMatrix();
        // 设置矩阵
        GL.LoadPixelMatrix(0, rt.width, rt.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;
    }

    private void Update()
    {
        if (Input.GetMouseButton(0))
        {
            Ray ray = cam.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                // hit.textureCoord是碰撞点的uv值,uv值是从0到1的,所以要乘以宽高才能得到具体坐标点
                var x = (int)(hit.textureCoord.x * rt.width);
                // 注意,uv坐标系和Graphics坐标系的y轴方向相反
                var y = (int)(rt.height - hit.textureCoord.y * rt.height);
                Draw(x, y);
            }
        }
	
		// 按左右方向键,旋转模型
        if(Input.GetKey(KeyCode.LeftArrow))
        {
            modelTransform.Rotate(0, 360 * Time.deltaTime, 0);
        }
        else if (Input.GetKey(KeyCode.RightArrow))
        {
            modelTransform.Rotate(0, -360 * Time.deltaTime, 0);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 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
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
4、ShaderGraph

创建一个PBR ShaderGraph,实现模型主贴图和RenderTexture的融合。
在这里插入图片描述
暴露出四个变量,方便在材质球中设置参数。
在这里插入图片描述

5、模型

为了体现我的艺术天分,我找了个手模。
在这里插入图片描述

6、材质球

创建一个材质球HandMat,使用上面做的ShaderGraph,给材质球赋值贴图。
在这里插入图片描述
最后将材质赋给模型。
在这里插入图片描述

7、挂脚本

DrawOn3D脚本挂到Main Camera上,并设置好参数。
RtRender Texture,用于画图案;
Brush Texture:笔刷图案,一个白点;
Blank Texture:一张纯黑色的空白图;
Cam:主摄像机,用于做射线检测;
Module Transform:用于旋转模型。
在这里插入图片描述

四、运行测试

运行Unity,测试效果如下:
在这里插入图片描述
可以通过材质球调节图案颜色和透明度。
在这里插入图片描述

五、结束

写完了,现在是23:01,收拾睡觉,大家晚安。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/107722
推荐阅读
相关标签
  

闽ICP备14008679号