Присоединяйтесь к списку рассылки Чтобы получить новые сообщения прямо на ваш почтовый ящик
Построен с
День 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”