Unity ColorSpace 之 【颜色空间】相关说明,以及【Linear】颜色校正 【Gamma】的简单整理

news2024/9/20 6:14:29

Unity ColorSpace 之 【颜色空间】相关说明,以及【Linear】颜色校正 【Gamma】的简单整理

目录

Unity ColorSpace 之 【颜色空间】相关说明,以及【Linear】颜色校正 【Gamma】的简单整理

一、简单介绍

二、在Unity中设置颜色空间

三、Unity中的Gamma和Linear颜色空间

1、Gamma颜色空间

2、Linear颜色空间

3、Gamma与Linear工作流程

四、Gamma 和 Linear 的差异简单分析

1、在 Gamma 颜色空间显示情况

2、 为什么 要把颜色空间设置为Linear呢

3、在 Linear 颜色空间显示情况

4、ColorSpace中的Linear和Gamma两种工作流程

5、sRGB及 结论

五、Linear 颜色校正 Gamma

六、关键代码


一、简单介绍

在图形渲染中,颜色空间(Color Space)是一个关键概念,它影响着最终图像的外观。在Unity中,理解并正确设置颜色空间可以显著提升图像质量。

颜色空间介绍 Unity 官网:Unity - Manual: Color space

Linear 和 Gamma 工作流介绍 Unity 官网:Unity - Manual: Linear or gamma workflow

颜色空间介绍 Unity 官网(中文):颜色空间 - Unity 手册

Linear 和 Gamma 工作流介绍 Unity 官网(中文):线性或伽马工作流程 - Unity 手册

  • 颜色空间的基本概念
  1. 颜色空间:颜色空间是颜色模型的具体实现,定义了特定条件下颜色的表示方式。常见的颜色空间包括sRGB和线性颜色空间。
  2. sRGB(标准RGB):一种常见的颜色空间,广泛应用于显示器、摄像机等设备。sRGB的色彩表现接近人类视觉感知,适合大多数显示设备。
  3. 线性颜色空间:一种颜色空间,颜色值与光强度成线性关系。适用于物理上更准确的光照计算。
  • Unity中的颜色空间

在Unity中,主要有两种颜色空间:Gamma和Linear。

  1. Gamma:默认的颜色空间,适用于较老的显示器和设备。Gamma颜色空间中,颜色值与显示器输出之间是非线性关系。Gamma空间是一种传统的颜色空间处理方式,它在许多旧的图形系统和软件中广泛使用。在Gamma空间中,颜色值(特别是亮度值)不是线性地映射到最终显示的颜色。这种非线性的映射是为了适应显示设备的特性,比如CRT显示器。然而,这种处理方式在现代图形处理中可能导致一些问题,比如不正确的光照计算和混合效果。
  2. Linear(线性):适用于现代高质量渲染,特别是在PBR(基于物理的渲染)中。线性颜色空间中,颜色值与显示器输出之间是线性关系,更符合物理现实。Linear空间(或称为物理颜色空间)是一种更现代的颜色处理方式。在Linear空间中,颜色值线性地映射到最终显示的颜色。这意味着光照计算和混合效果会更加准确,因为它们基于物理上正确的颜色处理。为了获得最佳效果,现代图形引擎和渲染管线通常使用Linear空间。

二、在Unity中设置颜色空间

1、打开Unity项目。

2、进入顶部菜单栏的Edit > Project Settings。

3、在Project Settings窗口中,选择Player。

4、在Player设置中,找到Other Settings部分。

5、在Color Space选项中,选择Linear或Gamma。

三、Unity中的Gamma和Linear颜色空间

颜色空间是影响游戏渲染效果的重要因素。理解Gamma和Linear颜色空间及其应用场景,能够帮助你更好地优化游戏的视觉效果。以下是对这两个颜色空间的详细介绍。

1、Gamma颜色空间

Gamma颜色空间中的颜色值与光的实际强度之间是非线性的关系。这种非线性关系通过一个称为Gamma校正的过程来实现,以使显示器上的图像看起来更自然。

特点

  • 视觉感知:人眼对亮度的感知是非线性的。例如,灰度值为128的像素并不会看起来是完全白色和完全黑色之间的中间色。Gamma校正使得图像显示符合人眼的感知特点​ (docs.unity3d)​。
  • 历史背景:传统的显示设备(如CRT显示器)使用Gamma校正,以便在有限的色彩深度(如8位每通道)下提供更好的视觉效果​ (docs.unity3d)​。
  • sRGB标准:Gamma颜色空间的标准是sRGB,这种标准广泛应用于图像和视频文件中。sRGB定义了一种从线性空间到Gamma空间的映射,最大化利用8位每通道的精度​ (docs.unity3d)​。

使用场景

  • 旧硬件:旧硬件通常仅支持Gamma颜色空间。
  • 快速开发:对于不追求极高渲染质量的项目,使用Gamma颜色空间可以简化开发流程。
2、Linear颜色空间

线性色空间中的颜色值与光的实际强度成线性关系。这意味着颜色值直接对应于光的物理强度。

特点

  • 物理精确性:线性色空间更接近真实世界的光照模型,适用于基于物理的渲染(PBR),可以提供更准确的光照和材质效果​ (docs.unity3d)​。
  • 渲染精度:在光照计算中,线性色空间可以提供数学上更精确的结果。例如,光的衰减和反射在线性色空间中表现更为真实​ (docs.unity3d)​。

使用场景

  • 现代高质量渲染:线性色空间适用于现代高质量渲染,特别是在高动态范围(HDR)渲染中。
  • PBR:基于物理的渲染需要使用线性色空间,以便准确模拟光照和材质的相互作用​
3、Gamma与Linear工作流程

3.1 Gamma工作流程

Gamma工作流程适用于传统的渲染需求,尤其是在不需要极高精度的场景中。Gamma颜色空间通过非线性转换,使得图像在8位色深下的显示效果更接近人眼感知。

  1. 颜色值的存储与显示:在Gamma空间中,颜色值经过Gamma校正后存储和显示。显示器在显示图像时,应用逆Gamma校正,使得图像符合人眼的感知。

    • 非线性转换公式其中 \gamma 通常约为2.2​ ​。
    • 存储方式:颜色值在存储时,已经过Gamma校正,以便在有限色深下提供更好的视觉效果。
  2. 光照计算

    • Gamma空间计算:在Gamma空间中,光照计算基于已经过Gamma校正的颜色值。尽管这简化了计算,但可能导致光照和阴影的表现不够精确。
    • 性能优势:Gamma工作流程通常计算量较小,适合资源有限的设备或较低性能的硬件​

3.2 Linear工作流程

 Linear工作流程提供更高的渲染精度,尤其适用于现代高质量渲染和基于物理的渲染(PBR)。在线性颜色空间中,颜色值与光的实际强度呈线性关系,这对于光照计算和材质表现非常重要。

  1. 颜色值的存储与显示

    • 线性存储:颜色值在存储时保持线性关系,这意味着存储的颜色值直接对应于光的物理强度。
    • Gamma校正显示:在显示器显示时,Unity会对线性颜色值进行Gamma校正,以便符合显示设备的需求​ 。
    • 转换公式其中\gamma通常约为 2.2​ ​。
  2. 光照计算

    • 线性空间计算:在Linear工作流程中,光照计算基于线性颜色值,这提供了更准确的光照和阴影表现。
    • 高动态范围(HDR):线性空间适用于HDR渲染,能够更好地表现高亮度和高对比度的场景​ 。

四、Gamma 和 Linear 的差异简单分析

有时会遇到下面的问题,就是把UI切好的图片放到Unity中,会发现有些颜色的差异,尤其是透明度混合方面会有很大的变化,这些都是由于Unity中颜色空间的设置问题,先给大家看一下Unity中的效果。

1、在 Gamma 颜色空间显示情况

用PS输出的70%透明度的纯白色的图片,和使用windows自带应用打开的图片没有任何区别,我们打开图片的设置,有一个sRGB选项,不管开启还是关闭这个选项,图片都不会有变。由于PS是使用Gamma空间进行图片制作和输出的,所以我们使用Gamma空间进行设置,可以得到一比一的效果。

sRGB选项:

2、 为什么 要把颜色空间设置为Linear呢

那既然我们显示都是正确的,我们为什么不直接使用Gamma空间,而是要把颜色空间设置为Linear呢,其中官网有这么一句:

线性强度响应

  • 使用伽马渲染时,提供给着色器的颜色和纹理已经应用了伽马校正。在着色器中使用它们时,高亮度的颜色实际上比预期亮度值更亮(相对于线性光照)。这意味着,随着光照强度的增加,表面会以非线性方式变亮。这将导致许多位置的光照亮度过高。此外还可能给模型和场景带来褪色的感觉。
  • 使用线性渲染时,在表面上产生的响应随着光照强度增加仍保持线性。因此,带来的表面着色真实得多,表面产生的颜色响应也好得多。

3、在 Linear 颜色空间显示情况

 所以我们在烘焙3d场景,或布置灯光时更倾向于选择Linear颜色空间得到最精确的结果,但是如果我们把颜色空间切换为Linear,就会发现UI有了一些不一样的变化,会发现颜色变亮浅了,效果如下

4、ColorSpace中的Linear和Gamma两种工作流程

对于我们PS输出的图片,如果我们不做特殊的设置,那么我们的图片就是在Gamma空间下进行输出和制作的,下面有一个很关键的知识点,就是两个颜色空间的透明度混合公式:

1)Linear Color Space

ret = (srcColor^2.2 * srcAlpha + dstColor^2.2 * (1 - srcAlpha) ) ^(1/2.2)

2)Gamma Color Space

ret = srcColor * srcAlpha + dstColor * (1 - srcAlpha)

值得注意的是:

  • 对于美术人员来说,他们在PS中通过透明度混合得到的最终效果,是使用Gamma空间下的计算公式进行计算得到的结果
  • Unity中的Linear Color Space下却是使用第一个公式进行处理的,我们要做的就是要是用Linear下的公式,通过一些变化,让他得到与Gamma下相同的结果

5、sRGB及 结论

图片属性上的sRGB,对于点了sRGB属性的图片,Unity会默认对其做一次变暗的操作,也就是2.2次幂,也就是流程图上写的Remove Gamma Correction,通过这个操作,会得到Gamma1.0空间下的颜色,然后将得到的颜色放入Shader中处理(Shader默认使用的是Linear Color Space的混合公式)。

经过对上图两个公式分析可得,如果我们把UI的图片的sRGB取消,那么就可以让Remove Gamma Correction过程忽略掉,也就是把Gamma0.45下的颜色直接带入的透明度公式,也就变成了下图公式

ret = (srcColor^0.45^2.2 * srcAlpha + dstColor^0.45^2.2 * (1 - srcAlpha) ) ^(1/2.2)
    =  (srcColor * srcAlpha + dstColor * (1 - srcAlpha) ) ^(1/2.2)

比较可知,这个 公式和 Gamma 空间下的透明度混合公式,只差一个2.2次方,所以我们只需要增加一个摄像机的后处理,将UI摄像机的结果进行一个2.2次方即可达到UI最终的效果。(PS:对于项目中的UI的Canvas我们会使用Scene Camera的模式,这样就可以在对应摄像机上添加后处理,也可以增加各种不同的UI特效)

五、Linear 颜色校正 Gamma

注意事项:

  • 图片sRGB要关闭掉
  • 如果UI打了图集,图集的sRGB也要关闭掉

1、创建脚本 UICameraLinearCorrect 、UILinearCorrectShader

2、然后再 Resources 文件夹下创建对应的材质

3、然后把 UICameraLinearCorrect 挂载到camera 上

4、运行场景,发现图片显示正常了,但是由于我们把整体颜色调暗了,所以场景中的物体的颜色也会变暗

可以发现场景中的UI颜色确实还原了,但是红色方块的颜色变化了 ,所以为了让场景颜色还原,我们需要再添加一个摄像机,将这个摄像机只看场景,原有的摄像机只看UI,同时将场景摄像机的颜色进行一个0.45次幂,还原他原来的颜色

5、创建 UICamera ,把 UICameraLinearCorrect 脚本挂载到上面,并且设置 camera 如图

6、创建脚本 SceneCameraLinearCorrect、SceneLinearCorrectShader

7、然后再 Resources 文件夹下创建对应的材质

8、把 SceneCameraLinearCorrect 挂载到 Main Camera 上,设置 Main Camera 如图

9、运行场景,这时 UI 显示正常,且 场景显示也正常了

六、关键代码

1、UICameraLinearCorrect

using UnityEngine;

/// <summary>
/// UI Canvas  线性矫正
/// </summary>
public class UICameraLinearCorrect : MonoBehaviour
{
    private Material mat;
    private Material RenderMat
    {
        get
        {
            if (mat == null)
            {
                mat = Resources.Load<Material>("Materials/UILinearCorrectMat");
            }
            return mat;
        }
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination, RenderMat);
    }
}

2、SceneCameraLinearCorrect


using UnityEngine;

/// <summary>
/// 场景camera 线性矫正
/// </summary>
public class SceneCameraLinearCorrect : MonoBehaviour
{
    private Material mat;
    private Material RenderMat
    {
        get
        {
            if (mat == null)
            {
                mat = Resources.Load<Material>("Materials/SceneLinearCorrectMat");
            }
            return mat;
        }
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination, RenderMat);
    }
}

3、UILinearCorrectShader

Shader "CustomShader/UILinearCorrectShader"
{
    Properties
    {
        _MainTex("Main Tex", 2D) = "white"{}
    }
        SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 100
        //Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            Cull Off ZWrite Off ZTest Always
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            sampler2D _MainTex;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };


            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 color = tex2D(_MainTex, i.uv);
                color = pow(color, 2.2);
                return color;
            }
            ENDCG
        }
    }
}

4、SceneLinearCorrectShader

Shader "CustomShader/SceneLinearCorrectShader"
{
    Properties
    {
        _MainTex("Main Tex", 2D) = "white"{}
    }
        SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 100
        //Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            // 制作后处理shader的时候要把这几个属性设置上
            Cull Off ZWrite Off ZTest Always
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            sampler2D _MainTex;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };


            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 color = tex2D(_MainTex, i.uv);
                color = pow(color, 1.0 / 2.2); // 1/2.2=0.45454545
                return color;
            }
            ENDCG
        }
    }
}

七、整理一些结论

  • 伽马校正:pow(color, 1/2.2), 变得更亮了,例如:color值为0.5,校正后是0.5^(1/2.2) = 0.7297
  • 移除伽马校正:pow(color, 2.2),变暗了,例如color值是0.7297,移除校正后是0.7297^2.2 = 0.5
  1. 当ProjectSetting中,选择Gamma 颜色空间时,那么Unity不会对输入的texture移除伽马校正,也不会在输出到屏幕时进行伽马校正,所有颜色都在Gamma空间中计算,最后输入到接受gamma颜色空间的屏幕上,屏幕设备会进行移除伽马校正。
  2. 当ProjectSetting中,选择Linear 颜色空间时,Unity会对勾选了sRGB的texture移除伽马校正,从而把贴图颜色转换到线性空间,在线性空间中进行一系列计算后,输出屏幕前,进行一次伽马校正,转换到gamma空间中,最后输入到接受gamma颜色空间的屏幕上,屏幕设备会进行移除伽马校正。
  3. 一般我们物体固有色的贴图都是在Gamma空间下的,而法线贴图,细节贴图等,则一般是线性的,当选择Linear空间时,固有色贴图需要勾选sRGB进行移除伽马校正,而法线贴图等则不需要勾选,因为它们本身就是线性的

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vortex GPGPU的硬件代码分析(Cache篇2)

文章目录 前言一、VX_cache.sv代码部分解读2——buffering/initialize1.1 core response buffering与VX_elastic_buffer模块解读1.1.1 VX_pipe_buffer模块解读1.1.1.1 一种握手信号的解释1.1.1.2 世界线收束——VX_pipe_buffer的核心代码解释1.1.1.3 VX_pipe_register模块解读与…

算法015:串联所有单词的子串

串联所有单词的子串. - 备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/substring-with-concatenation-of-all-words/ 如果是第一次接触这个题目&#xff0c;接触滑动…

埋点系统如何统计用户的平均停留时长?

Hello&#xff0c;大家好&#xff0c;欢迎使用Webfunny前端监控和埋点系统。 今天&#xff0c;我们将介绍webfunny的埋点系统如何统计用户的平均停留时长 一、页面beforeLeave事件 当你页面离开的时候&#xff0c;会触发一个心跳检测&#xff0c;但是这个可能不是100%触发&am…

跳表的简单学习

跳表&#xff08;SkipList&#xff09;学习 1. 什么是跳表&#xff1f; 基于“空间换时间”思想&#xff0c;通过给链表建立索引&#xff0c;使得链表能够实现二分查找。 跳表是可以实现二分查找的有序链表。 2. 从单链表到跳表 对于一般的单链表&#xff0c;在其中进行查…

EasyCVR视频技术:城市电力抢险的“千里眼”,助力抢险可视化

随着城市化进程的加速和电力需求的不断增长&#xff0c;电力系统的稳定运行对于城市的正常运转至关重要。然而&#xff0c;自然灾害、设备故障等因素常常导致电力中断&#xff0c;给城市居民的生活和企业的生产带来严重影响。在这种情况下&#xff0c;快速、高效的电力抢险工作…

【PVE】新增2.5G网卡作为主网卡暨iperf测速流程

【PVE】新增2.5G网卡作为主网卡暨iperf测速流程 新增网卡 新增网卡的首先当然需要关闭PVE母机&#xff0c;把新网卡插上&#xff0c;我用淘宝遥现金搞了个红包&#xff0c;花了26元买了块SSU的2.5G网卡。说实话这个价位连散热片都没有&#xff0c;确实挺丐的。稍后测下速度看…

从零开始做题:segmentFlow

题目 解题 import string import binascii def Crack_4B(crc_list):print(-------------Start Crack CRC 4B-------------)#crc_list [0xc0a3a573, 0x3cb6ab1c, 0x85bb0ad4, 0xf4fde00b]#文件的CRC32值列表&#xff0c;注意顺序comment chars string.printableflag 0 for …

文心一言的流式接口数据进行处理 增加属性

需求&#xff1a;需要对文心一言的流式接口数据进行处理 增加属性 return ResponseEntity.ok().header("Access-Control-Allow-Origin", "*").contentType(org.springframework.http.MediaType.TEXT_EVENT_STREAM).cacheControl(org.springframework.http…

Leetcode3201. 找出有效子序列的最大长度 I

Every day a Leetcode 题目来源&#xff1a;3201. 找出有效子序列的最大长度 I 解法1&#xff1a;枚举 全奇数、全偶数、奇偶交替三种情况的最大值即为所求。 代码&#xff1a; /** lc appleetcode.cn id3201 langcpp** [3201] 找出有效子序列的最大长度 I*/// lc codesta…

imx6ull/linux应用编程学习(16)emqx ,mqtt创建连接mqtt.fx

在很多项目中都需要自己的私人服务器&#xff0c;以保证数据的隐私性&#xff0c;这里我用的是emqx。 1.进入emqx官网 EMQX&#xff1a;用于物联网、车联网和工业物联网的企业级 MQTT 平台 点击试用cloud 申请成功后可得&#xff1a;&#xff08;右边的忽略&#xff09; 进入…

【matlab】大数据基础与应用实例

目录 引言 线性回归模型 基本形式 最小二乘法 多元线性回归 线性回归的假设 模型评估 应用 独热编码 原理 应用场景 优点 缺点 数据收集 数据可视化 数据处理与分析 完整代码 引言 线性回归模型 线性回归模型是一种用于预测连续值输出&#xff08;或称为因变…

「iOS」暑假第一周 —— ZARA的仿写

暑假第一周 ZARA的仿写 文章目录 暑假第一周 ZARA的仿写写在前面viewDidLoad 之中的优先级添加自定义字体下载想要的字体添加至info之中找到字体名字并应用 添加应用图标和启动页面 写在前面 暑假第一周留校学习&#xff0c;对于ZARA进行了仿写&#xff0c;在仿写的过程之中&a…

WPF学习(2) -- 样式基础

一、代码 <Window x:Class"学习.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/expression/blend/2008&…

计算机网络之网络互连

1.什么是网络互连 1.1网络互连的目的 将两个或者两个以上具有独立自治能力的计算机网络连接起来&#xff0c;实现数据流通&#xff0c;扩大资源共享范围&#xff0c;或者容纳更多用户。 网络互连包括&#xff1a; 同构网络、异构网络的互连&#xff0c; 局域网与局域网&…

【操作系统】文件管理——文件管理基础、文件的逻辑结构和目录结构(个人笔记)

学习日期&#xff1a;2024.7.14 内容摘要&#xff1a;文件管理基础、文件的逻辑结构、文件目录 文件管理基础 引言 文件是一组有意义的信息/数据的集合&#xff0c;计算机中存放着各种各样的文件&#xff0c;一个文件有哪些属性&#xff1f; 操作系统应该向上层的应用程序提…

网络 闲聊

闲谈 闲话 网络安全——>网络空间安全 网络空间&#xff1a;一个由信息基础设备组成互相依赖的网络 继&#xff1a;海、陆、空、天、的第五大空间 信息安全的一个发展&#xff1a; 通信保密阶段---计算机安全---信息系统安全---网络空间安全 棱镜门事件 棱镜计划&…

leetcode日记(38)字母异位词分组

最开始的想法是创建vector<vector<string>> result&#xff0c;然后遍历strs中字符串&#xff0c;遍历result中vector&#xff0c;比较vector中第一个string和strs中string&#xff0c;若为字母异位词&#xff0c;则加入vector&#xff0c;若无&#xff0c;则创建新…

数学基础 三角函数、两条平行线截距

三角函数变化公式 已知两条平行线&#xff0c;其中一条的起始点p1&#xff0c;p2&#xff0c;其中一条直线 p3&#xff0c;p4计算两条直线之间的截距 在二维平面上&#xff0c;当我们说“两条直线之间的截距”时&#xff0c;这通常意味着我们需要找到一条与这两条直线都相交的…

鸿蒙语言基础类库:【@system.battery (电量信息)】

电量信息 说明&#xff1a; 从API Version 6开始&#xff0c;该接口不再维护&#xff0c;推荐使用新接口[ohos.batteryInfo]。本模块首批接口从API version 3开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import battery from syste…

8款值得收藏的App推荐!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/ 值得一试的大众APP&#xff0c;它可能会给你的生活带来小小的改变。把下面的内容看完&#xff0c;我确信你一定会收获不少。 一、Todo清单——重…