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<MeshRenderer>().sharedMaterial.GetTexture("_Control");if(Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.shader == Shader.Find("Mya/texBlend/mya_4tex_blend_diffuce") || Select.gameObject.GetComponent<MeshRenderer>().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<MeshRenderer>().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<MeshRenderer>().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<MeshRenderer>().sharedMaterial.GetTexture("_Splat0")) as Texture;texLayer[1] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.GetTexture("_Splat1")) as Texture;texLayer[2] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.GetTexture("_Splat2")) as Texture;texLayer[3] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent<MeshRenderer>().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>();//获取当前模型的MeshFilterfloat orthographicSize = (brushSize * CurrentSelect.localScale.x) * (temp.sharedMesh.bounds.size.x / 200);//笔刷在模型上的正交大小MaskTex = (Texture2D)CurrentSelect.gameObject.GetComponent<MeshRenderer>().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 << LayerMask.NameToLayer("ground")))//射线检测名为"ground"的层{Handles.color = new Color(1f, 1f, 0f, 1f);//颜色Handles.DrawWireDisc(raycastHit.point, raycastHit.normal, orthographicSize);//根据笔刷大小在鼠标位置显示一个圆//鼠标点击或按下并拖动进行绘制if ((e.type == EventType.mouseDrag && e.alt == false && e.control == false && e.shift == false && e.button == 0) || (e.type == EventType.MouseDown && e.shift == false && e.alt == false && e.control == false && e.button == 0 && ToggleF == false)){//选择绘制的通道Color targetColor = new Color(1f, 0f, 0f, 0f);switch (selTex){case 0:targetColor = new Color(1f, 0f, 0f, 0f);break;case 1:targetColor = new Color(0f, 1f, 0f, 0f);break;case 2:targetColor = new Color(0f, 0f, 1f, 0f);break;case 3:targetColor = new Color(0f, 0f, 0f, 1f);break;}Vector2 pixelUV = raycastHit.textureCoord;//计算笔刷所覆盖的区域int PuX = Mathf.FloorToInt(pixelUV.x * MaskTex.width);int PuY = Mathf.FloorToInt(pixelUV.y * MaskTex.height);int x = Mathf.Clamp(PuX - brushSizeInPourcent / 2, 0, MaskTex.width - 1);int y = Mathf.Clamp(PuY - brushSizeInPourcent / 2, 0, MaskTex.height - 1);int width = Mathf.Clamp((PuX + brushSizeInPourcent / 2), 0, MaskTex.width) - x;int height = Mathf.Clamp((PuY + brushSizeInPourcent / 2), 0, MaskTex.height) - y;Color[] terrainBay = MaskTex.GetPixels(x, y, width, height, 0);//获取Control贴图被笔刷所覆盖的区域的颜色Texture2D TBrush = brushTex[selBrush] as Texture2D;//获取笔刷性状贴图float[] brushAlpha = new float[brushSizeInPourcent * brushSizeInPourcent];//笔刷透明度//根据笔刷贴图计算笔刷的透明度for (int i = 0; i < brushSizeInPourcent; i++){for (int j = 0; j < brushSizeInPourcent; j++){brushAlpha[j * brushSizeInPourcent + i] = TBrush.GetPixelBilinear(((float)i) / brushSizeInPourcent, ((float)j) / brushSizeInPourcent).a;}}//计算绘制后的颜色for (int i = 0; i < height; i++){for (int j = 0; j < width; j++){int index = (i * width) + j;float Stronger = brushAlpha[Mathf.Clamp((y + i) - (PuY - brushSizeInPourcent / 2), 0, brushSizeInPourcent - 1) * brushSizeInPourcent + Mathf.Clamp((x + j) - (PuX - brushSizeInPourcent / 2), 0, brushSizeInPourcent - 1)] * brushStronger;terrainBay[index] = Color.Lerp(terrainBay[index], targetColor, Stronger);}}Undo.RegisterCompleteObjectUndo(MaskTex, "meshPaint");//保存历史记录以便撤销MaskTex.SetPixels(x, y, width, height, terrainBay, 0);//把绘制后的Control贴图保存起来MaskTex.Apply();ToggleF = true;}else if (e.type == EventType.mouseUp && e.alt == false && e.button == 0 && ToggleF == true){SaveTexture();//绘制结束保存Control贴图ToggleF = false;}}}public void SaveTexture(){var path = AssetDatabase.GetAssetPath(MaskTex);var bytes = MaskTex.EncodeToPNG();File.WriteAllBytes(path, bytes);AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);//刷新}

完整代码

using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections;[CustomEditor(typeof(MeshPainter))]
[CanEditMultipleObjects]
public class MeshPainterStyle : Editor
{string contolTexName = "";bool isPaint;float brushSize = 16f;float brushStronger = 0.5f;Texture[] brushTex;Texture[] texLayer;int selBrush = 0;int selTex = 0;int brushSizeInPourcent;Texture2D MaskTex;void OnSceneGUI(){if (isPaint){Painter();}}public override void OnInspectorGUI(){if (Cheak()){GUIStyle boolBtnOn = new GUIStyle(GUI.skin.GetStyle("Button"));//得到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 layerTex(){Transform Select = Selection.activeTransform;texLayer = new Texture[4];texLayer[0] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.GetTexture("_Splat0")) as Texture;texLayer[1] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.GetTexture("_Splat1")) as Texture;texLayer[2] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.GetTexture("_Splat2")) as Texture;texLayer[3] = AssetPreview.GetAssetPreview(Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.GetTexture("_Splat3")) as Texture;}//获取笔刷  void IniBrush(){string MeshPaintEditorFolder = "Assets/MeshPaint/Editor/";ArrayList BrushList = new ArrayList();Texture BrushesTL;int BrushNum = 0;do{BrushesTL = (Texture)AssetDatabase.LoadAssetAtPath(MeshPaintEditorFolder + "Brushes/Brush" + BrushNum + ".png", typeof(Texture));if (BrushesTL){BrushList.Add(BrushesTL);}BrushNum++;} while (BrushesTL);brushTex = BrushList.ToArray(typeof(Texture)) as Texture[];}//检查bool Cheak(){bool Cheak = false;Transform Select = Selection.activeTransform;Texture ControlTex = Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.GetTexture("_Control");if(Select.gameObject.GetComponent<MeshRenderer>().sharedMaterial.shader == Shader.Find("Mya/texBlend/mya_4tex_blend_diffuce") || Select.gameObject.GetComponent<MeshRenderer>().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<MeshRenderer>().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<MeshRenderer>().sharedMaterial.SetTexture("_Control", ControlTex);}void Painter(){Transform CurrentSelect = Selection.activeTransform;MeshFilter temp = CurrentSelect.GetComponent<MeshFilter>();//获取当前模型的MeshFilterfloat orthographicSize = (brushSize * CurrentSelect.localScale.x) * (temp.sharedMesh.bounds.size.x / 200);//笔刷在模型上的正交大小MaskTex = (Texture2D)CurrentSelect.gameObject.GetComponent<MeshRenderer>().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 << LayerMask.NameToLayer("ground")))//射线检测名为"ground"的层{Handles.color = new Color(1f, 1f, 0f, 1f);//颜色Handles.DrawWireDisc(raycastHit.point, raycastHit.normal, orthographicSize);//根据笔刷大小在鼠标位置显示一个圆//鼠标点击或按下并拖动进行绘制if ((e.type == EventType.mouseDrag && e.alt == false && e.control == false && e.shift == false && e.button == 0) || (e.type == EventType.MouseDown && e.shift == false && e.alt == false && e.control == false && e.button == 0 && ToggleF == false)){//选择绘制的通道Color targetColor = new Color(1f, 0f, 0f, 0f);switch (selTex){case 0:targetColor = new Color(1f, 0f, 0f, 0f);break;case 1:targetColor = new Color(0f, 1f, 0f, 0f);break;case 2:targetColor = new Color(0f, 0f, 1f, 0f);break;case 3:targetColor = new Color(0f, 0f, 0f, 1f);break;}Vector2 pixelUV = raycastHit.textureCoord;//计算笔刷所覆盖的区域int PuX = Mathf.FloorToInt(pixelUV.x * MaskTex.width);int PuY = Mathf.FloorToInt(pixelUV.y * MaskTex.height);int x = Mathf.Clamp(PuX - brushSizeInPourcent / 2, 0, MaskTex.width - 1);int y = Mathf.Clamp(PuY - brushSizeInPourcent / 2, 0, MaskTex.height - 1);int width = Mathf.Clamp((PuX + brushSizeInPourcent / 2), 0, MaskTex.width) - x;int height = Mathf.Clamp((PuY + brushSizeInPourcent / 2), 0, MaskTex.height) - y;Color[] terrainBay = MaskTex.GetPixels(x, y, width, height, 0);//获取Control贴图被笔刷所覆盖的区域的颜色Texture2D TBrush = brushTex[selBrush] as Texture2D;//获取笔刷性状贴图float[] brushAlpha = new float[brushSizeInPourcent * brushSizeInPourcent];//笔刷透明度//根据笔刷贴图计算笔刷的透明度for (int i = 0; i < brushSizeInPourcent; i++){for (int j = 0; j < brushSizeInPourcent; j++){brushAlpha[j * brushSizeInPourcent + i] = TBrush.GetPixelBilinear(((float)i) / brushSizeInPourcent, ((float)j) / brushSizeInPourcent).a;}}//计算绘制后的颜色for (int i = 0; i < height; i++){for (int j = 0; j < width; j++){int index = (i * width) + j;float Stronger = brushAlpha[Mathf.Clamp((y + i) - (PuY - brushSizeInPourcent / 2), 0, brushSizeInPourcent - 1) * brushSizeInPourcent + Mathf.Clamp((x + j) - (PuX - brushSizeInPourcent / 2), 0, brushSizeInPourcent - 1)] * brushStronger;terrainBay[index] = Color.Lerp(terrainBay[index], targetColor, Stronger);}}Undo.RegisterCompleteObjectUndo(MaskTex, "meshPaint");//保存历史记录以便撤销MaskTex.SetPixels(x, y, width, height, terrainBay, 0);//把绘制后的Control贴图保存起来MaskTex.Apply();ToggleF = true;}else if (e.type == EventType.mouseUp && e.alt == false && e.button == 0 && ToggleF == true){SaveTexture();//绘制结束保存Control贴图ToggleF = false;}}}public void SaveTexture(){var path = AssetDatabase.GetAssetPath(MaskTex);var bytes = MaskTex.EncodeToPNG();File.WriteAllBytes(path, bytes);AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);//刷新}
}

到场景里面尝试绘制一下

源文件下载

http://pan.baidu.com/s/1nuGn0aX

unity在模型上绘制贴图相关推荐

  1. 漏斗模型_绘制漏斗图

    漏斗模型_绘制漏斗图 漏斗思维,它是一种线性的思考方式,一般按照任务的完成路径,识别出几个关键的行为转化节点,然后分析行为点间的转化与流失情况,进而定位问题,指导决策. 漏斗模型是指多个自定义事件序列 ...

  2. unity模型导入android背景透明,Unity导入模型一面有贴图另一面透明的解决方案

    出现这种情况是因为模型用了双面材质,而Unity不支持双面材质.故导入模型到Unity中,会出现一面有贴图,另一面透明的情况. 解决方法: 1.自定义一个双面材质Shader,代码如下: Shader ...

  3. Echarts 在地图上绘制流向图

    首先需要获取地图JSON数据包,点此下载(Echarts官方地图数据,包含世界.中国,及国内各省数据). 实现效果如图: 代码如下: <!-- 此示例下载自 https://echarts.ap ...

  4. 如何在GEC-6818上绘制彩虹图?

    1.三色旗 这个题比较简单,就是把屏幕分成三块, 屏幕总色素块为 800 *480= 384,000‬, 将屏幕分为三块,就是每 128,000‬个色块换一个颜色. #include <stdi ...

  5. Unity在UI上绘制直线

    prefabUILine为Image的预制作  代码 using UnityEngine;public class UILineFactory : MonoBehaviour {public Rect ...

  6. plotly系列| 使用plotly在地图上绘制散点图和密度图

    目录 Plotly简介 在地图上制作散点图和密度图 1 . 在python中使用pip命令安装 2 .导入包 3 .读取文件 4 . 在地图上绘制散点图 5 . 绘制密度图,其属性与绘制散点图相似 关 ...

  7. java 在底图上绘制线条_使用底图和geonamescache绘制k表示聚类

    java 在底图上绘制线条 This is the third of four stories that aim to address the issue of identifying disease ...

  8. d3js绘制y坐标轴_使用D3.js 绘制折线图

    在一个网站上绘制折线图使用了ant/g2,打包后的体积到了一兆多,这不行了,需要按需加载.但是它的支持不太友好,我尝试在官网上用它的方法来按需引入,不是缺这就是缺那,很不好用. 反正我这里只是画个折线 ...

  9. 3dmax模型+unfold展开uv+substancepainter绘制贴图+导入unity

    本篇文章涉及到的内容有以下几个: 1.3dmax中模型 2.unfold展开uv 3.substance painter的绘制贴图 4.导入unity 第1:使用3dmax中自带的模型,茶壶: 导出模 ...

最新文章

  1. 关于页面有多个验证控件和多个按钮的问题
  2. CSS3 - 清除浮动
  3. Windows - node版本管理工具NVM
  4. “约见”面试官系列之常见面试题之第六十三篇之get和post区别(建议收藏)
  5. promise 为什么出现
  6. 多项目同时进行 如何高效协作?
  7. 高斯烟羽模型matlab程序,高斯烟羽模型的改进及在危化品泄漏事故模拟中的应用...
  8. 深度学习入门学习路线及好课推荐
  9. mysql里一个中文多少个字节_mysql里中文占多少个字节?
  10. Unity3D Shader系列之画虚线方式分析与总结
  11. 无心插柳OR志在必得?阿里推“来往”的意图
  12. oracle cmd命令导入,oracle使用cmd命令导入数据库
  13. python3 基础语法分享
  14. java异常类_java中常见的异常类
  15. js获取url中的主域名
  16. 电阻(4)之上拉电阻与下拉电阻详解
  17. Flex 布局在IE浏览器下的兼容问题
  18. (一)LAMP (CGI,fastcgi, PHP,基于php的LAMP架构,php连接数据库)
  19. 前端知识体系思维导图
  20. 基于jeeSite的软件测试课程作业 流程记录

热门文章

  1. 写在腾讯大讲堂演讲之后
  2. server 群辉emby_群晖NAS | 安装使用Emby媒体服务器(解决无法启动的问题)
  3. 删除文件等用trash命令
  4. 企业如何实现内容多渠道一键合规投放?
  5. Zoom网页端,电脑客户端,平板手机端有什么区别与应用
  6. TransFER: Learning Relation-aware Facial Expression Representations with Transformers--2021.Fanglei
  7. [附源码]Java计算机毕业设计SSM飞羽羽毛球馆管理系统
  8. 自动给好友发送新春祝福的java脚本
  9. 潮人篮球ios android,潮人篮球ios怎么在电脑上玩?潮人篮球ios电脑版玩法教程!...
  10. 宅社AcgClub API上线