庄懂的TA笔记(十八)<特效:走马灯(序列帧) + 极坐标(UV转中心点)>

news2024/12/24 8:54:12

庄懂的TA笔记(十八)<特效:走马灯(序列帧) + 极坐标(UV转中心点)

大纲:

一、走马灯:序列帧

双通道,双Pass

二、极坐标:

三、分享:

正文:

一、走马灯:

0、前置 小知识点:

①、名词认识,符号认识:

Sequence = 序列

PolarCoord = Polar(极地的) + Coord(坐标)<Coordinated的简写>。

θ = 拼音,拼写,(XiTa) 代表含义,<温度,角度>。

参考连接:θ 百度百科 。

②、Subshader { } 的含义,不同LOD级别的回退。

通常手机游戏开发中,对不同机型做适配,会根据 高 中 低 配,写不同的优化程度的内容。

通常在 Subshader中写 LOD 500 ,LOD 100。

Subshader {

LOD 500

}

③、Subshader下的Tags,作用与整个shader。

④、Subshader下的Pass,可以有多个,也可以有不同名字不同LightMode,不同的混合模式(Blend One OneMinusSrcAlpha)

不同的名字有什么作用和好处呢?

例如A Pass写完了一个效果,然后里面内容完全不想改,可以通过UsePass(名字)索引到。

LightMode是什么意思呢?

在手机环境下用的都是"LightMode"="ForwardBase”,但在用URP 或HDRP时,是可以自己声明一个LightMode的,你的相机在渲染场景的时候,他会有一个模式,这个模式下只会渲染这个模式下的shader,例如,十二课写人物shader中,为什么你没有写关于阴影的事情,只是把他的.cginc包含进来,就有投影了呢,因为回退FallBack的shader里面,会有一个Pass,他的LightMode 是 ShaderCast.,就是产生阴影。

实际上就是,指定某个Tags,渲染阴影,而其他的Tags则不渲染阴影,和PPV机制类似。

特效的shader是不需要Fallback的 (因为特效不需要阴影),

人物是需要保留的 (因为人物需要阴影)。

1、案例展示:

双通道,双Pass实现。

2、实现思路:

①、双通道,双Pass:

复制AB的Pass 代码在第一层,(基础着色),

修改Pass下的Name为"FORWARD_AB"

复制AD的Pass代码在第二层,(序列帧着色)

修改Pass下的Name为"FORWARD_AD"

中间输出测试看一下,可以看到,普通的AB和 AB+AD的相比,AB+AD更亮一些。

那我们需要让AD更亮的,输出鬼火的序列。

②、声明参数:

声明一个序列帧图像,

_Sequence ("序列帧", 2d) = "gray"{}

声明一个行数,一个列数,因为序列帧贴图的行列是不同的,所以这里要让他们可变。

_RowCount ("行数", int) = 1

_ColCount ("列数", int) = 1

声明一个编号,用来记录 格子中每个图的变量。

_SequId ("序号",int) = 0

注意图像的 循环模式为Repeat。

③、实现AD的法线方向的挤出效果:

所以要在AD输入结构中 ,追加法线

float3 normal : Normal; //法线声明,挤出用。

在顶点shader中,用对象本地空间做挤出,那么本地空间顶点是谁呢,就是,v.vertex;

然后让他沿顶点方向,挤出一点距离怎么操作呢?

他的位置.xyz + 他自己的法线方向 * 挤出长度;

v.vertex.xyz += v.normal * 0.01;

(这里注意v.vertex4维的,v.normal3维的,所以v.vertex需要加上.xyz)

小知识:

这里也可以做成描边,现在是渲染正面,这里改成渲染反面就可以实现描边Cull Front.

描边的原理,也是挤出的思路~;

④、换算 序列UV, 实现序列图滚动。

视频里 庄佬 口头讲了很多,但是最终可以用一幅图来表达清楚他前期想要追加的所有概念。

行序号idU 0-4 = _RowCunt行数4

列序号idV 0-4 = _ColCunt列数4

_SequId序号:共16个

stepU (U轴步幅的长度1cm)

stepV (V轴步幅的长度1cm)

1、知道了行序号,和列序号,就可以确定序号 范围.

例如 序号9 ==id [V2] [U1]

2、初始位置偏移

idU 是我跳了几格(4), stepU步幅 是每格跳的长度(1cm)

idU * stepU = U轴向 的偏移 步数

4(偏移次数) * 1cm(偏移长度) = 4总偏移距离

公式:总偏移距离(因为要整除,准确对应图像位置) = 初始UV + float2(idU * stepU , idV * stepV);


3、代码中:获取 商数 (V轴列数) < 这里假设要拿到序号9>

求商 → idV = floor(序号 / 列数); 9/4=2(商) ... 1(余)

_SequId(序号数) / _ColCunt(列数) = idV (商数)

9 (序号) / 4(列数) = 2列 (商数) ...1(余数)

这里正好,把序号在队列中的位置表述出来9[U2][V1]

行号,和列号 都是从0开始算的.

float idV = floor(_SequId(序号总数) / _ColCunt(列数) );

floor的使用是代码使用中整除,取整的意思。


4、 获取 余数 (U轴行数)

求余 → idU = 序号 - 商 * 列数; 9 - 2*4 = 1 (余数)

float idU = _SequId - idV * _ColCunt;

这里取余的话就是 走公式就可以了。


5、获取步幅距离:

stepU(U轴步幅 距离) = 1(整张UV) / _ColCunt(4列数);

stepV(V轴步幅 距离) = 1(整张UV) / _RowCunt(3行数);

6、获取初始UV:

(小知识点:在shader中除法比较消耗内存,通常会在Edit编辑界面提前除好,在进入计算环节)

先缩小UV范围:根据前面计算好的U轴(stepU),和V轴(stepV) 步幅长度限定出范围。

initUV = o.uv * float2 (stepU,stepV);

然后设置初始位置:

因为程序运行贴图的机制,贴图原点通常在左下角,而不是左上角,所以这里需要将初始UV上移2格,也就是V轴上移2格。

initUV = o.uv * float2 (stepU,stepV) + float2(0.0 , stepV * (_RowCunt - 1 ));

这里开始没想明白为什么要 3(行数) -1 ,原因是,缩小后的UV本身也算一个单位所以要减去本身一个单位变成上移2个单位就正好是指定的位置了.

7、将上面计算出的 UV大小,和UV初始位置,赋给 o.uv;

o.uv = initUV - float2(idU * stepU , idV * stepV);

(注意:视频中,+ 号- 号,会影响 UV是上滚动,还是下滚动)

8、替换外部输入为随时间滚动:

先删除全局中的序号的变量(_SequId)

顶点shader中,声明一个局部 _SequId变量,并和_Time.x相乘,进行滚动,这里的_Speed范围给在(range(-10,10)),这样可控制,正向滚动,和负向滚动。

float _SequId = floor(_Time.y * _Speed);

视频中引用了下图的贴图进行滚动测试,这里在删除全局_SequId变量前记得先测试滚动顺序,以免发生视频中的问题。

9、回顾总结:

3、代码实现:

下列代码中庄懂老师改变了代码的结构,做了些优化,可能与上述的思路不太一致,可以做变式训练。

Shader "AP01/L18/Sequence" {
    Properties {
        _MainTex    ("RGB:颜色 A:透贴", 2d) = "gray"{}
        _Opacity    ("透明度", range(0, 1)) = 0.5
        _Sequence   ("序列帧", 2d) = "gray"{}
        _RowCount   ("行数", int) = 1
        _ColCount   ("列数", int) = 1
        _Speed      ("速度", range(0.0, 15.0)) = 1
        
    }
    SubShader {
        Tags {
            "Queue"="Transparent"               // 调整渲染顺序
            "RenderType"="Transparent"          // 对应改为Cutout
            "ForceNoShadowCasting"="True"       // 关闭阴影投射
            "IgnoreProjector"="True"            // 不响应投射器
        }

        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            Blend One OneMinusSrcAlpha          // 修改混合方式One/SrcAlpha OneMinusSrcAlpha
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            // 输入参数
            uniform sampler2D _MainTex;
            uniform half _Opacity;
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;       // 顶点位置 总是必要
                float2 uv : TEXCOORD0;          // UV信息 采样贴图用
            };
            // 输出结构
            struct VertexOutput {
                float4 pos : SV_POSITION;       // 顶点位置 总是必要
                float2 uv : TEXCOORD0;          // UV信息 采样贴图用
            };
            // 输入结构>>>顶点Shader>>>输出结构
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                    o.pos = UnityObjectToClipPos( v.vertex);    // 顶点位置 OS>CS
                    o.uv = v.uv;                                // UV信息 支持TilingOffset
                return o;
            }
            // 输出结构>>>像素
            half4 frag(VertexOutput i) : COLOR {
                half4 var_MainTex = tex2D(_MainTex, i.uv);      // 采样贴图 RGB颜色 A透贴
                half3 finalRGB = var_MainTex.rgb;
                half opacity = var_MainTex.a * _Opacity;
                return half4(finalRGB * opacity, opacity);                // 返回值
            }
            ENDCG
        }

        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            Blend One One          // 混合方式
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            // 输入参数
            uniform sampler2D _Sequence; uniform float4 _Sequence_ST;
            uniform half _Opacity;
            uniform half _RowCount;
            uniform half _ColCount;
            uniform half _Speed;
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;       // 顶点位置 总是必要
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;          // UV信息 采样贴图用
            };
            // 输出结构
            struct VertexOutput {
                float4 pos : SV_POSITION;       // 顶点位置 总是必要
                float2 uv : TEXCOORD0;          // UV信息 采样贴图用
            };
            // 输入结构>>>顶点Shader>>>输出结构
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                    v.vertex.xyz += v.normal * 0.03;            // 顶点位置法向挤出
                    o.pos = UnityObjectToClipPos(v.vertex);     // 顶点位置 OS>CS
                    o.uv = TRANSFORM_TEX(v.uv, _Sequence);      // 前置UV ST操作
                    float id = floor(_Time.z * _Speed);         // 计算序列id
                    float idV = floor(id / _ColCount);          // 计算V轴id
                    float idU = id - idV * _ColCount;           // 计算U轴id
                    float stepU = 1.0 / _ColCount;              // 计算U轴步幅
                    float stepV = 1.0 / _RowCount;              // 计算V轴步幅
                    float2 initUV = o.uv * float2(stepU, stepV) + float2(0.0, stepV * (_ColCount - 1.0));   // 计算初始UV
                    o.uv = initUV + float2(idU * stepU, idV * stepV);   // 计算序列帧UV
                return o;
            }
            // 输出结构>>>像素
            half4 frag(VertexOutput i) : COLOR {
                half4 var_Sequence = tex2D(_Sequence, i.uv);      // 采样贴图 RGB颜色 A透贴
                half3 finalRGB = var_Sequence.rgb;
                half opacity = var_Sequence.a * _Opacity;
                return half4(finalRGB * opacity, opacity);        // 返回值
            }
            ENDCG
        }
    }
}

①、面板参数定义:

②、输出结构:

③、顶点shader输入输出:

4、核心代码:

二、极坐标:

1、案例展示:

2、实现思路:

使用原理:

顶点shader下:传递 UV 和顶点瑟。

传递UV+传递顶点色

那为什么在顶点shader中只传 了 这两个就变成极坐标了呢?

那是因为我们在像素shader中做了极坐标的处理,那么为什么是在像素shader中做处理,而不是在顶点shader下做处理呢?

因为像素shader的UV是 在顶点shader中拿出UV做线性插值的, 线性插值,在笛卡尔坐标(横平竖直)UV是满足的,但是极坐标并不是笛卡尔坐标,是不能做线性插值的,所以你的极坐标的换算,只能在像素shader下去做。

过程:

换算UV中心点:

如何换算 UV呢,首先减去0.5,这是什么意思呢?

就相当于UV坐标的原点,往上挪0.5,0.5到图片中心位置(因为默认图片中心位置都在左下角),

实现极坐标中心定位,以图片中间滚动效果。

极坐标(高中知识),两个量,确定一个位置(角度 距中心长度)。

θ(theta,角度) = Y / X

极坐标角度的正切值 = Y / X ,相当于 U / V的值,

知道正切值 怎么换算 角度呢?

θ(theta角度) = atan2(i.uv.y , i.uv.x); // 笛卡尔坐标 换算成 极坐标

角度 ,距离

得出角度,角度的值域是 -拍,到 拍,那么现在要把值域换算成采样贴图的区间,需要把值域从,-拍,到拍转移到 0 到 1。

3、代码实现:

Shader "AP01/L18/PolarCoord" {
    Properties {
        _MainTex    ("RGB:颜色 A:透贴", 2d)   = "gray"{}
[HDR]   _Color      ("混合颜色", color)         = (1.0, 1.0, 1.0, 1.0)
        _Opacity    ("透明度", range(0, 1))     = 0.5
        
    }
    SubShader {
        Tags {
            "Queue"="Transparent"               // 调整渲染顺序
            "RenderType"="Transparent"          // 对应改为Cutout
            "ForceNoShadowCasting"="True"       // 关闭阴影投射
            "IgnoreProjector"="True"            // 不响应投射器
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            Blend One OneMinusSrcAlpha                       // 修改混合方式
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            // 输入参数
            uniform sampler2D _MainTex;
            uniform half _Opacity;
            uniform half3 _Color;
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;       // 顶点位置 总是必要
                float2 uv : TEXCOORD0;          // UV信息 采样贴图用
                float4 color : COLOR;
            };
            // 输出结构
            struct VertexOutput {
                float4 pos : SV_POSITION;       // 顶点位置 总是必要
                float2 uv : TEXCOORD0;          // UV信息 采样贴图用
                float4 color : COLOR;
            };
            // 输入结构>>>顶点Shader>>>输出结构
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                    o.pos = UnityObjectToClipPos( v.vertex);    // 顶点位置 OS>CS
                    o.uv = v.uv;                                // UV信息 支持TilingOffset
                    o.color = v.color;
                return o;
            }
            // 直角坐标转极坐标方法
            float2 RectToPolar(float2 uv, float2 centerUV) {
                uv = uv - centerUV;
                float theta = atan2(uv.y, uv.x);    // atan()值域[-π/2, π/2]一般不用; atan2()值域[-π, π]
                float r = length(uv);
                return float2(theta, r);
            }

            // 输出结构>>>像素
            half4 frag(VertexOutput i) : COLOR {
                // 直角坐标转极坐标
                float2 thetaR = RectToPolar(i.uv, float2(0.5, 0.5));
                // 极坐标转纹理采样UV
                float2 polarUV = float2(
                    thetaR.x / 3.141593 * 0.5 + 0.5,    // θ映射到[0, 1]
                    thetaR.y + frac(_Time.x * 3.0)      // r随时间流动
                );
                // 采样MainTex
                half4 var_MainTex = tex2D(_MainTex, polarUV);
                // 处理最终输出
                half3 finalRGB = (1 - var_MainTex.rgb) * _Color;
                half opacity = (1 - var_MainTex.r) * _Opacity * i.color.r;
                // 返回值
                return half4(finalRGB * opacity, opacity);
            }
            ENDCG
        }
    }
}

4、核心代码

5、代码示例:

三、分享:

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

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

相关文章

H3C交换机基于MAC的VLAN配置

配置需求或说明 1.1适用产品系列 本案例适用于如S7006、S7503E、S7506E、S7606、S10510、S10508等S7000、S7500E、S10500系列&#xff0c;且软件版本是V7的交换机 1.2配置需求及实现的效果 SWA和SWB的GE1/0/1分别连接两个会议室&#xff0c;PC1和PC2是会议用笔记本电脑&…

第八篇:强化学习值迭代及代码实现

你好&#xff0c;我是郭震&#xff08;zhenguo&#xff09; 前几天我们学习强化学习策略迭代&#xff0c;今天&#xff0c;强化学习第8篇&#xff1a;强化学习值迭代 值迭代是强化学习另一种求解方法&#xff0c;用于找到马尔可夫决策过程&#xff08;MDP&#xff09;中的最优值…

chatgpt赋能python:Python如何取两位小数?

Python如何取两位小数&#xff1f; 如果你是一个Python开发人员&#xff0c;想必你会遇到需要将数字取两位小数的情况。无论你是在处理金融数据&#xff0c;或者是在处理一些科学计算&#xff0c;都需要将结果保留到小数点后两位。在这篇文章中&#xff0c;我们将介绍如何在Py…

中国的互联网技术有多厉害?

1 很多人没有意识到&#xff0c;中国的互联网技术是相当厉害的。 给大家举几个例子。 我和朋友聊天的时候&#xff0c;手机上的app都在“侧耳倾听”&#xff0c;聊天的一些关键字很快就会出现在手机浏览器的搜索栏中。 携程会给我自动推荐景点&#xff0c;美团会给我推荐美食&…

大裁员继续,直到回归均值

作者| Mr.K 编辑| Emma 来源| 技术领导力(ID&#xff1a;jishulingdaoli) 关于裁员&#xff0c;不想再举个案&#xff0c;大家也都听烦了。还是给大家几个宏观数字吧。据专门追踪科技公司裁员人数的Layoffs.fyi网站统计&#xff0c;2023年以来&#xff0c;截至5月底&#xff…

chatgpt赋能python:Python断行:如何优雅地换行?

Python断行&#xff1a;如何优雅地换行&#xff1f; 简介 Python是一种直观、易于学习、优雅且精简的编程语言。但是&#xff0c;随着代码复杂度的增加&#xff0c;长行代码也变得越来越难以阅读。所以&#xff0c;如何正确地断行是编写整洁Python代码的关键之一。 为什么需…

Spark大数据处理学习笔记1.1 搭建Scala开发环境

文章目录 一、学习目标二、scala简介&#xff08;一&#xff09;Scala概述&#xff08;二&#xff09;函数式编程 三、windows上安装scala&#xff08;一&#xff09;到Scala官网下载Scala&#xff08;二&#xff09;安装Scala&#xff08;三&#xff09;配置Scala环境变量 四、…

前端——平台登录功能实战

这里写目录标题 一、登录界面1、新建LoginView.vue2、登录页面展示二、登录路由1、注册登录页面路由三、前端登录接口设计1、新建http.js2、新建user.js3、api.js四、登录页面调用登录接口五、前端配置路由守卫六、前端配置请求拦截器七、前端配置响应拦截器八、退出登录九、前…

简单易行的 Java 服务端生成动态 Word 文档下载

需求&#xff1a;某些合同&#xff0c;被制作成模板&#xff0c;以 Word 格式保存&#xff0c;输入相关的内容参数最终生成 Word 文档下载。这是企业级应用中很常见的需求。 解决方案&#xff1a;无非是模板技术&#xff0c;界定不变和变的内容&#xff0c;预留插值的标记&…

【最新计算机、电子毕业设计 本科 大专 设计+源码】

2022年 - 2023年 最新计算机、电子毕业设计 本科 大专 设计源码 下载前必看&#xff1a;纯小白教程&#xff0c;unity两种格式资源的使用方法&#xff0c;1打开现有项目、2导入package 大专毕设源码&#xff1a;数媒专业、计算机专业、电子专业通用50多款大专毕设小游戏【源码】…

一文说清Task及其调度问题

ask对于.NET的重要性毋庸置疑。通过最近的一些面试人员经历&#xff0c;发现很多人对与Task及其调度机制&#xff0c;以及线程和线程池之间的关系并没有清晰的认识。本文采用最简单的方式模拟了Task的实现&#xff0c;旨在说明Task是什么&#xff1f;它是如何被调度执行的&…

JUC源码分析:ReentrantLock

ReentrantLock进行上锁的流程如下图所示&#xff0c;我们将按照下面的流程分析ReentrantLock上锁的流程。 先进入ReentrantLock.lock方法。 再进入内部类NonfairSync的lock方法。 点击acquire方法进入AbstractQueuedSynchronizer.acquire方法。 进入tryAcquire方法回到Reentra…

【小林计网笔记】 IP篇

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 在这里插入图片描述 一、IP 基本认识1、IP的作用2、IP与MAC的关系 二、IP 地址的基础知识1、IP地址的定义2、IP地址的分类1、ABC类地址2、DE类地址3、IP地址分类的优…

rtthread系统中lwgps软件包的使用

开发环境&#xff1a;gd32f450开发板 嵌入式操作系统&#xff1a;rtthread 软件包&#xff1a;lwgps gps定位模块&#xff1a;正点原子ATK-1218-BD lwgps软件包的介绍&#xff1a;此项目是完成开源的lwgps与rt-thread的匹配。原工程地址&#xff1a;https://github.com/MaJerle…

linux 系统-备份与恢复

linux 系统-备份与恢复 基本介绍 实体机无法做快照&#xff0c;如果系统出现异常或者数据损坏&#xff0c;后果严重&#xff0c; 要重做系统&#xff0c;还会造成数据丢失。所以我们可以使用备份和恢复技术 linux 的备份和恢复很简单&#xff0c; 有两种方式&#xff1a; 把…

论文浅尝 | 通过对比学习优化用于命名实体识别的双编码器

笔记整理&#xff1a;陆星宇&#xff0c;东南大学硕士&#xff0c;研究方向为自然语言处理 链接&#xff1a;https://arxiv.org/abs/2208.14565 动机 命名实体识别&#xff08;NER&#xff09;是识别与命名实体相关的文本片段并将其分类到预定义的实体类型&#xff08;如人物、…

MegEngine 使用小技巧:用 mperf 进行安卓 opencl 算子的 roofline 分析

前言 roofline 分析是一种简单评估当前计算任务对当前平台计算/访存能力的利用情况的方法&#xff0c;可以帮助分析算子的优化方向和优化潜力。mperf 实现了安卓 mali/adreno 两种 gpu 平台的 roofline 分析能力&#xff0c;下面以 mali 平台为例&#xff0c;简单介绍一下操作步…

【操作系统】什么是用户态和内核态?用户态和内核态是如何切换的?

【操作系统】什么是用户态和内核态&#xff1f;用户态和内核态是如何切换的&#xff1f; 参考资料&#xff1a; 用户态到内核态切换 什么是用户态和内核态&#xff1f; 「操作系统」什么是用户态和内核态&#xff1f;为什么要区分 一、什么是用户态和内核态&#xff1f; 1.1、…

案例23:基于Java宿舍管理系统设计和实现开题报告

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Echarts的x轴调整间隔,可以用 xAxis数组子项的axisLabel.interval

Echarts的x轴调整间隔,可以用 xAxis数组子项的axisLabel.interval https://echarts.apache.org/zh/option.html#xAxis.axisLabel.interval xAxis.axisLabel.interval auto 默认值是’auto’ 可设置为 : number 或 Function 数字或函数 坐标轴刻度标签的显示间隔&#xff0c;…