美文网首页常用查询
拼接贴图接缝问题的解决方案

拼接贴图接缝问题的解决方案

作者: duanoldfive | 来源:发表于2022-07-01 11:28 被阅读0次

烦人的接缝

游戏中经常会用到在一个区域平铺某个贴图的需求,比如用碎石铺就的广场,我们通常会根据 顶点/像素 的世界坐标动态的计算UV值,达到贴图密度可调的目的。URP下Shader代码如下:

    float gridScale = 0.5;
    float2 roadUV = abs(frac(input.positionWS * gridScale).xz);
    surfaceData.albedo = SAMPLE_TEXTURE2D(_RoadTex, sampler_RoadTex, roadUV).rgb;
广场

细心的你一定注意到了,广场整体效果看起来还可以,但是有的地方有接缝的感觉,特别是摄像机移动或者旋转的过程中,尤其明显。(如红色箭头所指的地方)。

原因和解决方案

造成这个现象的原因主要由两个,一个是贴图的Mipmap,一个是贴图的过滤方式。我们导入贴图,一般默认是产生Mipmap, 过滤方式默认双线性过滤。
这种过滤方式会采样距离当前像素最近的四个纹素,然后根据像素到四个纹素点的距离进行插值来确定最终颜色,但是当UV值到达边缘值0或者1的时候,由于边缘像素对应的纹素少了一边或者两边,造成采样到的颜色和中间的像素颜色不一样,从而出现接缝现象。
Mip map 是根据距离摄像机远近不同,采用不同分辨率的贴图,而确定用哪个级别的贴图,需要用到UV的偏导数,而我们的UV是通过像素的世界坐标frac得到的,会导致偏导数不连续,从而采用了错误的Mipmap等级,使接缝变的更加明显。详见Unity Shader 关于tex2D中 dx dy 的猜想
知道了原因,我们就去修改一下试试效果,选中贴图,把Generate Mip Maps后面的勾选去掉,Filter Mode改成Point.别忘了点击Apply按钮应用设置。

设置贴图
再来看看效果:
效果
仔细观察刚才有接缝的地方,现在果然没有接缝了。

"就这样就好了吗?这也太简单了点吧?"

你的直觉是对的,事情肯定不是这么简单。
一般我们的地面会有多种材质,比如有石头路面,沙地,草地等等,怎么做呢?一般我们会把几种贴图合成到一张贴图中,类似Atlas,然后根据Mask贴图做混合,具体的可以研究一下刷地表的功能,这里主要讲接缝,就不展开了,我们为了实验,把两种贴图合成到一块看看会出现什么效果:

合成图片
效果
看起来不错,也没有接缝。以为万事大吉,可是等打包到手机平台,你就会发现还是出现了明显的接缝。
我分析可能是因为点过滤方式的采样,是取距离像素最近的纹素进行采样,像素落在两张贴图中间的时候(U值0.5的时候),会采样到另一侧的像素,所以接缝处有点土黄色,所以有一个解决方案是把每个贴图都外扩一定的像素,再合成一张贴图,然后对采样UV做一个clamp.这里就不详细介绍了,感兴趣的同学可以参考: 地形纹理合并

Textrue2DArray

今天我们要说的是另一种解决方案: Texture2DArray, 贴图数组,可以把它直接传给Shader,采样的时候可以指定index,然后就像采样单张贴图一样,系统会自动根据FilterMode,WrapMode处理采样中的各种问题,不会出现上面的两张贴图接缝处采样到另一边的问题,处理UV也简单很多。
详细文档地址: Texture2DArray

支持平台
可见该技术对平台有一定的要求,可以在运行时通过SystemInfo.supports2DArrayTextures来判断是否支持,不支持的可以按照之前的做法用拼图方式进行处理,不过随着硬件的发展,大部分设备都能够支持了。

创建资源

Texture2DArray没有办法通过Potoshop创建,也没有办法通过Unity Create菜单直接创建,只能通过脚本创建,所以需要写一个工具类,放到Editor文件夹下:

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

//创建Texture2DArray

public class TextureArray : EditorWindow
{
    public int PropertyNum = 10;

    public List<Texture2D> textures = new List<Texture2D>();

    [MenuItem("Tools/Texture2DArray")]
    static void Init()
    {   
        TextureArray window = (TextureArray)EditorWindow.GetWindow(typeof(TextureArray), false, "TextureArray", true);
        window.Show();
    }
    

    private float spaceNumber = 10f;

    private void OnGUI()
    {   
        GUILayout.Space(spaceNumber);
        EditorGUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();
        EditorGUILayout.LabelField("要合成的贴图:", GUILayout.Width(100), GUILayout.Height(30));
        GUILayout.FlexibleSpace();
        EditorGUILayout.EndHorizontal();
        for (int i=0; i<textures.Count; i++)
        {
            EditorGUILayout.BeginHorizontal();
            textures[i] = (Texture2D)EditorGUILayout.ObjectField(textures[i], typeof(Texture2D), true, GUILayout.Width(64), GUILayout.Height(64));
            if (GUILayout.Button("-", GUILayout.Width(30), GUILayout.Height(30)))
            {
                textures.RemoveAt(i);
                i--;
            }
            EditorGUILayout.EndHorizontal();
        }
        

        GUILayout.Space(spaceNumber);
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("+", GUILayout.Height(30)))
        {
            textures.Add(null);
        }
        EditorGUILayout.EndHorizontal();

        GUILayout.Space(spaceNumber);
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("创  建", GUILayout.Height(30)))
        {
            CreateTextureArray();
        }
        EditorGUILayout.EndHorizontal();
    }

    public void CreateTextureArray()
    {
        //如果没有指定要合成的贴图,或者都为空,则直接返回
        textures.RemoveAll(tex => tex == null);
        if (textures.Count == 0)
        {
            Debug.LogError("Please select textures for combine");
            return;
        }

        Texture2D firstTex = textures[0];

        //Create texture2DArray
        Texture2DArray texture2DArray = new Texture2DArray(firstTex.width,firstTex.height, textures.Count, firstTex.format, false, false);
        // Apply settings
        
        //texture2DArray.filterMode = firstTex.filterMode;
        //texture2DArray.wrapMode = firstTex.wrapMode;

        texture2DArray.filterMode = FilterMode.Point;
        texture2DArray.wrapMode = TextureWrapMode.Clamp;

        int index = 0;
        foreach(Texture2D tex in textures)
        {
            for (int m = 0; m < tex.mipmapCount; m++)
            {
                Graphics.CopyTexture(tex, 0, m, texture2DArray, index, m);
            }
            index++;
        }

        

        //Save 
        string path = EditorUtility.SaveFilePanel("Save As", "Assets", "texArray", "asset");
        if (path.Length > 0)
        {
            path = path.Substring(Application.dataPath.Length - 6);

            AssetDatabase.CreateAsset(texture2DArray, path);
        }
    }
}

然后Unity菜单中点击 Tools->Textrue2DArray,会弹出一个窗口,点击加号按钮,把要合成的贴图拖到对应的框内,等把所有要合成到一起的贴图全部处理好,点击合成按钮,选择位置,文件名,就会创建出一个Texture2DArray的资源了。
这里要注意的是,合成在一起的所有贴图要有一样的大小,格式,Import选项。
另外由于法线贴图都是线性空间的而不像普通贴图的gamma空间,所以要用这个工具处理法线贴图,请在new texture2DArray的时候,最后一个参数传true(表示线性空间)。最好自己加个参数,给用户选择。


创建窗口

Texture2DArray的使用

资源有了,现在就是怎么使用了,给要使用的Shader添加代码:

    Properties
    {
        ...
        _RoadTex("Road texture", 2DArray) = "" {}
     }
    SubShader
    {
      ...
      Pass
      {
         ...
        //声明变量
        TEXTURE2D_ARRAY(_RoadTex);  SAMPLER(sampler_RoadTex);
        ...
        half4 LitPassFragment(Varyings input) : SV_Target 
        {
            int index = 1;  //贴图索引,请根据项目需求自行设置,这里只是演示,固定取索引1
            //计算UV坐标
            float gridScale = 0.25;
            float2 roadUV = abs(frac(input.positionWS * gridScale).xz);
            //采样
            surfaceData.albedo = SAMPLE_TEXTURE2D_ARRAY(_RoadTex, sampler_RoadTex, roadUV, 1).rgb;
            ...
        }
      }
   }

Shader准备好以后,把刚才创建的Array资源拖到材质面板的Road texture字段处,运行项目,看看效果吧:


效果

完全看不到接缝了,打包到手机,同样完美。

感谢您的阅读,如果有什么意见建议欢迎联系我,共同进步。


最后给出项目地址 接缝项目

相关文章

  • 拼接贴图接缝问题的解决方案

    烦人的接缝 游戏中经常会用到在一个区域平铺某个贴图的需求,比如用碎石铺就的广场,我们通常会根据 顶点/像素 的世界...

  • 2019-08-11Seam-guided Local Alig

    缝线引导局部对齐的视差容错图像拼接(译) 一般做法:将图像对齐之后再接缝 本文:用接缝引导优化对齐; 在此基础上,...

  • 【Unity3D技术文档翻译】第3.6.2.3篇 UV重叠结果(

    上一章:【Unity3D技术文档翻译】第3.6.2.2篇 光照贴图接缝缝合(Lightmap seam stitc...

  • 你是怎么走过来的?

    最近丫头经过木板拼接而成的小桥都表示不敢走。虽然看似很细的拼接缝隙,她还是担心自己会从小洞里掉下去,只敢一...

  • 像素画:绘画工具Aseprite->制作瓦片贴图的原则

    瓦片贴图的图样是简单还是复杂需要根据游戏的需求制定。但是和角色不同,贴图必须是完美的正方形。这是因为我们在拼接贴图...

  • 铁岭46寸零接缝液晶拼接屏

    铁岭46寸零接缝液晶拼接屏应用行业: 广泛应用于工业流程显示系统等多个行业。深圳宇扬液晶拼接屏在多个行业有着较强的...

  • 赤峰46寸3.5mm接缝液晶拼接屏

    赤峰46寸3.5mm接缝液晶拼接屏应用行业: 广泛应用于粮仓系统等多个行业。深圳宇扬液晶拼接屏在多个行业有着较强的...

  • 阜新46寸零接缝液晶拼接屏

    阜新46寸零接缝液晶拼接屏应用行业: 广泛应用于能源安全监控系统等多个行业。深圳宇扬液晶拼接屏在多个行业有着较强的...

  • 天空球+云+山

    我们平时做天空球的uv接缝贴图不好处理,看了官方shader的源码,结合借鉴了candycat美女的动态噪波云效果...

  • 广东55寸3.5mm接缝液晶拼接屏

    广东55寸3.5mm接缝液晶拼接屏应用行业: 广泛应用于电影院等多个行业。深圳宇扬液晶拼接屏在多个行业有着较强的实...

网友评论

    本文标题:拼接贴图接缝问题的解决方案

    本文链接:https://www.haomeiwen.com/subject/diafbrtx.html