Movatterモバイル変換


[0]ホーム

URL:


  1. Web
  2. Web-APIs
  3. WebGL: 2D and 3D graphics for the web
  4. WebGL-Tutorial
  5. Verwendung von Texturen in WebGL

Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten.Erfahre mehr über dieses Experiment.

View in EnglishAlways switch to English

Verwendung von Texturen in WebGL

Da unser Beispielprogramm jetzt einen rotierenden 3D-Würfel hat, lassen Sie uns eine Textur darauf abbilden, anstatt seine Flächen in festen Farben darzustellen.

Laden von Texturen

Zuerst müssen wir Code hinzufügen, um die Texturen zu laden. In unserem Fall verwenden wir eine einzige Textur, die auf alle sechs Seiten unseres rotierenden Würfels abgebildet wird, aber dieselbe Technik kann für eine beliebige Anzahl von Texturen verwendet werden.

Hinweis:Es ist wichtig zu beachten, dass das Laden von Texturen denCross-Domain-Regeln folgt; das bedeutet, dass Sie Texturen nur von Websites laden können, für die Ihr Inhalt eine CORS-Zulassung hat. SieheCross-domain Texturen unten für Details.

Hinweis:Fügen Sie diese beiden Funktionen zu Ihrem Skript "webgl-demo.js" hinzu:

js
//// Initialize a texture and load an image.// When the image finished loading copy it into the texture.//function loadTexture(gl, url) {  const texture = gl.createTexture();  gl.bindTexture(gl.TEXTURE_2D, texture);  // Because images have to be downloaded over the internet  // they might take a moment until they are ready.  // Until then put a single pixel in the texture so we can  // use it immediately. When the image has finished downloading  // we'll update the texture with the contents of the image.  const level = 0;  const internalFormat = gl.RGBA;  const width = 1;  const height = 1;  const border = 0;  const srcFormat = gl.RGBA;  const srcType = gl.UNSIGNED_BYTE;  const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue  gl.texImage2D(    gl.TEXTURE_2D,    level,    internalFormat,    width,    height,    border,    srcFormat,    srcType,    pixel,  );  const image = new Image();  image.onload = () => {    gl.bindTexture(gl.TEXTURE_2D, texture);    gl.texImage2D(      gl.TEXTURE_2D,      level,      internalFormat,      srcFormat,      srcType,      image,    );    // WebGL1 has different requirements for power of 2 images    // vs. non power of 2 images so check if the image is a    // power of 2 in both dimensions.    if (isPowerOf2(image.width) && isPowerOf2(image.height)) {      // Yes, it's a power of 2. Generate mips.      gl.generateMipmap(gl.TEXTURE_2D);    } else {      // No, it's not a power of 2. Turn off mips and set      // wrapping to clamp to edge      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);    }  };  image.src = url;  return texture;}function isPowerOf2(value) {  return (value & (value - 1)) === 0;}

DieloadTexture()-Routine beginnt mit der Erstellung eines WebGL-Texturobjektstexture durch Aufruf der WebGL-FunktioncreateTexture(). Anschließend wird ein einzelnes blaues Pixel mittexImage2D() hochgeladen. Dadurch kann die Textur sofort als feste blaue Farbe verwendet werden, obwohl es einige Momente dauern kann, bis unser Bild heruntergeladen ist.

Um die Textur aus der Bilddatei zu laden, wird einImage-Objekt erstellt und dersrc wird auf die URL unseres Bildes gesetzt, das wir als Textur verwenden möchten. Die Funktion, die wirimage.onload zuweisen, wird aufgerufen, sobald das Bild heruntergeladen wurde. Zu diesem Zeitpunkt rufen wir erneuttexImage2D() auf, diesmal unter Verwendung des Bildes als Quelle für die Textur. Danach richten wir die Filterung und das Wrapping für die Textur ein, basierend darauf, ob das heruntergeladene Bild in beiden Dimensionen eine Zweierpotenz war oder nicht.

WebGL1 kann Texturen, die keine Zweierpotenz sind, nur mit aufNEAREST oderLINEAR gesetzter Filterung verwenden und kann für sie kein Mipmap generieren. Ihr Wrapping-Modus muss ebenfalls aufCLAMP_TO_EDGE gesetzt werden. Andererseits, wenn die Textur in beiden Dimensionen eine Zweierpotenz ist, kann WebGL eine hochwertigere Filterung durchführen, es kann Mipmap verwenden, und es kann den Wrapping-Modus aufREPEAT oderMIRRORED_REPEAT setzen.

Ein Beispiel für eine wiederholte Textur ist das Fliesen eines Bildes von ein paar Steinen, um eine Ziegelwand zu bedecken.

Mipmapping und UV-Wiederholung können mittexParameteri() deaktiviert werden. Dies wird Texturen, die keine Zweierpotenz haben (NPOT), zulassen, jedoch auf Kosten von Mipmapping, UV-Wrapping, UV-Tiling und Ihrer Kontrolle darüber, wie das Gerät Ihre Textur behandelt.

js
// gl.NEAREST is also allowed, instead of gl.LINEAR, as neither mipmap.gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);// Prevents s-coordinate wrapping (repeating).gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);// Prevents t-coordinate wrapping (repeating).gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

Erneut werden mit diesen Parametern kompatible WebGL-Geräte automatisch jede Auflösung für diese Textur akzeptieren (bis zu ihren maximalen Abmessungen). Ohne obige Konfiguration erfordert WebGL, dass alle Abtastungen von NPOT-Texturen fehlschlagen, indem sie transparentes Schwarz zurückgeben:rgb(0 0 0 / 0%).

Um das Bild zu laden, fügen Sie einen Aufruf zu unsererloadTexture()-Funktion innerhalb unserermain()-Funktion hinzu. Dies kann nach dem Aufruf voninitBuffers(gl) hinzugefügt werden.

Aber beachten Sie auch: Browser kopieren Pixel aus dem geladenen Bild in von oben nach unten Reihenfolge — von der oberen linken Ecke; aber WebGL möchte die Pixel in von unten nach oben Reihenfolge — beginnend von der unteren linken Ecke. (Für mehr Details sieheWarum ist meine WebGL-Textur verkehrt herum?.)

Um zu verhindern, dass die resultierende Bildtextur beim Rendern die falsche Ausrichtung hat, müssen wir auchpixelStorei() mit dem Parametergl.UNPACK_FLIP_Y_WEBGL setzen, um die Pixel in die von WebGL erwartete von unten nach oben Reihenfolge zu flippen.

Hinweis:Fügen Sie den folgenden Code zu Ihrermain()-Funktion hinzu, direkt nach dem Aufruf voninitBuffers():

js
// Load textureconst texture = loadTexture(gl, "cubetexture.png");// Flip image pixels into the bottom-to-top order that WebGL expects.gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);

Hinweis:Schließlich laden Sie die Dateicubetexture.png in das gleiche lokale Verzeichnis wie Ihre JavaScript-Dateien herunter.

Abbilden der Textur auf die Flächen

Zu diesem Zeitpunkt ist die Textur geladen und einsatzbereit. Aber bevor wir sie verwenden können, müssen wir das Mapping der Texturkoordinaten auf die Scheitelpunkte der Flächen unseres Würfels etablieren. Dies ersetzt den bisher vorhandenen Code zur Konfiguration von Farben für jede der Flächen des Würfels ininitBuffers().

Hinweis:Fügen Sie diese Funktion zu Ihrem Modul "init-buffer.js" hinzu:

js
function initTextureBuffer(gl) {  const textureCoordBuffer = gl.createBuffer();  gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);  const textureCoordinates = [    // Front    0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,    // Back    0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,    // Top    0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,    // Bottom    0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,    // Right    0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,    // Left    0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,  ];  gl.bufferData(    gl.ARRAY_BUFFER,    new Float32Array(textureCoordinates),    gl.STATIC_DRAW,  );  return textureCoordBuffer;}

Zuerst erzeugt dieser Code einen WebGL-Puffer, in dem wir die Texturkoordinaten für jede Fläche speichern werden, dann binden wir diesen Puffer als das Array, in das wir schreiben werden.

Das ArraytextureCoordinates definiert die Texturkoordinaten entsprechend jedem Scheitelpunkt jeder Fläche. Beachten Sie, dass die Texturkoordinaten im Bereich von 0,0 bis 1,0 liegen; die Dimensionen von Texturen sind normalisiert auf einen Bereich von 0,0 bis 1,0 unabhängig von ihrer tatsächlichen Größe, für das Ziel des Textur-Mappings.

Sobald wir das Textur-Mapping-Array eingerichtet haben, übergeben wir das Array in den Puffer, sodass WebGL diese Daten zur Verfügung hat.

Dann geben wir den neuen Puffer zurück.

Als Nächstes müssen wirinitBuffers() aktualisieren, um den Texturkoordinaten-Puffer anstelle des Farben-Puffers zu erstellen und zurückzugeben.

Hinweis:Ersetzen Sie in der FunktioninitBuffers() Ihres Moduls "init-buffers.js" den Aufruf voninitColorBuffer() durch die folgende Zeile:

js
const textureCoordBuffer = initTextureBuffer(gl);

Hinweis:Ersetzen Sie in der FunktioninitBuffers() Ihres Moduls "init-buffers.js" diereturn-Anweisung durch die folgende:

js
return {  position: positionBuffer,  textureCoord: textureCoordBuffer,  indices: indexBuffer,};

Aktualisierung der Shader

Das Shader-Programm muss ebenfalls aktualisiert werden, um die Texturen anstelle von festen Farben zu verwenden.

Der Vertex-Shader

Wir müssen den Vertex-Shader ersetzen, damit er anstelle von Farbwerten die Texturkoordinatendaten abruft.

Hinweis:Aktualisieren Sie dievsSource-Deklaration in Ihrermain()-Funktion wie folgt:

js
const vsSource = `    attribute vec4 aVertexPosition;    attribute vec2 aTextureCoord;    uniform mat4 uModelViewMatrix;    uniform mat4 uProjectionMatrix;    varying highp vec2 vTextureCoord;    void main(void) {      gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;      vTextureCoord = aTextureCoord;    }  `;

Die wesentliche Änderung hier ist, dass wir statt der Vertex-Farbe die Texturkoordinaten abrufen und an den Fragment-Shader übergeben; dies wird die Position innerhalb der Textur angeben, die dem Vertex entspricht.

Der Fragment-Shader

Auch der Fragment-Shader muss aktualisiert werden.

Hinweis:Aktualisieren Sie diefsSource-Deklaration in Ihrermain()-Funktion wie folgt:

js
const fsSource = `    varying highp vec2 vTextureCoord;    uniform sampler2D uSampler;    void main(void) {      gl_FragColor = texture2D(uSampler, vTextureCoord);    }  `;

Anstelle eines Farbwertes, der der Farbe des Fragments zugewiesen wird, wird die Farbe des Fragments durch das Abrufen desTexels (das ist, das Pixel innerhalb der Textur) basierend auf dem Wert vonvTextureCoord berechnet, das analog zu den Farben zwischen den Vertexen interpoliert wird.

Attribute und Uniform Locations

Da wir ein Attribut geändert und ein Uniform hinzugefügt haben, müssen wir ihre Positionen suchen.

Hinweis:Aktualisieren Sie dieprogramInfo-Deklaration in Ihrermain()-Funktion wie folgt:

js
const programInfo = {  program: shaderProgram,  attribLocations: {    vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),    textureCoord: gl.getAttribLocation(shaderProgram, "aTextureCoord"),  },  uniformLocations: {    projectionMatrix: gl.getUniformLocation(shaderProgram, "uProjectionMatrix"),    modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"),    uSampler: gl.getUniformLocation(shaderProgram, "uSampler"),  },};

Zeichnen des texturierten Würfels

Die Änderungen an derdrawScene()-Funktion sind einfach.

Hinweis:Fügen Sie in der FunktiondrawScene() Ihres Moduls "draw-scene.js" folgende Funktion hinzu:

js
// tell webgl how to pull out the texture coordinates from bufferfunction setTextureAttribute(gl, buffers, programInfo) {  const num = 2; // every coordinate composed of 2 values  const type = gl.FLOAT; // the data in the buffer is 32-bit float  const normalize = false; // don't normalize  const stride = 0; // how many bytes to get from one set to the next  const offset = 0; // how many bytes inside the buffer to start from  gl.bindBuffer(gl.ARRAY_BUFFER, buffers.textureCoord);  gl.vertexAttribPointer(    programInfo.attribLocations.textureCoord,    num,    type,    normalize,    stride,    offset,  );  gl.enableVertexAttribArray(programInfo.attribLocations.textureCoord);}

Hinweis:Ersetzen Sie in der FunktiondrawScene() Ihres Moduls "draw-scene.js" den Aufruf vonsetColorAttribute() durch die folgende Zeile:

js
setTextureAttribute(gl, buffers, programInfo);

Fügen Sie dann Code hinzu, um die Textur anzugeben, die auf die Flächen abzubilden ist.

Hinweis:Fügen Sie in IhrerdrawScene()-Funktion, direkt nach den beiden Aufrufen vongl.uniformMatrix4fv(), den folgenden Code hinzu:

js
// Tell WebGL we want to affect texture unit 0gl.activeTexture(gl.TEXTURE0);// Bind the texture to texture unit 0gl.bindTexture(gl.TEXTURE_2D, texture);// Tell the shader we bound the texture to texture unit 0gl.uniform1i(programInfo.uniformLocations.uSampler, 0);

WebGL stellt mindestens 8 Textureinheiten bereit; die erste davon istgl.TEXTURE0. Wir sagen WebGL, dass wir Einheit 0 beeinflussen möchten. Dann rufen wirbindTexture() auf, das die Textur an denTEXTURE_2D-Bindepunkt der Textureinheit 0 bindet. Anschließend teilen wir dem Shader mit, dass für denuSampler die Textureinheit 0 verwendet wird.

Schließlich fügen Sietexture als Parameter zur FunktiondrawScene() hinzu, sowohl dort, wo sie definiert ist, als auch dort, wo sie aufgerufen wird.

Aktualisieren Sie die Deklaration IhrerdrawScene()-Funktion, um den neuen Parameter hinzuzufügen:

js
function drawScene(gl, programInfo, buffers, texture, cubeRotation) {  // …}

Aktualisieren Sie den Ort in Ihrermain()-Funktion, an dem SiedrawScene() aufrufen:

js
drawScene(gl, programInfo, buffers, texture, cubeRotation);

An diesem Punkt sollte der rotierende Würfel betriebsbereit sein.

Vollständigen Code anzeigen |Dieses Demo auf einer neuen Seite öffnen

Cross-Domain-Texturen

Das Laden von WebGL-Texturen unterliegt Zugriffskontrollen zwischen Domänen. Damit Ihr Inhalt eine Textur von einer anderen Domäne lädt, muss eine CORS-Zulassung erlangt werden. SieheHTTP-Zugriffskontrolle für Details zu CORS.

Da WebGL jetzt erfordert, dass Texturen aus sicheren Kontexten geladen werden, können Sie keine Texturen verwenden, die ausfile:/// URLs in WebGL geladen werden. Das bedeutet, dass Sie einen sicheren Webserver benötigen, um Ihren Code zu testen und bereitzustellen. Für lokale Tests lesen Sie unseren LeitfadenWie richten Sie einen lokalen Testserver ein? für Hilfe.

Lesen Sie diesenhacks.mozilla.org Artikel für eine Erklärung, wie Sie CORS-zugelassene Bilder als WebGL-Texturen verwenden.

Vergiftete (schreibgeschützte) 2D-Canvas-Elemente können nicht als WebGL-Texturen verwendet werden. Ein 2D<canvas> wird beispielsweise vergiftet, wenn ein Bild von einer anderen Domäne darauf gezeichnet wird.

Help improve MDN

Learn how to contribute Diese Seite wurde automatisch aus dem Englischen übersetzt.

[8]ページ先頭

©2009-2025 Movatter.jp