赞
踩
在我的上一篇文章【基于高度进行混合的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>();//获取当前模型的MeshFilter
float 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>();//获取当前模型的MeshFilter
float 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);//刷新
}
}
到场景里面尝试绘制一下
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。