Signatur
Beschreibung
viewport() bestimmt, auf welchen rechteckigen Bereich des Zeichenpuffers WebGL die Ausgabe des Vertex-Shaders abbildet. Die Clip-Space-Koordinaten werden nach der perspektivischen Division zu normalisierten Gerätekoordinaten im Bereich -1 bis 1. Diese werden dann linear auf das Rechteck (x, y, width, height) in Pixeln umgerechnet: x_w = (x_ndc + 1) * width / 2 + x, entsprechend für y.
Wenn ein WebGL-Kontext erstellt wird, entspricht der Viewport anfangs der Größe des Canvas. Ändert sich später die Größe des Zeichenpuffers (canvas.width/canvas.height), wird der Viewport nicht automatisch angepasst. Du musst viewport() also nach jeder Größenänderung selbst aufrufen, sonst wird das Bild verzerrt oder nur ein Teil der Fläche gefüllt.
Typische Einsatzfälle sind: Größenanpassung nach dem Ändern der Canvas-Größe, geteilte Bildschirme (Split-Screen, mehrere Kameras in einem Canvas), Minimaps und das Rendern in Framebuffer-Objekte, deren Größe sich vom Canvas unterscheidet (z. B. Shadow-Maps oder Post-Processing-Texturen).
Der Ursprung (0, 0) liegt in WebGL unten links, nicht oben links wie bei CSS oder Canvas 2D.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $x Pflicht | number (GLint) | 0 | Horizontale Position der unteren linken Ecke des Viewports in Pixeln. |
| $y Pflicht | number (GLint) | 0 | Vertikale Position der unteren linken Ecke des Viewports in Pixeln (von unten gemessen). |
| $width Pflicht | number (GLsizei) | Breite des Canvas | Breite des Viewports in Pixeln. Negative Werte erzeugen den Fehler gl.INVALID_VALUE. |
| $height Pflicht | number (GLsizei) | Höhe des Canvas | Höhe des Viewports in Pixeln. Negative Werte erzeugen den Fehler gl.INVALID_VALUE. |
Rückgabewert
gl.getParameter(gl.VIEWPORT) als Int32Array aus.Beispiele
Viewport an die Canvas-Größe anpassen (inkl. HiDPI)
const canvas = document.querySelector('#glcanvas');
const gl = canvas.getContext('webgl');
const resize = () => {
const dpr = window.devicePixelRatio || 1;
const width = Math.round(canvas.clientWidth * dpr);
const height = Math.round(canvas.clientHeight * dpr);
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
}
// Viewport an den tatsächlichen Zeichenpuffer anpassen
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
console.log(gl.getParameter(gl.VIEWPORT));
};
new ResizeObserver(resize).observe(canvas);
resize();
Split-Screen: zwei Ansichten in einem Canvas
const gl = document.querySelector('canvas').getContext('webgl');
const w = gl.drawingBufferWidth;
const h = gl.drawingBufferHeight;
gl.enable(gl.SCISSOR_TEST);
// Linke Hälfte: rot
gl.viewport(0, 0, w / 2, h);
gl.scissor(0, 0, w / 2, h);
gl.clearColor(1, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// hier z. B. drawScene(cameraLinks)
// Rechte Hälfte: blau
gl.viewport(w / 2, 0, w / 2, h);
gl.scissor(w / 2, 0, w / 2, h);
gl.clearColor(0, 0, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// hier z. B. drawScene(cameraRechts)
gl.disable(gl.SCISSOR_TEST);
In ein Framebuffer-Objekt anderer Größe rendern
const gl = document.querySelector('canvas').getContext('webgl');
const size = 512;
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
const fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
// Viewport auf Texturgröße setzen
gl.viewport(0, 0, size, size);
gl.clearColor(0, 1, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// Zurück zum Canvas – Viewport wiederherstellen!
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
console.log(gl.getParameter(gl.VIEWPORT));
// Wichtig · Fallstricke
- Keine automatische Anpassung: Wenn du
canvas.width/canvas.heightänderst, bleibt der Viewport gleich. Rufe danach immergl.viewport()auf. - Nutze
drawingBufferWidth/drawingBufferHeightstattcanvas.width: Der Browser kann einen kleineren Zeichenpuffer anlegen, als angefordert wurde (z. B. bei Speicherknappheit). - Viewport ≠ Clipping:
gl.clear()beachtet den Viewport nicht und löscht immer den gesamten Puffer. Um nur einen Bereich zu löschen, brauchst du zusätzlichgl.scissor()mitgl.SCISSOR_TEST. Auch Punkte und breite Linien können über den Viewport-Rand hinaus gezeichnet werden. - Obergrenze: Breite und Höhe werden stillschweigend auf
gl.getParameter(gl.MAX_VIEWPORT_DIMS)begrenzt. Negative Werte führen zugl.INVALID_VALUE(auslesbar mitgl.getError()). - Koordinatensystem:
ywird von unten gemessen. Wenn du Mauskoordinaten aus DOM-Events umrechnest, musst du die y-Achse spiegeln. - Der Viewport ist globaler Kontext-Zustand und gehört nicht zum Framebuffer. Nach dem Wechsel des Framebuffers musst du ihn also selbst neu setzen.
- Die Methode gibt es in allen Browsern mit WebGL-Unterstützung. In
WebGL2RenderingContextfunktioniert sie genauso.