1. 项目概述
做Unity开发这么多年,透明纹理这块说难不难,说简单也真容易翻车。标题里这句“靠谱可用”四个字,看着朴素,其实背后藏着不少坑——网上搜到的方案要么是七八年前的旧代码,要么只讲了图片不讲视频,要么在Built-in管线里好好的,切到URP就彻底失效。这篇文章把我实战验证过的做法完整写出来,包含图像和视频两条路径,Shader代码可以直接抄走,参数含义也一并说清楚。
这个需求的常见场景,一个是展厅里的全息投影屏,一个是虚拟演播室的背景大屏,还有游戏里的粒子特效、广告牌、UI特效复用。本质就一句话:让Unity 3D对象表面的纹理呈现透明效果,而不是把贴图里的黑色或白色区域硬生生显示出来。如果要给视频实现透明,还得额外处理视频播放器的透明通道问题。
适合谁看?刚接触Shader的新手能在这里搞懂渲染队列、混合模式这些基础概念,有经验的开发者可以直接跳到视频透明部分的方案选型,看我对VideoPlayer和RenderTexture几种实现方式的对比。从原理到踩坑,一次讲透。
2. 为什么透明纹理在Unity里不能直接用
2.1 默认材质的限制
很多初学者会直接把一张带透明区域的PNG拖到Standard材质上,然后发现完全不透明。原因很简单:Unity的Standard Shader默认走的是不透明渲染路径,它把纹理的RGB颜色读出来,Alpha通道直接丢弃了。你需要明确告诉渲染管线“这个物体要参与透明混合计算”,而不是指望引擎自动识别纹理里的透明像素。
在做实验的时候,你可以在材质球上把Surface Options里的Surface Type从Opaque改成Transparent,这样Standard材质也能表现出简单透明效果。但Standard材质的问题是性能开销偏大,内部包含大量PBR光照计算,对透明渲染来说很多计算是白做的。而且如果多个透明物体互相重叠,Standard材质的深度写入处理相当粗糙,经常出现排序错乱、相互遮挡穿帮的画面。
对于视频透明这种需要抠像或Alpha通道连续变化的场景,手写一个专门用于透明混合的Unlit Shader才是更值得走的路。这也是本文所有方案的基础。
2.2 渲染队列与ZWrite的底层逻辑
透明渲染在Unity里要理解两个核心概念:渲染队列(Render Queue)和深度写入(ZWrite)。
渲染队列决定一个物体在渲染管线中被提交的顺序。数值越小,越先被渲染。Unity预设了Background(1000)、Geometry(2000)、AlphaTest(2450)、Transparent(3000)、Overlay(4000)这几档。固体物体一般在Geometry,玻璃、粒子等透明物体放在Transparent。把透明物体放在后面渲染的理由很简单:普通不透明物体需要先画进深度缓冲,后面再画透明物体时,深度测试能正确遮挡住不该看到的部分。如果反过来,先画的透明物体会被后画的不透明物体直接覆盖掉,效果完全乱套。
ZWrite指是否在渲染时向深度缓冲写入数据。这里有一个看起来矛盾但实际很合理的经验:透明物体通常要关闭ZWrite。因为透明混合的意义是让后面的物体颜色透过前面的物体,如果开启ZWrite,前面透明物体会把自己的深度写进缓冲,挡在它后面的物体深度测试不过,就不会被渲染,透明的意义就没了。
但关掉ZWrite会带来排序问题。渲染管线是按物体中心到相机距离排序的,如果两个透明物体相交或缠绕,排序容易出错,出现“后面物体被前面物体错误遮挡”的异常。这个问题在粒子系统尤其常见。解决办法是尽量控制透明物体的重叠,比如广告牌之间保持间距,或者用一些技巧调整渲染顺序,而不是指望Shader一劳永逸。
还是用一个生活类比来解释吧。就像透过毛玻璃看后面的人,毛玻璃自己不把深度信息刻在墙上,后面的人才能被看见。但两块毛玻璃前后叠放,你先看哪块、后看哪块就有讲究了。
2.3 混合模式(Blend Mode)到底在混合什么
混合模式是透明效果的关键公式。Shader里常见的写法是:
Blend SrcAlpha OneMinusSrcAlpha这行代码要翻译成数学语言,意思是片元颜色乘以自身Alpha作为源颜色,目标缓冲颜色乘以(1减Alpha)作为目标颜色,两者相加,得到最终的屏幕颜色。当Alpha等于1时,完全显示当前像素,不透明;当Alpha等于0时,完全显示背景颜色,当前像素不可见;Alpha为0.5时,则各自贡献一半。
最终颜色 = 源颜色 × Alpha + 目标颜色 × (1 - Alpha)这就是标准透明度混合公式。理解了它,你就不会把“透明”理解为“看不见”。
还有两种额外情况要区分。Alpha Blend是标准的半透明混合,适合玻璃、烟雾、全息投影。Alpha Test则是直接裁剪,像素Alpha小于某个阈值(通常是0.5)直接丢,大于阈值就保留,完全不透明。边缘非常硬,没有过渡。这个适合树叶、铁丝网、头发这些需要完全不透明的“镂空”贴图,但锯齿明显,需要配合抗锯齿措施。
2.4 内置管线、URP、HDRP的差异
很多从老教程里抄的Shader代码,在URP项目里会出现紫粉色错误,因为内置管线的Shader结构变了。实际上URP不仅改变了Shader的组织形式,还改变了光照模型,Tags里需要额外声明渲染管线的标识。
具体差异点在于:
- 内置管线的Shader用
Pass块直接写,结构相对自由。 - URP里要用
HLSLPROGRAM块,配合UniversalRenderPipeline的引用库。 - URP的Shader Graph为美术同学提供了更友好的操作界面,连节点就行,不需要碰代码。
- HDRP更复杂,透明、贴花、体积光都是独立框架,不适合新手。
我一般建议,如果只是做透明纹理展示,而且项目是URP,优先用Shader Graph的Unlit Master节点,能省掉大量调试时间。但如果你需要完全掌控代码,或者项目用的是内置管线,下面这套手写Shader的方案更通用。
3. 图像纹理透明实操:一张图走通全流程
3.1 贴图导入设置的正确姿势
我踩过的第一个坑是贴图带Alpha但导入时被Unity强制压缩成不透明格式。导入任何带透明通道的图片,都要在Inspector面板检查以下设置:
- Alpha Is Transparency必须勾选。这个选项告诉Unity这张纹理的Alpha通道代表透明信息,而不是透明度贴图或金属度贴图。
- 如果图片是线性工作流的,注意sRGB选项。一般彩色贴图保持sRGB(Color)即可,法线贴图、数据贴图要设为None。
- 压缩格式建议用ASTC或ETC2,对透明通道支持比较完整。旧式RGBA16压缩格式在移动端表现不佳。
一个容易忽略的细节:Alpha通道的位深。如果你从Photoshop导出的是8位通道,而项目用了HDR渲染或某些特殊后处理,带渐变的Alpha边缘会出现色阶断层,产生丑陋的锯齿状半透明边缘。这种场景建议导出16位PNG或TGA。
3.2 最简Transparent Shader的完整实现
下面这个Shader是我在多个项目里实际用过的基础版本,核心代码不到三十行,但完成了渲染队列、深度写入、混合模式三个关键配置:
Shader "Custom/SimpleTransparentImage" { Properties { _MainTex ("Texture", 2D) = "white" {} _AlphaScale ("Alpha Scale", Range(0,1)) = 1.0 } SubShader { Tags { "Queue"="Transparent" "RenderType"="Transparent" } Blend SrcAlpha OneMinusSrcAlpha ZWrite Off Cull Off Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; float _AlphaScale; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); col.a *= _AlphaScale; return col; } ENDCG } } }几个细节值得说。_AlphaScale参数用来统一调整整张贴图的透明度,比如UI交互时做一个淡入淡出效果,不需要改贴图资源,调材质参数就行。Cull Off表示双面渲染,有些广告牌位移后法线方向反转,正面背对相机时不会被剔除。做透明效果时,通常建议双面渲染,因为单面剔除很容易出现背面视角下物体消失的意外。
实际使用中,把材质球的Shader切换为这个自定义Shader,再把带Alpha的贴图拖到_MainTex槽位,透明的部分就直接生效。
3.3 让透明边缘更平滑的抗锯齿技巧
很多人在这一步遇到问题:PNG中边缘的半透明渐变区域显示正常,但边缘一圈出现黑色或白色描边。原因通常是纹理压缩格式对Alpha通道做了有损处理,或者图片本身带了白色背景没有完全抠干净。
除掉重新处理图片之外,Shader层面还有一道防线,就是在采样之前对透明区域做模糊处理。但最实用的还是用Gamma校正来处理采样值的Gamma空间到线性空间的转换问题。边缘变黑最常见的原因就是贴图在sRGB空间被读取,而混合计算在线性空间执行,导致Alpha和RGB不匹配。解决方案:
col.rgb /= max(col.a, 0.001);这叫预乘Alpha修复,适合处理颜色边缘发暗的情况。使用时要考虑贴图到底是直通Alpha还是预乘Alpha,两者不能混用。普通的PNG是直通Alpha贴图,但某些渲染软件导出的是预乘格式,这时候就用上面的修复方式。
另一个常见需求是边缘羽化,也就是让硬度不同的边缘有柔和的过渡。用主纹理的Alpha作为输入,再用Smoothstep算法做一次边缘过渡:
float alpha = tex2D(_MainTex, i.uv).a; alpha = smoothstep(_EdgeThreshold - _EdgeSoftness, _EdgeThreshold + _EdgeSoftness, alpha);这个技巧适合制作公告牌、贴花、破坏效果。不过要留意,smoothstep在URP的节点里可用,但手写Shader要确保版本兼容。
3.4 透明物体的深度测试陷阱
透明物体默认ZWrite Off,但深度测试ZTest是可选的,通常是ZTest LEqual,保持默认即可。如果要实现某些特殊效果,比如透视墙后面显示轮廓或强制显示被遮挡的透明物体,可以设置为ZTest Always,但绝大多数业务场景不需要改这个。
真正麻烦的是自遮挡问题。比如一个透明圆环从侧面观察时,正面部分和背面部分重叠,因为关闭了ZWrite,前面像素的深度值不会挡住后面像素,前后部分依然同时参与混合,就会出现重复叠加的“发白”效果。解决方案有两个:
- 用
ZWrite On配合Queue=AlphaTest,模拟Alpha Test,但这样会失去半透明混合效果。 - 用两个Pass,先写深度再混合,也就是经典的“深度预写透明”技巧,适合表现单面物体的正确遮挡。
其实大部分图像透明场景不需要这么复杂。多叠加快照时确实会有问题,比如粒子系统的火焰叠加,但这是引擎级限制,而不是Shader能完全解决的。可以用减少重叠、调整相机远近裁剪等方式改善。
4. 视频透明纹理的实现方案
4.1 视频带不带Alpha通道,决定了走哪条路
视频透明和图像透明最大的区别在于视频源的编码方式。常见的H.264、H.265编码格式在常规情况下只存储YUV色彩数据,不支持Alpha通道。因此,一个视频文件即使看起来有透明需求,它在内部也不会保存透明度信息。行业内做视频透明通常只有两条路:
- 使用支持Alpha通道的编码格式,比如Apple ProRes 4444、VP9的alpha模式、某些WebM格式。
- 使用绿幕/蓝幕视频,在运行时通过Shader实时抠像,把绿色或蓝色背景移除。
第一种效果好但兼容性差,第二种兼容性好但对拍摄要求高,抠像边缘处理不好容易发绿或发毛。Unity的VideoPlayer在Android和iOS上格式支持不尽相同,有的系统根本不认ProRes,这就更让方案选型变得重要。
我这里推荐的稳妥路线是这样的:
- 如果是宣传片、产品展示,能拿到带Alpha的mov素材,优先走VP8/VP9的alpha方案,或是ProRes配合特定系统的解码。
- 如果视频素材是绿幕拍摄,走实时抠像路线,在Shader里把绿色区域的像素Alpha设为0,这个方案兼容性最广。
- 如果只是需要视频整体淡入淡出,不需要抠像,直接把视频当普通纹理渲染,用
_AlphaScale参数统一调节即可。
4.2 VideoPlayer与RenderTexture的基础流程
让VideoPlayer播放视频并传给材质,基础流程分三步:
- 场景中创建VideoPlayer组件,指定VideoClip或URL。
- 创建RenderTexture,将VideoPlayer的targetTexture指向这个RenderTexture。
- 材质的
_MainTex属性设为这个RenderTexture,Shader逻辑与图像透明完全一致。
关键代码:
using UnityEngine; using UnityEngine.Video; public class VideoTextureAssigner : MonoBehaviour { public VideoPlayer videoPlayer; public RenderTexture renderTexture; public Material targetMaterial; void Start() { if (videoPlayer != null) { videoPlayer.targetTexture = renderTexture; videoPlayer.Prepare(); videoPlayer.prepareCompleted += OnPrepared; } } void OnPrepared(VideoPlayer vp) { targetMaterial.mainTexture = renderTexture; vp.Play(); } }注意,一定要等Prepare完成后再给材质赋纹理。否则材质会拿到空RenderTexture,显示纯白或黑屏。另外RenderTexture的分辨率应尽量与视频原始分辨率匹配,不匹配时会做缩放,造成模糊。
4.3 绿幕视频的实时抠像Shader
绿幕视频的Shader抠像核心是色度键控。思路是检测像素颜色与背景色的色差,接近背景色的像素Alpha归零,否则保留完整前景色。一个简易实现:
Shader "Custom/ChromaKeyUnlit" { Properties { _MainTex ("Texture", 2D) = "white" {} _KeyColor ("Key Color", Color) = (0, 1, 0, 1) _Sensitivity ("Sensitivity", Range(0,1)) = 0.2 _Smoothness ("Smoothness", Range(0,1)) = 0.1 } SubShader { Tags { "Queue"="Transparent" "RenderType"="Transparent" } Blend SrcAlpha OneMinusSrcAlpha ZWrite Off Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; float4 _KeyColor; float _Sensitivity; float _Smoothness; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); float dist = distance(col.rgb, _KeyColor.rgb); float alpha = 1.0 - smoothstep(_Sensitivity, _Sensitivity + _Smoothness, dist); col.a *= alpha; return col; } ENDCG } } }实拍时绿幕的光照不均匀,会导致部分区域抠不干净。因此在实际生产环境中,这个Shader还要增加边缘保留、去溢色(Spill Suppression)等额外功能。去溢色的常用思路是让绿色通道值向红色和蓝色通道收敛,减小前景物体边缘的绿光污染:
// 去溢色简化实现 float spill = max(0, col.g - max(col.r, col.b)); col.g -= spill * _SpillStrength;如果抠像对象是人物,头发丝边缘尤其容易抠出绿色毛边。配合后处理或二次抠图,硬件够的话还可以用键控专用算法。但要注意,如果项目里跑的是Mobile平台或低端设备,这些额外的计算和纹理采样会造成性能压力。
4.4 VideoPlayer播放透明视频的兼容性经验
关于VP9或带Alpha WebM的播放,Unity在不同平台支持情况差异很大。Windows平台可以用VideoPlayer直接播放VP9带alpha的WebM,但Android上表现不稳,某些渠道包甚至直接无法解码。要保证全平台可用,我的经验是准备两套素材:
- 高清平台用带Alpha的WebM。
- 低端或兼容性差的平台用绿幕素材加Shade运行实时抠像。
如果你需要视频压缩到小体积,又想保留透明效果,可以从视频编码层面降低帧率或分辨率,透明视频体积通常是不透明视频的两到三倍。别把4K透明视频直接丢到移动端,卡成幻灯片是必然的。
关于RenderTexture设成透明格式,其实Unity的RenderTexture默认使用ARGB32,支持透明。但需要手动确认创建时指定RenderTextureFormat.ARGB32,并设置depthBuffer为0或16。如果用了DefaultFormat,在某些平台可能被解析成不透明格式,哪怕视频有Alpha,最终也看不到透明效果。
5. Shader Graph方式实现透明纹理(URP专用)
5.1 Unlit Master节点的配置要点
如果你的项目管线是URP,用Shader Graph来做透明纹理其实比手写Shader更省心。创建一个Unlit类型的Shader Graph,然后在Master节点上把Surface设置为Transparent,Blend Mode选Alpha,即完成了最核心的两步。
Graph里只需要加一个Sample Texture 2D节点,把纹理的RGBA输出连接到Master节点的Base Color和Alpha上。注意,如果只是连Base Color,透明通道不会自动生效,必须单独连Alpha。这是美术同学最容易漏掉的一步。
还要在Graph Inspector的Graph Settings里把Render Face设置为Both,确保双面渲染。如果透明物体要正确的深度接收,可以在Master节点里的Depth Write设置做调整,但一般不用动。
5.2 视频纹理接入Shader Graph的步骤
视频接入Shader Graph的逻辑依然不变:VideoPlayer输出RenderTexture,把RenderTexture指定给Shader Graph的纹理属性。
细节上要注意,Shader Graph里要勾选纹理属性的Alpha Is Transparency标签,大部分透明视频接入失败都是因为这里没有设置。如果纹理用线性空间采样,可能出现颜色比预期暗或亮的情况,记得在纹理导入设置里确认sRGB选项和项目色彩空间匹配。
5.3 从内置管线迁移到URP的注意事项
迁移是很多开发者头疼的问题。内置管线的标准Shader无法直接在URP中使用,Shader Graph节点图相对独立,迁移相对简单;但手写的CGPROGRAM代码几乎全部要重写为HLSL。
如果你的项目里有几十个手写的透明Shader,迁移到URP就非常痛苦。我建议在迁移时先评估一下:项目里的大部分效果,用URP提供的Lit/Unlit再加上Shader Graph能否覆盖?能覆盖的尽量用Shader Graph重做;不能覆盖的再单独重写HLSL。毕竟HLSL的Shader代码量不大,但需要调试的问题却很多。
6. 透明纹理的性能与排序优化
6.1 控制Overdraw的实用技巧
透明渲染的最大性能杀手是Overdraw,即同一像素被多次写入。GPU每做一次混合都要读写帧缓冲,像素被重叠越多次开销越大。全屏的粒子特效或大量透明物体叠加,移动端很容易发热掉帧。
实操中我常用这样的办法:
- 用Additive混合模式替代Alpha Blend,适合火焰、光晕、闪电等亮色特效,开销更低。
- 尽量缩小透明物体的投影面积,粒子数量控制得精细一点。
- 尽量少用全屏透明覆盖物,尤其是后处理特效叠加透明材质,这种组合非常费GPU。
- 注意分辨率,帧缓冲读写量与分辨率成正比,在低端机上做分辨率缩放能解决很大问题。
6.2 透明物体渲染顺序的正确管理
渲染顺序的坑集中在多个透明物体交叉时。Unity的默认排序方式是按物体包围盒中心到相机距离来排序的,开发者可以通过MaterialPropertyBlock设置renderQueue动态调整。
比如一个半透明窗户后面还有一个半透明玻璃瓶,窗户的渲染顺序应该在瓶子后面,但在特殊视角下引擎可能排反。这种时候,手动在代码里根据距离调整材质队列值,或者把透明物体拆分成两个独立的渲染批次,都能解决。
经验法则是:
- 对于不重叠的透明物体,不用操心排序。
- 对于有交互的透明物体,遵循大的先画、小的后画的原则。
- 同一材质合批可以提高性能,但不同的材质实例会破坏合批,别随意给透明材质不同实例,尤其是URP合批机制会更严格。
6.3 移动端透明贴图的格式与压缩建议
图片纹理透明在移动端不仅是一个效果问题,还关系到内存占比。推荐格式:
- iOS:ASTC 4x4或6x6。
- Android:ETC2。
- 老设备不支持ETC2时,要准备RGBA16的降级方案,但内存占用会大幅提升。
视频纹理本身不占用贴图内存,因为视频解码后到RenderTexture是YUV转换后的RGB数据。RenderTexture分辨率越高,占用的GPU内存越高。720P在移动端比较稳妥,1080P在主流手机上没问题,但4K的RenderTexture尽量不要在移动端使用,内存带宽撑不住。
7. 常见问题排查速查表与避坑技巧
7.1 故障现象与排查步骤
这里有张我在工作群里经常分享的速查表,基本涵盖了透明纹理最常见的故障:
| 故障现象 | 可能原因 | 解决方案 |
|---|---|---|
| 图片完全不透明 | Shader的Surface Type不是Transparent | 检查材质球的渲染模式,或自定义Shader的Blend设置 |
| 物体显示为全黑 | 渲染队列设置错误,透明物体在不透明物体之前渲染,且未采样纹理 | 确认Queue=Transparent,检查纹理槽位是否为空 |
| 边缘有黑/白边 | 贴图Alpha通道被压缩;或者色彩空间不一致 | 勾选Alpha Is Transparency,转预乘Alpha或开启预乘修复 |
| 多个透明物体排序错乱 | ZWrite Off后深度缓冲不生效 | 调整渲染队列或拆分重叠物体;或用深度预写Pass |
| 视频播放黑屏 | VideoPlayer未Prepare完成就赋值纹理 | 监听prepareCompleted回调后再给材质赋纹理 |
| 视频显示不透明 | RenderTexture格式不支持Alpha | 创建ARGB32格式的RenderTexture |
| 绿幕视频抠不干净 | 色键容差太小或光照不均 | 调大_Sensitivity与_Smoothness,增加去溢色 |
| URP下材质变紫 | 内置管线Shader代码不能在URP使用 | 改用Shader Graph或HLSL重写URP兼容Shader |
7.2 排查过程中的几个实用工具
Shader开发调试时,可以利用Frame Debugger逐Pass查看绘制顺序,在Window菜单下找到Frame Debugger。它能显示每个物体的渲染队列、Shader属性、深度状态,很多透明排序问题在这里一眼就能看出来。
采集Profiler里的SetPass Calls,如果发现透明材质的Pass数量异常多,大概率是合批失败或Shader分支太多,要考虑简化Shader。
另外建议多用Debug.Log输出屏幕坐标下的渲染信息,或临时在Shader里用#if UNITY_EDITOR调试代码输出特定像素值,快速定位是采样问题还是混合问题,能节省不少时间。
7.3 关于透明视频“版权与素材压制”的补充
透明视频素材相对小众,网上很多来源五花八门。自己拍绿幕素材时要特别注意光源,不要用手机直出视频,压缩过度会导致绿幕噪点巨大,抠像时会惨不忍睹。如果是甲方给的素材,一定要确认编码格式,至少提前半天做兼容性测试,不要等到上线当天才发现播放不了。
8. 最后的经验总结
坦白说,透明纹理这块的技术难度并不算高,真正的门槛在于对渲染管线和底层原理的理解。我自己在好几个项目里反复折腾,最后的体会是:
- 能用Shader Graph就用Shader Graph,尤其是URP项目,省下的调试时间可以干很多事。
- 手写Shader时,先把渲染队列、ZWrite、Blend这三板斧搞懂,其他都是细枝末节。
- 视频透明里最不靠谱的就是跨平台编码兼容性,方案设计时就把多个平台的回退路径想好,比等到测试阶段再救火强太多。
这篇文章里的代码我都在实际项目里跑过,你拿到项目里稍微调一下参数,基本能直接落地。如果遇到内置管线和URP迁移的问题,记住一点:Shader语法差异只是表面,底层的渲染状态配置是一样的,真正要对齐的是混合、深度、队列,而不是代码长什么样。
最后再分享一个实用小技巧:给透明物体做点击交互时,很多人忽略射线检测同样会被透明部分阻断。如果希望点击穿透透明区域,要额外处理Physics.Raycast的碰撞体,或者通过Shader的Alpha值在代码里做二次判断。这种细节问题,往往比渲染效果本身更影响体验。
遇到问题欢迎留言交流,我尽量回复。