unity在模型上绘制贴图 您所在的位置:网站首页 如何在模型上画贴图 unity在模型上绘制贴图

unity在模型上绘制贴图

2024-07-15 05:00| 来源: 网络整理| 查看: 265

unity在模型上绘制贴图 前言

在我的上一篇文章【基于高度进行混合的shader】里面分享了如何利用高度图进行贴图的混合,里面使用了T4M插件来绘制控制混合的control贴图。 这里写图片描述

像T4M这样直接在mesh上对贴图进行绘制的功能对于美术的同学肯定不陌生,很多建模工具都支持直接在模型上对贴图进行绘制,如C4D的bodypaint工具、allegorithmic公司推出的Substance Painter都支持直接在模型上进行绘制。

这里分享一下如何在UNITY里实现这个在模型上绘画的功能。 这里写图片描述

准备工作

在unity中新建一个工程,创建所需要的几个文件夹 这里写图片描述

一个用来测试的mesh 这里写图片描述

用来测试的几张地表贴图 这里写图片描述

一些用来做笔刷的PNG图(直接从T4M里面扒过来的) 这里写图片描述

新建一个scene,把测试用的模型拖到场景中,给模型一个新的Material,并使用附件中的文件名为mya_T4M_4tex_blend_diffuse 的这个shader,并参考下图进行配置: 这里写图片描述

生成控制混合的贴图

创建一个meshPainter.cs文件,挂在这个模型上。

using UnityEngine; using System.Collections; [ExecuteInEditMode] [RequireComponent(typeof(MeshCollider))] public class meshPainter : MonoBehaviour { void Start () { } void Update () { } }

为了自定义脚本的Inspector显示,我们需要一个Style脚本,我们再创建一个meshPainterStyle.cs文件,放在Editor文件下

using UnityEngine; using UnityEditor; using System.IO; using System.Collections; [CustomEditor(typeof(meshPainter))] [CanEditMultipleObjects] public class meshPainterStyle : Editor { public override void OnInspectorGUI() { } }

在模型上绘制需要shader和control贴图,所以首先判断当前模型的shader是否正确以及是否有control贴图,如果shader不正确或者control贴图不存在就会显示警告信息,并显示一个生成control贴图的按钮。

string ContolTexName = ""; public override void OnInspectorGUI() { if (Cheak()) { } } //检查 bool Cheak() { bool Cheak = false; Transform Select = Selection.activeTransform; Texture ControlTex = Select.gameObject.GetComponent().sharedMaterial.GetTexture("_Control"); if(Select.gameObject.GetComponent().sharedMaterial.shader == Shader.Find("Mya/texBlend/mya_4tex_blend_diffuce") || Select.gameObject.GetComponent().sharedMaterial.shader == Shader.Find("Mya/texBlend/mya_4tex_blend_normal")) { if(ControlTex == null) { EditorGUILayout.HelpBox("当前模型材质球中未找到Control贴图,绘制功能不可用!", MessageType.Error); if (GUILayout.Button("创建Control贴图")) { creatContolTex(); //Select.gameObject.GetComponent().sharedMaterial.SetTexture("_Control", creatContolTex()); } } else { Cheak = true; } } else { EditorGUILayout.HelpBox("当前模型shader错误!请更换!", MessageType.Error); } return Cheak; } //创建Contol贴图 void creatContolTex() { //创建一个新的Contol贴图 string ContolTexFolder = "Assets/MeshPaint/Controler/"; Texture2D newMaskTex = new Texture2D(512, 512, TextureFormat.ARGB32, true); Color[] colorBase = new Color[512 * 512]; for(int t = 0; t< colorBase.Length; t++) { colorBase[t] = new Color(1, 0, 0, 0); } newMaskTex.SetPixels(colorBase); //判断是否重名 bool exporNameSuccess = true; for(int num = 1; exporNameSuccess; num++) { string Next = Selection.activeTransform.name +"_"+ num; if (!File.Exists(ContolTexFolder + Selection.activeTransform.name + ".png")) { ContolTexName = Selection.activeTransform.name; exporNameSuccess = false; } else if (!File.Exists(ContolTexFolder + Next + ".png")) { ContolTexName = Next; exporNameSuccess = false; } } string path = ContolTexFolder + ContolTexName + ".png"; byte[] bytes = newMaskTex.EncodeToPNG(); File.WriteAllBytes(path, bytes);//保存 AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);//导入资源 //Contol贴图的导入设置 TextureImporter textureIm = AssetImporter.GetAtPath(path) as TextureImporter; textureIm.textureFormat = TextureImporterFormat.ARGB32; textureIm.isReadable = true; textureIm.anisoLevel = 9; textureIm.mipmapEnabled = false; textureIm.wrapMode = TextureWrapMode.Clamp; AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);//刷新 setContolTex(path);//设置Contol贴图 } //设置Contol贴图 void setContolTex(string peth) { Texture2D ControlTex = (Texture2D)AssetDatabase.LoadAssetAtPath(peth, typeof(Texture2D)); Selection.activeTransform.gameObject.GetComponent().sharedMaterial.SetTexture("_Control", ControlTex); }

面板上显示如下: 这里写图片描述

功能面板的实现

我们先看一些面板上需要显示什么 这里写图片描述

一个编辑模式的开关一个控制笔刷大小的滑条一个控制笔刷强度的滑条用于选择绘制的通道的区域用于选择笔刷性状的区域

编辑模式的开关是一个使用的button样式的toogle,这样就能制作这种类似按钮的开关,点击会按下并保持,再次点击会抬起。

开关的图标使用了系统内置的EditCollider图标,关于如何使用unity内置的图标可以参考雨松大大的这篇文章:Unity3D研究院之系统内置系统图标大整理

而笔刷的大小和强度是两个简单的Slider

bool isPaint; float brushSize = 16f; float brushStronger = 0.5f; public override void OnInspectorGUI() { if (Cheak()) { GUIStyle boolBtnOn = new GUIStyle(GUI.skin.GetStyle("Button"));//得到Button样式 isPaint = GUILayout.Toggle(isPaint, EditorGUIUtility.IconContent("EditCollider"), boolBtnOn, GUILayout.Width(35), GUILayout.Height(25));//编辑模式开关 brushSize = (int)EditorGUILayout.Slider("Brush Size", brushSize, 1, 36);//笔刷大小 brushStronger = EditorGUILayout.Slider("Brush Stronger", brushStronger, 0, 1f);//笔刷强度 } }

这里写图片描述 Inspector面板已经正确显示出来了。

贴图选择和笔刷选择使用了GUILayout.SelectionGrid()方法,需要传一个Texture[]进去,我们先写两个函数得到这两个数组

//获取材质球中的贴图 void layerTex() { Transform Select = Selection.activeTransform; texLayer = new Texture[4]; texLayer[0] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent().sharedMaterial.GetTexture("_Splat0")) as Texture; texLayer[1] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent().sharedMaterial.GetTexture("_Splat1")) as Texture; texLayer[2] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent().sharedMaterial.GetTexture("_Splat2")) as Texture; texLayer[3] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent().sharedMaterial.GetTexture("_Splat3")) as Texture; } //获取笔刷 void IniBrush() { string T4MEditorFolder = "Assets/MeshPaint/Editor/"; ArrayList BrushList = new ArrayList(); Texture BrushesTL; int BrushNum = 0; //从Brush0.png这个文件名开始对Assets/MeshPaint/Editor/Brushes文件夹进行搜索,把搜到的图片加入到ArrayList里 do { BrushesTL = (Texture)AssetDatabase.LoadAssetAtPath(T4MEditorFolder + "Brushes/Brush" + BrushNum + ".png", typeof(Texture)); if (BrushesTL) { BrushList.Add(BrushesTL); } BrushNum++; } while (BrushesTL); brushTex = BrushList.ToArray(typeof(Texture)) as Texture[];//把ArrayList转为Texture[] }

通道选择直接获取shader的4个layer上的贴图,笔刷选择部分则检测到Assets/MeshPaint/Editor/Brushes/这个文件夹下面所有Brush+数字命名的png文件,获取这些文件组成一个Texture[];最后显示在Inspector面板上

public override void OnInspectorGUI() { GUIStyle boolBtnOn = new GUIStyle(GUI.skin.GetStyle("Button"));//得到Button样式 isPaint = GUILayout.Toggle(isPaint, EditorGUIUtility.IconContent("EditCollider"), boolBtnOn, GUILayout.Width(35), GUILayout.Height(25));//编辑模式开关 brushSize = (int)EditorGUILayout.Slider("Brush Size", brushSize, 1, 36);//笔刷大小 brushStronger = EditorGUILayout.Slider("Brush Stronger", brushStronger, 0, 1f);//笔刷强度 layerTex();//获取贴图 IniBrush();//获取笔刷 selTex = GUILayout.SelectionGrid(selTex, texLayer, 4, "gridlist", GUILayout.Width(340), GUILayout.Height(86));//通道选择 selBrush = GUILayout.SelectionGrid(selBrush, brushTex, 9, "gridlist", GUILayout.Width(340), GUILayout.Height(70));//笔刷选择 }

最后我们稍微整理下布局

public override void OnInspectorGUI() { if (Cheak()) { GUIStyle boolBtnOn = new GUIStyle(GUI.skin.GetStyle("Button")); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); isPaint = GUILayout.Toggle(isPaint, EditorGUIUtility.IconContent("EditCollider"), boolBtnOn, GUILayout.Width(35), GUILayout.Height(25)); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); brushSize = (int)EditorGUILayout.Slider("Brush Size", brushSize, 1, 36); brushStronger = EditorGUILayout.Slider("Brush Stronger", brushStronger, 0, 1f); IniBrush(); layerTex(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal("box", GUILayout.Width(340)); selTex = GUILayout.SelectionGrid(selTex, texLayer, 4, "gridlist", GUILayout.Width(340), GUILayout.Height(86)); GUILayout.EndHorizontal(); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal("box", GUILayout.Width(318)); selBrush = GUILayout.SelectionGrid(selBrush, brushTex, 9, "gridlist", GUILayout.Width(340), GUILayout.Height(70)); GUILayout.EndHorizontal(); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } }

此时面板如下图: 这里写图片描述

绘制功能

在模型上绘画的原理其实很简单,从鼠标位置发射一条射线,碰到模型后返回当前碰撞点的贴图坐标,再计算笔刷大小得到一个区域的像素,用一个颜色覆盖掉原来的像素颜色即可。

void Painter() { Transform CurrentSelect = Selection.activeTransform; MeshFilter temp = CurrentSelect.GetComponent();//获取当前模型的MeshFilter float orthographicSize = (brushSize * CurrentSelect.localScale.x) * (temp.sharedMesh.bounds.size.x / 200);//笔刷在模型上的正交大小 MaskTex = (Texture2D)CurrentSelect.gameObject.GetComponent().sharedMaterial.GetTexture("_Control");//从材质球中获取Control贴图 brushSizeInPourcent = (int)Mathf.Round((brushSize * MaskTex.width) / 100);//笔刷在模型上的大小 bool ToggleF = false; Event e = Event.current;//检测输入 HandleUtility.AddDefaultControl(0); RaycastHit raycastHit = new RaycastHit(); Ray terrain = HandleUtility.GUIPointToWorldRay(e.mousePosition);//从鼠标位置发射一条射线 if (Physics.Raycast(terrain, out raycastHit, Mathf.Infinity, 1


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有