当前位置: 首页 > article >正文

【Unity功能集】TextureShop纹理工坊(二)图层(上)

项目源码:后期发布

索引

  • 图层
    • TextureLayer
    • 可见性
    • 激活性
    • 可编辑性
      • 绘画区域、绘画板
      • 绘画区域锚点
      • 导入图像

图层

PS中,图层的概念贯穿始终(了解PS图层),他可以称作PS最基础也是最强大的特性之一。

那么,在TextureShop中,我们的第一个任务也即是设计并完成图层的功能。

TextureLayer

首先,定义TextureLayer类,此即为图层类,一个TextureLayer类对象代表一个图层实例:

    /// <summary>
    /// 图层
    /// </summary>
    public sealed class TextureLayer
    {
    
    }

可见性

基于图层的可见性(也即是人眼可见),我们需要每一个图层都是能够独立渲染的,所以图层需要携带一个渲染器,在此我决定使用RawImage来渲染,同时Texture2D对象作为渲染源,Material对象作为渲染材质:

    /// <summary>
    /// 图层
    /// </summary>
    public sealed class TextureLayer
    {
        private Material _material;
        private RawImage _target;
        private Texture2D _texture;
    }

这三者应是图层的内部逻辑对象,不需对外,也不能对外公开,所以他们为private的。

激活性

基于图层的可激活、可隐藏性(也即是可激活、可隐藏图层),我们加入一个属性:

    /// <summary>
    /// 图层
    /// </summary>
    public sealed class TextureLayer
    {
        private Material _material;
        private RawImage _target;
        private Texture2D _texture;
        
        /// <summary>
        /// 是否激活图层
        /// </summary>
        public bool IsShow
        {
            get
            {
                return _target.enabled;
            }
            set
            {
                _target.enabled = value;
            }
        }
    }

可编辑性

基于图层的可编辑性(也即是可编辑颜色、可编辑区域等),我们会发现如下的问题:

当我们的图层尺寸为512x512时,显示一张大于512x512尺寸的图像,显示效果如下:

在这里插入图片描述
超出512x512范围的图像内容(黑色区域)将被裁切!

而实际上,这部分内容不仅要保留,还需要在某些情况下显示出来,比如向右拖动图层时,左侧被黑色区域挡住的内容将显现出来,而中间的原显示内容将移动到右侧黑色区域中不可见。

由此,我们再次提出2个概念。

1.绘画区域,也即是可见的512x512区域被称作绘画区域,只有此区域是可见、可编辑、可导出的。
2.绘画板,也即是黑色区域被称作绘画板,默认2048*2048,存储了整张图像的所有颜色数据。

为此,定义一个用于表示画板的类Plate

    /// <summary>
    /// 绘画板
    /// </summary>
    public sealed class Plate
    {
        private Color[,] _colors;

        /// <summary>
        /// 宽度
        /// </summary>
        public int Width { get; private set; }
        /// <summary>
        /// 高度
        /// </summary>
        public int Height { get; private set; }
    }

二维数组_colors存储了整个画板中的颜色数据。

绘画区域、绘画板

绘画区域、绘画板都将单独用一个Plate(画板)表示,他们是独立存在的:

    /// <summary>
    /// 图层
    /// </summary>
    public sealed class TextureLayer
    {
    	//绘画板
        private Plate _plate;
        //绘画区域
        private Plate _region;
    }

绘画区域、绘画板的关系大概就是如下这样:
在这里插入图片描述

绘画区域锚点

此时,我们的图层在渲染图像时,会将绘画区域中的内容填充到渲染源(Texture2D),再绘制到屏幕上。

绘画区域中的内容,又是根据绘画区域所在的位置和尺寸,到整个绘画板中去提取颜色数据。

那么,如果给绘画区域一个锚点,则移动图层的功能,便可以直接实现为移动绘画区域锚点

    /// <summary>
    /// 图层
    /// </summary>
    public sealed class TextureLayer
    {
    	    /// <summary>
      		/// 绘画区域在绘画板中的偏移值(锚点)
      		/// </summary>
     	    public Vector2Int Offset { get; private set; }
		}

锚点(0,0),则代表左下角,锚点(1,1),则代表右上角。

在这里插入图片描述
在这里插入图片描述

导入图像

图层的绘画板作为其唯一存储颜色数据的目标,我们要实现导入一张外部图片,自动生成一个图层的功能,只需要读取外部图片,并填充到绘画板(_plate)中即可:

    /// <summary>
    /// 绘画板
    /// </summary>
    public sealed class Plate
    {
        private Color[,] _colors;

        /// <summary>
        /// 宽度
        /// </summary>
        public int Width { get; private set; }
        /// <summary>
        /// 高度
        /// </summary>
        public int Height { get; private set; }

        /// <summary>
        /// 绘画板
        /// </summary>
        /// <param name="width">宽度</param>
        /// <param name="height">高度</param>
        /// <param name="texture">导入的图像</param>
        public Plate(int width, int height, Texture2D texture = null)
        {
            _colors = new Color[width, height];
            if (texture != null)
            {
                if (texture.isReadable)
                {
                	//计算图片尺寸与绘画板尺寸的关系,使得图片始终于绘画板中居中
                    int offsetX = (width - texture.width) / 2;
                    if (offsetX < 0) offsetX = 0;
                    int offsetY = (height - texture.height) / 2;
                    if (offsetY < 0) offsetY = 0;

                    for (int x = 0; x < texture.width; x++)
                    {
                        for (int y = 0; y < texture.height; y++)
                        {
                            int pixelX = x + offsetX;
                            int pixelY = y + offsetY;
                            if (pixelX < width && pixelY < height)
                            {
                                _colors[pixelX, pixelY] = texture.GetPixel(x, y);
                            }
                        }
                    }
                }
                else
                {
                    Debug.LogError($"图像 {texture.name} 必须设置为可读写的,请在检视器面板勾选 Read/Write Enabled!");
                }
            }
            Width = width;
            Height = height;
        }
    }

同理的,在图层(TextureLayer)的构造方法中完成这一切:

    /// <summary>
    /// 图层
    /// </summary>
    public sealed class TextureLayer
    {
        private RectTransform _rectTransform;
        private Material _material;
        private RawImage _target;
        private Texture2D _texture;
        private Plate _plate;
        private Plate _region;

        /// <summary>
        /// 图层名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 是否显示图层
        /// </summary>
        public bool IsShow
        {
            get
            {
                return _target.enabled;
            }
            set
            {
                _target.enabled = value;
            }
        }
        /// <summary>
        /// 绘画区域在画板中的偏移值
        /// </summary>
        public Vector2Int Offset { get; private set; }

        /// <summary>
        /// 图层
        /// </summary>
        /// <param name="name">图层名称</param>
        /// <param name="paintAreaWidth">绘画区域宽度</param>
        /// <param name="paintAreaHeight">绘画区域高度</param>
        /// <param name="plateWidth">绘画板宽度</param>
        /// <param name="plateHeight">绘画板高度</param>
        /// <param name="parent">图层所属父级</param>
        /// <param name="texture">导入的图像</param>
        /// <param name="offset">偏移值</param>
        public TextureLayer(string name, int paintAreaWidth, int paintAreaHeight, int plateWidth, int plateHeight, RectTransform parent, Texture2D texture, Vector2Int offset = default)
        {
        	//创建图层的渲染对象
            _rectTransform = Utility.CreateRectTransform(name, parent, true);
            //创建材质
            _material = new Material(Utility.TextureLayerShader);
            _material.hideFlags = HideFlags.HideAndDontSave;
            //创建渲染器
            _target = _rectTransform.gameObject.AddComponent<RawImage>();
            _target.raycastTarget = true;
            _target.material = _material;
            //创建渲染源
            _texture = new Texture2D(paintAreaWidth, paintAreaHeight, TextureFormat.RGBA32, false);
            _texture.SetPixels(new Color[paintAreaWidth * paintAreaHeight]);
            _texture.wrapMode = TextureWrapMode.Clamp;
            _texture.name = Name;
            //创建绘画板、绘画区域
            _plate = new Plate(plateWidth, plateHeight, true, texture);
            _region = new Plate(paintAreaWidth, paintAreaHeight, false);
            //设置渲染源,此处没有采用直接设置为RawImage.texture的方式,因为在渲染Shader中,有一些我们自己的处理
            _material.SetTexture("_PaintTex", _texture);
            //设置初始偏移值(锚点),使得图像居中
            Offset = (offset != default) ? offset : new Vector2Int((_plate.Width - _region.Width) / 2, (_plate.Height - _region.Height) / 2);

            Name = name;
        }
    }

到这里,由于篇幅问题,图层(上)篇的内容暂时告一段落。


http://www.kler.cn/a/441228.html

相关文章:

  • < OS 有关> BaiduPCS-Go 程序的 菜单脚本 Script: BaiduPCS-Go.Menu.sh (bdgo.sh)
  • mysql.sock.lock 导致mysql重启失败
  • 【C++ 真题】P1706 全排列问题
  • 单路由及双路由端口映射指南
  • 【东雪莲病毒|罕见病毒|Traitor Virus】
  • 【信息系统项目管理师-选择真题】2010上半年综合知识答案和详解
  • 单词谜(详解版)
  • python —— 常用命令行的命令
  • JS逆向--反调试(SoJson为例)
  • 从构想到实现:EasyOne 多模态 AI 产品开发历程
  • 集成自然语言理解服务,让应用 “听得懂人话”
  • 解决Linux 虚拟机网段与虚拟机配置网段不一致
  • IP6822为智能手机提供无线充电方案的无线充电发射微控制SOC芯片
  • Day25 C++ 文件和流
  • SLM510A系列——24V,15到150mA单通道可调电流线性恒流LED驱动芯片
  • 浅说单调队列
  • java中输入输出流
  • vue3 父组件调用子组件 el-drawer 抽屉
  • linux 串口调试工具minicom使用详解
  • CSS基础与应用详解
  • 王佩丰24节Excel学习笔记——第十五讲:条件格式与公式
  • 浅谈Java注解之CachePut
  • springboot城镇保障性住房管理系统(代码+数据库+LW)
  • go语言使用websocket发送一条消息A,持续接收返回的消息
  • 代码随想录day21 | leetcode 669.修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树 二叉树总结篇
  • 【JavaEE】网络(4)