Упаковка данных в WebGL: float64 / int64Arrays в Chrome - PullRequest
4 голосов
/ 06 марта 2012

[Правка - одна проблема почти исправлена, изменен вопрос для ее отражения]

Я работаю над проектом webGL облака точек в Chrome, который отображает миллионы точек одновременно.

Чтобы сделать его более эффективным, я попытался упаковать свои данные - 6 xyz и rgb float - в два 64-битных целых числа (xy & zrgb) и планировал распаковать их в шейдер.

Я занимаюсь разработкой в ​​Chrome, и, на самом деле, webkit не поддерживает какие-либо типы 64-битных массивов ... даже с использованием Canary.Кроме того, Firefox поддерживает 64-битные массивы, но я все еще получаю сообщение об ошибке.

Проблемы возникают с этой строкой:

gl.bufferData(gl.ARRAY_BUFFER, new Float64Array(data.xy), gl.DYNAMIC_DRAW);

В Chrome я получаю ArrayBufferView не достаточно маленькое положительное целое число, в FF я получаю «недопустимые аргументы».

Итаку меня вопрос, есть ли способ отправить 64-битные числа в шейдер, предпочтительно с использованием Chrome, если нет, в FF?

Кроме того, хорошая ли это упаковка данных?Любые советы?!

Спасибо,

Джон

1 Ответ

8 голосов
/ 06 марта 2012

Важно знать, что WebGL на самом деле не заботится ни о каком формате TypedArray, который вы предоставляете.Независимо от того, что вы даете, он будет обрабатывать его как непрозрачный двоичный буфер.Важно то, как вы настроили свои vertexAttribPointer.Это учитывает некоторые очень удобные методы перетасовки данных назад и вперед.Например: я регулярно читаю Uint8Array из двоичного файла и предоставляю его как данные буфера, но связываю его как числа с плавающей запятой и целые числа.

TypedArrays также имеют прекрасную возможность выступать в качестве представлений в других типах массивов, что делаетлегко смешивать типы (если у вас нет проблем с выравниванием).В вашем конкретном случае я бы предложил сделать что-то вроде этого:

var floatBuffer = new Float32Array(verts.length * 4);
var byteBuffer = new Uint8Array(floatBuffer); // View the floatBuffer as bytes

for(i = 0; i < verts.length; ++i) {
    floatBuffer[i * 4 + 0] = verts.x;
    floatBuffer[i * 4 + 1] = verts.y;
    floatBuffer[i * 4 + 2] = verts.z;

    // RGBA values expected as 0-255
    byteBuffer[i * 16 + 12] = verts.r;
    byteBuffer[i * 16 + 13] = verts.g;
    byteBuffer[i * 16 + 14] = verts.b;
    byteBuffer[i * 16 + 15] = verts.a;
}

var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, floatBuffer, gl.STATIC_DRAW);

Это будет загружать плотно упакованный буфер вершин, содержащий 3 32-битных числа с плавающей запятой и 1 32-битный цвет в графический процессор.Не такой маленький, как предлагаемая пара 64-битных битов, но графический процессор, скорее всего, будет работать с ним лучше.Привязывая его для рендеринга позже, вы должны сделать это так:

gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.vertexAttribPointer(attributes.aPosition, 3, gl.FLOAT, false, 16, 0);
gl.vertexAttribPointer(attributes.aColor, 4, gl.UNSIGNED_BYTE, false, 16, 12);

С соответствующим кодом шейдера, похожим на это:

attribute vec3 aPosition;
attribute vec4 aColor;

void main() {
    // Manipulate the position and color as needed
}

Таким образом, вы получаете преимущества от использованиячередующиеся массивы, с которыми любит работать графический процессор, и которые должны отслеживать только один буфер (бонус!), плюс вы не теряете пространство, используя полный float для каждого компонента цвета.Если вы ДЕЙСТВИТЕЛЬНО хотите стать маленькими, вы можете использовать короткие позиции вместо плавающих для позиций, но мой прошлый опыт показал, что настольные графические процессоры не очень быстрые при использовании коротких атрибутов.

Надеюсь, чтопомогает!

...