Arhn - архитектура программирования

Как gl.drawElements находит соответствующий буфер массива вершин?

и спасибо вам заранее.

Я новичок в Webgl и не совсем понимаю связь между методом drawElements и текущим буфером вершин, который я хочу нарисовать. Я примерно понимаю, что происходит для метода drawArray (например, создание буфера, привязка его к контексту, заполнение его данными, указание на соответствующий атрибут, рисование). Но когда я пытаюсь сделать то же самое с массивом индексов и меньшим количеством данных вершин, я сталкиваюсь с ошибкой такого типа:

[.Offscreen-For-WebGL-0x7fae2b940800]GL ERROR :GL_INVALID_OPERATION : glDrawElements: bound to target 0x8893 : no buffer

Возможно, подсказка моего кода может вам помочь.

    const cube = new Program(renderer.gl, vertex3d, fragment); // my webgl program
    const cubeData = new Cube(); // Only array of vertices/indices
    const cubeVertexPosition = new ArrayBuffer(renderer.gl, cubeData.vertices, 'STATIC_DRAW'); // ARRAY_BUFFER
    const cubeVertexIndices = new IndexBuffer(renderer.gl, renderer.gl.UNSIGNED_SHORT, cubeData.indices, 'STATIC_DRAW'); // ELEMENT_ARRAY_BUFFER
cubeVertexPosition.attribute('aPosition', 3, 'FLOAT', false); // define attribute corresponding in vertex shader
    cubeVertexPosition.attributePointer(cube); // enableVertexAttribArray + vertexAttribPointer
    [...]
    cubeVertexIndices.draw('TRIANGLES', 0, 36); // drawElements with gl.UNSIGNED_SHORT type

Мне удалось нарисовать его с помощью drawArray :)

([[...] - только преобразование матриц для униформы);

Может быть, у вас есть быстрые советы, которые могли бы помочь мне понять эту черную магию,

Большое спасибо !

10.11.2017

Ответы:


1

drawArray использовать только одну или несколько ARRAY_BUFFER откуда рисуются вершины в порядке их расположения в буферах, из параметра first для параметра count.

drawElements используйте один или несколько ARRAY_BUFFER И ELEMENT_ARRAY_BUFFER, которые содержат индексы, указывающие на ARRAY_BUFFER вершины для рисования. В drawElements параметр count указывает количество индексов для чтения в ELEMENT_ARRAY_BUFFER, а offset указывает смещение в байтах, с которого начинается чтение ELEMENT_ARRAY_BUFFER (обычно FirstIndex*sizeof(type), где type может быть UNSIGNED_BYTE (1 байт), UNSIGNED_SHORT (2 байта) или UNSIGNED_INT (4 байта).

ELEMENT_ARRAY_BUFFER:

[0][1][2][1][2][0][1][2][3][3][1][2][3][4][5][...

ARRAY_BUFFER:

|   0   |   1    |    2   |    3   |    4   | ...
[x][y][z][x][y][z][x][y][z][x][y][z][x][y][z][...

Для правильной работы размер offset + count*sizeof(type) не должен превышать размер ELEMENT_ARRAY_BUFFER в байтах. Кроме того, индекс элементов в ELEMENT_ARRAY_BUFFER должен быть меньше количества вершин, содержащихся в ARRAY_BUFFER.

Как и drawArray, drawElements использует текущий связанный буфер (буферы) (с настроенными указателями атрибутов) в качестве источника данных. Разница с drawElements заключается в том, что вы должны указать буфер дополнительных элементов (индексов), используя цель ELEMENT_ARRAY_BUFFER, например:

gl.bindBuffer(gl.ARRAY_BUFFER, myVerticesA);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, myIndicesA);
// configure attributes pointers here
gl.drawElements(gl.TRIANGLES, 12345, gl.UNSIGNED_SHORT, 0);

«Как» drawElements будет принимать атрибуты в буфере(ах) ARRAY_BUFFER в соответствии с индексами, хранящимися в ELEMENT_ARRAY_BUFFER, зависит от того, как вы настроили указатели атрибутов.

Предположим, что у вас есть следующий буфер вершин с чередующимися позициями, нормалями и координатами текстуры:

|    p0    ||    n0    ||  t0  ||    p1    ||    n1    ||  t1  |
[px][py][pz][nx][ny][nz][tu][tv][px][py][pz][nx][ny][nz][tu][tv][...

Мы определяем указатели атрибутов следующим образом:

let stride = 8*4;  // 8*float (8 * 4 bytes)
let offp = 0;   // positions at beginning
let offn = 3*4; // normals after 3*float position.
let offt = 6*4; // tex coords after 3*float position + 3*float normal 
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, stride, offp);
gl.vertexAttribPointer(1, 3, gl.FLOAT, false, stride, offn);
gl.vertexAttribPointer(2, 2, gl.FLOAT, false, stride, offt);

Используя буфер элементов (индексов), GL просто сдвигает позиции указателей в соответствии с индексами, хранящимися в буфере ELEMENT_ARRAY_BUFFER:

 // pseudo-code
 for(let i = start_elem; i < start_elem+count_elem; i++) {

   let index = ELEMENT_ARRAY_BUFFER[i];

   attrib[0] = ARRAY_BUFFER[(index*stride)+offp];
   attrib[1] = ARRAY_BUFFER[(index*stride)+offn];
   attrib[2] = ARRAY_BUFFER[(index*stride)+offt];

 }
10.11.2017
  • Спасибо за ваш быстрый ответ. Это немного более явно для меня. Ваша схема двух массивов объясняет, как все связано, но ссылка явно не ясна. Есть способ указать, какой ARRAY_BUFFER связан с ELEMENT_ARRAY_BUFFER? 11.11.2017
  • Ссылка зависит от того, какие буферы связаны при вызове drawElements. Вы вызываете bindBuffer с ELEMENT_ARRAY_BUFFER как target, тогда у вас есть привязка буфера элементов (индексов), вы вызываете bindBuffer с ARRAY_BUFFER, у вас есть привязка буфера атрибутов (вершин). Это так просто. Я редактирую свой ответ, чтобы указать эту часть. 11.11.2017

  • 2

    Код, который вы разместили, не является WebGL. Вы используете какую-то библиотеку, которая ясна из кода. Такие вещи, как Program, IndexBuffer, ArrayBuffer, являются частью библиотеки, которую вы используете. То, как эта библиотека работает, зависит от этой библиотеки.

    В общем, WebGL имеет шейдеры, вершинный шейдер, работа которого состоит в том, чтобы установить gl_Position в координату пространства отсечения для каждой вершины, и фрагментный шейдер, работа которого состоит в том, чтобы установить gl_FragColor в цвет для каждого пикселя.

    Вершинный шейдер обычно получает данные о позициях из атрибутов. Атрибуты обычно получают свои данные из буферов. Вы указываете атрибуту, из какого буфера получать данные, сначала привязывая буфер к точке привязки ARRAY_BUFFER с помощью gl.bindBuffer(gl.ARRAY_BUFFER, someBuffer), а затем вызывая gl.vertexAttribPointer, который сообщает WebGL, как вы хотите получить данные из этого буфера (какого типа данные, сколько значений в нем). приходится на вершину, сколько байт пропускать между вершинами, как далеко в буфере начинать). gl.vertexAttribPointer сохраняет всю эту информацию для данного атрибута и ссылку на текущий буфер, который был привязан к точке привязки ARRAY_BUFFER, поэтому вы можете свободно привязывать там другой буфер для настройки другого атрибута.

    При вызове gl.drawArrays данные будут извлекаться из буферов, как вы указали в атрибутах шейдера, один набор значений для каждой итерации шейдера

    Что касается gl.drawElements, то он занимает еще один буфер, привязанный к ELEMENT_ARRAY_BUFFER, и когда вы вызываете gl.drawElements, вы сообщаете ему тип данных в этом буфере (gl.UNSIGNED_BYTE или gl.UNSIGNED_SHORT). Затем он использует значения этого буфера для извлечения значений из буфера атрибутов.

    gl.drawElements точно такое же, как gl.drawArrays, если вы поместите простое возрастающее значение в буфер. Пример

     const offset  = 0;
     const numVerts = 100;
    
     // process 100 vertices from the buffers pointed to by the attributes
     // in order 0 to 99
     gl.drawArrays(gl.POINTS, offset, numVerts)
    

    Фактически такой же, как

     // fill a buffer with numbers 0 to 99 (0 to numVerts)
     const numVerts = 100;
     const indexData = new Uint16Array(numVerts);
     for (let i = 0; i < numVerts; ++i) {
       indexData[i] = i;
     }
     const indexBuffer = gl.createBuffer();
     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
     gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW);
    
     // process 100 vertices from the buffers pointed to by the attributes
     // in order 0 to 99
     const offset  = 0;
     gl.drawElements(gl.POINTS, numVerts, gl.UNSIGNED_SHORT, offset);
    

    но, конечно, поскольку во втором случае вы предоставили indexData, он не должен быть последовательным.

    Я бы посоветовал прочитать другие учебники по webgl

    11.11.2017
  • Спасибо вам gman за ваш ответ! Извините за похожий код библиотеки. Это просто сделать тот же урок, который вы мне дали, без переписывания снова и снова. Дело в том, что я понимаю метод drawArray и успешно применял его раньше. Теперь я сосредоточусь на методе drawElements, который я не нахожу программным методом для привязки буфера массива к буферу индексов. 11.11.2017
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..