見た感じではおかしいところはないように思います...
期待通りにならない原因の可能性として思いつくところとしては、カメラとの距離を頂点単位で求めていることですかね。これをフラグメント単位で求めてみると変化はあるでしょうか(フラグメントシェーダーにdistの代わりにvertexWorldPosを送って、フラグメントシェーダー内で_WorldSpaceCameraPosとの距離を求めてdistとするような感じで...)?
十分に細かいメッシュなら頂点単位でもいいでしょうが、たとえばカメラ正面に画面を覆うような四角形を置いたような状況を想定すると、四角形中央のdistは四隅のdistよりも小さくなるべきなのに、四隅のdistが線形補間された結果四隅と同じdistになってしまう...といったことがあるかもしれません。
[追記]
どうやらイメージエフェクト用シェーダーを作成されているようですね。でしたら、アプローチを変える必要があるかと思います。
イメージエフェクトシェーダーの段階ではすでに個々のモデルの描画は終わってしまっており、いわば「描画後の映像が貼り付けられた画面を覆う板」を描画しようとしている段階と言えると思います。バーテックスシェーダーに与えられる頂点は個々のモデルの頂点ではなく画面を覆う板の四隅であり、_WorldSpaceCameraPosとの距離を求めても有用ではないでしょう。
プランとしては、まずシェーダーに変数を追加してカメラの投影行列の逆行列を渡してやり、クリッピング座標からカメラ座標を復元できるようにして、そしてそれに渡すべきクリッピング座標のxとyはUV座標から算出、zは_CameraDepthTextureから取得した値を使う...という感じでいかがでしょう。あとは復元した座標のカメラからの距離、つまりその座標ベクトルの長さをdistとすればいけそうな気がします。
カメラ用スクリプト
C#
1using UnityEngine;
2
3[RequireComponent(typeof(Camera))]
4public class DepthOfField : MonoBehaviour
5{
6 public Material DepthOfFieldMaterial; // パブリック変数にシェーダーのマテリアルをセットする方式にしましたが、Shader.Findで取ってきたシェーダーからマテリアルを作ってもいいでしょう
7
8 private void Start()
9 {
10 // カメラの投影行列を実行環境に合わせた形に変換し、さらにその逆行列をシェーダーに渡す
11 var cam = this.GetComponent<Camera>();
12 var invProjMat = GL.GetGPUProjectionMatrix(cam.projectionMatrix, false).inverse;
13
14 this.DepthOfFieldMaterial.SetMatrix("_InverseProjectionMatrix", invProjMat);
15 }
16
17 private void OnRenderImage(RenderTexture src, RenderTexture dest) {
18 Graphics.Blit(src, dest, this.DepthOfFieldMaterial);
19 }
20}
シェーダーコード
HLSL
1Shader "Effect/DepthOfField"
2{
3 Properties
4 {
5 _MainTex ("Texture", 2D) = "white" {}
6 _Depth ("Depth", FLOAT) = 0.8 // 距離しきい値0.8mとなるとかなりカメラに近い気がしますが、これは適宜調整すればいいでしょう
7 }
8 SubShader
9 {
10 // No culling or depth
11 Cull Off ZWrite Off ZTest Always
12
13 Pass
14 {
15 CGPROGRAM
16 #pragma vertex vert
17 #pragma fragment frag
18
19 #include "UnityCG.cginc"
20
21 float _Depth;
22 float4x4 _InverseProjectionMatrix; // カメラ投影行列の逆行列用に変数を追加
23
24 struct appdata
25 {
26 float4 vertex :POSITION;
27 float2 uv : TEXCOORD0;
28 };
29
30 struct v2f
31 {
32 float2 uv : TEXCOORD0;
33 float4 vertex : SV_POSITION;
34 };
35
36 v2f vert (appdata v)
37 {
38 v2f o;
39 o.vertex = UnityObjectToClipPos(v.vertex);
40 o.uv = v.uv;
41
42 return o;
43 }
44
45 sampler2D _MainTex;
46 sampler2D _CameraDepthTexture;
47
48 fixed4 frag (v2f i) : SV_Target
49 {
50 float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
51 float4 col = tex2D(_MainTex, i.uv);
52
53 // クリッピング座標をカメラ投影行列の逆行列で変換すれば、カメラを原点とするフラグメントの座標が得られるはず
54 float4 position = mul(_InverseProjectionMatrix, float4(i.uv * 2 - 1, depth, 1.0));
55 position /= position.w;
56
57 float dist = length(position.xyz);
58
59 // とりあえず、距離が_Depth以上なら真っ黒にしてみました
60 if (dist < _Depth)
61 {
62 return col;
63 }
64 else
65 {
66 return fixed4(0.0, 0.0, 0.0, col.a);
67 }
68
69 /*
70 // 別パターンとして、距離0〜_Depthでなめらかにに黒くするのも面白そうです
71 // アイディア次第でいろんな表現ができそうですね
72 return fixed4(lerp(col.rgb, 0.0, dist / _Depth), col.a);
73 */
74 }
75 ENDCG
76 }
77 }
78}
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/12/13 10:11
2017/12/13 16:12
2017/12/13 22:24
2017/12/14 10:07