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

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

ただいまの
回答率

90.53%

  • Unity

    5286questions

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

unityのマテリアルのサイズが一定にならない

解決済

回答 1

投稿 編集

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

pin03

score 13

サイズの違う二つのキューブに同じマテリアルをつけているのですが。サイズの大きいキューブに貼ったマテリアルが伸びてしまって全然違うマテリアルに見えてしまいます。オブジェクトのサイズ関係なくマテリアルの画像の大きさが一定になる設定をしたいのですが、どうのようにしたらよいのでしょうか?

(現在のマテリアルは模様の入った石の画像を読み込んでおり、インスペクターのTilingから数値を)

---------追記-------------------

·マテリアルをオブジェクトの数だけ作成し、そのオブジェクトごとにTiling値を調整する
·UVマッピングで対応する

という回答をいただいたのですが、
実は取り扱っているオブジェクトが大量にあり、それぞれ大きさちが違います。
そのすべてに同じマテリアルを充てたいです。(質問分のとおり、テクスチャが伸びないように)

上記の二つの方法でも対応できそうですが、かなり手間と時間がかかってしまいそうです。

エディター拡張などでテクスチャの大きさがオブジェクトの大きさに比例されないような
設定を作成できるのでしょうか?

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • KKK435

    2018/11/14 14:46 編集

    似た様な質問を受付中のまま繰り返すのは回答者に失礼なので改めて投稿するなら以前のは取り合えず解決済みにするなどしてはどうでしょう。個人的にはhttps://teratail.com/questions/154950の質問に対する回答のUV作成で対応可能な問題かと思いますがUnityだけで行いたいなど拘りがあるのでしょうか?

    キャンセル

  • pin03

    2018/11/14 15:13

    対応は可能なのでしょうが、UVマッピングに関しての知識がなく、また時間がなかったためunityの機能で、そのような設定があるはずだと思い込んで質問をいたしましたが、ないのかもしれませんね。。ご指摘いただきましたように前回の質問は解決済にいたします。

    キャンセル

回答 1

checkベストアンサー

+2

あるモデルに対してそれ専用のテクスチャを貼り付ける場合にはUVマッピングが適していますが(たいていの場合はUVマッピングでまかなえると思います)、今回のような様々なオブジェクトに同一のスケールで質感を与えたいケースは不得意でしょうね。

ここはモデルのUV座標を無視したマッピング技法の方が有利かもしれません。三平面マッピングは試してみる価値があるかと思います。アセットストアで「Triplanar」で検索しますと、有用そうなアセットが出てきました。
他にも、Unity - Unity-テクスチャを1マスごとに敷きつめる|teratailのKapustinさんの案や、GitHub - keijiro/StandardTriplanar: Unity's standard shader with triplanar mappingがご参考になるかと思います。

keijiroさんのマテリアルを試してみましたが、こちらは法線テクスチャ・オクルージョンテクスチャもセット可能で、岩肌などの表現にはほとんど問題ないかと思います。コードも比較的シンプルで読解しやすく、実行時の負荷も低めだろうと予想されます。

ですが、条件によっては粗が見えてしまうかもしれません。Standardマテリアル(Unityで標準的に使われる、モデルのUV座標に基づいたマッピング)の場合下図のように見えるシーンを...
Standard
keijiroさんのマテリアルに変更すると下図のようになります。
keijiro
これはわざと粗が目立つように作ったシーンでして、地面のPlaneと左のCylinderはスケールが(1, 1, 1)ですので木のブロックの大きさがほぼ同じになっていますが、上のCubeはスケール(2, 2, 1)、右のCapsuleは(2, 2, 2)、下のSphereは(1.5, 1, 1)で、ブロック模様が大きくなったり、引き伸ばされてしまっています。これはマッピング座標の決定にメッシュのモデル座標をそのまま使用しているためで、Unity上でのスケール設定が考慮されていないことによります。
また、静止画ではわかりにくいかもしれませんが、Cubeの手前側の面の凹凸が反転し、溝の部分が出っ張ってしまっています。これは法線テクスチャからサンプリングした法線をそのまま使っており、接空間の向き合わせを行っていないことによります。この接空間問題についてはNormal Mapping for a Triplanar Shader – Ben Golus – Mediumに詳しく解説されておりました。サイトの図を引用させていただきますと...

接空間を考慮しない陰影 期待される陰影
Naive Ground Truth

陰影が場所によってまちまちになってしまい、岩肌であれば目立たないものの、レンガのような人工物では気になりそうです。

上記スケール問題・接空間問題と、ついでにもう一つ、X方向に投影されたテクスチャが90°回転してしまう問題(キューブの側面が顕著です)の対策を検討してみました。コードの大部分が接空間の向き合わせで占拠されています...

Shader "Custom/ObjectTriplanar"
{
    Properties
    {
        _PixelsPerUnit ("Pixels Per Unit", Float) = 128.0
        _Color ("Color", Color) = (1, 1, 1, 1)
        _MainTex ("Albedo", 2D) = "white" { }
        _Glossiness ("Smoothness", Range(0.0, 1.0)) = 0.5
        [Gamma] _Metallic ("Metallic", Range(0.0, 1.0)) = 0.0
        _BumpScale ("Bump Scale", Float) = 1.0
        [Normal][NoScaleOffset] _BumpMap ("Normal", 2D) = "bump" { }
        [NoScaleOffset] _OcclusionMap ("Occlusion", 2D) = "white" { }
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" "DisableBatching" = "True" }

        CGPROGRAM

        // OpenGL ESには非対応...OpenGL ES対応にしたい場合、非正方行列を使用している箇所を書き換える必要がある
        #pragma exclude_renderers gles
        #pragma surface surf Standard vertex:vert fullforwardshadows addshadow
        #pragma target 3.5

        float _PixelsPerUnit;
        fixed4 _Color;
        sampler2D _MainTex;
        float4 _MainTex_ST;
        float4 _MainTex_TexelSize;
        half _Glossiness;
        half _Metallic;
        half _BumpScale;
        sampler2D _BumpMap;
        sampler2D _OcclusionMap;

        struct Input
        {
            float3 objectUV;
            float3 objectNormal;
            float4 objectTangent;
        };

        // カットオフ方式の混合比算出
        // カットオフが0.57くらいを上回ると、コーナー部分が欠落してしまうので注意
        float3 cutoffBlending(float3 normalAbs, float cutoff)
        {
            float3 factor = max(normalAbs - cutoff, 0.0001);
            return factor / dot(factor, (float3)1.0);
        }

        // 冪乗方式の混合比算出
        float3 powerBlending(float3 normalAbs, float exponent)
        {
            float3 factor = pow(normalAbs, exponent);
            return factor / dot(factor, (float3)1.0);
        }

        // Y-XZ非対称方式の混合比算出
        // 人工物のテクスチャにはこれが効果的か?
        float3 asymmetricBlending(float3 normalAbs, float exponent, float capCos, float borderHalfWidth)
        {
            float normalAbsXZMax = max(normalAbs.x, normalAbs.z);
            float2 factorXZ = pow(normalAbsXZMax > 0.0 ? normalAbs.xz / normalAbsXZMax: (float2)1.0, exponent);
            factorXZ /= dot(factorXZ, (float2)1.0);
             float factorY = smoothstep(capCos - borderHalfWidth, capCos + borderHalfWidth, normalAbs.y);
            float factorH = 1.0 - factorY;
            float3 factor = float3(factorXZ.x * factorH, factorY, factorXZ.y * factorH);
            return factor;
        }

        // 虚部xyz、実部wで表現される回転クォータニオンを行列形式にする
        float3x3 rotationFromQuaternion(float4 q)
        {
            float3 nA = q.xyz * 2.0;
            float3 nB = q.xyz * nA;
            float3 nC = q.xxy * nA.yzz;
            float3 nD = q.www * nA;
            float3 nE = 1.0 - (nB.yzx + nB.zxy);
            float3 nF = nC + nD.zyx;
            float3 nG = nC - nD.zyx;
            return float3x3(
                nE.x, nG.x, nF.y,
                nF.x, nE.y, nG.z,
                nG.y, nF.z, nE.z);
        }

        // fromからtoへの最短回転行列を作成する
        float3x3 fromToRotation(float3 from, float3 to)
        {
            float3 axisU = cross(from, to);
            float axisULength = length(axisU);
            float clampedAxisULength = max(axisULength, 0.001);
            float angleIsValid = sign(axisULength);
            float cosTheta = dot(from, to);
            float3 axis = angleIsValid > 0.0 ? axisU / clampedAxisULength : float3(1.0, 0.0, 0.0);
            float2 cosThetaHSinThetaH = sqrt(((float2)1.0 + float2(1.0, -1.0) * cosTheta) * 0.5);
            return rotationFromQuaternion(float4(axis * cosThetaHSinThetaH.y, cosThetaHSinThetaH.x));
        }

        void vert(inout appdata_full v, out Input IN)
        {
            UNITY_INITIALIZE_OUTPUT(Input, IN);

            // 3面のUV座標
            // オブジェクト座標をベースとし、それにワールドスケール推定値をかけて
            // ワールド空間におけるテクスチャのスケールが一定になるようにする
            IN.objectUV = v.vertex.xyz * float3(
                length(unity_ObjectToWorld._m00_m10_m20),
                length(unity_ObjectToWorld._m01_m11_m21),
                length(unity_ObjectToWorld._m02_m12_m22));

            // オブジェクト法線
            IN.objectNormal = v.normal;

            // オブジェクト接線
            IN.objectTangent = v.tangent;
        }

        void surf(Input IN, inout SurfaceOutputStandard o)
        {
            // オブジェクト空間->Unity接空間への変換を作成
            // 接線のwには従法線の反転の有無が格納されているので、それを従法線にかけている
            float3 objectNormal = normalize(IN.objectNormal);
            float3 objectTangent = normalize(IN.objectTangent.xyz);
            float3 objectBinormal = cross(objectNormal, objectTangent) * IN.objectTangent.w;
            float3x3 objectToTangent = float3x3(objectTangent, objectBinormal, objectNormal);

            // オブジェクト法線の成分の絶対値を求める...3方向のブレンド比算出に使う
            float3 objectNormalAbs = abs(objectNormal);

            // オブジェクト法線の成分の符号を求める...UV・接空間補正に使う
            float3 s = sign(objectNormal);

            // 3方向のUV座標を作成
            // サイコロを十字に切り開く展開をイメージしたため、keijiroさん方式と異なり3方向の式が非対称になっている
            //
            //    +Y
            // +Z -X -Z +X
            //    -Y
            //
            float2 uvMultiplier = _PixelsPerUnit * _MainTex_TexelSize.xy * _MainTex_ST.xy;
            float2 objectUVX = _MainTex_ST.zw + uvMultiplier * IN.objectUV.zy * float2(s.x, 1.0);
            float2 objectUVY = _MainTex_ST.zw + uvMultiplier * IN.objectUV.zx * float2(-1.0, s.y);
            float2 objectUVZ = _MainTex_ST.zw + uvMultiplier * IN.objectUV.xy * float2(-s.z, 1.0);

            // 3方向の平面接空間->オブジェクト空間への変換を作成
            // こちらもやはり3方向の接空間が非対称になっている
            // 接空間の基底は右手系になっている点に注意
            // テクスチャ赤が左から右、緑が下から上、青が奥から手前を指す
            float3 tPXNormal = float3(s.x, 0.0, 0.0);
            float3 tPYNormal = float3(0.0, s.y, 0.0);
            float3 tPZNormal = float3(0.0, 0.0, s.z);
            float3 tPXTangent = float3(0.0, 0.0, s.x);
            float3 tPYTangent = float3(0.0, 0.0, -1.0);
            float3 tPZTangent = float3(-s.z, 0.0, 0.0);
            float3 tPXBinormal = float3(0.0, 1.0, 0.0);
            float3 tPYBinormal = float3(s.y, 0.0, 0.0);
            float3 tPZBinormal = float3(0.0, 1.0, 0.0);
            float3x3 tPXToObject = transpose(float3x3(tPXTangent, tPXBinormal, tPXNormal));
            float3x3 tPYToObject = transpose(float3x3(tPYTangent, tPYBinormal, tPYNormal));
            float3x3 tPZToObject = transpose(float3x3(tPZTangent, tPZBinormal, tPZNormal));

            // 3方向の法線->オブジェクト法線への回転を求め、平面接空間->Unity接空間への合成変換を作成
            float3x3 tPXToTangent = mul(objectToTangent, mul(fromToRotation(tPXNormal, objectNormal), tPXToObject));
            float3x3 tPYToTangent = mul(objectToTangent, mul(fromToRotation(tPYNormal, objectNormal), tPYToObject));
            float3x3 tPZToTangent = mul(objectToTangent, mul(fromToRotation(tPZNormal, objectNormal), tPZToObject));

            // 3方向の法線をテクスチャから取得、ついでにUnity接空間上のベクトルに変換
            float3x3 normals = transpose(float3x3(
                mul(tPXToTangent, UnpackScaleNormal(tex2D(_BumpMap, objectUVX), _BumpScale)),
                mul(tPYToTangent, UnpackScaleNormal(tex2D(_BumpMap, objectUVY), _BumpScale)),
                mul(tPZToTangent, UnpackScaleNormal(tex2D(_BumpMap, objectUVZ), _BumpScale))));

            // 3方向のアルベド・遮蔽率をテクスチャから取得
            float4x3 albedoOcclusions = transpose(float3x4(
                tex2D(_MainTex, objectUVX).rgb, tex2D(_OcclusionMap, objectUVX).a,
                tex2D(_MainTex, objectUVY).rgb, tex2D(_OcclusionMap, objectUVY).a,
                tex2D(_MainTex, objectUVZ).rgb, tex2D(_OcclusionMap, objectUVZ).a));

            // 3方向の混合比を算出
            // Ben Golusさんの方法を参考にした、Y-XZ非対称混合を採用
            float3 factor = asymmetricBlending(objectNormalAbs, 64.0, 0.5, 1.0 / 64.0);
            // 冪乗混合の場合は下記
            //float3 factor = powerBlending(objectNormalAbs, 128.0);

            // アルベド・遮蔽率を合成
            float4 albedoOcclusion = mul(albedoOcclusions, factor) * float4(_Color.rgb, 1.0);

            // 法線を合成
            float3 normal = normalize(mul(normals, factor));

            //#define DEBUG_TANGENT_NORMAL
            //#define DEBUG_OBJECT_NORMAL
            //#define DEBUG_WORLD_NORMAL
            //#define DEBUG_ALBEDO
            //#define DEBUG_OCCLUSION

            o.Albedo = albedoOcclusion.rgb;
            o.Normal = float3(0.0, 0.0, 1.0);
            o.Metallic = 0.0;
            o.Smoothness = 0.0;
            o.Occlusion = albedoOcclusion.a;
            #if defined(DEBUG_TANGENT_NORMAL)
                o.Albedo = normal * 0.5 + 0.5;
            #elif defined(DEBUG_OBJECT_NORMAL)
                o.Albedo = mul(transpose(objectToTangent), normal) * 0.5 + 0.5;
            #elif defined(DEBUG_WORLD_NORMAL)
                o.Albedo = UnityObjectToWorldNormal(mul(transpose(objectToTangent), normal)) * 0.5 + 0.5;
            #elif defined(DEBUG_ALBEDO)
            #elif defined(DEBUG_OCCLUSION)
                o.Albedo = albedoOcclusion.a;
            #else
                o.Normal = normal;
                o.Metallic = _Metallic;
                o.Smoothness = _Glossiness;
            #endif
        }
        ENDCG
    }
    FallBack Off
}

これを使ったところ、下図のようにブロックの大きさが大体一定になりました。
ObjectTriplanar

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/11/21 10:10

    できました!ありがとうございます!

    キャンセル

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

  • Unity

    5286questions

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