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

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

ただいまの
回答率

88.81%

Unity2D テクスチャが潰れる

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 804
退会済みユーザー

退会済みユーザー

前提・実現したいこと

描画しているメッシュのテクスチャを潰れないようにしたい

該当のソースコード

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraControlScript : MonoBehaviour
{
    public Vector2 SPEED = new Vector2(0.3f, 0.3f);

    public float zoomSpeed = 100;
    public float targetOrtho;
    public float smoothSpeed = 200;
    public float minOrtho = 20;
    public float maxOrtho = 130;

    // Use this for initialization
    void Start()
    {
        targetOrtho = Camera.main.orthographicSize;
    }

    // Update is called once per frame
    void LateUpdate()
    {
        Vector3 pos = transform.position;
        pos.z = -10;
        pos.x = Mathf.Clamp(pos.x, 75, 175);
        pos.y = Mathf.Clamp(pos.y, 20, 230);

        transform.position = pos;
        Move();

        float scroll = Input.GetAxis("Mouse ScrollWheel");
        if (scroll != 0.0f)
        {
            targetOrtho -= scroll * zoomSpeed;
            targetOrtho = Mathf.Clamp(targetOrtho, minOrtho, maxOrtho);
        }

        Camera.main.orthographicSize = Mathf.MoveTowards(Camera.main.orthographicSize, targetOrtho, smoothSpeed * Time.deltaTime);

        float speed = Camera.main.orthographicSize / 43.333f;
        SPEED = new Vector2(speed, speed);
    }

    // 移動関数
    void Move()
    {
        Vector3 Position = transform.position;

        if (Input.GetKey("left"))
        {
            Position.x -= SPEED.x;
        }
        else if (Input.GetKey("right"))
        { 
            Position.x += SPEED.x;
        }
        else if (Input.GetKey("up"))
        { 
            Position.y += SPEED.y;
        }
        else if (Input.GetKey("down"))
        { 
            Position.y -= SPEED.y;
        }

        transform.position = Position;
    }
}

試したこと

Unityの2Dプロジェクトを使っています。
別のスクリプトでメッシュを生成しており、CameraのSizeを上記のスクリプトを使い、マウスのスクロールで変更させているのですが、Sizeが極端に小さくないと、テクスチャが綺麗に描画されません。Sizeが6を超えた辺りから、テクスチャが潰れて、ただの色にしか見えなくなってしまいます。

イメージ説明

使っているテクスチャ↓

イメージ説明

理想は、以下の画像のように、メッシュとの距離が遠くても、テクスチャが潰れない状態です。

イメージ説明

Cameraのズーム倍率を制御するCamera.fieldOfViewを使ってみましたが、2Dプロジェクトでは機能しませんでした。

Sizeを変更したときのメッシュのちらつきを消すために、Generate Mip Mapsをオンにしてあります。Filter ModeはPointで、Pixels Per Unitは、画像の縦幅と同じにしています。

CameraのSizeを変更しない方法があるのかもしれませんが、自分が調べた限りは出てきませんでした。どのようにすれば、テクスチャが潰れずに、ズーム出来るようになるでしょうか? 回答よろしくお願い致します。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

現状では画面上だと数ピクセル~数十ピクセル四方しかないタイル上に320×320のテクスチャを貼っている状態かと思いますので、テクスチャが縮小されすぎて潰れていると言えそうです。
ズームの仕組みをいじってもおそらくダメなんじゃないでしょうかね...?もし仮にカメラのSizeを変えずに、代わりにたとえばワールドマップのメッシュオブジェクトのスケールを拡大縮小することで縮尺を操作したとしても、結局遠距離視点では状況的に同じことになってディテールが潰れてしまうでしょう。

テクスチャの反復周期をもっと長くして、広い範囲にマッピングしてやる必要があるかと思います。先日C# - Unity2D 描画がちらつく|teratailの回答中で申し上げた「2枚ポリゴン化作戦」で使用したWorldシェーダーをベースに、下記のように改変してみました。

Shader "Unlit/World"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _DataTex ("Data", 2D) = "black" {}
        _Width ("Width", Int) = 250
        _Height ("Height", Int) = 250
        [IntRange] _Magnification ("Magnification", Range(1, 32)) = 8
        [Toggle] _Dynamic ("Dynamic", Float) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ _DYNAMIC_ON
            #include "UnityCG.cginc"

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

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

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _MainTex_TexelSize;
            sampler2D _DataTex;
            float4 _DataTex_TexelSize;
            int _Width;
            int _Height;
            int _Magnification; // 拡大率...たとえば8なら、マップ上の8x8タイルをテクスチャ反復の1周期とする

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

            // 固定拡大...テクスチャ反復数を縮尺によらず一定にする
            fixed4 fixedMagnification(v2f i)
            {
                float2 worldSize = float2(_Width, _Height);
                float2 worldUv = i.uv * worldSize;
                float2 tileCoord = floor(worldUv);
                float2 dataUv = (tileCoord + 0.5) * _DataTex_TexelSize.xy;
                float4 texCorners = tex2D(_DataTex, dataUv);

                // worldUvを拡大率で割ることでtileUvの周期を長くする
                worldUv /= _Magnification;
                float2 tileUv = frac(worldUv);

                // 画面上で1ピクセルずれるとUV座標がどれだけ飛ぶかを求めておく
                float2 gradScale = texCorners.zw - texCorners.xy;
                float2 dUvdx = ddx(worldUv) * gradScale;
                float2 dUvdy = ddy(worldUv) * gradScale;

                // UV座標の飛び幅だけUV矩形を小さくして隣接テクスチャの色漏れを防止する
                // テクスチャの外周が少し削ぎ落とされる欠点はありますが、テクスチャに幅広い余白を設けなくとも
                // 縮尺によらず安定して色漏れを防止できるんじゃないかと思います
                float2 texCornerOffset = min(gradScale, max(_MainTex_TexelSize.xy, dUvdx + dUvdy));

                // ミップマップを使う場合、tex2Dだと引数に与えたUV座標の変化率を基にミップレベルが選択されますが
                // 今回のように特殊なUV座標を使ってサンプリングすると、正しいレベルが選択されないことがあります
                // そんな時はtex2Dgradを使うと、レベル選択に使われるUV変化率を独自に与えてやることができます
                float2 uv = lerp(texCorners.xy + texCornerOffset, texCorners.zw - texCornerOffset, tileUv);
                fixed4 col = tex2Dgrad(_MainTex, uv, dUvdx, dUvdy);
                return col;
            }

            // 可変拡大...遠くから見たときほどテクスチャ反復数を減らす
            fixed4 dynamicMagnification(v2f i)
            {
                // 基本拡大量は固定拡大版と同じ
                float2 worldSize = float2(_Width, _Height);
                float2 worldUv = i.uv * worldSize;
                float2 tileCoord = floor(worldUv);
                float2 dataUv = (tileCoord + 0.5) * _DataTex_TexelSize.xy;
                float4 texCorners = tex2D(_DataTex, dataUv);
                worldUv /= _Magnification;
                float2 gradScale = texCorners.zw - texCorners.xy;
                float2 dUvdx = ddx(worldUv) * gradScale;
                float2 dUvdy = ddy(worldUv) * gradScale;

                // 縮尺の程度による追加の拡大量を算出する
                float2 magRatio = (dUvdx + dUvdy) * _MainTex_TexelSize.zw;
                float level = log2(max(1.0, min(magRatio.x, magRatio.y)));
                float levelIndex = floor(level);
                float levelRatio = frac(level);
                float extraScale = pow(0.5, levelIndex);

                // 追加拡大を施してテクスチャをサンプリング
                worldUv *= extraScale;
                dUvdx *= extraScale;
                dUvdy *= extraScale;
                float2 tileUv = frac(worldUv);
                float2 texCornerOffset = min(gradScale, max(_MainTex_TexelSize.xy, dUvdx + dUvdy));
                float2 uv = lerp(texCorners.xy + texCornerOffset, texCorners.zw - texCornerOffset, tileUv);
                float4 col0 = tex2Dgrad(_MainTex, uv, dUvdx, dUvdy);

                // さらにもう一段階拡大してテクスチャをサンプリングし...
                worldUv *= 0.5;
                dUvdx *= 0.5;
                dUvdy *= 0.5;
                tileUv = frac(worldUv);
                texCornerOffset = min(gradScale, max(_MainTex_TexelSize.xy, dUvdx + dUvdy));
                uv = lerp(texCorners.xy + texCornerOffset, texCorners.zw - texCornerOffset, tileUv);
                float4 col1 = tex2Dgrad(_MainTex, uv, dUvdx, dUvdy);

                // 2つを混ぜることでレベル間のつなぎ目をなめらかにする
                fixed4 col = lerp(col0, col1, levelRatio);
                return col;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                #ifdef _DYNAMIC_ON
                return dynamicMagnification(i);
                #else
                return fixedMagnification(i);
                #endif
            }
            ENDCG
        }
    }
}

Generate Mip Mapsオン、Filter ModeはTrilinearとし、マテリアルのMagnificationは32、Dynamicはオフだと下図のような見え方になりました。タイルの数は250×250ですが、テクスチャは32×32タイルで1周期になるようにしましたので、ある程度離れた位置からでも砂紋の形が見えます。ですがマップ全域が映るほどの距離になるとディテールがわかりにくくなりますね。

図1

Magnificationを4、Dynamicオンだと下図のような見え方になりました。遠距離であるほどテクスチャ反復数が小さくなるので、マップ全域でも砂紋(このスケールだと砂丘でしょうか)が見えています。一部分を拡大していくと全体と同じ形が再帰的に現れるような動作で面白そうだと思ったのですが、その分ちょっとDynamicオフよりも計算量は多くなってしまいます。

図2

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/07/07 13:22 編集

    いつも参考になる回答ありがとうございます。質問の内容とは直接関係ないのですが、BongoさんはどのようにしてUnityのスクリプトやシェーダーの勉強をしたのでしょうか?自分もBongoさんのようにスクリプトやシェーダーを書けるようになりたいのですが、独学しようにもインターネットの情報くらいしかないような気がしていて、上手くいきません。市販の本は入門ばかりで、あまり役に立たないような感じがしています。もしよかったら教えていただきたいです。

    キャンセル

  • 2019/07/08 06:28

    そうですね...私も時々本屋をのぞきに行ったりしますが、やはり需要的な理由からか入門書がほとんどで物足りないかもしれませんね。
    特に多少発展的な内容だったりシェーダープログラミングのことになったりすると、Unityについての本に絞ってしまうと大型書店でもまず見つからないと思います。ですが、Unityに限らなければいろいろ役立ちそうなものが見つかるんじゃないでしょうか。
    範囲が広がった分、逆にこれだという一冊を例示するのが難しいですが、たとえばシェーダー周りなら「リアルタイム レンダリング」( http://www.realtimerendering.com/ )とか「GPU Gems」( https://developer.nvidia.com/gpugems/GPUGems/ )とかでしょうか...?たとえ数年~十数年前の情報でも、グラフィックス描画の基本的な理屈は今でも変わりませんので役立つはずです。
    興味のおもむくまま使えそうなネタをつまみ食いしていってもいいでしょう。ご質問者さんは優れた学習意欲をお持ちのようですので、それらを起点に周辺情報を検索してみますとじきに習熟するかと思います。応援しております!

    キャンセル

  • 2019/07/08 17:58 編集

    お忙しい中、ご返信ありがとうございました。

    キャンセル

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

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

関連した質問

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