2013年6月21日 星期五

[Unity] 以ARGB4444搭配Dithering進行貼圖減量

  之前一直受ARGB Texture所困擾,在Mobile平台沒有合適的壓縮格式能在品質與容量之間取得較好的平衡,下面列出幾個格式的缺點
  • RGBA PVRTC 4 bits - iOS推薦的壓縮格式,但是4bits對於圖像品質的破壞太大
  • ARGB 32 bit - 雖能保有圖片品質,但所消耗的容量實在太龐大
  • ARGB 16 bit - 雖能降低貼圖容量,但是顏色有明顯的帶狀瑕疵(color banding)
  Android平台由於各家GPU所支援的壓縮格式各有不同,因此ARGB 16 bit也就是降低texture容量最適合的選項,但16 bit所造成的帶狀瑕疵十分明顯,若真的要使用也不太可能,正好前一陣子看到Unity IN上有Cytus開發者之一的syyang所發表的文章



  • 使用 RGBA4444 搭配 Dithering 減少記憶體用量 by syyang @ UNITYIN

  •   這篇文章正好解決了這個問題,利用Dithering在16bit color depth獲得較佳的圖像呈現,消除帶狀瑕疵。進行Dither處理之後,圖片的顏色資訊會被重新處理,檢視圖片可發現變的有顆粒狀感(抖色),當圖片在眼中的呈現越小時,就會發現看起來跟truecolor的圖片品質頗接近。由於是對顏色資訊做了重新的演算,所以並不受到Unity的轉檔所影響,只要在Import Settings中設定Format為16 bit的壓縮,即可省下容量又能保有還能接受的圖片品質。

      此外需要注意的是經過dither處理的texture若又被Unity重新壓縮到不同尺寸的圖片時,會加重顆粒感使圖片品質降低(因為顏色訊息被重新處理過了),因此輸出給Unity的texture asset必須要輸出成遊戲主要使用的texture size。此外,若處理過的貼圖是用於3D模型的話,經過UV的拉扯也會使圖片失真更加明顯。目前Unity本身並不支援dithering,在網上也還沒看到有人做出支援dither處理的script/plug-in,因此得要自行預先處理這些textures。

    經過UV拉扯、尺寸放大後失真變的相當明顯
    但dithering還是有其諸多限制,而最原始的作法當然就是使用RGB444的安全色系來繪製texture,及在各個channel中只使用:0, 15, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 207, 223, 239, 255。但除了顏色數便少之外,還得少用漸層表現,要犧牲的也是相當多,也許能在不影響美術風格的前提下選擇性使用。

      在以Gimp進行dithering處理時,須先將貼圖縮成最終使用的尺寸,在縮小圖片時三種內插法看起來效果幾乎相同,然後再執行Dither to ARGB,此時會多出一個圖檔視窗就是dithering後的圖檔,進行匯出之後即可。匯出PNG的設定所有選項皆可取消,但當texture需要再alpha邊緣作細緻的過度時,透明像素的色彩是必須要被儲存的。Compression Level 0跟9在色系較單純的圖片上是幾乎沒有差別。


    Gimp在安裝script之後可進行dithering處理
    Gimp匯出PNG時的設定

    參考資料


    延伸閱讀

    2013年6月6日 星期四

    [Unity] Dynamic Camera FOV

    遭遇情境

      今天想達成一個攝影機的效果,就是當有特定的物體越來越接近畫面邊緣時,就要動態的增減Camera的FOV,讓該特定物體不要跑出畫面。

    解法

      大致上很簡單,只有兩個步驟:

    1. 求出物件距離畫面邊緣的距離
    2. 利用Mathf.Lerp達成相對應的FOV調整

      先利用Camera.WorldToViewportPoint( Vector3 )求出物件在畫面上的位置,返回的是normalized value(0至1之間),如此就能得知物件相對於畫面邊緣的距離,0代表是在畫面左方邊緣,1則是在畫面右方邊緣。

      再來則是利用Mathf.Lerp來調整FOV,Lerp實際上的運作是
    public static float Lerp(float start, float end, float value)
    {
        return ((1.0f - value) * start) + (value * end);
    }
    
    在其3個arguments中,value是一種類似權重的概念,當他為0時返回的值會是start,為1時返回的即是end,為中間的數值時就是返回按照比例計算後的結果。

      因此我只要把WorldToViewportPoint求得的值,以想要的數值範圍作normalize後,讓距離變成0至1之間的數值,傳入Lerp之中就可達成動態FOV的效果。範例如下:

    // amount : Value between 0 and 1 indicating the weight of value2.
    // 為距離螢幕邊緣比重的值
    Camera.fieldOfView = Mathf.Lerp( float CameraMinFOV, float CameraMaxFOV , float amount );
    


    參考資料


    延伸閱讀

    2013年6月5日 星期三

    [Unity] Mecanim Animator .controller 文件結構紀錄

    此文章紀錄Mecanim Animator Controller檔案的內容結構,當角色數量龐大時,眾多的Animator Controller會造成極大的管理上的困擾,因此若能直接解析內部的訊息的話,即可利用Script做外部的直接修改,做出更方便的管理工具。

    而在初次看過其內容後,獲得了一個重點即是,各地方的命名十分重要,Controller內部的目標來源都是以fileID的形式運作,但同時有些東西也可以在Unity中做命名,雖然會需要多一個命名規則的訂制與管理的工,但是直接利用Name做修改會比用fileID方便許多,譬如說Transition的命名。

    參考文件
    在開始看文件內容之前,先說明fileID的運作方式,fileID實際上是由兩組數字組成,首先是該物件的Class ID Number,然後是一組五位數物件在所在檔案內的序號(偶數),舉個例子,現在有一個帶有3個Animation Clip的FBX檔案,則這3個Clip的fileID就是:( 7400000, 7400001, 7400002 ),74為Animation Clip的ID Number。

    以下開始文件內容:
    在每個區段(term,代表一個object)的一開始,都會先有一串的數據資訊
    • --- !u!91 &9100000
      !u!後代表的是物件類型
      &(ampersand)後的數字是該區段資訊獨有的fileID
    AnimatorController此部份包含了Animator的基本屬性,如:
    • m_Name
      Animator的名稱
    • m_AnimatorEvents
      Flags
    • m_Layers
      Layers
    在來是各個Transition的參數(State與State間的Transition),
    • m_Name
      Transition的命名,預設狀況下是無命名的。
      如果想要手動修改.controller文件的話,對所有的Transition做命名會方便非常多,可快速的知道各個Transition資訊的位置。
    • m_SrcState / m_DstState
      來源跟目的State,填入的資訊為fileID。
    • m_TransitionDuration
      Transition的時間長度,應該是以SrcState的Motion的時間長度為基準的趴數。
      例如來源動作的長度為2秒,而m_TransitionDuration的值為0.5時就是使用1杪做過渡。
    • m_TransitionOffset
      DstState相對於TransitionDuration起始點的offset。
      當DstState的起始點與TransitionDuration的起始點相同時,則m_TransitionOffset的值為0。
      當DstState的結束點與TransitionDuration的起始點相同時,則m_TransitionOffset的值為1。
    • m_Conditions
      進行Transition的條件,以下的參數皆為Condition的條件式
      • m_ConditionMode
        條件判斷的模式
        • 1,Bool的True
        • 2,Bool的False
        • 3,Float/Int的Greater
        • 4,Float/Int的Less
        • 5,ExitTime
        • 6,Int的Equals
        • 7,Int的NotEqual
      •  m_ConditionEvent
        要參照的Flag,m_ConditionMode為ExitTime時此欄位為空
      • m_EventTreshold
        條件式的參考值,Bool時不管是True或False值都填0
      • m_ExitTime
        ExitTime的float值
    再來是State的資訊

    最後是StateMachine的資訊


    [Unity] Mecanim 強制進入某個State的方法(ex立刻執行死亡動作)

    How to change to a specific state immediately.

    參考資料
      在Mecanim Animator Controller中有個叫做Any State的State,是一個一直處在作用中的State,只能連接給其他的State,其他的State是不能連接給他的。

    Animator - Any State

      假使想要有一個在任何State規則之上的動作流程時(即條件成立時能立即進入該流程),譬如說人物的死亡動作,當程式下達死亡指令時,你會想要死亡動作是立刻表演,不希望這個流程會被其他的State卡住。
      這時候Any State就能達到這個效果,在這裡以一個範例作解說:在Animator Parameters中新增一個IsDead的Bool,並新增一個接收Any State的死亡動作State,且Transition Conditions為IsDead == true,如此當死亡時程式下達指令給Animator(animator.SetBool("dead",true)),就會讓dead這個State立刻執行。
      但是因為Any State是一直在作用狀態中的,所以並不能一直讓觸發條件成立,必須要是一次性的命令,否則就會發生一直反覆進入dead State的狀況,舉例來說:IsDead被設為true且Animator已經進入dead State的時候,就必須要把IsDead設回false。

    防止重複觸發Any State的範例Code如下:
    static int deadState = Animator.StringToHash("Base Layer.dead");
    if ( animator.GetCurrentAnimatorStateInfo(0).nameHash == deadState )
    {
        animator.SetBool("IsDead", false);
    }
    
    補充說明,animator.GetCurrentAnimatorStateInfo(int IDX)所返回的是Hash,且Hash並不能轉回String,因此得要先把你所知道的State名稱轉成Hash(.StringToHash())再做比對。
      在這樣的需求之下代表State的命名必須是有規則的、固定的,為了方便作業流程,動作片段的取名與State相同也可以省去某些操作上的步驟(直接將動作片段拉入Animator生成state時會自動以動作片段名稱為state命名)。
      另外直接從Any State做一個Transition到預設動作上,也能用來跳出任何的State。

    Unity 4.3 更新

    在Unity 4.3中新增了一個新的Parameter類型:Trigger,其作用是達成一次性的判斷條件,非常實用。其功能就是當Animator.SetTrigger("ParameterName")命令下達之後,該parameter會被設為True,並在transition完成時會被設為False,以往都需要自己撰寫設回False的程序,現在利用Trigger這個Parameter就會方便很多。
    另外在4.3中Mecanim有大幅的優化,譬如Mecanim已幾乎適用所有的component、property...,可以利用Mecanim控制你的相機FOV、控制你的Script的參數...,再配上新支援的Event(以往ImportedAnimation不支援Event)以及優化過的Animation Window(Dope Sheet + Curves),Mecanim的實用程度已大幅提高。


    參考文章
    延伸




    2013年5月27日 星期一

    [Unity] Shader - ShaderLab


    參考資料:
    相關資料:
    參考範例:


    人生第一次的shader撰寫XD,超陽春Unlit受擊變色用shader,透過shaderLab簡單的功能只要少少幾行就能完成!呈現的效果為,不受光影響,單一貼圖表現,可以透過Color property的alpha來控制貼圖與color間的混合

    ● Opaque版本
    Shader "Custom/Unlit Opaque With Color" {
    	Properties {
    		_Color ("Color (RGB) Blending (A)", Color) = (1,0,0,0)
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    	}
    	SubShader {
    		Tags {"RenderType"="Opaque" "IgnoreProjector"="True"}
    		Lighting Off
    		Cull back
    		ZWrite On
    		
    		Pass {
    			SetTexture [_MainTex] {
    				ConstantColor [_Color]
    				combine constant lerp(constant) texture
    			}
    		}
    		
    	}
    	FallBack "Unlit/Diffuse"
    }
    

    ● Transparent Cutout版本
    Shader "Custom/Unlit Transparent Cutout With Color" {
    	Properties {
    		_Color ("Color (RGB) Blending (A)", Color) = (1,0,0,0)
    		_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
    		_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
    	}
    	SubShader {
    		Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
    		Lighting Off
    		Cull back
    		ZWrite On
    		Blend SrcAlpha OneMinusSrcAlpha
    		
    		Pass {
    			Alphatest Greater [_Cutoff]
    			SetTexture [_MainTex] {
    				ConstantColor [_Color]
    				combine constant lerp(constant) texture, texture
    			}
    		}
    		
    	}
    	FallBack "Unlit/TransparentCutout"
    }
    
    

    2013年5月23日 星期四

    Unity : 好站搜集

    Unity官方

    Unity

    NGUI

    Articles








    2013年5月9日 星期四

    [Unity] Prefab 不會 update 的問題

    近日在作業時遇到一個嚴重的問題!
    當我把一個fbx檔案包成prefab時,就會造成部分內容無法與原始的fbx檔案同步更新。

    目前只知道單純變動fbx檔案內的geometry, uv是會同步更新的,
    而hierarchy完全不會更新,
    skin資訊雖會更新,但是看起來只有更新了權重值,所以依然會有問題發生,
    若fbx檔案變動了material,也不會更新,
    此外相信不會更新的內容有非常多...
    而目前對於此問題沒有找到較好的解決方式...


    相關文章:

    2013年4月2日 星期二

    Unity - Editor Script Coding - 編寫Editor Script之參考資料

    入門站:
    資料:

    參考文章蒐集:
    • http://forum.unity3d.com/threads/92148-need-help-with-texture-import-setting-script

    零散紀錄:
    • 取得Type
      GameObject.GetType().Name
    • 取得某個選擇中的Asset的路徑(亦可作用於目錄)
      AssetDatabase.GetAssetPath(OBJECT)
    • 取得 某個Asset檔案 內的所有元件
      AssetDatabase.LoadAllAssetsAtPath("AssetPath")

    2013年3月21日 星期四

    3dsMax - 請小心FBX Export Preset的設定

    今天遇到了一個超奇妙的問題,當我輸出FBX要Bake Animation的時候,我的時間區段一直只能設定0到100之間,但昨天明明還是正常的,查了將近一整天一直無法解決,後來請高手支援一下才發現原來是個愚蠢的問題!

    那就是FBX Export Preset中可以設定Start跟End的最大最小值,而這該死的被設定成了0到100,至於為何突然會被設定成0至100之間,這還是一個謎!不過我猜是因為我用了MAXScript的FbxExporterSetParam的關係‧‧‧

    設定FBX Export Preset的步驟

    1. 在FBX Exporter dialog中按下Edit按鈕並選擇Edit preset
    2. 會跳出上圖左方的Tree List找到Bake Animation的Start/End項目並右鍵點擊選擇Edit min max
    3. 輸入Minimum及Maximum數值
    但目前還不知道要怎麼將他回復成預設狀態‧‧‧

    另外要用MAXScript設定Start/End的話可以使用

    FBXExporterSetParam "BakeFrameStart" [Integer value]
    
    FBXExporterSetParam "BakeFrameEnd" [Integer value]
    
    FBXExporterSetParam "BakeFrameStep" [Integer value]

    2013年3月19日 星期二

    [AE] Camera Mapping

    想用一張2D照片做出3D空間的效果,所使用的技術為Camera Mapping或Camera Projection。


    教學:
    1. 較陽春的作法,適合構圖簡單和沒這麼多時間生小孩的時候用XD,將元素切分成幾個圖層,拉出前後距離即可。
      How To Make Virtual 3D Photos In After Effects by AndrewKramer
    2. 使用AE的Light shadow做出投影的效果,只需用幾片板子即可做出簡單的3D空間效果。
      而在實際用過之後發現有幾點需要注意。在結構較複雜的場景中,板子的對位非常重要,否則會有很大的破綻。另一點則是,投影出來的圖像解析度跟原圖差距相當大,因為通常原圖是被投影到更大的面積上,且有角度造成的失真,儘管把Shadow Map Resolution調到最大也還是達不到可用的水準,目前還不知道較佳解決的方法,若解析度不佳,那這種方法也就沒路用了。
      教學:
      Camera Mapping in After Effects by Creative Cow
      2D footage to stereo 3D using camera projection mapping
      Camera Projection technique Tutorial
      其他範例:峽谷
    3. 利用建模做出更真實的3D效果。
      Discover projection mapping to animate photos in 3D
    4. 另外我有試出一種偷吃步的方式:傾斜大法!
      在AE中利用Effect/Corner pin做出Skew,再把與後方景物位差明顯的物件拆出來,不做傾斜並跟著會傾斜的底圖移動,就能有空間感的錯覺。但目前只試過水平運鏡的狀況,也許垂直運鏡跟深度運鏡會較難表現。
    延伸閱讀:
    [Wiki] 3D Projection 

    [MAXScript] Path處理

    以前在處理檔案路徑時,有些功能都自己寫Function來用,例如將兩段路徑結合成一個完整路徑等等。今天看MAXScript Reference時才發現,MAXScript自己就有許多Methods可以對路徑做各種的處理,非常方便。

    MAXScript Reference - PathConfig Struct
    以下從中節錄出較常用的項目

    pathConfig.appendPath 
    將路徑片段合併
    pathConfig.appendPath "c:\\temp" "test.txt"
    "c:\temp\test.txt"

    pathConfig.convertPathToLowerCase
    - 將路徑轉為小寫並將反斜槓統一

    pathConfig.convertPathToLowerCase "C:\\Temp\Test/pATH"
    "c:\temp\test\path"


    pathConfig.pathsResolveEquivalent
    - 比對兩個路徑是否相同

    pathConfig.pathsResolveEquivalent "scenes\\test.txt" "./scenes/test.txt"
    true


    pathConfig.removePathLeaf
    - 取得上一層的目錄路徑

    pathConfig.removePathLeaf "c:\\temp\\test"
    "c:\temp"



    pathConfig.stripPathToTopParent 
    - 取得最上層的路徑

    pathConfig.stripPathToTopParent "C:\\temp\\test\\test.tga"
    "C:\"
    pathConfig.stripPathToTopParent "\\\\SomeServer\\Projects\\Test"
    "\\SomeServer"



    pathConfig.removePathTopParent
    - 去除最上層的路徑

    pathConfig.removePathLeaf "c:\\temp\\test"
    "temp\test"


    pathConfig.stripPathToLeaf
    - 取得最深一層的路徑,與filenameFromPath作用相同

    pathConfig.stripPathToLeaf "C:\\temp\\test"
    "test"
    pathConfig.stripPathToLeaf "C:\\temp\\test\\"
    ""
    pathConfig.stripPathToLeaf "C:\\temp\\test\\somefile.tga"
    "somefile.tga"


    pathConfig.isAbsolutePath
    - 檢驗是否是絕對路徑(但不檢驗該路徑是否存在)

    pathConfig.isLegalPath
    - 檢驗是否是合法的路徑(但不檢驗該路徑是否存在)

    pathConfig.GetDir <#directory_type>
    - 傳回系統預設目錄路徑

    #font | #Scene | #export | #import | #help | #expression | #preview | #image | #Sound | #plugcfg | #maxstart | #vpost | #drivers | #autoback | #matlib | #scripts | #startupScripts | #defaults | #renderPresets | #ui | #maxroot | #renderoutput | #animations | #archives | #Photometric | #renderassets | #userScripts | #userMacros | #userStartupScripts | #temp | #userIcons | #maxData | #downloads | #proxies


    pathConfig.isPathRootedAtBackslash
    - 檢驗路徑是否是以斜槓為開頭

    要取得Max的一些預設路徑則從此查找
    MAXScript Reference - 3ds Max System Directories

    2013年3月12日 星期二

    如何不影響Scale的數值對Bone做到縮放尺寸的效果

    如何不影響Scale的數值對Bone做到縮放尺寸的效果

    例,在對原本Scale值為[1,1,1]的Bone,進行Scale 2倍的動作後,Scale值變為[2,2,2],若想在保持現有尺寸的狀態下把Bone的Scale值歸回[1,1,1],可利用Animation>Bone Tools,方法如下:
    1. 開啟Bone Tools視窗。
    2. 選擇要修改的Bone,執行Reset Scale,此時Scale值已被修正為1,但Bone的顯示有問題,用以下步驟進行修正。
    3. 將Bone On關掉再打開,做個Refresh動作。
    4. 執行Reset Stretch。
    5. 執行Realign。

    另外可用Max Script進行這個手續:
    for o in selection do
    (
        resetScale o
        o.setBoneEnable false currenttime
        o.setBoneEnable true currenttime
        o.resetBoneStretch()
        o.realignBoneToChild()
    )