unity3d混合贴图_unity3d 贴图混合叠加 - CSDN
  • 今天我们讨论一下,如何在unity3d的一个plane中显示多张贴图的一部分,组合贴图。 首先新建一个plane 然后在project面板新建一个材质 将材质赋给这个plane 然后编写shader whiteboard.shader 将这个...

    今天我们讨论一下,如何在unity3d的一个plane中显示多张贴图的一部分,组合贴图。

    首先新建一个plane



    然后在project面板新建一个材质





    将材质赋给这个plane


    然后编写shader whiteboard.shader

    将这个shader赋给材质

    Shader "Custom/WhiteBoard" {
    	Properties
        	{
    //下面三个是要叠加的贴图
            _MainTex ("Base (RGB), Alpha (A)", 2D) = "white" {}
    		_MiddleTex ("Middle (RGB),Alpha (A)", 2D) = "white" {}
    		_TopTex ("Top (RGB),Alpha (A)", 2D) = "white" {}
    		_WhiteBoardTex ("WhiteBoard (RGB),Alpha (A)", 2D) = "white" {}//需要一个白色的贴图做底色
    
            _StencilComp ("Stencil Comparison", Float) = 8
            _Stencil ("Stencil ID", Float) = 0
            _StencilOp ("Stencil Operation", Float) = 0
            _StencilWriteMask ("Stencil Write Mask", Float) = 255
            _StencilReadMask ("Stencil Read Mask", Float) = 255
    
            _ColorMask ("Color Mask", Float) = 15
            // 以 1 - _Height 长度为高度
            _Height ("Radius", Range(0,0.5)) = 0.5
    
    		_TopRate ("TopRate", Range(0,1)) = 0.33  //显示裁剪的比例
    		_MiddleRate ("MiddleRate", Range(0,1)) = 0.33
    		_ButtomRate ("ButtomRate", Range(0,1)) = 0.33
        }
        
        SubShader
        {
            LOD 100
    
            Tags
            {
                "Queue" = "Transparent"
                "IgnoreProjector" = "True"
                "RenderType" = "Transparent"
                "PreviewType"="Plane"
            }
    
            Stencil
            {
                Ref [_Stencil]
                Comp [_StencilComp]
                Pass [_StencilOp] 
                ReadMask [_StencilReadMask]
                WriteMask [_StencilWriteMask]
            }
            
            Cull Off
            Lighting Off
            ZWrite Off
            ZTest [unity_GUIZTestMode]
            Offset -1, -1
            Fog { Mode Off }
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMask [_ColorMask]
    
            
    
            Pass
            {
                CGPROGRAM
                    #pragma target 3.0
                    #pragma vertex vert
                    #pragma fragment frag
                    #include "UnityCG.cginc"
    
                    float _Height;
    				float _TopRate;
    				float _MiddleRate;
    				float _ButtomRate;
    
                    struct appdata_t
                    {
                        float4 vertex : POSITION;
                        float2 texcoord : TEXCOORD0;
                        fixed4 color : COLOR;
                    };
        
                    struct v2f
                    {
                        float4 vertex : SV_POSITION;
                        half2 texcoord : TEXCOORD0;
                        fixed4 color : COLOR;
                    };
        
                    sampler2D _MainTex;
    				sampler2D _MiddleTex;
    				sampler2D _TopTex;
    				sampler2D _WhiteBoardTex;
                    float4 _MainTex_ST;
                    
                    v2f vert (appdata_t v)
                    {
                        v2f o;
                        o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                        o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                        o.color = v.color;
    #ifdef UNITY_HALF_TEXEL_OFFSET
                        o.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
    #endif
                        return o;
                    }
                    
                    fixed4 frag (v2f i) : COLOR
                    {
    				    fixed4 col2 = tex2D(_MiddleTex, i.texcoord) ;//获得贴图的rgba,颜色像素信息
    					fixed4 base_col = tex2D(_MainTex, i.texcoord) ;
    					fixed4 top_pic = tex2D(_TopTex, i.texcoord) ;
    					fixed4 white_canva = tex2D(_WhiteBoardTex, i.texcoord) ;
    
                        float2 uv = i.texcoord.xy; //输入的面的uv坐标
                        float4 c = i.color;  //输入的面的颜色信息
    					float4 c1 = i.color;
    					float4 c3 = i.color;
    					float4 c_alpha_mask = i.color;
    			
    					fixed4 col4 = col2;
                  
                        if( uv.y < _TopRate && uv.y > 0.0)
                        {        
    							c.rgba = col2.rgba;//根据不同的uv坐标显示不同贴图的颜色信息
                        }
                         if( uv.y > _TopRate + _MiddleRate && uv.y <1.0)
                        {        
    							c1.rgba = base_col.rgba;
                        }
    
    					if(uv.y>_TopRate && uv.y<_TopRate + _MiddleRate)
    					{
    						c3.rgba = top_pic.rgba;
    					}
    					
                        fixed4 col_a = white_canva * c * c1 *c3;//将颜色信息与白色底图叠加
                        return col_a; //最后输出叠加后的颜色信息,贴图就叠加完成
                    }
                                    
                ENDCG
            }
        }
    }
    



    最后看到效果



    这样就可以实现在不同的uv坐标下,显示不同的贴图,达到贴图的叠加了。

    展开全文
  • unity在模型上绘制贴图 前言 在我的上一篇文章【基于高度进行混合的shader】里面分享了如何利用高度图进行贴图混合,里面使用了T4M插件来绘制控制混合的control贴图。 像T4M这样直接在mesh上对贴图进行绘制的功能...

    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>();//获取当前模型的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);//刷新
        }
    }
    

    到场景里面尝试绘制一下
    这里写图片描述

    源文件下载

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

    展开全文
  • Shader "Blend 2 Textures, Simply Lit" {    Properties { _Color ("Color", Color) = (1,1,1) _Blend ("Blend", Range (0,1)) = 0.5  _MainTex ("Texture 1", 2D) = ""  ..._Texture2 ("Texture 2", 2D) = ...
    Shader "Blend 2 Textures, Simply Lit" { 
     
    Properties {
    _Color ("Color", Color) = (1,1,1)
    _Blend ("Blend", Range (0,1)) = 0.5 
    _MainTex ("Texture 1", 2D) = "" 
    _Texture2 ("Texture 2", 2D) = ""
    }
     
    Category {
    Material {
    Ambient[_Color]
    Diffuse[_Color]
    }
     
    // iPhone 3GS and later
    SubShader {Pass {
    Lighting On
    SetTexture[_MainTex]
    SetTexture[_Texture2] { 
    ConstantColor (0,0,0, [_Blend]) 
    Combine texture Lerp(constant) previous
    }
    SetTexture[_] {Combine previous * primary Double}
    }}
     
    // pre-3GS devices, including the September 2009 8GB iPod touch
    SubShader {
    Pass {
    SetTexture[_MainTex]
    SetTexture[_Texture2] {
    ConstantColor (0,0,0, [_Blend])
    Combine texture Lerp(constant) previous
    }
    }
    Pass {
    Lighting On
    Blend DstColor SrcColor
    }
    }
    }
     


    展开全文
  • 纹理压缩,大多情况会在地形着色器中使用,当我们遇到需要将一组纹理混合显示...准备工作:四张地形贴图 和 包含四个混合纹理的灰度贴图Shader "Example / 001" { Properties { _MainTint ("Diffuse Tint", Color)

    纹理压缩,大多情况会在地形着色器中使用,当我们遇到需要将一组纹理混合显示在一个游戏物体上的需求时,我们就需要使用一种特殊的纹理操作或者纹理压缩的方式来完美的进行纹理混合。

    准备工作:四张地形贴图 和 包含四个混合纹理的灰度贴图

    Shader "Example / 001" 
    {
        Properties 
        {
            _MainTint ("Diffuse Tint", Color) = (1,1,1,1)
    
            //两个颜色属性
            _ColorA ("Terrain Color A", Color) = (1,1,1,1)
            _ColorB ("Terrain Color B", Color) = (1,1,1,1)
            //2D贴图
            _RTexture ("Red Channel Texture", 2D) = ""{}
            _GTexture ("Green Channel Texture", 2D) = ""{}
            _BTexture ("Blue Channel Texture", 2D) = ""{}
            _ATexture ("Alpha Channel Texture", 2D) = ""{}
            //混合后的纹理
            _BlendTex ("Blend Texture", 2D) = ""{}
        }
    
        SubShader 
        {
            Tags { "RenderType"="Opaque" }
            LOD 200
    
    
    
            CGPROGRAM
            #pragma surface surf Lambert
            #pragma target 4.0
    
            //创建与CG内的属性的连接
            float4 _MainTint;
            float4 _ColorA;
            float4 _ColorB;
            sampler2D _RTexture;
            sampler2D _GTexture;
            sampler2D _BTexture;
            sampler2D _BlendTex;
            sampler2D _ATexture;
    
            struct Input 
            {
                //获取每个纹理的uv坐标,方便对其偏移
                float2 uv_RTexture;
                float2 uv_GTexture;
                float2 uv_BTexture;
                float2 uv_ATexture;
                float2 uv_BlendTex;
            };
    
            void surf (Input IN, inout SurfaceOutput o) 
            {
                //从混合纹理采样,获取像素数据
                //返回float4(rgba or xyzw)
                float4 blendData = tex2D(_BlendTex, IN.uv_BlendTex);
    
                //从这些纹理中获取我们需要的纹理uv数据
                float4 rTexData = tex2D(_RTexture, IN.uv_RTexture);
                float4 gTexData = tex2D(_GTexture, IN.uv_GTexture);
                float4 bTexData = tex2D(_BTexture, IN.uv_BTexture);
                float4 aTexData = tex2D(_ATexture, IN.uv_ATexture);
    
                //需要一个新的RGBA值来混合所有的纹理
                //lerp插值的计算前几节说过了,举个例子 ,blendData.g = 0的时候函数返回rTexData,blendData.g = 1的时候,函数返回gTexData
                //所以当gTexData的值更偏向哪一边的时候,显示出来的颜色就更偏向哪一边。
                float4 finalColor;
                finalColor = lerp(rTexData, gTexData, blendData.g);
                finalColor = lerp(finalColor, bTexData, blendData.b);
                finalColor = lerp(finalColor, aTexData, blendData.a);
                finalColor.a = 1.0;
    
                //添加我们的地形着色
                //混合纹理的r值将在_ColorA _ColorB中进行插值计算得出
                float4 terrainLayers = lerp(_ColorA, _ColorB, blendData.r);
                finalColor *= terrainLayers;
                //最终颜色取(0,1)
                finalColor = saturate(finalColor);
    
                o.Albedo = finalColor.rgb * _MainTint.rgb;
                o.Alpha = finalColor.a;
            }
            ENDCG
        } 
        FallBack "Diffuse"
    }
    

    需要注意的是
    lerp插值计算, 这个函数使我们得到参数1 和 参数2之间的值 并使用参数3来计算混合量

    展开全文
  • 使用Unity3D 中的 ShaderLab 实现两张不同贴图之前的混合 类似于3dsMAX 中的Blend材质. 1.在Properties 中定义三个变量.我们需要使用的..1. _Color 主要是用它的 Alpha 来进行两张图的混合 ,2.两张需要进行操作的...
  • Unity3D 游戏贴图(法线贴图,漫反射贴图,高光贴图)   原帖网址http://www.u3dpro.com/read.php?tid=207 感谢jdk900网友的辛苦编写  我们都知道,一个三维场景的画面的好坏,百分之四十取决于模型,...
  • 2D贴图绘制方式有两种,第一种由 GUI 绘制,第二种是将贴图以材质的形式绘制在游戏对象中。 GUI绘制 在屏幕中绘制一张静态贴图,需要使用 GUI.DrawTexture() 方法,该方法原型如下: GUI.DrawTexture(位置, 图片...
  • 复习第三章:(学习请看下面的文章链接)原文:https://catlikecoding.com/unity/tutorials/rendering/part-3/译文:http://gad.qq.com/program/translateview/7173931第三章使用多张纹理贴图1. 对多张纹理贴图进行...
  • 因此我们需要使用多张不一样的贴图根据一系列参数,混合成一张皮肤贴图。但是混合的方式多种多样,怎么才能实现比较好的效果呢,经过一段时间的学习跟实践开发,以及对一些其他游戏的研究,大致纹理的混合有以下的...
  • 分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow也欢迎大家转载本...分享知识,造福人民,实现我们中华民族伟大复兴!&nbsp;&nbsp;&nbsp;... 我们都知道,一个三维场
  • Shader "Unlit/Test" { Properties { _MainTex ("Texture", 2D) = "white" {} _MainColor("MainColor",Color)=(1,1,1,1) _DColor("DColor"...{
1 2 3 4 5 ... 20
收藏数 2,672
精华内容 1,068
关键字:

unity3d混合贴图