共计 8215 个字符,预计需要花费 21 分钟才能阅读完成。
- 原文地址: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);
}
酷???? 当初咱们能够渲染图像了,然而还有很多对于纹理的常识须要学习,咱们今天见。