2

これは少し複雑ですが、非常に単純な問題になることを願っています。その方法は次のとおりです。Unity を使用して、実行時に頂点、面、UV、テクスチャ参照などを多数含む bsp ファイルからマップ ゲームオブジェクトを生成しています。作成されたメッシュは本来あるべき姿であり、すべてのテクスチャは問題なく表示されます。ただし、問題が 1 つあります。非常に多くのマテリアルで非常に多くのメッシュが作成されているため、多くのドロー コールが発生し、プログラムが遅くなります。そこで、ドローコールを減らす方法を探したところ、解決策が見つかりました。すべてのメッシュを 1 つの大きなメッシュに結合し、使用されるすべてのテクスチャを組み合わせてテクスチャ アトラスを作成します。メッシュの組み合わせはうまく機能し、テクスチャの組み合わせもうまくいきます。次に、UV マッピングの問題に直面しました。そこで、NVidia のホワイト ペーパーから、tex2d 関数を使用してテクスチャからテクセルを補間するカスタム シェーダーを作成するソリューションを見つけました。UV 位置とその導関数を使用します。これでうまくいくと思いますが、私のメッシュには本当に奇妙な三角形があり、このソリューションが台無しになっていると思います。下の画像では、メッシュが結合されている場合と分離されている場合の違いを見ることができます。

変更された UV とカスタム シェーダーを使用した結合されたメッシュ

元の UV でメッシュを分離する

これは、モデルの色を設定するためにシェーダーで使用しているコードです。

o.Albedo = tex2D (_MainTex, IN.uv2_BlendTex, ddx(IN.uv_MainTex), ddy(IN.uv_MainTex)).rgb;

ご覧のとおり、元の UV の非タイル バージョンである 2 つ目の UV を追加しました。frac() 関数を使用してこれを行いますが、シェーダーではなく C# コードで行います。テクスチャはさまざまなサイズになる可能性があるため、その時点でテクスチャ サイズにアクセスできるため、シェーダーに到達する前に UV を計算する必要がありました。

2 つの UV を計算するために使用したコードは次のとおりです。

                Rect surfaceTextureRect = uvReMappers[textureIndex];
                Mesh surfaceMesh = allFaces[i].mesh;
                Vector2[] atlasTiledUVs = new Vector2[surfaceMesh.uv.Length];
                Vector2[] atlasClampedUVs = new Vector2[surfaceMesh.uv.Length];
                for (int j = 0; j < atlasClampedUVs.Length; j++)
                {
                    Vector2 clampedUV = new Vector2((surfaceMesh.uv[j].x - Mathf.Floor(surfaceMesh.uv[j].x)), (surfaceMesh.uv[j].y - Mathf.Floor(surfaceMesh.uv[j].y)));
                    float atlasClampedX = (clampedUV.x * surfaceTextureRect.width) + surfaceTextureRect.x;
                    float atlasClampedY = (clampedUV.y * surfaceTextureRect.height) + surfaceTextureRect.y;
                    atlasTiledUVs[j] = new Vector2((surfaceMesh.uv[j].x * surfaceTextureRect.width) + surfaceTextureRect.x, (surfaceMesh.uv[j].y * surfaceTextureRect.height) + surfaceTextureRect.y);
                    atlasClampedUVs[j] = new Vector2(atlasClampedX, atlasClampedY);
                    if (i < 10) { Debug.Log(i + " Original: " + surfaceMesh.uv[j] + " ClampedUV: " + clampedUV); }
                }
                surfaceMesh.uv = atlasTiledUVs;
                surfaceMesh.uv2 = atlasClampedUVs;

配列 uvReMappers は、Texture2D 関数 PackTextures() を使用するときに作成される Rect の配列です。

長くなって申し訳ありませんが、私の質問は次のとおりです。テクスチャがゆがむのはなぜですか。メッシュの三角形化の方法によるものですか、それともカスタム シェーダーの書き方によるものですか。そして最後に、どうすれば修正できますか。

お時間をいただきありがとうございます。何度も書いて申し訳ありませんが、以前に質問を投稿したことがありません。私はいつもオンラインでほぼすべての問題の答えを見つけていますが、この問題を解決する方法を何日も探していました. 具体的すぎて答えが見つからないかもしれないと思います。十分な情報を提供できたことを願っています。

4

3 に答える 3

3

私はついに問題を解決しました!したがって、シェーダーの前に UV を計算するべきではないことがわかりました。代わりに、シェーダーが必要とする情報を UV を介して渡し、新しいテクセル位置を直接計算できるようにしました。

シェーダーの前のコードは次のとおりです。

Rect surfaceTextureRect = uvReMappers[textureIndex];
Mesh surfaceMesh = allFaces[i].mesh;
Vector2[] atlasTexturePosition = new Vector2[surfaceMesh.uv.Length];
Vector2[] atlasTextureSize = new Vector2[surfaceMesh.uv.Length];
for (int j = 0; j < atlasTexturePosition.Length; j++)
{
    atlasTexturePosition[j] = new Vector2(surfaceTextureRect.x, surfaceTextureRect.y);
    atlasTextureSize[j] = new Vector2(surfaceTextureRect.width, surfaceTextureRect.height);
}
surfaceMesh.uv2 = atlasTexturePosition;
surfaceMesh.uv3 = atlasTextureSize;

シェーダー コードは次のとおりです。

tex2D(_MainTex, float2((frac(IN.uv.x) * IN.uv3.x) + IN.uv2.x, (frac(IN.uv.y) * IN.uv3.y) + IN.uv2.y));
于 2015-10-11T17:28:57.277 に答える
2

私は別のアプローチを取り、CPU でテクスチャ アトラスを作成しました。そこから、UV マッピングは通常の UV マッピングと同じように、アトラスから頂点情報にテクスチャを割り当てるだけでした...

私のシナリオは、Minecraft からボクセル ベースの惑星のレンダリングまで、あらゆるものを処理できるカスタム ボクセル エンジンであり、まだ処理できないシナリオは見つかっていません。

これがアトラスのコードです...

using UnityEngine;
using Voxels.Objects;

namespace Engine.MeshGeneration.Texturing
{
    /// <summary>
    /// Packed texture set to be used for mapping texture info on 
    /// dynamically generated meshes.
    /// </summary>
    public class TextureAtlas
    {
        /// <summary>
        /// Texture definitions within the atlas.
        /// </summary>
        public TextureDef[] Textures { get; set; }

        public TextureAtlas()
        {
            SetupTextures();
        }

        protected virtual void SetupTextures()
        {
            // default for bas atlas is a material with a single texture in the atlas
            Textures = new TextureDef[]
            {
                new TextureDef 
                { 
                    VoxelType = 0, 
                    Faces =  new[] { Face.Top, Face.Bottom, Face.Left, Face.Right, Face.Front, Face.Back },
                    Bounds = new[] {
                        new Vector2(0,1), 
                        new Vector2(1, 1),
                        new Vector2(1,0),
                        new Vector2(0, 0)
                    }
                }
            };
        }


        public static TextureDef[] GenerateTextureSet(IntVector2 textureSizeInPixels, IntVector2 atlasSizeInPixels)
        {
            int x = atlasSizeInPixels.X / textureSizeInPixels.X;
            int z = atlasSizeInPixels.Z / textureSizeInPixels.Z;
            int i = 0;
            var result = new TextureDef[x * z];
            var uvSize = new Vector2(1f / ((float)x), 1f / ((float)z));

            for (int tx = 0; tx < x; tx++)
                for (int tz = 0; tz < z; tz++)
                {
                    // for perf, types are limited to 255 (1 byte)
                    if(i < 255)
                    {
                        result[i] = new TextureDef
                        {
                            VoxelType = (byte)i,
                            Faces = new[] { Face.Top, Face.Bottom, Face.Left, Face.Right, Face.Front, Face.Back },
                            Bounds = new[] {
                                new Vector2(tx * uvSize.x, (tz + 1f) * uvSize.y), 
                                new Vector2((tx + 1f) * uvSize.x, (tz + 1f) * uvSize.y),
                                new Vector2((tx + 1f) * uvSize.x, tz * uvSize.y),
                                new Vector2(tx * uvSize.x, tz * uvSize.y)
                            }
                        };

                        i++;
                    }
                    else
                        break;
                }

             return result;
        }
    }
}

アトラス内のテクスチャ定義については...

using UnityEngine;
using Voxels.Objects;

namespace Engine.MeshGeneration.Texturing
{
    /// <summary>
    /// Represents an area within the atlas texture 
    /// from which a single texture can be pulled.
    /// </summary>
    public class TextureDef
    {
        /// <summary>
        /// The voxel block type to use this texture for.
        /// </summary>
        public byte VoxelType { get; set; }

        /// <summary>
        /// Faces this texture should be applied to on voxels of the above type.
        /// </summary>
        public Face[] Faces { get; set; }

        /// <summary>
        /// Atlas start ref
        /// </summary>
        public Vector2[] Bounds { get; set; }
    }
}

UV マッピングを直接制御する必要があるカスタム シナリオでは、テクスチャ アトラスを継承してから SetupTextures() メソッドをオーバーライドしますが、ほとんどの場合、テクスチャがすべて同じサイズのアトラスを作成するので、GenerateTextureSet を呼び出すだけでUV マッピングの計算が必要だと思います。

特定のボクセル タイプの特定の面の UV 座標は、次のようになります。

IEnumerable<Vector2> UVCoords(byte voxelType, Face face, TextureAtlas atlas)
        {
            return atlas.Textures
                .Where(a => a.VoxelType == voxelType && a.Faces.Contains(face))
                .First()
                .Bounds;
        }

あなたの場合、おそらくパックから選択したテクスチャにマップする別の方法がありますが、基本的に、私の場合は面とタイプの組み合わせが、必要な UV マッピング セットを決定するものです。

これにより、カスタム シェーダー ロジックに依存する代わりに、任意の標準シェーダーでメッシュを使用できるようになります。

于 2015-10-10T16:22:05.363 に答える