Unity引擎修改模型顶点色的工具

这篇具有很好参考价值的文章主要介绍了Unity引擎修改模型顶点色的工具。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

大家好,我是阿赵。
  之前分享过怎样通过MaxScript在3DsMax里面修改模型的顶点色。不过由于很多时候顶点色的编辑需要根据在游戏引擎里面的实际情况和shader的情况来动态调整,所以如果能在引擎里面直接修改模型的顶点色,将会方便很多。于是我写了下面这个在Unity引擎里面修改模型顶点色的工具。

一、功能介绍

1、模型选择和网格生成

这是工具的界面,选择一个或者多个带有网格模型的GameObject,点击开始编辑
Unity引擎修改模型顶点色的工具,TA通用技术,unity,游戏引擎,顶点颜色,工具

如果选择的网格模型是属于不能编辑的类型,比如unity自带网格,fbx里面的网格,工具会提示生成可编辑网格
Unity引擎修改模型顶点色的工具,TA通用技术,unity,游戏引擎,顶点颜色,工具

工具将会把网格模型复制一份,保存成asset格式的资源文件。

2、笔刷功能

开始编辑之后,把鼠标放在模型上,会出现笔刷,笔刷的大小、衰减、强度等可以调节
Unity引擎修改模型顶点色的工具,TA通用技术,unity,游戏引擎,顶点颜色,工具
Unity引擎修改模型顶点色的工具,TA通用技术,unity,游戏引擎,顶点颜色,工具

其中笔刷的半径可以用快捷键”[“和”]”控制,衰减可以用快捷键”-”和”=”来控制

3、顶点色显示

为了能在使用工具的过程中知道绘制的效果,所以有必要把顶点色给显示出来。
Unity引擎修改模型顶点色的工具,TA通用技术,unity,游戏引擎,顶点颜色,工具

不过如果模型默认有颜色,可能会导致刷的过程比较难看得清楚,所以也可以进入线框模式看
Unity引擎修改模型顶点色的工具,TA通用技术,unity,游戏引擎,顶点颜色,工具
Unity引擎修改模型顶点色的工具,TA通用技术,unity,游戏引擎,顶点颜色,工具

如果模型的顶点很多,那么显示顶点也会比较耗时,表现就是在刷颜色的时候有点卡,所以也提供了一个显示模式,是指显示笔刷覆盖范围的点
Unity引擎修改模型顶点色的工具,TA通用技术,unity,游戏引擎,顶点颜色,工具

为了便于观察单个通道的当前值,所以在单个通道模式下,可以选择单色显示或者是黑白显示,这样看起来会更加直观
Unity引擎修改模型顶点色的工具,TA通用技术,unity,游戏引擎,顶点颜色,工具
Unity引擎修改模型顶点色的工具,TA通用技术,unity,游戏引擎,顶点颜色,工具

4、刷颜色

  调一下强度,其实就是笔刷的透明度,就可以在模型上面刷顶点颜色了。如果是刷所有通道,可以直接选择颜色。如果是单个通道,可以选择是增加或者减少该通道的颜色。
Unity引擎修改模型顶点色的工具,TA通用技术,unity,游戏引擎,顶点颜色,工具

  这里加入了权重模式的选项。所谓的权重模式,是保证RGBA4个通道的颜色值加起来等于1,如果增加某个通道的颜色,就会相应的减少其他几个通道的颜色。在用顶点色做某些混合功能的时候,这个权重模式会比较的有用。

二、技术点

1、判断笔刷位置

  本来很简单的一件事情,鼠标在屏幕位置发射射线,求和模型网格的碰撞点就可以了。不过如果用Physics.Raycast方法,是需要模型网格上有碰撞体才能检测到的。而在这个工具的环境下,我需要的是没有碰撞体也能检测得到网格模型的碰撞点。
  其实这个方法是Unity自带了的:

bool HandleUtility.IntersectRayMesh(Ray ray,Mesh mesh,Matrix4x4 matrix,out RaycastHit hit)

可惜的是,这个方法Unity居然是隐藏的,并没有暴露出来给我们用。所以只能使用反射的手段去调用:

private bool IntersectRayMesh(Ray ray,Mesh sharedMesh,Matrix4x4 matrix,out RaycastHit hit)
{
    System.Type type = typeof(HandleUtility);
    System.Reflection.MethodInfo method = type.GetMethod("IntersectRayMesh", (BindingFlags.Static | BindingFlags.NonPublic));
    RaycastHit tempHit = new RaycastHit();

    object[] objArr = new object[4];
    objArr[0] = ray;
    objArr[1] = sharedMesh;
    objArr[2] = matrix;
    objArr[3] = null;
    bool isSuccess = (bool)method.Invoke(null,objArr);
    hit = (RaycastHit)objArr[3];      
    return isSuccess;
}

2、锁定操作

在刷顶点色的时候,我们肯定不希望鼠标还有原来的操作行为,比如框选、位移旋转之类的操作,所以需要对操作进行锁定:
通过这个方法来锁定鼠标操作:
HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));
然后记录一下当前使用的工具,并把当前工具置为None。这样就不会出现移动的坐标轴之类了,记录当前使用的工具是为了退出编辑的时候可以还原。

LastTool = Tools.current;
Tools.current = Tool.None;

3、绘制笔刷和顶点

在Scene视图里面绘制图形,需要添加duringSceneGui 处理

SceneView.duringSceneGui += OnSceneGUI;
void OnSceneGUI(SceneView sceneView)
{
}

然后绘制的命令都写在OnSceneGUI里面。
这里主要用到了3个命令:
绘制线条

Handles.DrawLine

绘制圆盘

Handles.DrawWireDisc

绘制点

Handles.DotHandleCap

然后改变绘制颜色是用

Handles.color

4、在不影响mesh本身的情况下修改颜色

如果只是为了改变mesh的顶点色,那么直接修改Mesh.colors就可以了,不过这个毕竟只是一个编辑的过程,我们也有可能撤销修改,甚至直接不保存这次的修改。所以不能直接修改Mesh.colors。
我这里是使用了一个数据类MeshEditData来记录了每个mesh上面的所有顶点颜色,然后在刷顶点色的过程中,只是改变这个MeshEditData对象里面的颜色。等确定保存的时候,才把color数据写入mesh。

5、撤回操作

Unity本身是自带了Undo的操作的,按道理只需要调用Undo相关的方法,就可以实现撤回操作了。
不过不论是Undo.RecordObject还是Undo.RecordObjects,都需要传入Object作为参数,也就是Unity的对象。而我记录的是自定义对象,并不是Object,所以不能直接这么用。
于是可以这样操作:
通过注册undoRedoPerformed 方法,来自定义撤回的时候执行的方法
Undo.undoRedoPerformed += this.OnUndo;
在每次笔刷开始刷之前,也就是鼠标左键点下的时候,记录一下当前所有网格的颜色值,并存到一个数组里面。
然后在OnUndo方法里面,检查回撤的数组,如果有,就把数组最后入栈的颜色值取出,并赋值给当前的数据。
值得注意的有2点:
1.undoRedoPerformed 方法不论是ctrl+z还是ctrl+y都会执行的
2.undoRedoPerformed 方法是需要前面有操作,才会记录堆栈,如果我们一直在Scene视图刷颜色,他只会记录一次操作,所以也只能回退一次,这里需要每次绘制完一笔之后,也就是鼠标左键抬起的时候,手动调用一下Undo.RegisterCompleteObjectUndo方法

三、工具源码

总共3个c#脚本文章来源地址https://www.toymoban.com/news/detail-680347.html

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

using UnityEngine;
using UnityEditor;
using System.IO;
using System.Reflection;

namespace azhao.tools.VertexColorPainer
{
    public enum BrushChannelType
    {
        ALL = 0,
        RED = 1,
        GREEN = 2,
        BLUE = 3,
        ALPHA = 4
    }
    public enum SetColorMode
    {
        ADD = 0,
        WEIGHT = 1
    }

    public enum OperatorType
    {
        ADD = 0,
        REDUCE = 1
    }
    public class VertexColorPainerWin : EditorWindow
    {
        static private VertexColorPainerWin _instance;

        public static VertexColorPainerWin Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = (VertexColorPainerWin)EditorWindow.GetWindow(typeof(VertexColorPainerWin));
                    _instance.titleContent = new GUIContent("顶点颜色刷");
                    _instance.maxSize = _instance.minSize = new Vector2(600, 600);
                }
                return _instance;
            }
        }
        [MenuItem("Tools/顶点颜色刷")]
        static void ShowWin()
        {
            VertexColorPainerWin.Instance.Show();
        }
        #region 生命周期
        // Start is called before the first frame update
        void Start()
        {

        }
        private void OnEnable()
        {
            SceneView.duringSceneGui += OnSceneGUI;
            Undo.undoRedoPerformed -= this.OnUndo;
            Undo.undoRedoPerformed += this.OnUndo;
        }

        private void OnDisable()
        {
            SceneView.duringSceneGui -= OnSceneGUI;
            Undo.undoRedoPerformed -= this.OnUndo;
            Tools.current = LastTool;
        }
        void OnDestroy()
        {
            SceneView.duringSceneGui -= this.OnSceneGUI;
            Undo.undoRedoPerformed -= this.OnUndo;
        }
        // Update is called once per frame
        void Update()
        {
            
        }
        #endregion

        Tool LastTool = Tool.None;
        private bool isEditMode = false;
        private List<GameObject> meshObjList;
        List<MeshEditData> meshEditList;

        private string val = "";
        private float brushSize = 0.5f;
        private float brushFalloff = 1;
        private Color brushColor = Color.white;
        private bool isShowPointColor = false;
        private string[] channelSelectStr = new string[] { "所有通道", "R通道", "G通道", "B通道", "A通道" };
        private int channelSelectIndex = 0;
        private string[] showPointStr = new string[] { "不显示", "笔刷范围显示", "全部显示" };
        private int showPointIndex = 0;
        private string[] showColorType = new string[] { "原色", "单色","黑白" };
        private int showColorIndex = 0;
        string[] drawTypeStr = new string[] { "颜色叠加", "权重模式" };
        int drawTypeIndex = 0;
        string[] drawAddTypeStr = new string[] { "增加", "减少" };
        int drawAddTypeIndex = 0;
        private float brushSizeScale = 0.01f;
        private float brushAlpha = 1;
        private bool isPaint = false;
        void OnGUI()
        {
            ShowHelp();
            ShowSelectobj();
            if(isEditMode)
            {
                if(HasSelectObj() == false)
                {
                    CleanMeshList();
                    isEditMode = false;
                }
                ShowObjInfo();
                ShowTitle();
                ShowCtrl();
                ShowContent();
                
            }


        }

        #region 说明
        private bool isShowHelp = true;
        private void ShowHelp()
        {
            if(GUILayout.Button("来自阿赵的使用说明",GUILayout.Height(40)))
            {
                isShowHelp = !isShowHelp;
            }
            if(isShowHelp == false)
            {
                return;
            }
            GUILayout.Label("1、在场景中想编辑的模型,点击开始编辑");
            GUILayout.Label("2、如果模型中的网格不可编辑,会生成课编辑网格,路径在Assets/meshes/");
            GUILayout.Label("3、根据情况选择刷所有通道颜色还是单个通道颜色");
            GUILayout.Label("4、可以选择叠加模式或者权重模式");
            GUILayout.Label("\t叠加模式是直接绘制指定颜色");
            GUILayout.Label("\t权重模式是保证RGBA通道加起来等于1");
            GUILayout.Label("5、绘制不合适可以按Ctrl+Z撤回");
        }
        #endregion

        #region 物体选择编辑
        private bool HasSelectObj()
        {
            if(meshEditList == null||meshEditList.Count==0)
            {
                return false;
            }
            if(meshObjList == null||meshObjList.Count==0)
            {
                return false;
            }
            for(int i = 0;i<meshObjList.Count;i++)
            {
                if(meshObjList[i]==null)
                {
                    return false;
                }
            }
            return true;
        }
        
        private void ShowSelectobj()
        {
            ShowLine("编辑模型顶点色工具");


            if (isEditMode == true)
            {
                if (GUILayout.Button("保存编辑", GUILayout.Width(600), GUILayout.Height(40)))
                {
                    SaveMeshes(); 
                }
                if (GUILayout.Button("退出编辑",GUILayout.Width(600),GUILayout.Height(40)))
                {
                    OnEditEnd();
                }
            }
            else
            {
                if (GUILayout.Button("开始编辑", GUILayout.Width(600), GUILayout.Height(40)))
                {
                    OnEditBegin();
                }
            }
        }

        private void OnEditEnd()
        {
            Tools.current = LastTool;
            if (HasEditableMesh())
            {
                if(ShowSelectTips("是否保存网格?", "保存", "退出"))
                {
                    SaveMeshes();
                }
                isEditMode = false;
            }
        }

        private bool HasEditableMesh()
        {
            if(meshEditList != null&& meshEditList.Count>0)
            {
                return true;
            }
            return false;
        }

        private void OnEditBegin()
        {
            LastTool = Tools.current;
            Tools.current = Tool.None;
            meshObjList = new List<GameObject>();
            meshEditList = new List<MeshEditData>();
            GameObject[] gos = Selection.gameObjects;
            if(gos!=null)
            {
                for(int i = 0;i<gos.Length;i++)
                {
                    Transform[] trs = gos[i].GetComponentsInChildren<Transform>();
                    for(int j = 0;j<trs.Length;j++)
                    {
                        if (trs[j].gameObject.activeSelf == true)
                        {
                            if (meshObjList.IndexOf(trs[j].gameObject) < 0)
                            {
                                meshObjList.Add(trs[j].gameObject);
                            }
                        }
                    }

                }
            }

            if(meshObjList == null||meshObjList.Count==0)
            {
                ShowTips("没有可以编辑的模型,请用鼠标选中场景中的模型");
                return;
            }
            if(CheckMeshCanEdit()==true)
            {
                isEditMode = true;
            }
        }

        private void CleanMeshList()
        {
            meshEditList = null;
        }

        private bool CheckMeshCanEdit(int count = 0)
        {
            count++;
            if(count>=3)
            {
                return false;
            }
            if(meshObjList == null)
            {
                return false;
            }
            meshEditList = new List<MeshEditData>();
            for(int i = 0;i<meshObjList.Count;i++)
            {
                if(meshObjList[i].activeSelf == false)
                {
                    continue;
                }
                MeshEditData data = new MeshEditData(meshObjList[i]);
                if(data.mesh!=null)
                {
                    meshEditList.Add(data);
                }
            }

            if(HasEditableMesh()==false)
            {
;
                CleanMeshList();
                ShowTips("选择的物体里面没有可以编辑的网格模型");
                return false;
            }
            bool hasOrigMesh = false;
            if(meshEditList != null&& meshEditList.Count>0)
            {
                for(int i = 0;i< meshEditList.Count;i++)
                {
                    Mesh mesh = meshEditList[i].mesh;
                    string path = AssetDatabase.GetAssetPath(mesh);
                    if(path.EndsWith(".asset")==false)
                    {
                        hasOrigMesh = true;
                        break;
                    }
                }
            }
            if(hasOrigMesh == true)
            {                
                if (ShowSelectTips("当前模型里面有不可以直接编辑的网格,是否生成可编辑的网格?", "生成", "取消编辑"))
                {
                    bool successCreate = CreateCopyMeshAsset();
                    if(successCreate == false)
                    {
                        CleanMeshList();

                        return false;
                    }
                    else
                    {
                        CleanMeshList();
                        return CheckMeshCanEdit(count);
                    }
                }
                else
                {
                    CleanMeshList();
                    return false;
                }
            }
            else
            {
                return true;
            }
        }
        private void SaveMeshes()
        {
            if (meshEditList != null && meshEditList.Count > 0)
            {
                for (int i = 0; i < meshEditList.Count; i++)
                {
                    meshEditList[i].SaveMesh();

                }
            }
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }

        #endregion

        #region 笔刷操作编辑

        private void ShowObjInfo()
        {

            GUILayout.Label("当前编辑的对象:", GUILayout.Width(120));
            if(meshEditList != null&& meshEditList.Count>0)
            {
                for(int i = 0;i< meshEditList.Count;i++)
                {
                    EditorGUILayout.ObjectField(meshEditList[i].gameObject, typeof(Object), true, GUILayout.Width(200));
                }
            }

        }

        private void ShowCtrl()
        {

            GUILayout.BeginHorizontal();
            GUILayout.Label("是否显示顶点色", GUILayout.Width(120));
            //isShowPointColor = EditorGUILayout.Toggle(isShowPointColor, GUILayout.Width(40));
            showPointIndex = GUILayout.SelectionGrid(showPointIndex, showPointStr, 4, GUILayout.Width(500));
            GUILayout.EndHorizontal();
            if(showPointIndex>0&& channelSelectIndex>0)
            {
                GUILayout.BeginHorizontal();
                GUILayout.Label("显示颜色类型:", GUILayout.Width(120));
                showColorIndex = GUILayout.SelectionGrid(showColorIndex, showColorType, 3, GUILayout.Width(300));                
                GUILayout.EndHorizontal();
            }
            GUILayout.BeginHorizontal();
            GUILayout.Label("半径:", GUILayout.Width(80));
            brushSize = EditorGUILayout.Slider(brushSize, 0.1f, 2, GUILayout.Width(400));
            if(brushSize<0.1f)
            {
                brushSize = 0.1f;
            }
            if(brushSize>2)
            {
                brushSize = 10;
            }
            GUILayout.EndHorizontal();
            GUILayout.BeginHorizontal();
            GUILayout.Label("衰减:", GUILayout.Width(80));
            brushFalloff = EditorGUILayout.Slider(brushFalloff, 0, 1, GUILayout.Width(200));
            if(brushFalloff<0)
            {
                brushFalloff = 0;
            }
            if(brushFalloff>1)
            {
                brushFalloff = 1;
            }
            GUILayout.EndHorizontal();
            GUILayout.BeginHorizontal();
            GUILayout.Label("强度:", GUILayout.Width(80));
            brushAlpha = EditorGUILayout.Slider(brushAlpha, 0.1f, 1, GUILayout.Width(200));
            GUILayout.EndHorizontal();
        }

        private void ShowTitle()
        {
            channelSelectIndex = GUILayout.SelectionGrid(channelSelectIndex, channelSelectStr, 5, GUILayout.Width(500));
        }

        private void ShowContent()
        {
            if(channelSelectIndex == (int)BrushChannelType.ALL)
            {
                ShowAllColorCtrl();
            }
            else
            {
                ShowOneChannelColorCtrl();
            }
        }

        private void ShowAllColorCtrl()
        {
            GUILayout.BeginHorizontal();
            GUILayout.Label("笔刷颜色:", GUILayout.Width(100));
            brushColor = EditorGUILayout.ColorField(brushColor, GUILayout.Width(100));
            GUILayout.EndHorizontal();
        }


        private void ShowOneChannelColorCtrl()
        {
            GUILayout.BeginHorizontal();
            GUILayout.Label("绘制模式:", GUILayout.Width(100));
            drawTypeIndex = GUILayout.SelectionGrid(drawTypeIndex, drawTypeStr, 2, GUILayout.Width(200));
            GUILayout.EndHorizontal();
            GUILayout.BeginHorizontal();
            GUILayout.Label("颜色叠加:", GUILayout.Width(100));
            drawAddTypeIndex = GUILayout.SelectionGrid(drawAddTypeIndex, drawAddTypeStr, 2, GUILayout.Width(200));
            GUILayout.EndHorizontal();

        }
        #endregion





        #region 显示顶点色
        private void ShowPointColorFunc()
        {
            if(meshEditList != null&& meshEditList.Count>0)
            {
                for (int i = 0; i < meshEditList.Count; i++)
                {
                    ShowOneMeshPointColor(meshEditList[i]);
                }
            }

        }

        private void ShowPointColorFunc(Vector3 center,Vector3 normal)
        {
            if (meshEditList != null && meshEditList.Count > 0)
            {
                for (int i = 0; i < meshEditList.Count; i++)
                {

                    ShowOneMeshPointColor(meshEditList[i], center,normal);
                }
            }

        }



        private void ShowOneMeshPointColor(MeshEditData data)
        {
            ShowOneMeshPointColor(data, Vector3.zero, Vector3.zero,false);
        }
        private void ShowOneMeshPointColor(MeshEditData data, Vector3 center,Vector3 normal,bool needCheckDistance = true)
        {

            Vector3[] verts = data.mesh.vertices;
            float r = (brushSize + brushSize * brushFalloff);
            r = r * r;
            //Debug.Log(mesh.vertices.Length + "," + mesh.colors.Length);
            Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;
            for (int j = 0; j < verts.Length; j++)
            {
                Vector3 pos = data.transform.TransformPoint(verts[j]);
                if (needCheckDistance==true)
                {                    
                    float tempR = (pos.x - center.x) * (pos.x - center.x) + (pos.y - center.y) * (pos.y - center.y) + (pos.z - center.z) * (pos.z - center.z);
                    if (tempR > r)
                    {
                        continue;
                    }
                }

                if (data.colors.Length > j)
                {
                    if (channelSelectIndex == (int)BrushChannelType.ALL||showColorIndex ==0)
                    {
                        Handles.color = data.colors[j];
                    }
                    else
                    {
                        if (channelSelectIndex == (int)BrushChannelType.RED)
                        {
                            if (showColorIndex == 1)
                            {
                                Handles.color = new Color(data.colors[j].r, 0, 0, 1);
                            }
                            else if (showColorIndex == 2)
                            {
                                Handles.color = new Color(data.colors[j].r, data.colors[j].r, data.colors[j].r, 1);
                            }

                        }
                        else if (channelSelectIndex == (int)BrushChannelType.GREEN)
                        {
                            if (showColorIndex == 1)
                            {
                                Handles.color = new Color(0, data.colors[j].g, 0, 1);
                            }
                            else if (showColorIndex == 2)
                            {
                                Handles.color = new Color(data.colors[j].g, data.colors[j].g, data.colors[j].g, 1);
                            }
                        }
                        else if (channelSelectIndex == (int)BrushChannelType.BLUE)
                        {
                            if (showColorIndex == 1)
                            {
                                Handles.color = new Color(0, 0, data.colors[j].b, 1);
                            }
                            else if (showColorIndex == 2)
                            {
                                Handles.color = new Color(data.colors[j].b, data.colors[j].b, data.colors[j].b, 1);
                            }
                        }
                        else if (channelSelectIndex == (int)BrushChannelType.ALPHA)
                        {
                            Handles.color = new Color(data.colors[j].a, data.colors[j].a, data.colors[j].a, 1);
                        }

                    }
                }
                
                Handles.DotHandleCap(0, pos, Quaternion.identity, HandleUtility.GetHandleSize(pos) * 0.03f, EventType.Repaint);
            }
        }

        #endregion

        #region 绘制顶点色
        private void PaintPointColor(Vector3 center, Vector3 normal)
        {
            if (meshEditList != null && meshEditList.Count > 0)
            {
                for (int i = 0; i < meshEditList.Count; i++)
                {

                    PaintOneMeshPoint(meshEditList[i], center, normal);
                }
            }
        }

        private void PaintOneMeshPoint(MeshEditData data, Vector3 center, Vector3 normal)
        {
            Vector3[] verts = data.mesh.vertices;
            float r = (brushSize + brushSize * brushFalloff);
            r = r * r;
            //Debug.Log(mesh.vertices.Length + "," + mesh.colors.Length);
            Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;
            for (int j = 0; j < verts.Length; j++)
            {
                Vector3 pos = data.transform.TransformPoint(verts[j]);

                float tempR = (pos.x - center.x) * (pos.x - center.x) + (pos.y - center.y) * (pos.y - center.y) + (pos.z - center.z) * (pos.z - center.z);
                if (tempR > r)
                {
                    continue;
                }
                float falloff = 1;
                if(brushFalloff>0)
                {
                    falloff = Vector3.Distance(pos, center) - brushSize / (brushSize + brushSize * brushFalloff*0.1f);
                    falloff = 1 - falloff;
                    if(falloff<0)
                    {
                        falloff = 0;
                    }
                    if(falloff>1)
                    {
                        falloff = 1;
                    }
                }
                

                if(channelSelectIndex == (int)BrushChannelType.ALL)
                {
                    data.DrawVertexColor(j, brushColor, falloff* brushAlpha * Time.deltaTime);
                }
                else
                {
                    data.DrawVertexChannelColor(j, channelSelectIndex, drawTypeIndex, drawAddTypeIndex, falloff* brushAlpha * Time.deltaTime*0.1f);
                }
               
            }
        }

        #endregion

        void OnSceneGUI(SceneView sceneView)
        {
            if (isEditMode == false)
            {
                return;
            }
            HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));
            if (meshObjList == null)
            {
                return;
            }

            if (HasEditableMesh() == false)
            {
                return;
            }

            Event e = Event.current;
            if (e.isKey)
            {
                //Debug.Log(e.keyCode);
                if (e.keyCode == KeyCode.RightBracket)
                {
                    brushSize += brushSizeScale;
                    if (brushSize > 2)
                    {
                        brushSize = 2;
                    }
                }
                else if (e.keyCode == KeyCode.LeftBracket)
                {
                    brushSize -= brushSizeScale;
                    if (brushSize < 0.1f)
                    {
                        brushSize = 0.1f;
                    }
                }
                else if (e.keyCode == KeyCode.Minus)
                {
                    brushFalloff -= 0.1f;
                    if (brushFalloff < 0)
                    {
                        brushFalloff = 0;
                    }
                }
                else if (e.keyCode == KeyCode.Equals)
                {
                    brushFalloff += 0.1f;
                    if (brushFalloff > 1)
                    {
                        brushFalloff = 1;
                    }
                }
            }
            if (e.rawType == EventType.MouseDown && e.button == 0)
            {
                AddToUndo();
                isPaint = true;

            }
            if (e.rawType == EventType.MouseUp && e.button == 0)
            {
                isPaint = false;
            }
            Vector3 mousePos = e.mousePosition;
            Camera camera = SceneView.currentDrawingSceneView.camera;
            float mult = 1;
#if UNITY_5_4_OR_NEWER
            mult = EditorGUIUtility.pixelsPerPoint;
#endif
            mousePos.y = camera.pixelHeight - mousePos.y * mult;
            mousePos.x *= mult;
            //Vector3 fakePoint = mousePos;
            //fakePoint.z = 20;
            //Vector3 point = sceneView.camera.ScreenToWorldPoint(fakePoint);
            float minDis = 99999;
            bool isHit = false;
            Vector3 center = Vector3.zero;
            Vector3 normal = Vector3.zero;

            Ray ray = camera.ScreenPointToRay(mousePos);
            float num = 1000;
            if (meshEditList != null && meshEditList.Count > 0)
            {
                for (int i = 0; i < meshEditList.Count; i++)
                {
                    Mesh sharedMesh = meshEditList[i].mesh;
                    RaycastHit hit;
                    bool hasHit = IntersectRayMesh(ray, sharedMesh, meshEditList[i].transform.localToWorldMatrix, out hit);
                    if (hasHit == false || hit.distance > num)
                    {
                        continue;
                    }
                    isHit = true;
                    if (hit.distance < minDis)
                    {
                        center = hit.point;
                        normal = hit.normal;
                        minDis = hit.distance;
                    }

                }
            }

            if (isHit == true)
            {
                Color tempColor;
                if (channelSelectIndex == (int)BrushChannelType.RED)
                {
                    tempColor = Color.red;
                }
                else if (channelSelectIndex == (int)BrushChannelType.GREEN)
                {
                    tempColor = Color.green;
                }
                else if (channelSelectIndex == (int)BrushChannelType.BLUE)
                {
                    tempColor = Color.blue;
                }
                else if (channelSelectIndex == (int)BrushChannelType.ALPHA)
                {
                    tempColor = Color.gray;
                }
                else
                {
                    tempColor = brushColor;
                }
                DrawBrush(tempColor, center, normal, brushSize, brushFalloff);
                if (showPointIndex == 1)
                {
                    ShowPointColorFunc(center, normal);
                }
                if (isPaint)
                {
                    PaintPointColor(center, normal);
                }
                sceneView.Repaint();
                HandleUtility.Repaint();
            }

            if (showPointIndex == 2)
            {
                ShowPointColorFunc();
            }

        }
        #region 辅助方法
        private bool IntersectRayMesh(Ray ray,Mesh sharedMesh,Matrix4x4 matrix,out RaycastHit hit)
        {
            System.Type type = typeof(HandleUtility);
            System.Reflection.MethodInfo method = type.GetMethod("IntersectRayMesh", (BindingFlags.Static | BindingFlags.NonPublic));
            RaycastHit tempHit = new RaycastHit();

            object[] objArr = new object[4];
            objArr[0] = ray;
            objArr[1] = sharedMesh;
            objArr[2] = matrix;
            objArr[3] = null;
            bool isSuccess = (bool)method.Invoke(null,objArr);
            hit = (RaycastHit)objArr[3];      
            return isSuccess;
        }

        private void DrawBrush(Color col, Vector3 center, Vector3 normal, float radius,float falloff)
        {
            if(col.a<0.5f)
            {
                col.a = 0.5f;
            }
            Handles.color = col;
            Handles.DrawWireDisc(center,HandleUtility.GetHandleSize(center)* normal,  radius);            
            Handles.DrawLine(center, center + radius * normal );
            if(falloff>0)
            {
                Handles.color = col * 0.7f;
                Handles.DrawWireDisc(center, HandleUtility.GetHandleSize(center) * normal, (radius + radius* falloff));
            }

        }

        private bool CreateCopyMeshAsset()
        {
            if(meshObjList==null)
            {
                return false;
            }


            if(HasEditableMesh() == false)
            {
                return false;
            }
            string path = Application.dataPath + "/meshes";
            if(Directory.Exists(path)==false)
            {
                Directory.CreateDirectory(path);
                AssetDatabase.Refresh();
            }
            bool hasError = false;
            Dictionary<Mesh, Mesh> dict = new Dictionary<Mesh, Mesh>();
            for(int i = 0;i<meshEditList.Count;i++)
            {
                Mesh mesh = meshEditList[i].mesh;
                string meshPath = AssetDatabase.GetAssetPath(mesh);
                if(meshPath.EndsWith(".asset"))
                {
                    continue;
                }
                if(dict.ContainsKey(mesh))
                {
                    meshEditList[i].SetShareMesh(dict[mesh]);
                }
                else
                {
                    Mesh newMesh = CopyMesh(mesh);
                    string savePath = GetNewSavePath(mesh.name);


                    AssetDatabase.CreateAsset(newMesh, savePath);
                    AssetDatabase.Refresh();
                    Mesh loadMesh = (Mesh)AssetDatabase.LoadAssetAtPath(savePath, typeof(Mesh));
                    dict.Add(mesh, loadMesh);
                    meshEditList[i].SetShareMesh(loadMesh); 

                }
            }

           
            if (hasError == true)
            {
                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh();
                return false;
            }

            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            return true;
        }

        private string GetNewSavePath(string assetName)
        {
            string savePath = "Assets/meshes/" + assetName + ".asset";
            if(AssetDatabase.LoadAssetAtPath(savePath,typeof(Object))==null)
            {
                return savePath;
            }
            int i = 1;
            while(true)
            {
                i++;
                savePath = "Assets/meshes/" + assetName+i + ".asset";
                if (AssetDatabase.LoadAssetAtPath(savePath, typeof(Object)) == null)
                {
                    return savePath;
                }
            }
        }

        private Mesh CopyMesh(Mesh mesh)
        {
            Mesh newMesh = new Mesh();
            newMesh.vertices = mesh.vertices;
            newMesh.uv = mesh.uv;
            newMesh.triangles = mesh.triangles;
            newMesh.normals = mesh.normals;
            if(mesh.colors.Length==0)
            {
                newMesh.colors = new Color[mesh.vertices.Length];
                Color[] cols = new Color[mesh.vertices.Length];
                for (int i = 0;i<mesh.vertices.Length; i++)
                {
                    cols[i] = Color.black;
                }
                newMesh.SetColors(cols);
            }
            else
            {
                newMesh.colors = mesh.colors;
            }
            return newMesh;
        }
        #endregion
        #region Undo
        private List<DrawColorUndoData> undoList;
        private void AddToUndo()
        {
            if(meshEditList == null||meshEditList.Count == 0)
            {
                return;
            }
            Undo.RegisterCompleteObjectUndo(this, "paint color");
            if (undoList == null)
            {
                undoList = new List<DrawColorUndoData>();
            }
            DrawColorUndoData data = new DrawColorUndoData(meshEditList);
            undoList.Add(data);
        }

        private void OnUndo()
        {
            if(undoList == null||undoList.Count==0)
            {
                return;
            }
            DrawColorUndoData data = undoList[undoList.Count - 1];
            undoList.RemoveAt(undoList.Count-1);
            if(meshEditList!=null&&meshEditList.Count>0)
            {
                data.Undo(meshEditList);
            }            
        }
        #endregion

        #region 其他选项

        private void ShowTips(string str)
        {
            Debug.Log(str);
            EditorUtility.DisplayDialog("提示", str, "确定");
        }
        private void ShowErrorTips(string str)
        {
            Debug.Log(str);
            EditorUtility.DisplayDialog("提示", str, "确定");
            throw new System.Exception("errorTips:" + str);
        }

        private bool ShowSelectTips(string str,string okStr = "确定",string cancelStr = "取消")
        {
            return EditorUtility.DisplayDialog("提示", str, okStr,cancelStr);
        }

        private void ShowProgress(string title, string content, float rate)
        {
            EditorUtility.DisplayProgressBar(title, content, rate);
        }

        private void HideProgress()
        {
            EditorUtility.ClearProgressBar();
        }

        private void ShowLine(string str = "", int w = -1, int h = 20)
        {
            if(w<0)
            {
                w = Mathf.FloorToInt(this.maxSize.x);
            }
            if (string.IsNullOrEmpty(str))
            {
                h = 5;
            }
            GUILayout.Box(str, GUILayout.Width(w), GUILayout.Height(h));
        }

        private string SelectFolder(string str)
        {
            return EditorUtility.OpenFolderPanel("选择文件夹", str, "");
        }

        private string SelectFile(string str, string ex = "")
        {
            return EditorUtility.OpenFilePanel("选择文件", str, ex);
        }
        private void ShowTextArea(string str, int w = 500, int h = 60)
        {
            ShowLine();
            GUILayout.Label(str, GUILayout.Width(w), GUILayout.Height(h));
            ShowLine();
        }
        #endregion


    }

    
}
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace azhao.tools.VertexColorPainer
{
    public class MeshEditData
    {
        public Transform transform;
        public GameObject gameObject;
        private SkinnedMeshRenderer skinnedMeshRenderer;
        private MeshFilter meshFilter;
        public Mesh mesh;
        public Color[] colors;
        public MeshEditData(GameObject go)
        {
            transform = go.transform;
            gameObject = go;
            skinnedMeshRenderer = go.GetComponent<SkinnedMeshRenderer>();
            if (skinnedMeshRenderer != null)
            {
                mesh = skinnedMeshRenderer.sharedMesh;
            }
            else
            {
                meshFilter = go.GetComponent<MeshFilter>();
                if (meshFilter != null)
                {
                    mesh = meshFilter.sharedMesh;
                }
            }
            if (mesh != null)
            {
                InitMeshColor();
            }
        }

        public void SetShareMesh(Mesh m)
        {
            mesh = m;
            if(skinnedMeshRenderer!=null)
            {
                skinnedMeshRenderer.sharedMesh = mesh;
            }
            if(meshFilter!=null)
            {
                meshFilter.sharedMesh = mesh;
            }
        }

        private void InitMeshColor()
        {
            if (mesh == null)
            {
                return;
            }

            int vertCount = mesh.vertexCount;
            colors = new Color[vertCount];
            for (int i = 0; i < vertCount; i++)
            {
                if (mesh.colors != null && i < mesh.colors.Length)
                {
                    colors[i] = mesh.colors[i];
                }
                else
                {
                    colors[i] = Color.black;
                }
            }

        }

        public void DrawVertexColor(int index,Color col,float alpha)
        {
            if(colors == null||colors.Length<=index)
            {
                return;
            }
            Color newColor = colors[index] * (1 - alpha) + col * alpha;
            colors[index] = newColor;
;        }

        public void DrawVertexChannelColor(int index,int channel,int mode,int operatorType,float alpha)
        {
            if (colors == null || colors.Length <= index)
            {
                return;
            }
            float val = 0;
            if(channel== (int)BrushChannelType.RED)
            {
                val = colors[index].r;
            }
            else if(channel == (int)BrushChannelType.GREEN)
            {
                val = colors[index].g;
            }
            else if (channel == (int)BrushChannelType.BLUE)
            {
                val = colors[index].b;
            }
            else if (channel == (int)BrushChannelType.ALPHA)
            {
                val = colors[index].a;
            }

            if(operatorType == (int)OperatorType.ADD)
            {
                val += alpha;
                if(val>1)
                {
                    val = 1;
                }
            }
            else
            {
                val -= alpha;
                if(val<0)
                {
                    val = 0;
                }
            }

            if (channel == (int)BrushChannelType.RED)
            {
                if(mode == (int)SetColorMode.WEIGHT)
                {
                    float leftVal = 1 - val;
                    float totalVal = colors[index].g + colors[index].b + colors[index].a;
                    if(totalVal==0)
                    {
                        colors[index].g = colors[index].b = colors[index].a = leftVal / 3;
                    }
                    else
                    {
                        colors[index].g = leftVal * colors[index].g / totalVal;
                        colors[index].b = leftVal * colors[index].b / totalVal;
                        colors[index].a = leftVal * colors[index].a / totalVal;
                    }

                }
                colors[index].r = val;
            }
            else if (channel == (int)BrushChannelType.GREEN)
            {
                if (mode == (int)SetColorMode.WEIGHT)
                {
                    float leftVal = 1 - val;
                    float totalVal = colors[index].r + colors[index].b + colors[index].a;
                    if (totalVal == 0)
                    {
                        colors[index].r = colors[index].b = colors[index].a = leftVal / 3;
                    }
                    else
                    {
                        colors[index].r = leftVal * colors[index].r / totalVal;
                        colors[index].b = leftVal * colors[index].b / totalVal;
                        colors[index].a = leftVal * colors[index].a / totalVal;
                    }
                }
                colors[index].g = val;
            }
            else if (channel == (int)BrushChannelType.BLUE)
            {
                if (mode == (int)SetColorMode.WEIGHT)
                {
                    float leftVal = 1 - val;
                    float totalVal = colors[index].r + colors[index].g + colors[index].a;
                    if (totalVal == 0)
                    {
                        colors[index].r = colors[index].g = colors[index].a = leftVal / 3;
                    }
                    else
                    {
                        colors[index].r = leftVal * colors[index].r / totalVal;
                        colors[index].g = leftVal * colors[index].g / totalVal;
                        colors[index].a = leftVal * colors[index].a / totalVal;
                    }
                }
                colors[index].b = val;
            }
            else if (channel == (int)BrushChannelType.ALPHA)
            {
                if (mode == (int)SetColorMode.WEIGHT)
                {
                    float leftVal = 1 - val;
                    float totalVal = colors[index].r + colors[index].g + colors[index].b;
                    if (totalVal == 0)
                    {
                        colors[index].r = colors[index].g = colors[index].b = leftVal / 3;
                    }
                    else
                    {
                        colors[index].r = leftVal * colors[index].r / totalVal;
                        colors[index].g = leftVal * colors[index].g / totalVal;
                        colors[index].b = leftVal * colors[index].b / totalVal;
                    }
                }
                colors[index].a = val;
            }

        }

        public void SaveMesh()
        {
            if(mesh==null)
            {
                return;
            }
            mesh.colors = colors;            
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace azhao.tools.VertexColorPainer
{
    public class DrawColorUndoData
    {
        private List<Color[]> colors;
        public DrawColorUndoData(List<MeshEditData> data)
        {
            colors = new List<Color[]>();
            for(int i = 0;i<data.Count;i++)
            {
                colors.Add((Color[])data[i].colors.Clone());
            }
        }

        public void Undo(List<MeshEditData> data)
        {
            if(colors == null||colors.Count!=data.Count)
            {
                Debug.Log("xxxUndo  return");
                return;
            }
            for(int i = 0;i<data.Count;i++)
            {
                data[i].colors = colors[i];
            }
        }
}
}

到了这里,关于Unity引擎修改模型顶点色的工具的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 虚幻引擎中导出模型,并导入到Unity

    在虚幻商城中添加glTF 免费插件 打开虚幻引擎插件面板,启用glTF 插件,并重启引擎 选择要导出的模型资源,右键选择导出,选择格式为 gltf。 blender导入刚刚导出的gltf格式文件 再导出为fbx格式 此时就可以在unity中正常使用了

    2024年02月13日
    浏览(52)
  • Edge-TTS:微软推出的,免费、开源、支持多种中文语音语色的AI工具

    项目地址:rany2/edge-tts: Use Microsoft Edge\\\'s online text-to-speech service from Python WITHOUT needing Microsoft Edge or Windows or an API key (github.com) Edge-TTS是由微软推出的文本转语音Python库,通过微软Azure Cognitive Services转化文本为自然语音。适合需要语音功能的开发者,GitHub上超3000星。作为国内付费

    2024年04月11日
    浏览(47)
  • ​Edge-TTS:微软推出的,免费、开源、支持多种中文语音语色的AI工具

    Edge-TTS是由微软推出的文本转语音Python库,通过微软Azure Cognitive Services转化文本为自然语音。适合需要语音功能的开发者,GitHub上超3000星。作为国内付费TTS服务的替代品,Edge-TTS支持40多种语言和300种声音,提供优质的语音输出,满足不同开发需求。 Edge-TTS 项目地址: https

    2024年04月26日
    浏览(40)
  • Fabric.js 拖拽顶点修改多边形形状

    我正在参加「掘金·启航计划」 戴尬猴,我是德育处主任 这次要介绍的一个demo是\\\"拖拽多边形定点修改多边形形状\\\"。 其实 Fabric.js 官网也有这个demo:Fabric.js demos · Custom controls, polygon 。但这个demo可能对于刚接触 Fabric.js 的工友来说有点过于复杂,所以本文就把该demo进一步简

    2024年02月03日
    浏览(124)
  • Unity引擎打包AssetBundle后模型网格数据丢失问题

    大家好,我是阿赵。 在项目里面,有时候会遇到这样一个问题。在Unity编辑器里面编写了一个shader,在编辑器看,效果都是没有问题的。但如果把资源打包成AssetBundle后,会发现shader的表现不对了。遇到了这种问题的时候,一般人会各自的怀疑,怀疑是不是shader写得不对,导

    2024年02月07日
    浏览(53)
  • [unity]三角形顶点顺序

    详见官方文档:Unity - Manual: Mesh data (unity3d.com) 翻译: 拓扑描述网格具有的面类型。 网格的拓扑定义了索引缓冲区的结构,索引缓冲区又描述了顶点位置如何组合成面。每种类型的拓扑都使用索引数组中不同数量的元素来定义单个面。 Unity支持以下网格拓扑: 三角形 Quad 线

    2024年02月07日
    浏览(54)
  • 【Unity 实用工具篇】✨| ReferenceFinder 引用查找插件,提高引擎查找使用效率

    前言 ReferenceFinder 是一个比较小众的插件

    2024年02月14日
    浏览(61)
  • Unity中的模型双面显示修改操作

    目录 前言 一、Unity 版本 二、操作步骤 1.原模型单面效果  2.修改为双面操作

    2024年02月11日
    浏览(33)
  • Unity中URP下的顶点偏移

    在上篇文章中,我们实现了URP下的半透明效果。 Unity中URP下的半透明效果实现 在这篇文章中,我们实现一下像鬼魂一样的顶点偏移效果。 在顶点着色器中,对模型本地空间坐标在转化成齐次裁剪坐标前,进行赋值修改 y = A sin(ωx + φ) + B v.vertexOS.z += sin(_Time.y); 这里用模型顶点

    2024年02月04日
    浏览(49)
  • Unity中URP下的SimpleLit顶点着色器

    在上一篇文章中,我们了解了URP下SimpleLit的整体框架。 Unity中URP下 SimpleLit框架 我们在这篇文章中,来了解一下URP下SimpleLit中,光照核心部分的顶点着色器干了什么,方便之后我们自定义自己的光照Shader。 因为被Unity封装的原因。所以,顶点着色器 和 片元着色器 主要是在这

    2024年01月17日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包