質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

2回答

1631閲覧

カメラが描画するピクセルの角度を取得したい

退会済みユーザー

退会済みユーザー

総合スコア0

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2018/06/27 13:47

編集2018/06/29 09:51

実現したいこと

現在Unityのシェーダー内で描画するピクセルの(カメラの注視点を基準としたローカルな)角度を取得し、
その角度に応じた値を描画するといったプログラムを作成したいと考えています。

発生している問題

精一杯調べましたが、どういったものを使えば良いのか分かりませんでした。

補足情報

プログラムに触れて間もないため、文章に不備があるかと思いますがご教授いただけると幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Bongo

2018/06/27 21:04

シェーダーによって変わってきそうですね。作りかけの状態のコードをご提示いただくと回答がつきやすいかと思います。
guest

回答2

0

ベストアンサー

画面中心方向(カメラ視線の真っ正面)とピクセル方向のなす角度...という解釈でやってみましたが、このような意味でいいでしょうか?

本来のシーンは下図のようにオブジェクトが配置されており、角度の変化を強調するためにカメラ画角を120°に広げています。

オリジナル

サーフェイスシェーダーの場合は、入力データにviewDirを追加して、それを使うことができるでしょう。

HLSL

1Shader "Custom/EyeVecSurfaceShader" 2{ 3 Properties { } 4 SubShader 5 { 6 Tags { "RenderType" = "Opaque" } 7 8 CGPROGRAM 9 10 #pragma surface surf Standard finalcolor:finalcol 11 #pragma target 3.0 12 13 struct Input 14 { 15 float3 viewDir; 16 }; 17 18 void surf(Input IN, inout SurfaceOutputStandard o) 19 { 20 // 今回は色づけはfinalcolで行うので、surfでは何もしていません 21 // 実際に応用する上では、せっかくサーフェイスシェーダーを使うのであれば 22 // surf内で角度を求めて質感に反映させることになるでしょう 23 } 24 25 #define PI 3.14159265358979323846 26 27 inline float3 angleToColor(float angle) 28 { 29 // 角度を可視化する 30 // 10°毎に色づけし、0°で赤、90°で緑、180°で青にする 31 float f = 2.0 * angle / PI; 32 float t0 = saturate(f); 33 float t1 = saturate(f - 1.0); 34 float3 color = lerp(lerp(float3(1.0, 0.0, 0.0), float3(0.0, 1.0, 0.0), t0), float3(0.0, 0.0, 1.0), t1); 35 float intensity = pow(abs(cos(angle * 18.0)), 1024.0); 36 37 return color * intensity; 38 } 39 40 void finalcol(Input IN, SurfaceOutputStandard o, inout fixed4 color) 41 { 42 float3 eyeVec = -mul(UNITY_MATRIX_V, IN.viewDir).xyz; // viewDirをワールド座標系からカメラ座標系に直し、正負反転してeyeVecとする 43 float angle = acos(-eyeVec.z); // eyeVecと(0, 0, -1)の内積のアークコサイン 44 45 color = fixed4(angleToColor(angle), 1.0); 46 } 47 48 ENDCG 49 } 50 FallBack "Diffuse" 51}

結果は下図のようになりました。

サーフェイスシェーダー

Unlitシェーダーの場合は、バーテックスシェーダーで求めたビュー座標をフラグメントシェーダーに伝達して使うのがいいかと思います。

HLSL

1Shader "Unlit/EyeVecUnlitShader" 2{ 3 Properties { } 4 SubShader 5 { 6 Tags { "RenderType" = "Opaque" } 7 8 Pass 9 { 10 CGPROGRAM 11 12 #pragma vertex vert 13 #pragma fragment frag 14 15 #include "UnityCG.cginc" 16 17 struct appdata 18 { 19 float4 vertex: POSITION; 20 }; 21 22 struct v2f 23 { 24 float3 viewPos: TEXCOORD0; 25 float4 vertex: SV_POSITION; 26 }; 27 28 v2f vert(appdata v) 29 { 30 v2f o; 31 o.vertex = UnityObjectToClipPos(v.vertex); 32 o.viewPos = UnityObjectToViewPos(v.vertex); // 頂点をカメラ座標系に変換 33 return o; 34 } 35 36 #define PI 3.14159265358979323846 37 38 inline float3 angleToColor(float angle) 39 { 40 // 角度を可視化する 41 // 10°毎に色づけし、0°で赤、90°で緑、180°で青にする 42 float f = 2.0 * angle / PI; 43 float t0 = saturate(f); 44 float t1 = saturate(f - 1.0); 45 float3 color = lerp(lerp(float3(1.0, 0.0, 0.0), float3(0.0, 1.0, 0.0), t0), float3(0.0, 0.0, 1.0), t1); 46 float intensity = pow(abs(cos(angle * 18.0)), 1024.0); 47 48 return color * intensity; 49 } 50 51 fixed4 frag(v2f i): SV_Target 52 { 53 float3 eyeVec = normalize(i.viewPos); // 頂点間で線形補間されたviewPosを正規化しeyeVecとする 54 float angle = acos(-eyeVec.z); // eyeVecと(0, 0, -1)の内積のアークコサイン 55 56 return fixed4(angleToColor(angle), 1.0); 57 } 58 59 ENDCG 60 } 61 } 62}

結果は下図のようにサーフェイスシェーダーの場合と同じです。

Unlitシェーダー

イメージエフェクトシェーダーの場合は、クリッピング座標からビュー座標を求めるのはどうでしょうか。
Unity - Unity Cg/HLSLで頂点とカメラ間の距離(104534)|teratailに投稿したものを改変しました。

HLSL

1Shader "Hidden/EyeVecImageEffectShader" 2{ 3 Properties 4 { 5 _MainTex ("Texture", 2D) = "white" { } 6 } 7 SubShader 8 { 9 Cull Off 10 ZWrite Off 11 ZTest Always 12 13 Pass 14 { 15 CGPROGRAM 16 17 #pragma vertex vert 18 #pragma fragment frag 19 20 #include "UnityCG.cginc" 21 22 struct appdata 23 { 24 float4 vertex: POSITION; 25 float2 uv: TEXCOORD0; 26 }; 27 28 struct v2f 29 { 30 float2 uv: TEXCOORD0; 31 float4 vertex: SV_POSITION; 32 }; 33 34 v2f vert(appdata v) 35 { 36 v2f o; 37 o.vertex = UnityObjectToClipPos(v.vertex); 38 o.uv = v.uv; 39 return o; 40 } 41 42 sampler2D _MainTex; 43 float4x4 _InverseProjectionMatrix; // カメラ投影行列の逆行列 44 45 #define PI 3.14159265358979323846 46 47 inline float3 angleToColor(float angle) 48 { 49 // 角度を可視化する 50 // 10°毎に色づけし、0°で赤、90°で緑、180°で青にする 51 float f = 2.0 * angle / PI; 52 float t0 = saturate(f); 53 float t1 = saturate(f - 1.0); 54 float3 color = lerp(lerp(float3(1.0, 0.0, 0.0), float3(0.0, 1.0, 0.0), t0), float3(0.0, 0.0, 1.0), t1); 55 float intensity = pow(abs(cos(angle * 18.0)), 1024.0); 56 57 return color * intensity; 58 } 59 60 fixed4 frag(v2f i): SV_Target 61 { 62 // クリッピング座標からカメラ座標を得る 63 // 欲しいのは方角だけなので、わざわざデプステクスチャから深度を取得してz成分に渡してやらなくても結果は変わらない 64 float4 position = mul(_InverseProjectionMatrix, float4(i.uv * 2 - 1, 0.0, 1.0)); 65 position /= position.w; 66 67 float3 eyeVec = normalize(position.xyz); // カメラ座標を正規化しeyeVecとする 68 float angle = acos(-eyeVec.z); // eyeVecと(0, 0, -1)の内積のアークコサイン 69 70 return fixed4(angleToColor(angle), 1.0); 71 } 72 73 ENDCG 74 } 75 } 76}

カメラ用スクリプト

C#

1using UnityEngine; 2 3[RequireComponent(typeof(Camera))] 4[ExecuteInEditMode] 5public class EyeVecImageEffectShader : MonoBehaviour 6{ 7 private Material effectMaterial; 8 9 private void CreateMaterial() 10 { 11 if (this.effectMaterial == null) 12 { 13 this.effectMaterial = new Material(Shader.Find("Hidden/EyeVecImageEffectShader")); 14 } 15 } 16 17 private void OnRenderImage(RenderTexture src, RenderTexture dest) 18 { 19 var invProjMat = GL.GetGPUProjectionMatrix(Camera.current.projectionMatrix, false).inverse; 20 this.CreateMaterial(); 21 this.effectMaterial.SetMatrix("_InverseProjectionMatrix", invProjMat); 22 Graphics.Blit(src, dest, this.effectMaterial); 23 } 24}

イメージエフェクトシェーダーですので、結果は先の2つと異なり画面全体に効果がかかっています。

イメージエフェクトシェーダー

投稿2018/06/30 00:31

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/07/01 13:58 編集

考えていたものができました! ご教授いただきありがとうございました!
guest

0

単位ベクトルとは、長さが1のベクトルであるので意味がわからないので、おそらくワールド座標系におけるカメラからXY平面上のピクセルに対応した点へのベクトルという意味と、描画時の画面左下からピクセルへのベクトルの2つの意味で答えておきます。

座標周りはこのブログが良くまとまっているのでここも見ておくと良いかと思います。
http://tkengo.github.io/blog/2015/01/10/opengl-es-2-2d-knowledge-3/

コードとしては、以下のようにすることで取得可能です。

shader

1#pragma vertex vert 2 3struct myData { 4 float4 pos : POSITION; 5 ... 6} 7 8myData vert(appdata_base v) 9{ 10 float3 cameraPos = _WorldSpaceCameraPos; 11 float3 cameraToVert = v.vertex - cameraPos; 12 float3 cameraToVertOnXY = (_WorldSpaceCameraPos.z / cameraToVert.z) * cameraToVert; 13 myData d; 14 d.pos = UnityObjectToClipPos(v.vertex); 15 ... 16 return d; 17} 18fixed4 frag(myData d): SV_Target 19{ 20 // d.posはClipPositionなので、そのまま使うことも可能です。 21 // ComputeScreenPosを使うとスクリーン上での正確な位置になります。 22 // スクリーン左下を原点とした座標になります。 23 float4 screenPos = ComputeScreenPos(d.pos); 24 // スクリーン中心を原点とした座標 25 float4 screenPosFromCenter = screenPos - float4(_ScreenParams.x / 2, _ScreenParams.y / 2,0,0); 26 ... 27} 28

投稿2018/06/28 06:51

takezoux2

総合スコア3

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/06/29 09:47

すいません、「単位ベクトル」という表現が曖昧でしたので質問文章を訂正させていただきました。 大変申し訳ございませんが、よろしければご教授いただけると幸いです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問