Unity3d 使用Unity中的自定义着色器正确平铺atlas纹理

Unity3d 使用Unity中的自定义着色器正确平铺atlas纹理,unity3d,shader,texture-atlas,Unity3d,Shader,Texture Atlas,这有点复杂,但我希望它可以归结为一个相当简单的问题。下面是它的运行方式:我使用Unity在运行时从一个bsp文件生成一个地图游戏对象,该文件包含一大堆顶点、面、UV、纹理参考等等。创建的网格完全符合它们的要求,所有纹理都很好。但是有一个问题,使用如此多的材质创建了如此多的网格,导致了许多绘制调用,这使得程序运行缓慢。因此,我寻找了一种减少抽签调用的方法,并找到了一个解决方案。将所有网格组合成一个大网格,并通过组合使用的所有纹理创建纹理图集。组合网格效果很好,组合纹理效果也很好。然后我面临uv映射

这有点复杂,但我希望它可以归结为一个相当简单的问题。下面是它的运行方式:我使用Unity在运行时从一个bsp文件生成一个地图游戏对象,该文件包含一大堆顶点、面、UV、纹理参考等等。创建的网格完全符合它们的要求,所有纹理都很好。但是有一个问题,使用如此多的材质创建了如此多的网格,导致了许多绘制调用,这使得程序运行缓慢。因此,我寻找了一种减少抽签调用的方法,并找到了一个解决方案。将所有网格组合成一个大网格,并通过组合使用的所有纹理创建纹理图集。组合网格效果很好,组合纹理效果也很好。然后我面临uv映射的问题。所以我从英伟达白皮书中找到了一个解决方案,制作一个定制的着色器,使用TXD2D函数利用纹理的UV位置来从纹理中插入纹理。我认为这会奏效,但我的网格有非常奇怪的三角形,我认为它们破坏了这个解决方案。在下面的图像中,您可以看到网格合并时与网格分离时的区别:

这是我在着色器中用于设置模型颜色的代码:

o.Albedo = tex2D (_MainTex, IN.uv2_BlendTex, ddx(IN.uv_MainTex), ddy(IN.uv_MainTex)).rgb;
如您所见,我添加了第二个UV,它是原始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;
Rect surfacetexturerelect=uvReMappers[textureIndex];
网格表面网格=所有面[i]。网格;
Vector2[]atlasTiledUVs=新Vector2[surfaceMesh.uv.Length];
Vector2[]atlasClampedUVs=新Vector2[surfaceMesh.uv.Length];
对于(int j=0;j
数组uvReMappers是在使用Texture2D函数PackTextures()时创建的Rect数组

抱歉花了这么长时间,但我的问题是:为什么纹理会扭曲。是因为网格三角化的方式,还是因为我编写自定义着色器的方式。最后,我该如何修复它


谢谢你抽出时间。我很抱歉写了这么多,但我从来没有张贴过一个问题之前。我总是在网上找到几乎所有问题的答案,但我已经搜索了几天如何解决这个问题。我觉得这可能太具体了,无法找到答案。我希望我已经提供了足够的信息。

我采取了不同的方法,在cpu上创建了一个纹理图谱,从那里UV贴图就像普通UV贴图一样,我所要做的就是为图谱中的顶点信息指定一个纹理

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; }
    }
}
我的场景是一个定制的体素引擎,它可以处理从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; }
    }
}
使用UnityEngine;
使用体素。对象;
命名空间引擎.MeshGeneration.Texturing
{
/// 
///压缩纹理集,用于将纹理信息映射到
///动态生成的网格。
/// 
公共类织物
{
/// 
///图集中的纹理定义。
/// 
public TextureDef[]纹理{get;set;}
公共结构
{
设置纹理();
}
受保护的虚拟void SetupTextures()
{
//bas atlas的默认值是atlas中具有单个纹理的材质
纹理=新纹理DEF[]
{
新织物
{ 
体素类型=0,
Faces=new[]{Face.Top,Face.Bottom,Face.Left,Face.Right,Face.Front,Face.Back},
边界=新[]{
新矢量2(0,1),
新矢量2(1,1),
新矢量2(1,0),
新矢量2(0,0)
}
}
};
}
公共静态纹理Def[]GenerateTextReset(IntVector2 textureSizeInPixels、IntVector2 atlasSizeInPixels)
{
int x=atlasSizeInPixels.x/textureSizeInPixels.x;
intz=atlasSizeInPixels.z/textureSizeInPixels.z;
int i=0;
var结果=新纹理def[x*z];
var uvSize=新矢量2(1f/((浮点)x),1f/((浮点)z));
用于(int-tx=0;txtex2D(_MainTex, float2((frac(IN.uv.x) * IN.uv3.x) + IN.uv2.x, (frac(IN.uv.y) * IN.uv3.y) + IN.uv2.y));
        struct v2f
        {
            float2 uv : TEXCOORD0;
            float4 vertex : SV_POSITION;
        };            


        float modFunction(float number, float divisor){
            //2018-05-24: copied from an answer by Nicol Bolas: https://stackoverflow.com/questions/35155598/unable-to-use-in-glsl
            return (number - (divisor * floor(number/divisor)));
        }


        fixed4 frag (v2f i) : SV_Target
        {
            fixed4 curColor = tex2D(_MainTex, i.uv);                
            fixed4 pattern = tex2D(_PatternTex, 
                float2(
                    modFunction(i.uv.x*_MainTex_TexelSize.z,_PatternTex_TexelSize.z) *_PatternTex_TexelSize.x,
                    modFunction(i.uv.y*_MainTex_TexelSize.w,_PatternTex_TexelSize.w) *_PatternTex_TexelSize.y
                )
            );
            fixed4 col = curColor * pattern;                
            col.rgb *= col.a;
            return col;
        }