Ontwikkelgids voor video-geprojecteerde decal
Functieoverzicht
Video-geprojecteerde decal is een uitbreiding van Geprojecteerde decal: de geprojecteerde inhoud is niet langer een statische afbeelding, maar een video. De aanpak is om de video te decoderen met Unity's ingebouwde VideoPlayer en elk frame dynamisch bij te werken op de decal-textuur, zodat de video zoals een diaprojector op het oppervlak van de 3DGS-puntenwolk wordt geprojecteerd.
Omdat de decal zelf een in wereldruimte vastgelegde perspectiefprojectie is, wordt het videoframe volgens de pose van de projector op het puntenwolkoppervlak "geplakt". Punten binnen het projectorfrustum worden door het huidige videoframe gekleurd en punten buiten het frustum worden niet beïnvloed.
Opmerking:
Net als de normale decal is de videodecal alleen effectief op pc / Mac (URP-pipeline). Het aanroepen van de gerelateerde interfaces op mobiele of VR-platformen wordt door de SDK genegeerd en drukt een waarschuwing af.
Deze gids legt alleen de incrementele onderdelen met betrekking tot "video" uit. Lees voor de basis — scèneopzet (Main Camera / LCCManager / LCCRenderer / Projector), het toevoegen van de Decal Depth Feature aan de URP Renderer, het afstellen van occlusie-culling en de gedetailleerde decal-API's — eerst de Ontwikkelgids voor geprojecteerde decal.
Implementatieprincipe
De decal-interface SetDecalTexture(Texture2D tex) van de SDK accepteert alleen Texture2D, maar ondersteunt het dynamisch bijwerken van deze textuur tijdens runtime. De uitvoer van de decodering van Unity's VideoPlayer is een RenderTexture (op de GPU). Daarom is de hele gegevenspijplijn:
VideoPlayer decodeert → RenderTexture (GPU) → kopieer naar Texture2D → SetDecalTexture(frametextuur) → projecteer op de puntenwolk
Er zijn twee kernpunten, die ook de twee "uitlijningen" zijn die deze gids benadrukt:
- Ruimtelijke uitlijning: de positie, grootte en oriëntatie van het videoframe dat op de puntenwolk wordt geprojecteerd, worden bepaald door de projectorparameters (pose, FOV, Aspect, Near/Far); zie Ruimtelijke uitlijning van video en scène.
- Temporele uitlijning: de videoframerate is vaak veel lager dan de scènerendering-framerate (de scène kan op honderden FPS draaien), dus je kunt de textuur niet één keer per renderframe kopiëren en pushen. Gebruik
VideoPlayer.frameom te detecteren of er een nieuw gedecodeerd frame is verschenen en werk alleen bij wanneer er een nieuw frame arriveert; zie Uitlijnen van de videoframerate en de renderingframerate.

Gegevenspijplijn van video-geprojecteerde decal: VideoPlayer → RenderTexture → Texture2D → SetDecalTexture
Vereisten
De videodecal deelt dezelfde set vereisten als de normale decal. Bevestig het volgende voordat je begint:
- De scène heeft al Main Camera, LCCManager, LCCRenderer en Projector opgezet (zie Ontwikkelgids voor geprojecteerde decal · Stap 1).
- De URP-pipeline is momenteel in gebruik; als occlusie-culling nodig is, is de Decal Depth Feature geregistreerd in de URP Renderer Asset.
- Als de Console fouten meldt en er geen effect zichtbaar is, schakel dan de compatibiliteitsmodus van Render Graph uit (zie de sectie Veelgestelde vraag van de Ontwikkelgids voor geprojecteerde decal).
- Bereid een video-asset voor: dit kan een
VideoClip-asset in het project zijn, of een videobestand-URL op schijf / netwerk (bijv.file:///D:/sample.mp4).
Stap 1: Het script schrijven
Maak een nieuw C#-script aan met de naam VideoStreaming.cs. Gebaseerd op het skelet van de normale Decal.cs vervangt het de "statische decal-textuur" door "het aanmaken van een VideoPlayer tijdens runtime om de video te decoderen, en het bijwerken van de decal-textuur in Update() op basis van de gedecodeerde frames". Het kan rechtstreeks in je project worden gekopieerd.
using LCCCore;
using UnityEngine;
using UnityEngine.Video;
public class VideoStreaming : MonoBehaviour
{
[Header("Vereist")]
[Tooltip("De LCCManager-instantie in de scène")]
public LCCManager m_manager;
[Tooltip("Een geldig LCC-scènepad")]
public string m_filePath;
[Header("Videobron (gedecodeerde frames worden als decal geprojecteerd)")]
[Tooltip("Te projecteren videoclip. Heeft prioriteit boven de onderstaande URL")]
public VideoClip m_videoClip;
[Tooltip("Videobestandspad / URL, gebruikt wanneer geen clip is toegewezen (bijv. file:///D:/xxx.mp4)")]
public string m_videoUrl;
[Tooltip("Herhaal afspelen")]
public bool m_videoLoop = true;
[Header("Projector (projecteert langs +Z)")]
[Tooltip("Projectorpose. Laat leeg om de transform van dit object te gebruiken")]
public Transform m_projector;
[Range(1f, 179f)] public float m_fov = 60f;
[Tooltip("Projectie-beeldverhouding; <=0 gebruikt automatisch de beeldverhouding van de video")]
public float m_aspect = 0f;
public float m_near = 0.1f;
public float m_far = 100f;
[Header("Decal-uiterlijk")]
[Range(0f, 1f)] public float m_opacity = 1.0f;
[Range(0f, 0.5f)][Tooltip("Breedte van de randfade (UV-verhouding). 0 = harde rand")]
public float m_edgeFade = 0.1f;
[Tooltip("Ververs de projectie elke frame, zodat je de projector kunt slepen / parameters tijdens runtime kunt aanpassen")]
public bool m_updateEveryFrame = true;
[Header("Occlusie-culling (alleen pc/Mac)")]
[Tooltip("Wanneer aan, worden naar achteren gerichte/afgeschermde punten achter de voorste geometrie niet langer door de decal gekleurd (diaprojector-effect); wanneer uit, is het röntgenachtig")]
public bool m_occlusion = false;
[Range(0f, 1f)][Tooltip("Dekkingsdrempel: alleen splats met een dekking >= deze waarde dragen bij aan de dieptekaart, waardoor zwevende/mistige punten worden weggefilterd. 0 = alles doet mee")]
public float m_occlusionCutoff = 0f;
[Range(0f, 0.5f)][Tooltip("Relatieve dieptetolerantie. Te klein -> zelfocclusie nabij het oppervlak; te groot -> gelekt röntgenkleuren. Standaard 0.05")]
public float m_occlusionBias = 0.05f;
[Range(0, 16)][Tooltip("Vierkante footprint-straalcap (px), de belangrijkste knop voor het verwijderen van röntgen-doorlekken. 0 = enkele texel, standaard 2")]
public int m_occlusionFootprint = 2;
[Tooltip("Resolutie van de dieptekaart (px, macht van twee 256~4096). Verlaag deze om gaten onder scheerhoeken op te vullen. Standaard 1024")]
public int m_occlusionResolution = 1024;
// -- Runtimestatus --
private bool m_loaded;
private bool m_lastOcclusion;
private float m_lastCutoff = float.NaN;
private float m_lastBias = float.NaN;
private int m_lastFootprint = -1;
private int m_lastResolution = -1;
// -- Videostatus --
private VideoPlayer m_videoPlayer;
private RenderTexture m_videoRT; // VideoPlayer decodeert de video hier (GPU)
private Texture2D m_frameTex; // het huidige frame wordt hierheen gekopieerd en aan de decal gevoerd
private bool m_videoReady;
private long m_lastVideoFrame = -1; // de laatste frame-index die naar de decal is gepusht
void Start()
{
if (m_manager == null || string.IsNullOrEmpty(m_filePath))
{
Debug.LogError("[VideoStreaming] m_manager of m_filePath is niet toegewezen.");
enabled = false;
return;
}
// 1) Initialiseer de renderpipeline naar de pc-editormodus voor één scène
m_manager.SetPlatformType(PlatformType.PC);
m_manager.SwitchRenderPass(ActiveRenderMode.SingleRender);
m_manager.SetEditorMode(true);
// 2) Start het decoderen van de video parallel aan het laden van de scène
SetupVideo();
// 3) Laad de puntenwolkscène en schakel vervolgens de decal in (decal is een globale status; het is het veiligst om deze in te stellen nadat de scène gereed is)
var renderer = m_manager.GetRender(this.transform);
Debug.Log($"[VideoStreaming] Start met laden: {m_filePath}");
renderer.Load(m_filePath,
_ => { /* voortgangscallback, ongebruikt in dit voorbeeld */ },
OnSceneLoaded);
}
/// <summary>Maak de VideoPlayer tijdens runtime aan en begin met het voorbereiden van de clip/URL.</summary>
private void SetupVideo()
{
m_videoPlayer = gameObject.AddComponent<VideoPlayer>();
m_videoPlayer.playOnAwake = false;
m_videoPlayer.isLooping = m_videoLoop;
m_videoPlayer.renderMode = VideoRenderMode.RenderTexture;
m_videoPlayer.audioOutputMode = VideoAudioOutputMode.None; // demo: geen audio
if (m_videoClip != null)
{
m_videoPlayer.source = VideoSource.VideoClip;
m_videoPlayer.clip = m_videoClip;
}
else
{
m_videoPlayer.source = VideoSource.Url;
m_videoPlayer.url = m_videoUrl;
}
// Wijs de framebuffer toe zodra de resolutie bekend is en start vervolgens het afspelen.
m_videoPlayer.prepareCompleted += OnVideoPrepared;
m_videoPlayer.Prepare();
}
private void OnVideoPrepared(VideoPlayer vp)
{
int w = (int)vp.width;
int h = (int)vp.height;
m_videoRT = new RenderTexture(w, h, 0, RenderTextureFormat.ARGB32);
m_videoRT.Create();
vp.targetTexture = m_videoRT;
m_frameTex = new Texture2D(w, h, TextureFormat.RGBA32, false);
m_videoReady = true;
vp.Play();
Debug.Log($"[VideoStreaming] Video voorbereid: {w}x{h}, wordt afgespeeld.");
}
private void OnSceneLoaded()
{
Debug.Log("[VideoStreaming] Laden voltooid, decal-projectie wordt ingeschakeld.");
// -- Basisprojectie: 5 API's --
// De textuurinhoud wordt aangestuurd door gedecodeerde frames in Update(); hier pushen we eerst het huidige beschikbare frame.
if (m_frameTex != null) m_manager.SetDecalTexture(m_frameTex);
m_manager.SetDecalOpacity(m_opacity); // algehele dekking
m_manager.SetDecalEdgeFade(m_edgeFade); // randfade
ApplyProjection(); // projectorpose (roept intern SetDecalProjection aan)
m_manager.SetDecalEnabled(true); // decal inschakelen (_DECAL-keyword)
// -- Occlusie-culling: 2 API's -- moet na ApplyProjection (de dieptekaart heeft een geldige projector-VP-matrix nodig)
m_manager.SetDecalOcclusionSettings(BuildOcclusionSettings());
m_manager.SetDecalOcclusion(m_occlusion);
CacheOcclusionState();
m_loaded = true;
}
void Update()
{
if (!m_loaded) return;
// --- Videoframe-update, ontkoppeld van render-FPS ---
// VideoPlayer.frame wordt alleen verhoogd op de eigen afspeelsnelheid van de video. Zelfs als de scène op honderden FPS rendert,
// blijft de index gelijk tussen twee aangrenzende videoframes, dus alleen wanneer er daadwerkelijk een nieuw gedecodeerd frame verschijnt,
// kopiëren we dat frame en pushen we de decal-textuur opnieuw.
if (m_videoReady && m_videoPlayer.frame != m_lastVideoFrame)
{
m_lastVideoFrame = m_videoPlayer.frame;
// Kopieer de RenderTexture op de GPU naar de Texture2D die door de decal-API wordt geaccepteerd.
// Dit draait alleen op de videoframerate, dus de readback-kosten zijn beheersbaar.
RenderTexture prev = RenderTexture.active;
RenderTexture.active = m_videoRT;
m_frameTex.ReadPixels(new Rect(0, 0, m_videoRT.width, m_videoRT.height), 0, 0, false);
m_frameTex.Apply(false);
RenderTexture.active = prev;
m_manager.SetDecalTexture(m_frameTex);
}
// occlusie tijdens runtime in-/uitschakelen
if (m_occlusion != m_lastOcclusion)
{
m_manager.SetDecalOcclusion(m_occlusion);
m_lastOcclusion = m_occlusion;
}
// als een occlusiekwaliteitsparameter is gewijzigd -> de struct één keer doorsturen
if (!Mathf.Approximately(m_occlusionCutoff, m_lastCutoff) ||
!Mathf.Approximately(m_occlusionBias, m_lastBias) ||
m_occlusionFootprint != m_lastFootprint ||
m_occlusionResolution != m_lastResolution)
{
m_manager.SetDecalOcclusionSettings(BuildOcclusionSettings());
CacheOcclusionState();
}
// ververs projector / uiterlijk elke frame voor handig runtime-afstellen
if (!m_updateEveryFrame) return;
m_manager.SetDecalOpacity(m_opacity);
m_manager.SetDecalEdgeFade(m_edgeFade);
ApplyProjection();
}
/// <summary>Bouw de projectiematrix op basis van de huidige projectorpose en perspectiefparameters.</summary>
private void ApplyProjection()
{
Transform p = m_projector != null ? m_projector : this.transform;
float aspect = m_aspect > 0f
? m_aspect
: (m_videoRT != null ? (float)m_videoRT.width / m_videoRT.height : 1920.0f / 1080.0f);
m_manager.SetDecalProjection(p.position, p.rotation, m_fov, aspect, m_near, m_far);
}
/// <summary>Verzamel de 4 occlusieparameters in een struct.</summary>
private DecalOcclusionSettings BuildOcclusionSettings()
{
return new DecalOcclusionSettings
{
opacityCutoff = m_occlusionCutoff,
depthBias = m_occlusionBias,
footprintMaxPx = m_occlusionFootprint,
resolution = m_occlusionResolution,
};
}
private void CacheOcclusionState()
{
m_lastOcclusion = m_occlusion;
m_lastCutoff = m_occlusionCutoff;
m_lastBias = m_occlusionBias;
m_lastFootprint = m_occlusionFootprint;
m_lastResolution = m_occlusionResolution;
}
private void OnDestroy()
{
if (m_videoPlayer != null) m_videoPlayer.prepareCompleted -= OnVideoPrepared;
if (m_videoRT != null)
{
m_videoRT.Release();
Destroy(m_videoRT);
}
if (m_frameTex != null) Destroy(m_frameTex);
}
// -- Handige debug-ingangspunten (rechtermuisklik in Inspector) --
[ContextMenu("Decal aan")] private void DecalOn() { m_manager?.SetDecalEnabled(true); }
[ContextMenu("Decal uit")] private void DecalOff() { m_manager?.SetDecalEnabled(false); }
[ContextMenu("Occlusie aan")] private void OcclusionOn() { m_occlusion = true; m_manager?.SetDecalOcclusion(true); m_lastOcclusion = true; }
[ContextMenu("Occlusie uit")] private void OcclusionOff() { m_occlusion = false; m_manager?.SetDecalOcclusion(false); m_lastOcclusion = false; }
}
Opmerking:
- Video en scène laden parallel: in
Start()wordt eerstSetupVideo()aangeroepen omVideoPlayer.Prepare()te starten en daarna wordt de puntenwolkscène-Loadgestart. De twee blokkeren elkaar niet.- De framebuffer wordt pas toegewezen nadat de video gereed is: de afmetingen van
RenderTextureenTexture2Dworden ontleend aan de werkelijke resolutie van de video (vp.width/vp.height), dus ze worden aangemaakt in deprepareCompleted-callbackOnVideoPrepared.- De decal wordt pas ingeschakeld nadat de scène is geladen: consistent met de normale decal worden
SetDecalTexture/SetDecalEnabledin deOnSceneLoaded-callback geplaatst. Als de video op dat moment nog niet gereed is, ism_frameTexnull en wordt de eerste textuurinstelling overgeslagen; deze wordt ingevuld wanneer het eerste frame inUpdate()arriveert.
Stap 2: Het script toevoegen en eigenschappen configureren
- Voeg het
VideoStreaming.cs-script toe aan het LCCRenderer-GameObject in het Hiërarchie-venster. - Sleep in het Inspector-venster het LCCManager-GameObject naar de eigenschap Manager van het script.
- Sleep het Projector-GameObject naar de eigenschap Projector van het script.
- Vul het LCC-scènebestandspad in bij de eigenschap File Path.
- Geef de videobron op, kies een van twee:
- Sleep een VideoClip-asset uit het project naar de eigenschap Video Clip; of
- Vul het videobestandspad in bij de eigenschap Video Url (bijv.
file:///D:/sample.mp4). - Wanneer beide zijn ingevuld, heeft Video Clip prioriteit.
- Andere eigenschappen kunnen naar behoefte worden aangepast; hun betekenis wordt in de onderstaande tabel weergegeven.

Inspector-eigenschapinstellingen voor het VideoStreaming-script
| Veld | Betekenis | Standaardwaarde |
|---|---|---|
| Manager | De LCCManager-instantie in de scène | Vereist |
| File Path | Een geldig LCC-scènepad | Vereist |
| Video Clip | De te projecteren video-asset, hogere prioriteit dan Video Url | Leeg |
| Video Url | Videobestandspad / URL, gebruikt wanneer geen clip is opgegeven | Leeg |
| Video Loop | Of het afspelen moet worden herhaald | Aangevinkt |
| Projector | Projectorpose, +Z is de projectierichting; leeg gebruikt de Transform van dit object | Leeg |
| Fov | Perspectiefgezichtsveld van de projector | 60 |
| Aspect | Projectie-beeldverhouding; bij kleiner dan of gelijk aan 0 wordt deze automatisch berekend op basis van de beeldverhouding van de video | 0 (auto) |
| Near / Far | Nabije / verre knipvlakken van het projectorfrustum | 0.1 / 100 |
| Opacity | Algehele decal-dekking | 1 |
| Edge Fade | Breedte van de randfade (UV-verhouding), 0 is een harde rand | 0.1 |
| Update Every Frame | Ververs projector / uiterlijk elke frame, handig voor runtime-afstellen | Aangevinkt |
| Occlusion | Of occlusie-culling moet worden ingeschakeld | Uit |
| Occlusion Cutoff / Bias / Footprint / Resolution | Kwaliteitsparameters van occlusie-culling; zie Ontwikkelgids voor geprojecteerde decal | 0 / 0.05 / 2 / 1024 |
Stap 3: Uitvoeren en het effect observeren
Klik op Play in Unity. De video begint met afspelen nadat Prepare is voltooid; nadat de scène is geladen, wordt de decal automatisch ingeschakeld en worden de videoframes frame voor frame op het 3DGS-oppervlak geprojecteerd. Wanneer Update Every Frame is aangevinkt, kun je de projector rechtstreeks in de Scene-weergave slepen en zie je het videoframe in realtime over het puntenwolkoppervlak bewegen.

Videoframes geprojecteerd op het 3DGS-oppervlak na het uitvoeren
Ruimtelijke uitlijning van video en scène
"Ruimtelijke uitlijning" betekent dat het videoframe nauwkeurig op de puntenwolk wordt geprojecteerd op de positie, oriëntatie en grootte die je wilt. De positie van het videoframe wordt volledig bepaald door de projector (SetDecalProjection), onafhankelijk van de video-inhoud. De parameters die de uitlijning beïnvloeden, zijn als volgt:
| Parameter | Bron | Effect op de uitlijning |
|---|---|---|
| Projector positie (Projector.position) | Transform van de Projector | Bepaalt de wereldcoördinaat van de frustumtop, dat wil zeggen waarvandaan de video wordt geprojecteerd. Door deze te verplaatsen verschuift de plek waar het frame op de puntenwolk terechtkomt. |
| Projector oriëntatie (Projector.rotation) | Transform van de Projector | +Z is de projectierichting. Door de projector te draaien verandert de projectierichting en verschuift / kantelt het frame navenant op het oppervlak. |
| Fov (verticaal gezichtsveld) | Scriptveld m_fov | Bepaalt de openingshoek van het frustum en beïnvloedt rechtstreeks de grootte van het frame op de puntenwolk: hoe groter de FOV, hoe groter het frame; hoe kleiner, hoe geconcentreerder. Bereik [1, 179]. |
| Aspect (beeldverhouding) | Scriptveld m_aspect | Bepaalt de horizontale/verticale verhouding van het frame. Bij instelling op 0 wordt automatisch de beeldverhouding van de videoresolutie gebruikt (m_videoRT.width / height), wat de aanbevolen manier is om te voorkomen dat de video wordt uitgerekt. Een verkeerde handmatige waarde zorgt ervoor dat het frame wordt uitgerekt. |
| Near / Far (nabije / verre knipvlakken) | Scriptvelden m_near / m_far | Bepaalt het effectieve diepte-interval van het frustum langs de projectierichting. Alleen punten binnen [near, far] worden gekleurd. Een te kleine Far knipt verre puntenwolken af en een te grote Near mist nabije punten. Het moet het dieptebereik van de doelpuntenwolk dekken. |
Algemene uitlijnstappen:
- Houd eerst Aspect op 0 (auto) om framevervorming te voorkomen en richt je op het aanpassen van positie en grootte.
- Sleep de positie en oriëntatie van de Projector om het frame op het doelgebied te centreren (vink Update Every Frame aan voor realtime preview).
- Pas Fov aan om de dekkingsgrootte van het frame te bepalen.
- Controleer of het Near / Far-interval de diepte van de doelpuntenwolk volledig dekt, om te voorkomen dat het frame door de knipvlakken wordt afgesneden.
- Stel Aspect handmatig in als je een specifieke verhouding wilt afdwingen (bijv. een 16:9-video op een vierkant gebied projecteren).
Tip:
De frameverhouding van de video wordt bepaald door Aspect, terwijl de resolutie van de video alleen de helderheid en readback-kosten beïnvloedt, niet de fysieke grootte die op de puntenwolk wordt geprojecteerd — de grootte wordt samen bepaald door Fov en de projectieafstand.

Effect van projectorpose / Fov / Aspect / Near-Far op waar en hoe groot het videoframe op de puntenwolk terechtkomt
Uitlijnen van de videoframerate en de renderingframerate
Dit is het meest kritieke onderdeel van de videodecal. De videoframerate (bijv. 25/30 FPS) is doorgaans veel lager dan de scènerendering-framerate (een 3DGS-scène kan op honderden FPS draaien). Als je elke renderframe een "readback + SetDecalTexture" uitvoert, zijn er twee problemen:
- Overbodig werk: hetzelfde videoframe wordt herhaaldelijk gekopieerd en herhaaldelijk gepusht, wat pure verspilling is.
- Prestatiebelasting:
ReadPixelsis een GPU→CPU-readback met niet-triviale kosten; dit elke frame doen bij een hoge renderingframerate veroorzaakt merkbare framedrops.
Ontkoppelingsstrategie: gebruik VideoPlayer.frame als hartslag
VideoPlayer.frame is de huidige gedecodeerde frame-index, die alleen op de eigen afspeelsnelheid van de video wordt verhoogd. Hoe hoog de renderingframerate ook is, deze index verandert niet tussen twee videoframes. Werk op basis hiervan de textuur alleen bij wanneer "de index verandert":
if (m_videoReady && m_videoPlayer.frame != m_lastVideoFrame)
{
m_lastVideoFrame = m_videoPlayer.frame;
// Voer slechts één keer een readback uit en push de decal wanneer er een nieuw gedecodeerd frame verschijnt
RenderTexture prev = RenderTexture.active;
RenderTexture.active = m_videoRT;
m_frameTex.ReadPixels(new Rect(0, 0, m_videoRT.width, m_videoRT.height), 0, 0, false);
m_frameTex.Apply(false);
RenderTexture.active = prev;
m_manager.SetDecalTexture(m_frameTex);
}
Op deze manier:
- Wanneer de scène op 300 FPS rendert en de video 30 FPS is, voeren slechts ongeveer 30 van de 300 renderframes daadwerkelijk de readback en push uit; de overige frames worden direct overgeslagen door de
if. - De hartslag wordt door de speler zelf bepaald; er is geen handgeschreven timer nodig om de videoframerate te matchen en er worden geen frames gedropt of gedupliceerd door schommelingen in de renderingframerate.
- De snelheid, looping en pauzeren van de video worden allemaal intern door
VideoPlayerbeheerd en deframe-index geeft ze waarheidsgetrouw weer; het script heeft geen extra synchronisatie nodig.
Waarom geen per-frame timer?
Het handmatig accumuleren van
Time.deltaTimeom het videoframe-interval te benaderen, vereist het afhandelen van zowel jitter in de renderingframerate als veranderingen in videosnelheid / frame-skipping, en kan gemakkelijk afwijken van de daadwerkelijke interne decodeervoortgang van de speler. Het rechtstreeks lezen vanVideoPlayer.framemaakt de speler de enige bron van waarheid — het eenvoudigst en het robuustst.
Over Update Every Frame:
De
m_updateEveryFramein het script bepaalt of de projectorpose / dekking / fade elke frame worden ververst, wat een aparte kwestie is van de videoframe-update. De videoframe-update volgt altijd deVideoPlayer.frame-hartslag en wordt niet door deze schakelaar beïnvloed.
Opmerkingen over prestaties en formaten
- De readback-kosten zijn evenredig met de resolutie.
ReadPixels+Applyis een GPU→CPU→GPU-retour; hoe hoger de resolutie, hoe langzamer. Dit voorbeeld gebruikt het voor de eenvoud en voldoende demonstratie. Als de videoresolutie zeer hoog is of de prestaties gevoelig zijn, kun je overschakelen naar een pure GPU-kopieGraphics.CopyTexture(m_videoRT, m_frameTex), maar de bron- en doelformaten moeten overeenkomen (RenderTextureFormat.ARGB32komt overeen metTextureFormat.RGBA32) en sommige platformen beperken CopyTexture. SetDecalTextureaccepteert alleenTexture2D. Dit is precies waarom deRenderTexturemoet worden teruggelezen naar eenTexture2D; je kunt deRenderTexturevan deVideoPlayerniet rechtstreeks aan de decal-interface doorgeven.- Hergebruik de framebuffer, maak deze niet elke frame opnieuw aan. In het voorbeeld wordt
m_frameTexslechts één keer aangemaakt wanneer de video gereed is en wordt daarna elke frame dezelfdeTexture2Dhergebruikt, waardoor frequente allocatie / GC wordt vermeden. - Geef resources op tijd vrij. Voer in
OnDestroyRelease()enDestroyuit op deRenderTextureenTexture2Den meldprepareCompletedaf om lekken te voorkomen. - Audio is niet aangesloten. Het voorbeeld stelt
audioOutputModein opNoneen demonstreert alleen de beelden. Wijzig dit naar deAudioSource-modus en koppel eenAudioSourceals geluid nodig is.
Samenvatting van de verschillen met de normale decal
| Aspect | Normale decal (Decal.cs) | Videodecal (VideoStreaming.cs) |
|---|---|---|
| Decal-inhoud | Statische Texture2D | Texture2D uit per-frame videodecodering |
| Textuurbron | Sleep een afbeelding in de Inspector | VideoPlayer decoderen → RenderTexture → readback |
| Tijdstip van textuurupdate | Eén keer instellen | Frame voor frame bijgewerkt op de VideoPlayer.frame-hartslag |
| Extra component | Geen | AddComponent<VideoPlayer>() tijdens runtime |
| Aspect-autowaarde | Beeldverhouding van de afbeelding | Beeldverhouding van de video |
| De rest (projector / occlusie / API) | Precies hetzelfde | Precies hetzelfde |
Belangrijkste betrokken API's
Aan de SDK-zijde gebruikt de videodecal alleen de dynamische update-mogelijkheid van SetDecalTexture; de andere decal-API's zijn precies hetzelfde als de normale decal. Zie Ontwikkelgids voor geprojecteerde decal · Decal-gerelateerde API's.
SetDecalTexture (dynamische update)
// Retourwaarde: void
public void SetDecalTexture(Texture2D tex)
Stelt de decal-textuur in. RGB is kleur en A is het eigen masker van de decal. Deze methode ondersteunt herhaaldelijk aanroepen tijdens runtime: elke keer dat een nieuwe Texture2D wordt doorgegeven, wordt de huidige decal-inhoud onmiddellijk vervangen — dit is precies de basis van per-frame videoprojectie: roep het één keer aan wanneer er in Update() een nieuw gedecodeerd frame verschijnt.
| Parameter | Type | Beschrijving |
|---|---|---|
tex | Texture2D | Decal-textuur, kan tijdens runtime herhaaldelijk worden vervangen |
De andere gerelateerde ingebouwde Unity-typen (VideoPlayer / RenderTexture / Texture2D) zijn standaard Unity-API's; zie de officiële Unity-documentatie voor het gebruik.