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

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

ただいまの
回答率

90.51%

  • Unity

    4002questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

  • Unity3D

    1291questions

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

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

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 270

hae

score 2

 実現したいこと

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

 発生している問題

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

 補足情報

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Bongo

    2018/06/28 06:04

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

    キャンセル

回答 2

checkベストアンサー

+1

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

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

オリジナル

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

Shader "Custom/EyeVecSurfaceShader"
{
    Properties { }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }

        CGPROGRAM

        #pragma surface surf Standard finalcolor:finalcol
        #pragma target 3.0

        struct Input
        {
            float3 viewDir;
        };

        void surf(Input IN, inout SurfaceOutputStandard o)
        {
            // 今回は色づけはfinalcolで行うので、surfでは何もしていません
            // 実際に応用する上では、せっかくサーフェイスシェーダーを使うのであれば
            // surf内で角度を求めて質感に反映させることになるでしょう
        }

        #define PI 3.14159265358979323846

        inline float3 angleToColor(float angle)
        {
            // 角度を可視化する
            // 10°毎に色づけし、0°で赤、90°で緑、180°で青にする
            float f = 2.0 * angle / PI;
            float t0 = saturate(f);
            float t1 = saturate(f - 1.0);
            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);
            float intensity = pow(abs(cos(angle * 18.0)), 1024.0);

            return color * intensity;
        }

        void finalcol(Input IN, SurfaceOutputStandard o, inout fixed4 color)
        {
            float3 eyeVec = -mul(UNITY_MATRIX_V, IN.viewDir).xyz; // viewDirをワールド座標系からカメラ座標系に直し、正負反転してeyeVecとする
            float angle = acos(-eyeVec.z); // eyeVecと(0, 0, -1)の内積のアークコサイン

            color = fixed4(angleToColor(angle), 1.0);
        }

        ENDCG
    }
    FallBack "Diffuse"
}

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

サーフェイスシェーダー

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

Shader "Unlit/EyeVecUnlitShader"
{
    Properties { }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }

        Pass
        {
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex: POSITION;
            };

            struct v2f
            {
                float3 viewPos: TEXCOORD0;
                float4 vertex: SV_POSITION;
            };

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.viewPos = UnityObjectToViewPos(v.vertex); // 頂点をカメラ座標系に変換
                return o;
            }

            #define PI 3.14159265358979323846

            inline float3 angleToColor(float angle)
            {
                // 角度を可視化する
                // 10°毎に色づけし、0°で赤、90°で緑、180°で青にする
                float f = 2.0 * angle / PI;
                float t0 = saturate(f);
                float t1 = saturate(f - 1.0);
                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);
                float intensity = pow(abs(cos(angle * 18.0)), 1024.0);

                return color * intensity;
            }

            fixed4 frag(v2f i): SV_Target
            {
                float3 eyeVec = normalize(i.viewPos); // 頂点間で線形補間されたviewPosを正規化しeyeVecとする
                float angle = acos(-eyeVec.z); // eyeVecと(0, 0, -1)の内積のアークコサイン

                return fixed4(angleToColor(angle), 1.0);
            }

            ENDCG
        }
    }
}

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

Unlitシェーダー

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

Shader "Hidden/EyeVecImageEffectShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" { }
    }
    SubShader
    {
        Cull Off
        ZWrite Off
        ZTest Always

        Pass
        {
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex: POSITION;
                float2 uv: TEXCOORD0;
            };

            struct v2f
            {
                float2 uv: TEXCOORD0;
                float4 vertex: SV_POSITION;
            };

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;
            float4x4 _InverseProjectionMatrix; // カメラ投影行列の逆行列

            #define PI 3.14159265358979323846

            inline float3 angleToColor(float angle)
            {
                // 角度を可視化する
                // 10°毎に色づけし、0°で赤、90°で緑、180°で青にする
                float f = 2.0 * angle / PI;
                float t0 = saturate(f);
                float t1 = saturate(f - 1.0);
                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);
                float intensity = pow(abs(cos(angle * 18.0)), 1024.0);

                return color * intensity;
            }

            fixed4 frag(v2f i): SV_Target
            {
                // クリッピング座標からカメラ座標を得る
                // 欲しいのは方角だけなので、わざわざデプステクスチャから深度を取得してz成分に渡してやらなくても結果は変わらない
                float4 position = mul(_InverseProjectionMatrix, float4(i.uv * 2 - 1, 0.0, 1.0));
                position /= position.w;

                float3 eyeVec = normalize(position.xyz); // カメラ座標を正規化しeyeVecとする
                float angle = acos(-eyeVec.z); // eyeVecと(0, 0, -1)の内積のアークコサイン

                return fixed4(angleToColor(angle), 1.0);
            }

            ENDCG
        }
    }
}

カメラ用スクリプト

using UnityEngine;

[RequireComponent(typeof(Camera))]
[ExecuteInEditMode]
public class EyeVecImageEffectShader : MonoBehaviour
{
    private Material effectMaterial;

    private void CreateMaterial()
    {
        if (this.effectMaterial == null)
        {
            this.effectMaterial = new Material(Shader.Find("Hidden/EyeVecImageEffectShader"));
        }
    }

    private void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        var invProjMat = GL.GetGPUProjectionMatrix(Camera.current.projectionMatrix, false).inverse;
        this.CreateMaterial();
        this.effectMaterial.SetMatrix("_InverseProjectionMatrix", invProjMat);
        Graphics.Blit(src, dest, this.effectMaterial);
    }
}

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/01 22:58 編集

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

    キャンセル

0

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

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

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

#pragma vertex vert

struct myData {
  float4 pos : POSITION;
  ...
}

myData vert(appdata_base v)
{
    float3 cameraPos = _WorldSpaceCameraPos;
    float3 cameraToVert = v.vertex - cameraPos;
    float3 cameraToVertOnXY = (_WorldSpaceCameraPos.z / cameraToVert.z) * cameraToVert;
    myData d;
    d.pos = UnityObjectToClipPos(v.vertex);
    ...
    return d;
}
fixed4 frag(myData d): SV_Target
{
    // d.posはClipPositionなので、そのまま使うことも可能です。
    // ComputeScreenPosを使うとスクリーン上での正確な位置になります。
    // スクリーン左下を原点とした座標になります。
    float4 screenPos = ComputeScreenPos(d.pos);
    // スクリーン中心を原点とした座標
    float4 screenPosFromCenter = screenPos - float4(_ScreenParams.x / 2, _ScreenParams.y / 2,0,0);
    ...
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/29 18:47

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

    キャンセル

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

  • ただいまの回答率 90.51%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Unity

    4002questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

  • Unity3D

    1291questions

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