Присоединяйтесь к списку рассылки Чтобы получить новые сообщения прямо на ваш почтовый ящик
Построен с
День 16. Раскращая куб и изучение глубинного буфера
Привет 👋
Добро пожаловать в Webgl Месяц
Вчера мы сделали куб, но все лица имеют одинаковый цвет, давайте изменим это.
Давайте определим цвета лица
📄 SRC/3D.JS
20, 21, 22, 20, 22, 23, // left ]); + const faceColors = [ + [1.0, 1.0, 1.0, 1.0], // Front face: white + [1.0, 0.0, 0.0, 1.0], // Back face: red + [0.0, 1.0, 0.0, 1.0], // Top face: green + [0.0, 0.0, 1.0, 1.0], // Bottom face: blue + [1.0, 1.0, 0.0, 1.0], // Right face: yellow + [1.0, 0.0, 1.0, 1.0], // Left face: purple + ]; + const vertexBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, cubeVertices, gl.STATIC_DRAW); const indexBuffer = new GLBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
Теперь нам нужно повторить цвета лица для каждой вершины лица
📄 SRC/3D.JS
[1.0, 0.0, 1.0, 1.0], // Left face: purple
];
+ const colors = [];
+
+ for (var j = 0; j < faceColors.length; ++j) {
+ const c = faceColors[j];
+ colors.push(
+ ...c, // vertex 1
+ ...c, // vertex 2
+ ...c, // vertex 3
+ ...c, // vertex 4
+ );
+ }
+
+
const vertexBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, cubeVertices, gl.STATIC_DRAW);
const indexBuffer = new GLBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
и создать буфер webgl
📄 SRC/3D.JS
const vertexBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, cubeVertices, gl.STATIC_DRAW); + const colorsBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); const indexBuffer = new GLBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); vertexBuffer.bind(gl);
Далее нам нужно определить атрибут, чтобы перенести цвет от JS к вершинскому шейдеру, и варьируется, чтобы перенести его от вершины к фрагментированию шейдера
📄 src/shaders/3d.v.glsl
attribute vec3 position;
+ attribute vec4 color;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
+ varying vec4 vColor;
+
void main() {
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
+ vColor = color;
}
и используйте его вместо жестко -кодированного красного цвета в фрагменте.
📄 src/shaders/3d.f.glsl
precision mediump float;
+ varying vec4 vColor;
+
void main() {
- gl_FragColor = vec4(1, 0, 0, 1);
+ gl_FragColor = vColor;
}
и, наконец, настроить атрибут Vertex в JS
📄 SRC/3D.JS
vertexBuffer.bind(gl); gl.vertexAttribPointer(programInfo.attributeLocations.position, 3, gl.FLOAT, false, 0, 0); + colorsBuffer.bind(gl); + gl.vertexAttribPointer(programInfo.attributeLocations.color, 4, gl.FLOAT, false, 0, 0); + const modelMatrix = mat4.create(); const viewMatrix = mat4.create(); const projectionMatrix = mat4.create();
Хорошо, цвета есть, но что -то не так
Давайте посмотрим, что происходит более подробно, при постепенном рендеринге лицах
let count = 3;
function frame() {
if (count <= index.data.length) {
gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_BYTE, 0);
count += 3;
setTimeout(frame, 500);
}
}
Похоже, треугольники, которые позже перекрывают те, которые на самом деле ближе к зрителю 😕 Как мы это исправляем?
📄 SRC/3D.JS
gl.linkProgram(program); gl.useProgram(program); + gl.enable(gl.DEPTH_TEST); + const programInfo = setupShaderInput(gl, program, vShaderSource, fShaderSource); const cubeVertices = new Float32Array([
После того, как вершины собираются в примитивы (треугольники) фрагментные шейдеры, рисует каждый пиксель внутри треугольника, но перед расчетом цветового фрагмента проходит некоторые «тесты». Одним из этих тестов является глубина, и нам нужно вручную включить это.
Другие типы тестов:
gl.scissors_test– Является ли фрагмент внутри определенного треугольника (не путайте это с помощью Viewport, существует специальная ножница [ https://developer.mozilla.org/en-us/docs/web/api/webglrenderingcontext/scissor ] Метод)глин Stencil_test– Подобно глубине, но мы можем вручную определить «маску» и отказаться от некоторых пикселей (мы будем работать с буфером трафарета в следующих учебных пособиях)- Тест на владение Pixel – некоторые пиксели на экране могут принадлежать другим контекстам OpenGL (представьте, что ваш браузер перекрывается другим окном), поэтому эти пиксели отбрасываются (не окрашены)
Круто, теперь у нас есть работающий 3D -куб, но мы дублируем множество цветов, чтобы заполнить буфер вершины, мы можем сделать это лучше? Мы используем фиксированную цветовую палитру (6 цветов), поэтому мы можем передать эти цвета шейдеру и использовать только индекс этого цвета.
Давайте отбросим Color attrbiute и представим Colorindex вместо этого
📄 src/shaders/3d.v.glsl
attribute vec3 position; - attribute vec4 color; + attribute float colorIndex; uniform mat4 modelMatrix; uniform mat4 viewMatrix;
Шейдеры поддерживают «массивы» униформы, поэтому мы можем передать нашу цветовую палитру в этот массив и использовать индекс, чтобы получить цвет из него
📄 src/shaders/3d.v.glsl
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
+ uniform vec4 colors[6];
varying vec4 vColor;
void main() {
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
- vColor = color;
+ vColor = colors[int(colorIndex)];
}
Нам нужно внести соответствующие изменения в атрибут индекса настройки цвета
📄 SRC/3D.JS
const colors = [];
for (var j = 0; j < faceColors.length; ++j) {
- const c = faceColors[j];
- colors.push(
- ...c, // vertex 1
- ...c, // vertex 2
- ...c, // vertex 3
- ...c, // vertex 4
- );
+ colors.push(j, j, j, j);
}
gl.vertexAttribPointer(programInfo.attributeLocations.position, 3, gl.FLOAT, false, 0, 0);
colorsBuffer.bind(gl);
- gl.vertexAttribPointer(programInfo.attributeLocations.color, 4, gl.FLOAT, false, 0, 0);
+ gl.vertexAttribPointer(programInfo.attributeLocations.colorIndex, 1, gl.FLOAT, false, 0, 0);
const modelMatrix = mat4.create();
const viewMatrix = mat4.create();
Чтобы заполнить униформу массива, нам нужно установить каждый \ “Item \” в этом массиве индивидуально, как
gl.uniform4fv(programInfo.uniformLocations[`colors[0]`], color[0]); gl.uniform4fv(programInfo.uniformLocations[`colors[1]`], colors[1]); gl.uniform4fv(programInfo.uniformLocations[`colors[2]`], colors[2]); ...
Очевидно, это можно сделать в петле.
📄 SRC/3D.JS
colors.push(j, j, j, j);
}
+ faceColors.forEach((color, index) => {
+ gl.uniform4fv(programInfo.uniformLocations[`colors[${index}]`], color);
+ });
const vertexBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, cubeVertices, gl.STATIC_DRAW);
const colorsBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
Хорошо, у нас такой же результат, но используя в 4 раза меньше данных в атрибутах.
Это может показаться ненужной оптимизацией, но это может помочь, когда вам придется часто обновлять большие буферы
Вот и все на сегодня!
Увидимся в следующих учебниках 👋
Это серия постов в блоге, связанных с WebGL. Новый пост будет доступен каждый день
Присоединяйтесь к списку рассылки Чтобы получить новые сообщения прямо на ваш почтовый ящик
Исходный код доступен здесь
Построен с
Оригинал: “https://dev.to/lesnitsky/webgl-month-day-16-colorizing-cube-depth-buffer-and-array-uniforms-4nhc”