Unity uGUI用、魚眼レンズシェーダー。樽型歪曲収差の実装。
(Barrel Distortion Shader for Unity uGUI)
関連ページ
参考URL
前回
逆魚眼レンズシェーダー
を作ったので、次は魚眼シェーダーを作ろうと思った。
逆魚眼を応用すればすぐ作れるかと思ったらそんな事なくて、結構苦労した。
とりあえず動くまでには漕ぎつけたが、厳密な魚眼レンズとは見た目が違う。
現状であんまり満足行ってないので、いつかコードを改修する予定。
魚眼レンズシェーダー概要
魚眼レンズの視覚効果は、正式には樽型歪曲収差という。
画面の中心に近づくほど極端に手前に迫ってくるような歪みを与える。
下の動画は今回のシェーダーのサンプル。
逆魚眼同様に、歪みの掛け方を3タイプ用意した。x軸+y軸、y軸のみ、x軸のみのパターンが選べる。
魚眼レンズシェーダー、コード全文
以下コード全文。uGUI Imageへの反映方法は
こちらの記事
参照。
今回の場合GrabPassを使っているので、
こちら
の知識も必要になる。
Shader
"UI/Barrel"
{
Properties
{
[HideInInspector]_MainTex("-",2D)="white"{}
[KeywordEnum(All,
Vertical,
Horizontal)]
_Direction("Direction",
Int)
=
0
_DistortionRange
("DistortionRange",
Range(0.0,
10.0))
=
10
_EasePow
("EasePow",
Range(0,
10))
=
9
}
SubShader
{
Tags
{
"Queue"
=
"Transparent"
"RenderType"="Transparent"
}
Cull
Off
ZWrite
Off
Blend
SrcAlpha
OneMinusSrcAlpha
GrabPass{}
Pass
{
CGPROGRAM
#pragma
vertex
vert
#pragma
fragment
frag
#include
"UnityCG.cginc"
sampler2D
_GrabTexture;
#pragma
multi_compile
_DIRECTION_ALL
_DIRECTION_VERTICAL
_DIRECTION_HORIZONTAL
fixed
_DistortionRange;
fixed
_EasePow;
struct
appdata
{
fixed4
vertex
:
POSITION;
fixed4
uv
:
TEXCOORD0;
};
struct
v2f
{
fixed4
vertex
:
SV_POSITION;
fixed2
uv
:
TEXCOORD0;
};
v2f
vert(appdata
i)
{
v2f
o;
o.vertex
=
UnityObjectToClipPos(i.vertex);
o.uv
=
ComputeGrabScreenPos(o.vertex);
return
o;
}
fixed2
getIsUnderHalf(v2f
i)
{
//x軸が0.5より小さければ1を代入
fixed
isUnderHalfX
=
step(i.uv.x,
0.5);
//y軸が0.5より小さければ1を代入
fixed
isUnderHalfY
=
step(i.uv.y,
0.5);
return
fixed2(isUnderHalfX,
isUnderHalfY);
}
fixed2
getFarFromSafe(v2f
i)
{
fixed
fixRange
=
_DistortionRange
*
0.1;
fixed
safeRange
=
1
-
fixRange;
fixed
farFromSafeX
=
abs(0.5
-
i.uv.x)
-
(safeRange
/
2);
fixed
farFromSafeY
=
abs(0.5
-
i.uv.y)
-
(safeRange
/
2);
return
fixed2(farFromSafeX,
farFromSafeY);
}
fixed2
getFarFromCenter(v2f
i)
{
fixed
farFromCenterX
=
abs(0.5
-
i.uv.x);
fixed
farFromCenterY
=
abs(0.5
-
i.uv.y);
return
fixed2(farFromCenterX,
farFromCenterY);
}
fixed
getFixEase()
{
fixed
easePowMax
=
10;
return
(easePowMax
+
0.5)
-
_EasePow;
}
fixed2
getMainDist(fixed2
farFromSafe)
{
//InQuadのEaseでfarFromSafeが大きい程指数関数的に座標がずれるようにする
fixed
quadX
=
farFromSafe.x
*
(farFromSafe.x
-
2);
fixed
quadY
=
farFromSafe.y
*
(farFromSafe.y
-
2);
//指数関数の伸びが強すぎるのでfixEaseで抑える
//_EasePowのrangeが0~10なのでfixEaseの振れ幅は10.1~0.1
fixed
fixEase
=
getFixEase();
fixed
mainDistX
=
pow(quadX
*
quadX,
2)
/
fixEase;
fixed
mainDistY
=
pow(quadY
*
quadY,
2)
/
fixEase;
return
fixed2(mainDistX,
mainDistY);
}
fixed2
getSubDist(fixed2
farFromCenter,
fixed2
mainDist)
{
fixed
subDistX
=
farFromCenter.x
*
mainDist.y;
fixed
subDistY
=
farFromCenter.y
*
mainDist.x;
return
fixed2(subDistX,
subDistY);
}
fixed2
getMainReduce(fixed2
isUnderHalf,
fixed2
mainDist)
{
fixed
mainReduceX
=
(isUnderHalf.x
*
-1
*
mainDist.x)
+
((1
-
isUnderHalf.x)
*
(mainDist.x));
fixed
mainReduceY
=
(isUnderHalf.y
*
-1
*
mainDist.y)
+
((1
-
isUnderHalf.y)
*
(mainDist.y));
return
fixed2(mainReduceX,
mainReduceY);
}
fixed2
getSubReduce(fixed2
isUnderHalf,
fixed2
subDist)
{
fixed
subReduceX
=
(isUnderHalf.x
*
-1
*
subDist.x)
+
((1
-
isUnderHalf.x)
*
(subDist.x));
fixed
subReduceY
=
(isUnderHalf.y
*
-1
*
subDist.y)
+
((1
-
isUnderHalf.y)
*
(subDist.y));
return
fixed2(subReduceX,
subReduceY);
}
fixed2
getUseDist(fixed2
farFromSafe)
{
//x軸が歪み対象エリア内かどうか
fixed
useDistX
=
step(0,
farFromSafe.x);
//y軸が歪み対象エリア内かどうか
fixed
useDistY
=
step(0,
farFromSafe.y);
return
fixed2(useDistX,
useDistY);
}
fixed4
frag(v2f
i)
:
SV_Target
{
//対象のy軸とx軸座標が中心点を超えてるかどうかを習得
fixed2
isUnderHalf
=
getIsUnderHalf(i);
//非歪みエリアから外側にx軸、y軸がどれだけ離れてるかを取得
fixed2
farFromSafe
=
getFarFromSafe(i);
//中心からx軸、y軸がどれだけ離れてるかを取得
fixed2
farFromCenter
=
getFarFromCenter(i);
//メインの歪みの基本値を取得
fixed2
mainDist
=
getMainDist(farFromSafe);
//サブの歪みの基本値を取得
fixed2
subDist
=
getSubDist(farFromCenter,
mainDist);
//メインの座標の減退値を取得
fixed2
mainReduce
=
getMainReduce(isUnderHalf,
mainDist);
//サブの座標の減退値を取得
fixed2
subReduce
=
getSubReduce(isUnderHalf,
subDist);
//対象のx軸、y軸が歪み対象エリア内かどうか
fixed2
useDist
=
getUseDist(farFromSafe);
#ifdef
_DIRECTION_ALL
i.uv.x
=
i.uv.x
+
useDist.x
*
mainReduce.x
+
useDist.y
*
subReduce.x;
i.uv.y
=
i.uv.y
+
useDist.y
*
mainReduce.y
+
useDist.x
*
subReduce.y;
#elif
_DIRECTION_VERTICAL
i.uv.x
=
i.uv.x
+
useDist.y
*
subReduce.x
*
2;
i.uv.y
=
i.uv.y
+
useDist.y
*
mainReduce.y;
#else
i.uv.x
=
i.uv.x
+
useDist.x
*
mainReduce.x;
i.uv.y
=
i.uv.y
+
useDist.x
*
subReduce.y
*
2;
#endif
fixed
fixEase
=
getFixEase();
fixed
isUnderOne
=
step(fixEase,
1);
fixed
magni
=
1
+
(1
/
fixEase);
fixed
shift
=
(1
-
1
/
magni)
/
2;
fixed4
col
=
tex2D(_GrabTexture,
i.uv
*
(1
/
magni)
+
shift);
return
col;
}
ENDCG
}
}
}
0
0