2018年4月17日 星期二

Define base shader architecture

使用Define架構的方式來實作Shader。
好處:
1.Reused(代碼可以重複利用)
2.彈性(可以輕易地組合出不同的shader)
3.效能調效(可以輕易利用define來開關功能)
4.方便除錯(同上)
5.使用者容易理解(看define就知道做了那些事)
缺點:
1.代碼比較複雜(define的地方太多,建議先把代碼寫好測完,之後再考慮怎麼切…)

比較適合應用的例子有Lighting的計算,水系統…等。
以下用目前實作的水系統當作例子:
//========================================================
//一般平面水
Shader "CustomShader/PlaneWater"
{
Properties
{
...
}
SubShader
{
...
LOD 500 //在高階硬體以上的時候使用
Pass
{
...
CGPROGRAM
#pragma vertex WaterSimulateVertexProgram
#pragma fragment WaterSimulateFragmentProgram
#pragma multi_compile_fog
#define PROCESS_FOG //霧氣功能
#define PROCESS_SMALL_NORMAL //小波Normal
#define PROCESS_MEDIUM_NORMAL //中波Normal
#define PROCESS_LARGE_NORMAL //大波Normal
#define PROCESS_DEPTH //深度處理
#define PROCESS_REFRACTION //折射處理
#define PROCESS_REFLECTION //反射處理
#define PROCESS_EDGE_FOAM //水邊緣處理
#define PROCESS_WAVE_FOAM //浪泡沫處理
#include "WaterSimulation.cginc"
ENDCG
}
}
SubShader
{
...
LOD 100 //在低階硬體以上的時候使用
Pass
{
...
CGPROGRAM
#pragma vertex WaterSimulateVertexProgram
#pragma fragment WaterSimulateFragmentProgram
#pragma multi_compile_fog
#define PROCESS_FOG //霧氣功能
#define PROCESS_SMALL_NORMAL //小波Normal
#define PROCESS_LARGE_NORMAL //大波Normal
#include "WaterSimulation.cginc"
ENDCG
}
}
}
//========================================================
//無限大點位移水
Shader "CustomShader/ProjectedGridWater"
{
SubShader
{
...
LOD 500 //在高階硬體以上的時候使用
Pass
{
...
CGPROGRAM
#pragma vertex WaterSimulateVertexProgram
#pragma fragment WaterSimulateFragmentProgram
#pragma multi_compile_fog
#define PROCESS_FOG //霧氣功能
#define PROCESS_SMALL_NORMAL //小波Normal
#define PROCESS_MEDIUM_NORMAL //中波Normal
#define PROCESS_LARGE_NORMAL //大波Normal
#define PROCESS_DEPTH //深度處理
#define PROCESS_REFRACTION //折射處理
#define PROCESS_REFLECTION //反射處理
#define PROCESS_EDGE_FOAM //水邊緣處理
#define PROCESS_WAVE_FOAM //浪泡沫處理
#define PROCESS_PROJECTEDGRID //GPU點位移(高低)處理
#define PROCESS_DECAL //平貼水面特效處理
#include "WaterSimulation.cginc"
ENDCG
}
}
}
//========================================================
//簡單湖泊
Shader "CustomShader/Lake"
{
SubShader
{
...
LOD 100 //在低階硬體以上的時候使用
Pass
{
...
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
CGPROGRAM
#pragma vertex WaterSimulateVertexProgram
#pragma fragment WaterSimulateFragmentProgram
#pragma multi_compile_fog
#define PROCESS_FOG //霧氣功能
#define PROCESS_SMALL_NORMAL //小波Normal
#define PROCESS_LARGE_NORMAL //大波Normal
#define PROCESS_CUBEMAP_REFLECTION //Cubeamp反射
#define PROCESS_WITH_VERTEXCOLOR //整合Vertex color
#include "WaterSimulation.cginc"
ENDCG
}
}
}

2018年3月27日 星期二

Interactive ripple + refraction + reflection + wave decal in Unity.

這是完全基於GUP的pixel shader及normal的運作,算是一種高明的騙術
,視角要是平貼於水面就破功了,不過對俯視類型的遊戲來說,還算
堪用,尤其是當美術需要為符合模型外型,模擬真實的水波佈了很多
的particle Emitter是較有效率的做法。

但這並不是通解,它是基於一個投射於整個水平大小的平行視角的鏡
頭來進行水波物件的拍射,所以沒辦法做很大的場景(一張RTT要去
貼在整個水面,而且沒辦法tile)。

其中類似Compute shader的應用蠻有意思的,這開擴了我對Shader運算的觀念…

相關影片:

2018年3月15日 星期四

Screen space interactive grass

在mobile的效能的考量下,做全場景的physic interactive grass可能太浪費,有沒有其他的方式可以簡單地表現interactive grass的效果呢?
我自己自行設計一個以Screen space的方式來處理這部份,這樣可以省下相關物理處理(做場景的screen query蠻傷的),流程大概如下:
  1. 在畫草之前,先針對要互動的物件拍射一個interactive depth map。
  2. 在畫草的vertex shader地方,使用這張貼圖來進行互動處理,當然免不了要處理depth to world space position。

相關影片如下:

Design cross platform AR framework in unity

其實我覺得這個應該要讓Unity自己來的(它最利害的不就是跨平台嗎?),但可能因為目前Google的ARCore才正式Release不久,而且相關Unity方面的整合也是Google自己寫的,不像IOS就是Unity自己官方提供的,所以就沒處理這部份。

目前的需求還是比較單純的,只要有一個定位的空間plane,然後把整個遊戲場景擺放在這個plane上面即可。架構如下:





















使用上只要先import相關package檔,並把ARSessionProcessor放到場景即可跟ARSessionProcessor這個Component溝通,並開始開發遊戲。做好後可直接在Android及IOS device上進行測試,並且可在PC上進行相關的測試驗證。

這邊是hit AR plane函式接口處理,範例如下:
protected virtual void Update()
{
  if (!_targetSessionProcessor)
            return;
  //取得螢幕點擊資訊
        Vector3 touchPosition;
#if UNITY_EDITOR_WIN
        if (!Input.GetMouseButtonUp(0))
            return;
        touchPosition = Input.mousePosition;
#else
        Touch touch;
        if (Input.touchCount < 1 || (touch = Input.GetTouch(0)).phase != TouchPhase.Began)
            return;
        touchPosition = touch.position;
#endif
  //呼叫RayCast相關函式,取得點擊AR plane相關資訊
        Ray outTestRay;
        bool outHitNewPlane;
        Vector3 outHitPoint;
        Quaternion outDestRotation;
        if (!_targetSessionProcessor.ProcessARRayCast(ref touchPosition, out outHitNewPlane,
   out outTestRay, out outHitPoint, out outDestRotation))
            return;
  //假如點擊到新的AR plane,就處理相關場景功能(場景產生或搬移場景)
        if (outHitNewPlane)
        {
            if ((!_rootTransform) && (_scenePrefab))
            {
                GameObject go = Instantiate(_scenePrefab);
                if (go)
                {
                    _rootTransform = go.transform;
                    _sceneBoxCollider = go.GetComponentInChildren();
                }
            }
            outHitPoint.y += _offestHeight;
            _rootTransform.position = outHitPoint;
            _rootTransform.rotation = outDestRotation;
            return;
        }

        if ((!_rootTransform) || (!_sceneBoxCollider))
            return;
  //如果點到的是同一個plane,就處理物件功能(產生物件或調整物件位置)
        RaycastHit hitInfo;
        if (!_sceneBoxCollider.Raycast(outTestRay, out hitInfo, 100.0f))
            return;

        if (!_objectTransform)
        {
            GameObject go = Instantiate(_objectPrefab, _rootTransform);
            if (go)
                _objectTransform = go.transform;
        }
        _objectTransform.position = hitInfo.point;
}

相關Demo影片:

2017年12月11日 星期一

My new infinity GPU vertex animated ocean on mobile average fps 60 (Samsung 7)

其實這個系統做一半,因為專案趕進度的關係,就停下來了,最近比較有時間把它給順好。 總結一下Implement的過程:

Vertex program:
1.計算 projected(目的是為了無限大:參考了Habib's Water ShaderCeto Ocean System
2.計算 projected position
3.計算 Perlin noise on GPU(目的是為了加速計算大量的點位移-高度)
4.計算 Vertex Normal (之後會拿來當normal map的world space)
5.Animated UV(normal and foam map)

Fragment(Pixel)Program:
1.計算深度(高度及透視深度的混合,參考Ceto Ocean System,有點類似霧氣擴散的效果)
2.將Normal map轉換成World Space
3.計算diffuse
4.計算specular
5.計算distortion(refraction或reflection參數分開)
6.計算泡沫效果,並將其與refraction混合(只有岸邊淺水的地方才有邊緣泡沫)
7.計算水顏色,淺水、深水與refractoin依照深度及折射率進行混色,之後再依照反射率跟reflection進行混色,完成了所謂的SeaColor。
8.簡單模擬Fersnel Terms(依照一張Fersnel map,混合沒有考慮Diffuse及有考慮Diffuse的狀況,進行混色):
fixed3 SpecAndDiffuse = SeaColor * Diffuse + Specular;
fixed3 SpecNoDiffuse = SeaColor + Specular;
fixed4 OutColor = fixed4(lerp(SpecAndDiffuse, SpecNoDiffuse, FresnelRatio), 1.0);
9.再加上海浪模擬(依照Normal與Up軸的dot值,決定海浪貼圖顯示的強弱)
10.最後再加上fog的顏色,便大功告成
成果如下:

2017年11月29日 星期三

Dynamic clouds shadow with screen space projector

一般我們在模擬動態雲陰影或AE選擇範圍時,最常用的就是projector(通常會用multi pass technique)。在Unity架構下Projector也是用multi pass實現(可想而知Draw call Batch會多一倍),但它卻會打斷Unity的合批(Batching)處理,對於好不容易規劃好的優化架構相當嚴重。

有沒有好的其他解決方案呢?其實有,那就是screen space projector,它和一般的projector一樣會多畫一次,主要是要把深度資料繪製出來(其實如果可以access native buffer,就可以省掉),但卻不會打斷合批處理,算是一個效能與效果兼顧的折衷方案。

以下的Demo影片會分別顯示WithoutAnyProjector、OriginalProjector、ScreenSpaceProjector三種模式,可以從中看到Batching數量的變化。





Dream continues in...

2017年11月11日 星期六

模擬海平面以下的無水效果

這個部份主要是應用了DepthMask的機制,目前我的深度貼是採用ObliqueMatrix的方式,所以深度是只有儲存高度,而不是一般的深度貼圖,至於Render Ocean Depth的Shader則分三種,利用Unity的Replace Shader的機制來處理Opaque、Transparent、Depth Mask三種不同的類型物件,程式碼的概念如下:
Shader "RenderOceanDepth"
{
Properties
{
[HideInInspector]_MainTex("MainTex", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
//負責繪製一般的Opaque深度資料…
}
}
SubShader
{
Tags{
"Queue" = "Transparent"
"RenderType" = "Transparent"
}
LOD 100
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
//負責繪製帶有Alpha物件深度資料…
}
}
SubShader
{
Tags{
"RenderType" = "FilterDepth"
}
LOD 100
Pass
{
//負責繪製深度遮閉物件,深度傳回值皆為零…
}
}
}
視頻除了展示可以繪製水平下以下的地表(不含水),還展示了類似摩西分紅海的模擬,但由於Screen base的Depth資料,在不同的視角還是有破綻…




Dream continues in...