• 原文地址:Day 8. Textures
  • 原文作者:Andrei Lesnitsky

这是 WebGL 系列的第 8 天教程,每天都有新文章公布。

订阅或者退出邮件列表以便及时获取更新内容。

源代码在这里

嘿????欢送回到 WebGL 系列教程。

咱们曾经学习了几种将色彩数据传递到着色器的办法,然而还有另外一种办法,它十分弱小,也就是明天咱们将学习的纹理。

让咱们创立简略的着色器:

???? src/shaders/texture.f.glsl

precision mediump float;  void main() { gl_FragColor = vec4(1, 0, 0, 1);}  

???? src/shaders/texture.v.glsl

attribute vec2 position;  void main() { gl_Position = vec4(position, 0, 1);}  

???? src/texture.js

import vShaderSource from './shaders/texture.v.glsl';import fShaderSource from './shaders/texture.f.glsl';  

获取 webgl 上下文:

???? src/texture.js

 import vShaderSource from './shaders/texture.v.glsl'; import fShaderSource from './shaders/texture.f.glsl';+ + const canvas = document.querySelector('canvas');+ const gl = canvas.getContext('webgl');  

创立着色器:

???? src/texture.js

 import vShaderSource from './shaders/texture.v.glsl'; import fShaderSource from './shaders/texture.f.glsl';+ import { compileShader } from './gl-helpers'; const canvas = document.querySelector('canvas'); const gl = canvas.getContext('webgl');+ + const vShader = gl.createShader(gl.VERTEX_SHADER);+ const fShader = gl.createShader(gl.FRAGMENT_SHADER);+ + compileShader(gl, vShader, vShaderSource);+ compileShader(gl, fShader, fShaderSource);  

增加 program

???? src/texture.js

 compileShader(gl, vShader, vShaderSource); compileShader(gl, fShader, fShaderSource);+ + const program = gl.createProgram();+ + gl.attachShader(program, vShader);+ gl.attachShader(program, fShader);+ + gl.linkProgram(program);+ gl.useProgram(program);  

创立一个顶点地位缓冲区并填充数据:

???? src/texture.js

 import vShaderSource from './shaders/texture.v.glsl'; import fShaderSource from './shaders/texture.f.glsl'; import { compileShader } from './gl-helpers';+ import { createRect } from './shape-helpers';+  const canvas = document.querySelector('canvas'); const gl = canvas.getContext('webgl'); gl.linkProgram(program); gl.useProgram(program);+ + const vertexPosition = new Float32Array(createRect(-1, -1, 2, 2));+ const vertexPositionBuffer = gl.createBuffer();+ + gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);+ gl.bufferData(gl.ARRAY_BUFFER, vertexPosition, gl.STATIC_DRAW);  

设置地位属性:

???? src/texture.js

 gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer); gl.bufferData(gl.ARRAY_BUFFER, vertexPosition, gl.STATIC_DRAW);+ + const attributeLocations = {+     position: gl.getAttribLocation(program, 'position'),+ };+ + gl.enableVertexAttribArray(attributeLocations.position);+ gl.vertexAttribPointer(attributeLocations.position, 2, gl.FLOAT, false, 0, 0);  

设置索引缓冲区:

???? src/texture.js

 gl.enableVertexAttribArray(attributeLocations.position); gl.vertexAttribPointer(attributeLocations.position, 2, gl.FLOAT, false, 0, 0);+ + const vertexIndices = new Uint8Array([0, 1, 2, 1, 2, 3]);+ const indexBuffer = gl.createBuffer();+ + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, vertexIndices, gl.STATIC_DRAW);  

同时公布一个绘制调用:

???? src/texture.js

 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, vertexIndices, gl.STATIC_DRAW);+ + gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0);  

当初,咱们能够进行纹理的操作:

您能够将图像上传到 GPU 并应用它来计算像素色彩。在通常状况下,当画布大小雷同或至多与图像大小成比例时,咱们能够读取每个像素色彩并将其用作 gl_FragColor

让咱们帮忙加载图像:

???? src/gl-helpers.js

 throw new Error(log); } }+ + export async function loadImage(src) {+     const img = new Image();+ +     let _resolve;+     const p = new Promise((resolve) => _resolve = resolve);+ +     img.onload = () => {+         _resolve(img);+     }+ +     img.src = src;+ +     return p;+ }  

加载图像并创立 webgl 纹理:

???? src/texture.js

 import vShaderSource from './shaders/texture.v.glsl'; import fShaderSource from './shaders/texture.f.glsl';- import { compileShader } from './gl-helpers';+ import { compileShader, loadImage } from './gl-helpers'; import { createRect } from './shape-helpers';+ import textureImageSrc from '../assets/images/texture.jpg'; const canvas = document.querySelector('canvas'); const gl = canvas.getContext('webgl'); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, vertexIndices, gl.STATIC_DRAW);- gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0);+ loadImage(textureImageSrc).then((textureImg) => {+     const texture = gl.createTexture();+ +     gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0);+ });  

[GTI} 增加图片:

???? assets/images/texture.jpg

咱们还须要一个适合的 webpack 辅助加载:

???? package.json

 "homepage": "https://github.com/lesnitsky/webgl-month#readme", "devDependencies": { "raw-loader": "^3.0.0",+     "url-loader": "^2.0.1", "webpack": "^4.35.2", "webpack-cli": "^3.3.5" }  

???? webpack.config.js

 test: /.glsl$/, use: 'raw-loader', },+ +             {+                 test: /.jpg$/,+                 use: 'url-loader',+             }, ], },  

要对纹理进行操作,咱们须要与对缓冲区进行雷同的操作 – 将两者绑定:

???? src/texture.js

 loadImage(textureImageSrc).then((textureImg) => { const texture = gl.createTexture();+     gl.bindTexture(gl.TEXTURE_2D, texture);+  gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0); });  

并将图像上传到绑定的纹理:

???? src/texture.js

 gl.bindTexture(gl.TEXTURE_2D, texture);+     gl.texImage2D(+         gl.TEXTURE_2D,+     );+  gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0); });  

当初临时疏忽第二个参数,咱们稍后再探讨:

???? src/texture.js

 gl.texImage2D( gl.TEXTURE_2D,+         0, ); gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0);  

第三和第四个参数别离指定外部纹理格局和源(图像)格局,也就是图像中的 gl.RGBA。查看此页面以获取无关格局的更多详细信息

???? src/texture.js

 gl.texImage2D( gl.TEXTURE_2D, 0,+         gl.RGBA,+         gl.RGBA, ); gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0);  

另一个参数指定源类型 (0..255 示意 UNSIGNED_BYTE)

???? src/texture.js

 0, gl.RGBA, gl.RGBA,+         gl.UNSIGNED_BYTE, ); gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0);  

还有图像自身:

???? src/texture.js

 gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,+         textureImg, ); gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0);  

咱们还须要指定不同的纹理参数。在接下来的教程中,咱们将探讨这个参数。

???? src/texture.js

 textureImg, );+     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);+     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);+  gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0); });  

为了可能在着色器中应用纹理,咱们须要指定一个对立的 sampler2D 类型:

???? src/shaders/texture.f.glsl

 precision mediump float;+ uniform sampler2D texture;+  void main() { gl_FragColor = vec4(1, 0, 0, 1); }  

同时对立对 sampler2D 类型指定相应的值。有一种应用多种纹理的办法,咱们将在下一个教程中探讨它

???? src/texture.js

 position: gl.getAttribLocation(program, 'position'), };+ const uniformLocations = {+     texture: gl.getUniformLocation(program, 'texture'),+ };+  gl.enableVertexAttribArray(attributeLocations.position); gl.vertexAttribPointer(attributeLocations.position, 2, gl.FLOAT, false, 0, 0); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);+     gl.activeTexture(gl.TEXTURE0);+     gl.uniform1i(uniformLocations.texture, 0);+  gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0); });  

咱们还要将画布解析器传递给着色器

???? src/shaders/texture.f.glsl

 precision mediump float; uniform sampler2D texture;+ uniform vec2 resolution; void main() { gl_FragColor = vec4(1, 0, 0, 1);  

???? src/texture.js

 const uniformLocations = { texture: gl.getUniformLocation(program, 'texture'),+     resolution: gl.getUniformLocation(program, 'resolution'), }; gl.enableVertexAttribArray(attributeLocations.position); gl.activeTexture(gl.TEXTURE0); gl.uniform1i(uniformLocations.texture, 0);+     gl.uniform2fv(uniformLocations.resolution, [canvas.width, canvas.height]);+  gl.drawElements(gl.TRIANGLES, vertexIndices.length, gl.UNSIGNED_BYTE, 0); });  

将蕴含每个像素坐标的非凡变量 gl_FragCoordresolution 联结应用,咱们能够失去一个 texture coordinate (图像像素的坐标),纹理坐标范畴为 [0..1]

???? src/shaders/texture.f.glsl

 uniform vec2 resolution; void main() {+     vec2 texCoord = gl_FragCoord.xy / resolution; gl_FragColor = vec4(1, 0, 0, 1); }  

同时应用 texture2D 渲染整个图像。

???? src/shaders/texture.f.glsl

 void main() { vec2 texCoord = gl_FragCoord.xy / resolution;-     gl_FragColor = vec4(1, 0, 0, 1);+     gl_FragColor = texture2D(texture, texCoord); }  

酷???? 当初咱们能够渲染图像了,然而还有很多对于纹理的常识须要学习,咱们今天见。