Рубрики
Без рубрики

Webgl Месяц. День 16. Раскращая куб: буфер глубины и униформа массива

Изучение того, как передавать цвета лиц кубиков и уменьшить количество данных вершины с помощью униформы массива. Tagged с Webgl, начинающими, JavaScript.

Присоединяйтесь к списку рассылки Чтобы получить новые сообщения прямо на ваш почтовый ящик

Построен с

День 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”