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

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

ただいまの
回答率

88.58%

unityの鏡面表現にリフレクションプローブを使っていますが、うまく映り込みしません。

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,850

pin03

score 23

unityにて、建物の天井を鏡面にするためにリフレクションプローブを使っています。
天井は平らなのですが、上から見た時の形状は四角ではなく複雑な形をしています。

鏡面っぽくはなっています。しかし、映り込みが明らかにずれていたりして違和感があります。
自然に映るようにするための方法を試しましたが、その条件の一つとして、プローブの効果範囲(黄色い線で表示される)を対象のオブジェクトにピッタリ合わせないとならないようです。

天井の輪郭は複雑なので、うまくいきませんでした。
どのような方法であれば正確な映り込みを実現できるのでしょうか?

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+1

縁が複雑な形であっても、反射面が平面ならば、反射像を別途レンダリングして鏡面に投影するテクニックが使えるのではないかと思います。
MirrorReflection4 - Unify Community Wikiを試してみたところ、下図のように天井に不定形の鏡を設置することができました。

鏡

コードについては、2018.2.14f1でも少し修正するだけで動作しました。
MirrorReflection.csの...

  • 166行目のreflectionCamera = go.camera;reflectionCamera = go.GetComponent<Camera>();にする
  • 170行目のreflectionCamera.gameObject.AddComponent("FlareLayer");reflectionCamera.gameObject.AddComponent<FlareLayer>();にする

参考:UnityのMirrorRefrectionによる鏡の表現 - Qiita

追記

メッシュの点検用スクリプトを作ってみました。
Editorフォルダを作って、中に下記スクリプトを入れ...

using System.Linq;
using UnityEditor;
using UnityEngine;

public static class MirrorMeshChecker
{
    [MenuItem("Utility/Check Mirror Mesh")]
    public static void CheckMirrorMesh()
    {
        var mesh = Selection.activeObject as Mesh;
        if (mesh == null)
        {
            return;
        }

        var vertices = mesh.vertices;
        var triangles = mesh.triangles;
        var faceCount = triangles.Length / 3;
        var normals = new Vector3[faceCount];
        for (var i = 0; i < faceCount; i++)
        {
            var j = i * 3;
            var v0 = vertices[triangles[j]];
            var v1 = vertices[triangles[j + 1]];
            var v2 = vertices[triangles[j + 2]];
            normals[i] = Vector3.Cross(v1 - v0, v2 - v0).normalized;
        }

        var normal = normals.Aggregate((sum, n) => sum + n).normalized;
        var normalVariance = normals.Aggregate(
            Vector3.zero,
            (variance, n) =>
            {
                var d = n - normal;
                return variance + Vector3.Scale(d, d);
            });
        var plane = new Plane(normal, Vector3.zero);
        var distances = vertices.Select(v => plane.GetDistanceToPoint(v)).ToArray();
        var distance = distances.Average();
        var distanceVariance = distances.Aggregate(
            0.0f,
            (variance, r) =>
            {
                var d = r - distance;
                return variance + (d * d);
            });
        Debug.LogFormat("Normal:{0}", normal.ToString("F8"));
        Debug.LogFormat("Normal Variance:{0}", normalVariance.ToString("F8"));
        Debug.LogFormat("Distance:{0:F8}", distance);
        Debug.LogFormat("Distance Variance:{0:F8}", distanceVariance);
    }
}


鏡メッシュを選択し...
選択
「Utility」→「Check Mirror Mesh」を実行すると...
実行
コンソールに結果が表示されるかと思います。
結果

Normalがほぼ(0, 1, 0)で、Normal Varianceがほぼ(0, 0, 0)、DistanceとDistance Varianceがいずれもほぼ0ならメッシュに問題はなさそうなので、不具合の原因は他の部分にあると思われます。
Normal、Distanceが上記と異なっていても、Normal Varianceがほぼ(0, 0, 0)、Distance Varianceがほぼ0ならシェーダーの修正で対応可能なはずです。
Normal Variance、Distance Varianceが0から大きくずれているようですと、鏡が平面でない可能性が大きいです。その場合はモデルを修正する必要がありそうです...

追記

プロジェクトファイルのご提示ありがとうございます。とても参考になりました。鏡のオブジェクトは「l2_slab」、メッシュは「_I_u_W_F_N」でよろしいでしょうか。

このメッシュは2つのサブメッシュから構成されており、鏡マテリアルの面も天井平面だけでなく側面も含んだ非平面のようでした。
回答に追記したMirrorMeshCheckerは、複数のサブメッシュを持たない平面メッシュを想定したものでしたので、正しく面の向き・位置を求めることができなかったようです。

さしあたり、下記のようにMirrorMeshCheckerに面法線が下方を向いている面だけを計算に加味するよう泥縄的改修を加えて実行してみたところ...

using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

public static class MirrorMeshChecker
{
    [MenuItem("Utility/Check Mirror Mesh")]
    public static void CheckMirrorMesh()
    {
        var mesh = Selection.activeObject as Mesh;
        if (mesh == null)
        {
            return;
        }

        var subMeshCount = mesh.subMeshCount;
        var vertices = mesh.vertices;
        for (var k = 0; k < subMeshCount; k++)
        {
            var triangles = mesh.GetTriangles(k);
            var faceCount = triangles.Length / 3;
            var normals = new List<Vector3>();
            var faceVertices = new HashSet<Vector3>();
            for (var i = 0; i < faceCount; i++)
            {
                var j = i * 3;
                var v0 = vertices[triangles[j]];
                var v1 = vertices[triangles[j + 1]];
                var v2 = vertices[triangles[j + 2]];
                var n = Vector3.Cross(v1 - v0, v2 - v0).normalized;
                if (Vector3.Dot(n, Vector3.down) < 0.9f)
                {
                    continue;
                }
                normals.Add(n);
                faceVertices.Add(v0);
                faceVertices.Add(v1);
                faceVertices.Add(v2);
            }

            if (normals.Count <= 0)
            {
                continue;
            }
            var normal = normals.Aggregate((sum, n) => sum + n).normalized;
            var normalVariance = normals.Aggregate(
                Vector3.zero,
                (variance, n) =>
                {
                    var d = n - normal;
                    return variance + Vector3.Scale(d, d);
                });
            var plane = new Plane(normal, Vector3.zero);
            var distances = faceVertices.Select(v => plane.GetDistanceToPoint(v)).ToArray();
            var distance = distances.Average();
            var distanceVariance = distances.Aggregate(
                0.0f,
                (variance, r) =>
                {
                    var d = r - distance;
                    return variance + (d * d);
                });
            Debug.LogFormat("Submesh {0}:", k);
            Debug.LogFormat("\tNormal:{0}", normal.ToString("F8"));
            Debug.LogFormat("\tNormal Variance:{0}", normalVariance.ToString("F8"));
            Debug.LogFormat("\tDistance:{0:F8}", distance);
            Debug.LogFormat("\tDistance Variance:{0:F8}", distanceVariance);
        }
    }
}

下図のように向きは(0, -1, 0)で位置は約-5.2、つまり地上5.2mの位置にある...という結果になりました。

コンソール

そこで、MirrorReflection.csの46、47行目を下記のように変更すると...

        Vector3 pos = transform.TransformPoint(new Vector3(0.0f, 5.2f, 0.0f));
        Vector3 normal = -transform.up;

下図のように、天井に風景が映り込みました。

鏡天井

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/11/09 11:10

    とんでもありません。プログラミングは本当に最近始めたばかりで全く気が付きませんでした。。
    重ねてお聞きして申し訳ないのですが、
    このミラー天井の反射率を下げて白くしたりするのもプログラミングの領域になるのでしょうか?

    キャンセル

  • 2018/11/09 12:01

    その辺のカスタマイズは、Mirror.shaderの方を編集することになりますね。
    「UnityのMirrorRefrectionによる鏡の表現 - Qiita」のサイト内の図では、反射像が薄暗くなっています。この程度でしたらマテリアルの「Base (RGB)」に適当なテクスチャをセットするだけで可能ですが、現状では反射像を暗くする方向にしか対応できませんので、より白っぽくしたいとか、つるつるに磨かれた石のようないくらか陰影のある見た目にしたいといった場合はMirror.shaderの改造が必要になるでしょう。

    キャンセル

  • 2018/11/09 13:33

    シェーダーによる調整になるのですね。
    やってみようと思います。
    いろいろ教えていただきありがとうございました!

    キャンセル

0

Unityは使ったことがないですが、見た感じParallax-Corrected Cubemapのような技法で鏡面反射を表現する仕組みですかね。
これは鏡に映る対象が直方体の面に一致しているときのみ正しい結果が出ます。
その代わり曲面や無数の向きの違う鏡面を軽い処理で扱えます。
使う鏡面が1枚の平面なら必要ありません。Bongoさんの回答の手法が適切です。
天井の輪郭が複雑というのは部屋の壁も不定形でしょうか。でしたら対象を部屋に合わせられないのでどうやってもずれます。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/11/07 15:28

    回答ありがとうございます!

    キャンセル

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

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

関連した質問

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