- 原文地址: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_FragCoord
与 resolution
联结应用,咱们能够失去一个 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); }
酷???? 当初咱们能够渲染图像了,然而还有很多对于纹理的常识须要学习,咱们今天见。