使用Unity 2017.3
我有一个RawImage,我在其中显示一系列作为Texture2D加载的图像。完美无缝地工作。
然后我使用示例VideoPlayer代码将视频显示到同一个RawImage中,然后分配
rawImage.texture = videoPlayer.texture;
视频播放效果非常好,但作为从静止图像切换到视频的一部分,RawImage中有明显的闪烁,就好像显示了一帧或两帧黑色一样。视频的第一帧与我显示的最后一个静态图像相匹配,因此我原本期望过渡非常无缝。
请注意,视频已经准备好了#34;在所有这些之前 - 我的代码yield
直到videoPlayer.isPrepared
返回true
,然后告诉视频播放并设置纹理。
我想也许有一个问题纹理还没有完全准备好,但我在调用Play之前和设置纹理之前尝试过一次或两次,但这对闪烁没有任何影响。
我看到这个项目:https://answers.unity.com/questions/1294011/raw-image-flickering-when-texture-changed.html表明这与设置的材质实例有关。我不完全理解答案中提出的解决方案,也不了解我如何使其适应我自己的案例,但也许对于那些比我更熟练的人来说意味着什么。
关于如何摆脱那个flickery框架的任何建议?
编辑:这是代码
public class VideoAnimation : MonoBehaviour, IAnimation {
private VideoPlayer _videoPlayer;
private UnityAction _completeAction;
private bool _prepareStarted;
public void Configure(VideoClip clip, bool looping)
{
_videoPlayer = gameObject.AddComponent<VideoPlayer>();
_videoPlayer.playOnAwake = false;
_videoPlayer.isLooping = looping;
_videoPlayer.source = VideoSource.VideoClip;
_videoPlayer.clip = clip;
_videoPlayer.skipOnDrop = true;
}
public void Prepare()
{
_prepareStarted = true;
_videoPlayer.Prepare();
}
public void Begin(RawImage destImage, UnityAction completeAction)
{
_completeAction = completeAction;
_videoPlayer.loopPointReached += OnLoopPointReached;
StartCoroutine(GetVideoPlaying(destImage));
}
private IEnumerator GetVideoPlaying(RawImage destImage)
{
if (!_prepareStarted)
{
_videoPlayer.Prepare();
}
while(!_videoPlayer.isPrepared)
{
yield return null;
}
_videoPlayer.Play();
destImage.texture = _videoPlayer.texture;
}
public void OnLoopPointReached(VideoPlayer source)
{
if (_completeAction != null)
{
_completeAction();
}
}
public void End()
{
_videoPlayer.Stop();
_videoPlayer.loopPointReached -= OnLoopPointReached;
}
public class Factory : Factory<VideoAnimation>
{
}
}
在我处理的特定情况下,提前调用Configure
和Prepare
,而RawImage
显示视频之前的最后一个静态图像。然后,当它显示视频时,会调用Begin
。因此,_prepareStarted
在调用true
时已经Begin
。插入日志消息显示isPrepared
在我拨打true
时正在返回Begin
,因此我也不会在那里循环播放。
我尝试改变两行的顺序
_videoPlayer.Play();
destImage.texture = _videoPlayer.texture;
但它似乎没有改变任何东西。我还认为,VideoPlayer
可能以某种方式在普通视频之前输出黑框,但在yield
之后和纹理集之前插入Play
或三个没有区别。
在插入Texture
纹理之前,我见过的所有样本都没有RawImage
VideoPlayer
。所以在那些中,RawImage
开始是黑色的,这意味着额外的黑色框架不会引人注目。
编辑#2:
嗯,我想出了一个解决方案,我认为,这有点解释。
首先,VideoPlayer.frame
被记录为&#34; VideoPlayer当前正在显示的帧索引。&#34;这不是严格意义上的。或者,它可能是VideoPlayer管道中的某个地方,但它并不是使用VideoPlayer纹理的代码可以观察到的帧。
当您Prepare
VideoPlayer时,至少在我使用它的模式下,VideoPlayer会创建一个内部RenderTexture。你会想想,一旦玩家准备好了,那个纹理就会包含视频的第一帧。它没有。在那之前有一个非常明显的延迟。因此,当我的代码将RawImage纹理设置为播放器的纹理时,它正在安排一个纹理,至少在那个时刻,它是空的以便显示。这完美地解释了黑色闪烁,因为它是背景画布的颜色。
所以我第一次尝试解决方案是在这里插入循环:
_videoPlayer.Play();
while(_videoPlayer.frame < 1)
{
yield return null;
}
destImage.texture = _videoPlayer.texture;
在Play
和纹理集之间。
我认为,尽管有文档,但frame
可能会显示关于的框架。如果是这样,这应该导致第一个(第0个)帧已经在缓冲区中,并且将消除闪烁。不。仍然闪烁着。但是当我改为
_videoPlayer.Play();
while(_videoPlayer.frame < 2)
{
yield return null;
}
destImage.texture = _videoPlayer.texture;
然后过渡是无缝的。所以我最初尝试在两者之间插入产量是正确的方法 - 我只是没有插入足够的。简而言之,事实上。我在循环中插入了一个计数器,它显示我在上面的循环中产生了4次,这是我所期望的,因为视频是30fps,而我在我的计算机上以60fps运行。 (同步锁定已开启。)
最后的实验表明:
_videoPlayer.Play();
while(_videoPlayer.frame < 1)
{
yield return null;
}
yield return null;
destImage.texture = _videoPlayer.texture;
也没有导致闪烁。 (或者,至少,不是我能看到的那个。)因此,一旦VideoPlayer报告它显示第二帧(根据文档数字是基于0的),在过渡之前需要一个额外的游戏帧。无缝。 (除非我的眼睛能看到第60个闪烁的第二个闪烁。)那个游戏框架可能与Unity的图形管道或VideoPlayer管道有关 - 我不知道
所以,最重要的是,从您拨打Play
开始,直到VideoPlayer纹理中的任何内容实际出现在屏幕上的任何内容都会有明显的延迟,除非您等待为此,你将展示什么&#34;没有&#34; (在我的情况下,导致黑色背景闪烁。)
我发现,由于VideoPlayer正在生成RenderTexture,因此也可以将之前的静态纹理blit到VideoPlayer的纹理(这样就会有一些东西)然后再进行立即切换。另一个试运行...
答案 0 :(得分:0)
嗯,让我们尝试使用着色器,也许它可以帮到你。
首先,我们必须创建自定义着色器,它必须像标准UI着色器一样工作。 您可以在this link下载所有内置着色器。
取UI-Default.shader
并修改它。我为你修改了它!
只需在Unity中创建shader
并粘贴此代码:
Shader "Custom/CustomShaderForUI"
{
Properties
{
//[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_CustomTexture ("Texture", 2D) = "white" {} // <--------------- new property
_Color ("Tint", Color) = (1,1,1,1)
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
Name "Default"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
v2f vert(appdata_t v)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
OUT.worldPosition = v.vertex;
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
OUT.texcoord = v.texcoord;
OUT.color = v.color * _Color;
return OUT;
}
//sampler2D _MainTex;
sampler2D _CustomTexture; // <---------------------- new property
fixed4 frag(v2f IN) : SV_Target
{
//half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
half4 color = (tex2D(_CustomTexture, IN.texcoord) + _TextureSampleAdd) * IN.color; // <- using new property
#ifdef UNITY_UI_CLIP_RECT
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#endif
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
}
接下来,创建一个材质并将着色器中的RenderTexture设置为纹理字段(RawImage组件中的不是)。
希望这有帮助!
答案 1 :(得分:0)
尝试在加载视频时隐藏RawImage游戏对象。这应该可以解决由于VideoPlayer未完全加载而引起的任何闪烁。
public VideoPlayer _videoPlayer;
public RawImage _videoImage;
private void PlayClip(VideoClip videoClip)
{
StartCoroutine(PlayClipCoroutine(videoClip));
}
private IEnumerator PlayClipCoroutine(VideoClip clip)
{
_videoImage.gameObject.SetActive(false);
_videoPlayer.clip = clip;
_videoPlayer.Prepare();
while (!_videoPlayer.isPrepared)
{
yield return null;
}
_videoPlayer.Play();
_videoImage.texture = _videoPlayer.texture;
_videoImage.gameObject.SetActive(true);
}