【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;
        }
    }

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/938884.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

贪心算法 part01

class Solution { public:int maxSubArray(vector<int>& nums) {int result INT32_MIN;int count 0;for (int i 0; i < nums.size(); i) {count nums[i];if (count > result) { // 取区间累计的最大值&#xff08;相当于不断确定最大子序终止位置&#xff…

二、FIFO缓存

FIFO缓存 1.FIFO缓存介绍2.FIFO缓存实现3.FIFO缓存总结 1.FIFO缓存介绍 FIFO&#xff08;First-In-First-Out&#xff09;缓存 是一种简单的缓存淘汰策略&#xff0c;它基于先进先出的原则来管理数据。当缓存达到容量限制并需要淘汰元素时&#xff0c;最先进入缓存的元素会被移…

王佩丰24节Excel学习笔记——第十四讲:日期函数

【以 Excel2010 系列学习&#xff0c;用 Office LTSC 专业增强版 2021 实践】 【本章小技巧】 掌握date()日期函数&#xff0c;配合年月日时分秒使用使用datedif()函数计算两个日期之前的差&#xff0c;重点记住参数三&#xff0c;差的值以哪种类型显示。使用weeknum/weekday,…

python--在服务器上面创建conda环境

今天刚开始使用服务器的时候使用上面的公共环境发现老师缺少模块&#xff0c; [guoyupingcins195 ~]$ conda --version Traceback (most recent call last): File "/home/miniconda3/bin/conda", line 12, in <module> from conda.cli import main Fil…

Trimble天宝三维激光扫描仪在建筑工程竣工测量中的应用【沪敖3D】

竣工测量是建筑项目竣工阶段的一个至关重要的环节&#xff0c;它为建筑工程的质量验收和成果核查提供了核心的参考依据。传统的竣工测量方法&#xff0c;如全站仪测量&#xff0c;主要依赖于现场人工操作&#xff0c;存在一些明显的局限性&#xff0c;例如作业时间长、工作量大…

SEO初学者-搜索引擎如何工作

搜索引擎基础搜索引擎是如何建立索引的搜索引擎如何对网页进行排名搜索引擎是如何个性化搜索结果的 搜索引擎的工作方式是使用网络爬虫抓取数十亿个页面。爬虫也称为蜘蛛或机器人&#xff0c;它们在网络上导航并跟踪链接以查找新页面。然后&#xff0c;这些页面会被添加到搜索引…

react中实现导出excel文件

react中实现导出excel文件 一、安装依赖二、实现导出功能三、自定义列标题四、设置列宽度五、样式优化1、安装扩展库2、设置样式3、扩展样式功能 在 React 项目中实现点击按钮后导出数据为 Excel 文件&#xff0c;可以使用 xlsx 和 file-saver 这两个库。 一、安装依赖 在项目…

Latex中表格添加底部文本注释并调整对齐

如何实现从第一个表到第三个表的转换&#xff0c; 其中主要涉及到两点&#xff1a; &#xff08;1&#xff09;底部脚注与表格自动对齐并缩进换行 &#xff08;2&#xff09;表格自适应页面宽度 底部脚注的对齐与换行缩进需要用到 \usepackage{threeparttable} \usepackage{…

MySQL基础 -----MySQL数据类型

目录 INT类型 tinyint类型 类型大小范围 测试tinyint类型数据 float类型 测试&#xff1a; 测试正常数据范围的数据 测试插入范围超过临界值的数据&#xff1a; 测试float类型的四舍五入 ​编辑 decimal类型 同样测试&#xff1a; 字符串类型 char类型 测试&…

【HarmonyOS NEXT】Web 组件的基础用法以及 H5 侧与原生侧的双向数据通讯

关键词&#xff1a;鸿蒙、ArkTs、Web组件、通讯、数据 官方文档Web组件用法介绍&#xff1a;文档中心 Web 组件加载沙箱中页面可参考我的另一篇文章&#xff1a;【HarmonyOS NEXT】 如何将rawfile中文件复制到沙箱中_鸿蒙rawfile 复制到沙箱-CSDN博客 目录 如何在鸿蒙应用中加…

ONES 功能上新|ONES Copilot、ONES Wiki 新功能一览

ONES Copilot 可基于工作项的标题、描述、属性信息&#xff0c;对工作项产生的动态和评论生成总结。 针对不同类型的工作项&#xff0c;总结输出的内容有对应的侧重点。 应用场景&#xff1a; 在一些流程步骤复杂、上下游参与成员角色丰富的场景中&#xff0c;工作项动态往往会…

使用qemu搭建armv7嵌入式开发环境

目录 目录 1 概述 2 环境准备 2.1 vexpress系列开发板介绍 2.2 安装工具 2.2.1 安装交叉工具链 2.2.2 安装qemu 2.2.3 安装其他工具 3 启动uboot 3.1 uboot下载与编译 3.1.1 下载 3.1.2 编译 3.2 使用qemu启动uboot 4 启动kernel 4.1 下载和编译kernel 4.1.1 下…

28.操作数据库

第三方库pymysql 使用安装命令 pip install pymysql 连接数据库、选择库、获取游标&#xff0c;执行创建表语句 from pymysql import Connection# 获取到mysql数据库连接对象 conn Connection(host"localhost", passwd"123456", user"root", …

docker(wsl)命令 帮助文档

WSL wsl使用教程 wsl -l -v 列出所有已安装的 Linux 发行版 wsl -t Ubuntu-22.04 --shutdown 关闭所有正在运行的WSL发行版。如果你只想关闭特定的发行版 wsl -d Ubuntu-22.04 登录到Ubuntu环境 wsl --list --running 查看正在wsl中运行的linux发行版 wsl --unregister (系统名…

JVM系列之内存区域

每日禅语 有一位年轻和尚&#xff0c;一心求道&#xff0c;多年苦修参禅&#xff0c;但一直没有开悟。有一天&#xff0c;他打听到深山中有一古寺&#xff0c;住持和尚修炼圆通&#xff0c;是得道高僧。于是&#xff0c;年轻和尚打点行装&#xff0c;跋山涉水&#xff0c;千辛万…

【ADS射频电路学习笔记】2.阻抗匹配电路设计

本节课学习smith圆图匹配 1.史密斯圆图各功能介绍 首先调出s参数的控件 并增加两个端口 调出smith chart matching的控件 连接好端口在ADS中&#xff0c;默认是从负载端&#xff08;term2&#xff09;向源端&#xff08;term1&#xff09;做匹配的。 调节s参数控件的的频率扫…

springcloud-gateway获取应用响应信息乱码

客户端通过springcloud gateway跳转访问tongweb上的应用&#xff0c;接口响应信息乱码。使用postman直接访问tongweb上的应用&#xff0c;响应信息显示正常。 用户gateway中自定义了实现GlobalFilter的Filter类&#xff0c;在该类中获取了上游应用接口的响应信息&#xff0c;直…

泷羽sec学习打卡-brupsuite8伪造IP和爬虫审计

声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都 与本人无关,切莫逾越法律红线,否则后果自负 关于brupsuite的那些事儿-Brup-FaskIP 伪造IP配置环境brupsuite导入配置1、扩展中先配置python环境2、安…

【优选算法---分治】快速排序三路划分(颜色分类、快速排序、数组第K大的元素、数组中最小的K个元素)

一、颜色分类 题目链接: 75. 颜色分类 - 力扣&#xff08;LeetCode&#xff09; 题目介绍&#xff1a; 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums &#xff0c;原地 对它们进行排序&#xff0c;使得相同颜色的元素相邻&#xff0c;并按照红色、白色、蓝色顺序…

【译】仅有 Text2SQL 是不够的: 用 TAG 统一人工智能和数据库

原文地址&#xff1a;Text2SQL is Not Enough: Unifying AI and Databases with TAG 摘要 通过数据库为自然语言问题提供服务的人工智能系统有望释放出巨大的价值。此类系统可让用户利用语言模型&#xff08;LM&#xff09;的强大推理和知识能力&#xff0c;以及数据管理系统…