1. 程式人生 > >Unity-程式碼中動態設定Material引數

Unity-程式碼中動態設定Material引數

我們知道在Unity中可以在程式碼中動態地改變Material監視面板中的引數,如改變數值大小或替換貼圖。常用的API有:SetColor , SetFloat, SetInt, SetTexture.

然而今天在設定一個StandardShader的材質貼圖的時候,發現設定了法線貼圖但是場景中的物體並沒有法線凹凸的效果,需要啟用一下材質面板才行。經過反覆檢查以及查詢資料,主要有以下兩個問題:

// 貼圖型別
string[] TEXTURE_TYPE = { "_MetallicGlossMap", "_BumpMap", "_ParallaxMap", "_OcclusionMap", "_DetailMask"
, "_DetailAlbedoMap", "_DetailNormalMap"}; // Material需要設定的關鍵字 string[] TEXTURE_KEYWORD = { "_METALLICGLOSSMAP", "_NORMALMAP", "_PARALLAXMAP", "", "_DETAIL_MULX2", "_DETAIL_MULX2", "_DETAIL_MULX2" };
  1. 法線貼圖匯入進來需要設定型別為NormalMap

    // 設定法線貼圖的型別
    if (fileName == diffuseName + TEXTURE_TYPE[1])
    {
        TextureImporter importer = (TextureImporter)AssetImporter.GetAtPath
    (filePath); importer.textureType = TextureImporterType.NormalMap; importer.SaveAndReimport(); }

  2. 使用標準著色器(StandardShader)的Material要設定啟用相應的關鍵字

    // 在Material.SetTexture之前 開啟相應的KeyWord
    mat.EnableKeyword(TEXTURE_KEYWORD[i]);

    以下是我查文件自己理解的,不一定準確。

    一個Material所使用的標準著色器在Unity中其實是多個著色器的集合。因為一個材質的著色器不可能涵蓋所有的功能,比如GI、霧效、HDR等高耗能的效果,所以Unity把標準著色器分成了帶有不同特殊功能的著色器變體(Shader Variant)。當把NormalMap分配給材質,就是激活了支援法線貼圖的著色器變體;把視差貼圖分配給材質,就是激活了支援視差貼圖的著色器變體。所以,如果要把某個特殊的貼圖賦給材質,就要開啟材質相應的關鍵字,以啟用支援相應功能的著色器變體。

    需要專門開啟的關鍵字有以下幾個:

    關鍵字 特性
    _NORMALMAP 法線對映
    _ALPHATEST_ON 用於CutOut渲染模式
    _ALPHABLEND_ON 用於Fade渲染模式
    _ALPHAPREMULTIPLY_ON 用於Transparent渲染模式
    _EMISSION 設定自發光
    _PARALLAXMAP 設定視差貼圖
    _DETAIL_MULX2 用於設定第二個貼圖通道
    _METALLICGLOSSMAP 在 Metallic工作流中設定金屬度貼圖
    _SPECGLOSSMAP 在 Specular工作流中設定高光貼圖

    下面附上設定材質屬性的部分程式碼:

    /// <summary> 
    /// 設定材質中shader的相關屬性 
    ///<para name = "mat"> 需要設定的Material </para> 
    ///<para name = "meshMat"> 要傳入shader的資料集合 </para> 
    ///<para name = "fbxName"> 模型的名字,在這裡主要是為了得到材質貼圖資料夾的位置 </para>
    /// </summary>
    void SetShader(Material mat, ShaderData data, string fbxName)
    {  // 這裡預設貼圖資源中主貼圖的名字就是材質名,其他貼圖的名字是材質名+貼圖型別
        string diffuseName = mat.name;
        // textureFiles用於記錄貼圖資料夾中所有的圖片檔案,記錄它們的貼圖名和路徑
        Dictionary<string, string> textureFiles = new Dictionary<string, string>();
        // texturePath是之前記錄好的一個fbx模型對應的貼圖資料夾的路徑
        string[] filesPath = Directory.GetFiles(texturePath[fbxName]);
        foreach (string filePath in filesPath)
        {   // TEXTURE_EXT是預設的圖片字尾名,用於標記圖片格式(如.jpg,.png,.tif等)
            if (Array.IndexOf(TEXTURE_EXT, Path.GetExtension(filePath)) != -1)
            {
                string fileName = Path.GetFileNameWithoutExtension(filePath);
                if (fileName.IndexOf(diffuseName) == 0)
                {
                    textureFiles[fileName] = filePath;
                    Debug.Log(fileName + " , " + filePath);
                    // 設定法線貼圖的型別
                    if (fileName == diffuseName + TEXTURE_TYPE[1])
                    {
                        TextureImporter importer = (TextureImporter)AssetImporter.GetAtPath(filePath);
                        importer.textureType = TextureImporterType.NormalMap;
                        importer.SaveAndReimport();
                    }
                }
            }
        }
    
        // 設定材質的主貼圖,也就是Albedo貼圖
        if (textureFiles.ContainsKey(diffuseName))
        {
            Debug.Log("MainTexture Exist");
            mat.mainTexture = AssetDatabase.LoadAssetAtPath<Texture>(textureFiles[diffuseName]);
        }
        // 設定其他特殊型別的貼圖
        for (int i = 0; i < TEXTURE_TYPE.Length; ++i)
        {
            if (textureFiles.ContainsKey(diffuseName + TEXTURE_TYPE[i]))
            {
                Debug.Log(TEXTURE_TYPE[i] + " Exist ");
                if (TEXTURE_KEYWORD[i] != "")
                    mat.EnableKeyword(TEXTURE_KEYWORD[i]);
                mat.SetTexture(TEXTURE_TYPE[i], AssetDatabase.LoadAssetAtPath<Texture>(textureFiles[diffuseName + TEXTURE_TYPE[i]]));
            }
        }
    
        mat.color = data.color;
        mat.SetFloat("_Metallic", data.metallic);
        mat.SetFloat("_Glossiness", data.glossiness);
        mat.SetColor("_EmissionColor", data.emissionColor);
    }