HTML in WebGL-canvas

Als je HTML rendert in een WebGL-canvas, kun je live DOM-subtrees gebruiken als dynamische GPU-texturen, shader-effecten toepassen op de live DOM en interactieve HTML integreren in 3D-scènes.

In deze handleiding wordt uitgelegd hoe u HTML rendert in een WebGL-canvas. Meer informatie over HTML-in-Canvas vind je in het API-overzicht.

Voor de weergave van HTML in een WebGL-canvas zijn de volgende stappen vereist:

  1. Bereid je canvas voor. Stel de juiste kenmerken declaratief in om de browser te laten weten dat deze HTML moet renderen in een canvas. Deze instelling is identiek voor 2D-, WebGL- en WebGPU-contexten. Meer informatie over hoe je je canvas instelt en hoe je HTML-elementen declareert die onafhankelijk moeten worden getekend.
  2. Pas de HTML toe als textuur:
    1. Wijs GPU-textuuropslag vooraf toe.
    2. Upload de DOM-momentopname als een textuur.
  3. Synchroniseer het DOM en de pixels.

Een HTML-element tekenen als een WebGL-textuur

In WebGL worden HTML-elementen geüpload naar de GPU als 2D-texturen met texElementSubImage2D().

Textuuropslag vooraf toewijzen

In tegenstelling tot een 2D-canvas, dat het back-upgeheugen automatisch beheert, vereist een WebGL-context expliciet geheugenbeheer.

Voordat je DOM-pixels uploadt, moet je GPU-geheugen vooraf toewijzen. Zo kan de browser snelle updates van subafbeeldingen uitvoeren zonder dure herallocaties van GPU-geheugen te activeren.

Wijs de buffer vooraf toe met de geselecteerde grootteparameters en srcData ingesteld op null:

// Only allocate texture backing once
if (!textureBufferAllocated) {
  // Pre-allocate texture backing with pre-determined size.
  gl.texImage2D(
    /* target */ gl.TEXTURE_2D,
    /* level */ 0,
    /* internalformat */ gl.RGBA8,
    /* width */ myElementWidth,
    /* height */ myElementHeight,
    /* border */ 0,
    /* format */ gl.RGBA,
    /* type */ gl.UNSIGNED_BYTE,
    /* srcData */ null
  );

  textureBufferAllocated = true;
}

Als je wilt dat de textuur overeenkomt met de natuurlijke vormgevingsgrootte van het HTML-element, gebruik je canvas.getElementImageDefaultSize() om de standaardgrootte op te halen in canvasrasterpixels:

const { width, height } = canvas.getElementImageDefaultSize(myElement);
// Pre-allocate texture backing. Only do this once, or whenever the element
// changes size.
gl.texImage2D(
  /* target */ gl.TEXTURE_2D,
  /* level */ 0,
  /* internalformat */ gl.RGBA8,
  /* width */ Math.ceil(width),
  /* height */ Math.ceil(height),
  /* border */ 0,
  /* format */ gl.RGBA,
  /* type */ gl.UNSIGNED_BYTE,
  /* srcData */ null
);

De DOM-momentopname uploaden als textuur

De volgende stap is het HTML-element uploaden naar uw textuur in uw render-loop of paint-listener.

canvas.onpaint = () => {
  // 1. Pre-allocate texture backing first
  // ...

  // 2. Upload HTML snapshot into the WebGL texture
  gl.texElementSubImage2D(
    /* target  */ gl.TEXTURE_2D,
    /* level   */ 0,
    /* xoffset */ 0,
    /* yoffset */ 0,
    /* element */ myElement // or an ElementImage from `captureElementImage()`
  );

  // ...
};

Geometrie synchroniseren

In tegenstelling tot 2D-contexten kan de browser niet automatisch afleiden waar de textuur op het scherm verschijnt in een 3D-context. Je moet DOM-hit-testing en toegankelijkheidsbegrenzingsvakken handmatig uitlijnen met je 3D-mesh.

Maak hiervoor een DOMMatrix die het randvak van het element toewijst aan de gerenderde canvaspixellocatie en pas deze toe met updateElementGeometry():

canvas.addEventListener("paint", () => {
  // 1. Upload fresh DOM snapshot to GPU texture
  gl.bindTexture(gl.TEXTURE_2D, myTexture);
  gl.texElementSubImage2D(gl.TEXTURE_2D, 0, 0, 0, myElement);

  // 2. Draw your 3D scene
  draw3DScene();

  // 3. Construct the CSS-pixel transform mapping the element's
  // border box to its drawn position on the canvas as needed
  // (for example: scale and translate)
  const canvasTransform = new DOMMatrix();
  // scale X
  canvasTransform.a = canvas.clientWidth / myElement.offsetWidth;
  // scale Y
  canvasTransform.d = canvas.clientHeight / myElement.offsetHeight;
  // translate X (CSS px)
  canvasTransform.e = 50;
  // translate Y (CSS px)
  canvasTransform.f = 100;

  // 4. Update DOM position, accessibility focus rings, and pointer hit-testing
  canvas.updateElementGeometry(myElement, {
    canvasTransform: canvasTransform,
  });
});

Volgende stappen