XGRIDSDocs
  • 简体中文
  • English
  • 繁體中文
  • 日本語
  • Deutsch
  • Español
  • Italiano
  • Français
  • Русский
  • Polski
  • Nederlands
  • 简体中文
  • English
  • 繁體中文
  • 日本語
  • Deutsch
  • Español
  • Italiano
  • Français
  • Русский
  • Polski
  • Nederlands
  • Lixel PortalCam-serie

    • PortalCam P1

      • Productoverzicht
      • Basisbediening
      • De LCC Scan App gebruiken
      • Onderhoud en verzorging
      • Veelgestelde vragen
  • Lixel K-serie

    • Lixel K1

      • Productoverzicht
      • Basisbediening
      • Apparaat activeren en verbinden
      • Scanworkflow
      • Puntenwolkgegevens met absolute coördinaten verkrijgen
      • Kaartfusie
      • Suggesties voor routeplanning bij typische scènes
      • Voorzorgsmaatregelen
      • FAQ
    • Lixel K2

      • Productoverzicht
      • Basisbediening
      • Apparaat activeren en verbinden
      • Scanworkflow
      • Puntenwolkgegevens met absolute coördinaten verkrijgen
      • Kaartfusie
      • Suggesties voor routeplanning bij typische scènes
      • Voorzorgsmaatregelen
      • FAQ
  • Lixel L-serie

    • Lixel L2 Pro

      • Productoverzicht
      • Basisbediening
      • Apparaatactivering en -verbinding
      • Scanworkflow
      • Puntenwolkgegevens met absolute coördinaten opnemen
      • Meetpunt
      • Appendix
      • FAQ
    • Lixel L3

      • Productoverzicht
      • Basisfuncties van het apparaat
      • Apparaatactivering en -verbinding
      • Gegevensopname
      • Absolute coördinaten opnemen
      • Appendix
  • Accessoires

    • Stick Light

      • Installatiegids
      • Basisbediening
      • FAQ
  • Lixel Studio

    • Versie en copyright
    • Installatie en activering
    • Software-interface
    • Bestandsbewerkingen
    • Projectverwerking
    • Gereedschappen
    • 2D-tekenen
    • Toepassingen
    • Instellingen
    • Apparaatverbinding
  • 07-plugin-sdk

    • Unity

      • LCC Unity SDK Introductie
      • Technische functies
      • Vereisten voor de ontwikkelomgeving
      • Snelstartgids voor pc
      • Optimalisatiegids voor projectinstellingen
      • Ontwikkelgids voor Pico 4 Ultra
      • Pc-streaminggids voor Pico 4 Ultra
      • Ontwikkelgids voor Quest 3
      • Pc-streaminggids voor Quest 3
      • Ontwikkelgids voor OpenXR
      • Business Streaming-gids voor Pico 4 Ultra
      • Woordenlijst
      • Veelgestelde vragen
      • Contact opnemen
      • Ontwikkelingshandleiding voor functies

        • Ontwikkelgids voor gemengde rendering van meerdere scènes
        • Ontwikkelgids voor het ontvangen van schaduwen
        • Ontwikkelgids voor realtime belichting
        • Ontwikkelgids voor multi-camera rendering
        • Ontwikkelgids voor het renderen van afbeeldingen met hoge resolutie
        • Ontwikkelgids voor compatibiliteit van de gegevensformaten LCC en LCC2
        • Ontwikkelgids voor laadeffecten
        • Ontwikkelgids voor het schakelen tussen Mip-renderingmodi
        • Ontwikkelgids voor rand- en hoeksnapping
        • Ontwikkelgids voor geprojecteerde decal
        • Ontwikkelgids voor video-geprojecteerde decal
        • Ontwikkelgids voor markeren
        • Ontwikkelgids voor realtime knippen
        • Ontwikkelgids voor kleuraanpassing
        • Ontwikkelgids voor het ophalen van scène-Meshgegevens
        • Ontwikkelgids voor de Editor-modus
        • Configuratiegids voor renderprestatieparameters
        • Ontwikkelgids voor het samen verpakken van de applicatie en de gegevens
        • Ontwikkelgids voor het botsingssysteem
        • Gids voor macOS-ontwikkelingsmachtigingsproblemen
        • Ontwikkelgids voor compilatie en verpakking voor iOS-apparaten
      • API-referentie

        • LCCManager
        • Renderer
        • Gegevenstypen
      • Wijzigingslogboek

        • v2.3.0
        • v2.2.10
        • v2.2.0
        • v2.1.19
        • v2.0.14

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:

  1. 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.
  2. 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.frame om 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.
Diagram van de gegevenspijplijn van video-geprojecteerde decal

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:

  1. De scène heeft al Main Camera, LCCManager, LCCRenderer en Projector opgezet (zie Ontwikkelgids voor geprojecteerde decal · Stap 1).
  2. De URP-pipeline is momenteel in gebruik; als occlusie-culling nodig is, is de Decal Depth Feature geregistreerd in de URP Renderer Asset.
  3. 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).
  4. 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 eerst SetupVideo() aangeroepen om VideoPlayer.Prepare() te starten en daarna wordt de puntenwolkscène-Load gestart. De twee blokkeren elkaar niet.
  • De framebuffer wordt pas toegewezen nadat de video gereed is: de afmetingen van RenderTexture en Texture2D worden ontleend aan de werkelijke resolutie van de video (vp.width / vp.height), dus ze worden aangemaakt in de prepareCompleted-callback OnVideoPrepared.
  • De decal wordt pas ingeschakeld nadat de scène is geladen: consistent met de normale decal worden SetDecalTexture / SetDecalEnabled in de OnSceneLoaded-callback geplaatst. Als de video op dat moment nog niet gereed is, is m_frameTex null en wordt de eerste textuurinstelling overgeslagen; deze wordt ingevuld wanneer het eerste frame in Update() arriveert.

Stap 2: Het script toevoegen en eigenschappen configureren

  1. Voeg het VideoStreaming.cs-script toe aan het LCCRenderer-GameObject in het Hiërarchie-venster.
  2. Sleep in het Inspector-venster het LCCManager-GameObject naar de eigenschap Manager van het script.
  3. Sleep het Projector-GameObject naar de eigenschap Projector van het script.
  4. Vul het LCC-scènebestandspad in bij de eigenschap File Path.
  5. 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.
  6. Andere eigenschappen kunnen naar behoefte worden aangepast; hun betekenis wordt in de onderstaande tabel weergegeven.
Inspector-eigenschapinstellingen voor het VideoStreaming-script

Inspector-eigenschapinstellingen voor het VideoStreaming-script

VeldBetekenisStandaardwaarde
ManagerDe LCCManager-instantie in de scèneVereist
File PathEen geldig LCC-scènepadVereist
Video ClipDe te projecteren video-asset, hogere prioriteit dan Video UrlLeeg
Video UrlVideobestandspad / URL, gebruikt wanneer geen clip is opgegevenLeeg
Video LoopOf het afspelen moet worden herhaaldAangevinkt
ProjectorProjectorpose, +Z is de projectierichting; leeg gebruikt de Transform van dit objectLeeg
FovPerspectiefgezichtsveld van de projector60
AspectProjectie-beeldverhouding; bij kleiner dan of gelijk aan 0 wordt deze automatisch berekend op basis van de beeldverhouding van de video0 (auto)
Near / FarNabije / verre knipvlakken van het projectorfrustum0.1 / 100
OpacityAlgehele decal-dekking1
Edge FadeBreedte van de randfade (UV-verhouding), 0 is een harde rand0.1
Update Every FrameVervers projector / uiterlijk elke frame, handig voor runtime-afstellenAangevinkt
OcclusionOf occlusie-culling moet worden ingeschakeldUit
Occlusion Cutoff / Bias / Footprint / ResolutionKwaliteitsparameters van occlusie-culling; zie Ontwikkelgids voor geprojecteerde decal0 / 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

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:

ParameterBronEffect op de uitlijning
Projector positie (Projector.position)Transform van de ProjectorBepaalt 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_fovBepaalt 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_aspectBepaalt 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_farBepaalt 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:

  1. Houd eerst Aspect op 0 (auto) om framevervorming te voorkomen en richt je op het aanpassen van positie en grootte.
  2. 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).
  3. Pas Fov aan om de dekkingsgrootte van het frame te bepalen.
  4. Controleer of het Near / Far-interval de diepte van de doelpuntenwolk volledig dekt, om te voorkomen dat het frame door de knipvlakken wordt afgesneden.
  5. 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 projectorparameters op waar en hoe groot het videoframe op de puntenwolk terechtkomt

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: ReadPixels is 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 VideoPlayer beheerd en de frame-index geeft ze waarheidsgetrouw weer; het script heeft geen extra synchronisatie nodig.

Waarom geen per-frame timer?

Het handmatig accumuleren van Time.deltaTime om 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 van VideoPlayer.frame maakt de speler de enige bron van waarheid — het eenvoudigst en het robuustst.

Over Update Every Frame:

De m_updateEveryFrame in 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 de VideoPlayer.frame-hartslag en wordt niet door deze schakelaar beïnvloed.

Opmerkingen over prestaties en formaten

  • De readback-kosten zijn evenredig met de resolutie. ReadPixels + Apply is 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-kopie Graphics.CopyTexture(m_videoRT, m_frameTex), maar de bron- en doelformaten moeten overeenkomen (RenderTextureFormat.ARGB32 komt overeen met TextureFormat.RGBA32) en sommige platformen beperken CopyTexture.
  • SetDecalTexture accepteert alleen Texture2D. Dit is precies waarom de RenderTexture moet worden teruggelezen naar een Texture2D; je kunt de RenderTexture van de VideoPlayer niet rechtstreeks aan de decal-interface doorgeven.
  • Hergebruik de framebuffer, maak deze niet elke frame opnieuw aan. In het voorbeeld wordt m_frameTex slechts één keer aangemaakt wanneer de video gereed is en wordt daarna elke frame dezelfde Texture2D hergebruikt, waardoor frequente allocatie / GC wordt vermeden.
  • Geef resources op tijd vrij. Voer in OnDestroy Release() en Destroy uit op de RenderTexture en Texture2D en meld prepareCompleted af om lekken te voorkomen.
  • Audio is niet aangesloten. Het voorbeeld stelt audioOutputMode in op None en demonstreert alleen de beelden. Wijzig dit naar de AudioSource-modus en koppel een AudioSource als geluid nodig is.

Samenvatting van de verschillen met de normale decal

AspectNormale decal (Decal.cs)Videodecal (VideoStreaming.cs)
Decal-inhoudStatische Texture2DTexture2D uit per-frame videodecodering
TextuurbronSleep een afbeelding in de InspectorVideoPlayer decoderen → RenderTexture → readback
Tijdstip van textuurupdateEén keer instellenFrame voor frame bijgewerkt op de VideoPlayer.frame-hartslag
Extra componentGeenAddComponent<VideoPlayer>() tijdens runtime
Aspect-autowaardeBeeldverhouding van de afbeeldingBeeldverhouding van de video
De rest (projector / occlusie / API)Precies hetzelfdePrecies 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.

ParameterTypeBeschrijving
texTexture2DDecal-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.