Unity Fogの処理を追う。builtin_shaders調査の手順メモ
UNITY_TRANSFER_FOGの処理で調査した事のメモです。また、builtin_shaders調査の手順メモ。
builtin_shaders-2019.3.9f1をDLして調査。 unity3d.com
Create > Shader > Unlitで生成される最低限のshaderコードと比較しながら調べています。 生成されたコードはこれ。
Shader "Unlit/NewUnlitShader" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = tex2D(_MainTex, i.uv); // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } } }
UNITY_FOG_COORDSとは?
こんな感じで頂点データの出力に記述されている。
struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; };
define UNITY_FOG_COORDS(
で全ファイルを検索。下記が該当箇所。
#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2) #define UNITY_FOG_COORDS(idx) UNITY_FOG_COORDS_PACKED(idx, float1)
UNITY_FOG_COORDS_PACKED
の処理はすぐ上にある。
#define UNITY_FOG_COORDS_PACKED(idx, vectype) vectype fogCoord : TEXCOORD##idx;
UNITY_FOG_COORDS(1) と記述した箇所に、
float fogCoord TEXCOORD1;
を追加するのみ。 FOG_LINEAR、FOG_EXP、FOG_EXP2のどれかのフラグが立っていない場合は、何も定義は追加されない。 フラグが立っている時だけ記述を追加する最適化のため。
UNITY_TRANSFER_FOGとは?
こんな感じで、頂点shaderで使用される。ここの UNITY_TRANSFER_FOG
を調べる。
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
define UNITY_TRANSFER_FOG(
で全ファイルから検索。コメント入れた部分の真下が該当箇所。
#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2) #define UNITY_FOG_COORDS(idx) UNITY_FOG_COORDS_PACKED(idx, float1) #if (SHADER_TARGET < 30) || defined(SHADER_API_MOBILE) // mobile or SM2.0: calculate fog factor per-vertex // shader modelが3.0より下、もしくはモバイル用。頂点単位でfogかける /* → */ #define UNITY_TRANSFER_FOG(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.fogCoord.x = unityFogFactor #define UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.tSpace1.y = tangentSign; o.tSpace2.y = unityFogFactor #define UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.worldPos.w = unityFogFactor #define UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.eyeVec.w = unityFogFactor #else // SM3.0 and PC/console: calculate fog distance per-vertex, and fog factor per-pixel // shader model3.0以上かつpc/console用。fog distanceは頂点ごと、fog factorはpixelごと /* → */ #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = (outpos).z #define UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,outpos) o.tSpace2.y = (outpos).z #define UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,outpos) o.worldPos.w = (outpos).z #define UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o,outpos) o.eyeVec.w = (outpos).z #endif #else #define UNITY_FOG_COORDS(idx) // 上記以外 /**/#define UNITY_TRANSFER_FOG(o,outpos) #define UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,outpos) #define UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,outpos) #define UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o,outpos) #endif
今回はmobileを調査。最初の箇所を更に深堀りしていく。
マクロの中にマクロがあるので、その中のUNITY_CALC_FOG_FACTOR
を調査する。
UNITY_CALC_FOG_FACTORとは?
define UNITY_CALC_FOG_FACTOR(
で検索。
#define UNITY_CALC_FOG_FACTOR(coord) UNITY_CALC_FOG_FACTOR_RAW(UNITY_Z_0_FAR_FROM_CLIPSPACE(coord))
が該当箇所。
また中にマクロがある。先にUNITY_Z_0_FAR_FROM_CLIPSPACE
を調べる。
UNITY_Z_0_FAR_FROM_CLIPSPACEとは?
define UNITY_Z_0_FAR_FROM_CLIPSPACE(
で検索。下記が該当箇所。
#if defined(UNITY_REVERSED_Z) #if UNITY_REVERSED_Z == 1 //D3d with reversed Z => z clip range is [near, 0] -> remapping to [0, far] //max is required to protect ourselves from near plane not being correct/meaningfull in case of oblique matrices. #define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) max(((1.0-(coord)/_ProjectionParams.y)*_ProjectionParams.z),0) #else //GL with reversed z => z clip range is [near, -far] -> should remap in theory but dont do it in practice to save some perf (range is close enough) #define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) max(-(coord), 0) #endif #elif UNITY_UV_STARTS_AT_TOP //D3d without reversed z => z clip range is [0, far] -> nothing to do #define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) (coord) #else //Opengl => z clip range is [-near, far] -> should remap in theory but dont do it in practice to save some perf (range is close enough) #define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) (coord) #endif
フラグらしきUNITY_REVERSED_Z
があるので調査。
UNITY_REVERSED_Z とは?
注: DX11 / 12、PS4、XboxOne、およびMetalでは、Zバッファーの範囲は1〜0で、UNITY_REVERSED_Zが定義されています。他のプラットフォームでは、範囲は0〜1です。 docs.unity3d.com
今回はMetalを前提とする。metalはUNITY_REVERSED_Zフラグが立っている。 その中の処理に進む
//D3d with reversed Z => z clip range is [near, 0] -> remapping to [0, far] //max is required to protect ourselves from near plane not being correct/meaningfull in case of oblique matrices. // 斜行行列の場合、ニアプレーンが正しくない/意味がないことから身を守るためにmaxが必要です。 #define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) max(((1.0-(coord)/_ProjectionParams.y)*_ProjectionParams.z),0)
今度は_ProjectionParamsというパラメータが出てきたので調べる。
_ProjectionParamsとは?
カメラのnear, far値が取得できるようです。
型はfloat4。xは1.0(反転投影行列でレンダリングしている場合は-1.0),yはカメラのnear plane,zはカメラの far plane,wは1 / FarPlaneです. docs.unity3d.com
UNITY_CALC_FOG_FACTOR
まで戻ると、今度はUNITY_CALC_FOG_FACTOR_RAW
マクロがあるので、これを調べる。
UNITY_CALC_FOG_FACTOR_RAWとは?
define UNITY_CALC_FOG_FACTOR_RAW(
で検索。
下記が該当。
#if defined(FOG_LINEAR) // factor = (end-z)/(end-start) = z * (-1/(end-start)) + (end/(end-start)) #define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = (coord) * unity_FogParams.z + unity_FogParams.w #elif defined(FOG_EXP) // factor = exp(-density*z) #define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = unity_FogParams.y * (coord); unityFogFactor = exp2(-unityFogFactor) #elif defined(FOG_EXP2) // factor = exp(-(density*z)^2) #define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = unity_FogParams.x * (coord); unityFogFactor = exp2(-unityFogFactor*unityFogFactor) #else #define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = 0.0 #endif
fogには3つのモードがあり、 FOG_LINEAR、FOG_EXP、FOG_EXP2がある。
今回はFOG_EXPを調べる。
// factor = exp(-density*z) #define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = unity_FogParams.y * (coord); unityFogFactor = exp2(-unityFogFactor)
全てのマクロを展開する
UNITY_TRANSFER_FOG(o, o.vertex);
は、
float z = max(((1.0-(o.vertex.z) / _ProjectionParams.y) * _ProjectionParams.z), 0); float unityFogFactor = unity_FogParams.y * z; unityFogFactor = exp2(-unityFogFactor); o.fogCoord.x = unityFogFactor;
となる。
UNITY_TRANSFER_FOG(o, o.vertex);
を置き換えて、挙動が変化しない事を確認。 (window > Rendering > lighting settingsのfog modeもチェックする事)