Пишите в множественные 3D-текстуры во фрагментном шейдере OpenGL

У меня есть 3D-текстура, в которую я записываю данные и использую ее в качестве вокселей во фрагментном шейдере следующим образом:

#extension GL_ARB_shader_image_size : enable
...
layout (binding = 0, rgba8) coherent uniform image3D volumeTexture;
...
void main(){
vec4 fragmentColor = ...
vec3 coords = ...
imageStore(volumeTexture, ivec3(coords), fragmentColor);
}

и текстура определяется таким образом

glGenTextures(1, &volumeTexture);
glBindTexture(GL_TEXTURE_3D, volumeTexture);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, volumeDimensions, volumeDimensions, volumeDimensions, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);

и тогда это, когда я должен использовать это

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, volumeTexture);

Теперь моя проблема заключается в том, что я хотел бы иметь эту версию без карты и без использования функции opengl, потому что я заметил, что она очень медленная. Поэтому я думал о том, чтобы писать в 3D-текстуре на всех уровнях одновременно, например, максимальное разрешение составляет 512 ^ 3, и когда я записываю 1 воксельное значение VALUE в этом 3-мттексе, я также пишу 0,125 * VALUE для 256 ^ 3. воксел и 0,015625 * VALUE для 126 ^ 3 и т. д. Так как я использую imageStore, который использует атомарность, все значения будут записаны, и с использованием этих весов я автоматически получу среднее значение (не совсем как интерполяция, но я мог бы получить приятный результат тем не мение).
Итак, мой вопрос: как лучше всего иметь несколько 3d-текстур и записывать их все одновременно?

3

Решение

Я полагаю, что аппаратное мипмапирование происходит так же быстро, как и вы. Я всегда предполагал, что попытка создания пользовательского мипмапинга будет медленнее, поскольку вам придется по очереди связывать и растеризовывать каждый слой вручную. Атомика вызовет огромное раздор и будет удивительно медленной. Даже без атомарности вы бы отрицали красивую O (log n) конструкцию мипмапов.

Вы должны быть действительно осторожнее с imageStore что касается порядка доступа и кеша. Я бы начал здесь и попробовал бы выполнить другую индексацию (например, строка / столбец или столбец / строка).

Вы можете попробовать нарисовать текстуру более старым способом, привязав ее к FBO и нарисовав полноэкранный треугольник (большой треугольник, который покрывает область просмотра) с помощью glDrawElementsInstanced, В геометрическом шейдере установите gl_Layer к идентификатору экземпляра. Растеризатор создает фрагменты для х / у, а слой дает z.

Наконец, 512 ^ 3 — это просто огромная текстура даже по сегодняшним стандартам. Может быть, выясните свою теоретическую максимальную пропускную способность графического процессора, чтобы понять, как далеко вы находитесь. НАПРИМЕР. Допустим, ваш графический процессор может делать 200 ГБ / с. В любом случае вы, вероятно, получите только 100 в хорошем случае. Ваша текстура 512 ^ 3 имеет размер 512 МБ, поэтому вы можете записать ее в течение ~ 5 мс (возможно, это выглядит очень быстро, возможно, я допустил ошибку). Ожидайте некоторую дополнительную нагрузку и задержку от остальной части конвейера, порождения и выполнения потоков и т. Д. Если вы пишете сложные вещи, то пропускная способность памяти не является узким местом, и моя оценка выходит за рамки. Поэтому попробуйте сначала написать нули. Затем попробуйте изменить coords xyz порядок.


Обновление: вместо того, чтобы использовать фрагментный шейдер для создания ваших потоков, вместо него можно использовать вершинный шейдер, и теоретически он избегает накладных расходов растеризатора, хотя я видел случаи, когда он не работает так же хорошо. Вы glEnable(GL_RASTERIZER_DISCARD), glDrawArrays(GL_POINTS, 0, numThreads) и использовать gl_VertexID в качестве индекса вашей нити.

2

Другие решения