как вернуть маску сэмплированных пикселей

Я использую программу, доступную на GitHub (здесь), для преобразования равнопрямоугольного изображения (360 -степенные изображения) в вид в перспективе (окно просмотра) с использованием OpenGL. Для этого используется подход трассировки лучей, чтобы найти пересечение точек фрагментов на единичной сфере, а позже эта информация будет использоваться для выборки необходимых пикселей из равнопрямоугольных изображений и проецирования их на вид в перспективе. Все работает нормально, но теперь вместе с результатом проекции мне нужно, чтобы программа вернула мне также изображение маски (типа байт без знака и того же размера, что и равнопрямоугольное изображение), которое представляет пиксели, выбранные из равнопрямоугольных изображений.

Как я могу изменить фрагментный шейдер, чтобы он возвращал мне эту беззнаковую байтовую маску (0 представляет собой не-выборки, а 255 представляет выборочные пиксели) в то же время, когда он отображает представление в буфере кадра?

Чтобы быть более конкретным, мой текущий фрагментный шейдер выглядит следующим образом:

const float pi = 3.141592653589793;
varying vec3 planePoints;
uniform sampler2D tex;
void main()
{
        vec2 equirectangularTexturePos;
        vec3 spherepos = normalize(planePoints);
        equirectangularTexturePos.x = (atan(spherepos.y, -spherepos.x)+pi)/(2.0*pi);
        equirectangularTexturePos.y = acos(spherepos.z)/(pi);
        gl_FragColor = texture2D(tex, equirectangularTexturePos);
}

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

texture2D(mask, equirectangularTexturePos) = 255;

person Navid Mahmoudian    schedule 01.11.2018    source источник
comment
Вы можете использовать imageStore() из khronos. org/registry/OpenGL/extensions/ARB/ для записи в отдельное изображение в позиции, которую вы выбрали из исходного равнопрямоугольного изображения. (для этого потребуется это расширение или OpenGL 4.2 Core). Проблема в том, что на самом деле сложно запросить тексельный след доступа к текстуре. Это не так сложно, когда вы используете линейный доступ, но усложняется, когда вы используете мип-отображение или даже анизотропную фильтрацию. Возможно, в этом поможет NV_shader_texture_footprint.   -  person Kai Burjack    schedule 01.11.2018
comment
@httpdigest Я добавил в пост свой фрагментный шейдер. не могли бы вы объяснить, как мне написать изображение маски? что касается того, как я загружаю равнопрямоугольные изображения, я загружаю их следующим образом: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);   -  person Navid Mahmoudian    schedule 01.11.2018
comment
Просто прочитайте спецификацию упомянутого расширения Image Load Store.   -  person Kai Burjack    schedule 01.11.2018
comment
Я не понимаю. Ваша FS получает все пиксели путем выборки (gl_FragColor = texture2D(.... Вы имеете в виду, что вам нужны пиксели, которые не генерируются в FS? т. е. пиксели в цвете фона, которые остаются неизменными?   -  person Ripi2    schedule 01.11.2018
comment
@Ripi2, равнопрямоугольное изображение — это большое изображение, в котором хранятся данные сферической камеры (представьте себе атлас мира, обернутый вокруг сферы). Эта программа пытается преобразовать равнопрямоугольное изображение в перспективную проекцию, т.е. предположим, что в центре сферы расположена виртуальная камера. Цель этой программы состоит в том, чтобы спроецировать область сферы, на которую нанесено равнопрямоугольное изображение, на плоскость изображения виртуальной камеры. Поэтому не вся область равнопрямоугольного изображения будет выбрана. Эта маска указывает области выборки.   -  person Navid Mahmoudian    schedule 02.11.2018


Ответы (1)


Я просто пишу это, чтобы уточнить возможное решение, на которое я пытался намекнуть в комментарии выше, используя Image Load Store. Во-первых, вам нужно создать новую текстуру того же размера, что и ваша равнопрямоугольная текстура (вы можете использовать GL_R8 в качестве внутреннего формата). Вы можете сделать это с помощью glTexImage2D или glTexStorage2D.

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

Для первого приближения мы просто используем усеченную целочисленную координату вашей исходной позиции образца (для вашего вызова texture2D). На самом деле, поскольку вы используете GL_LINEAR в качестве функции фильтрации, вы должны вычислить ближайшие 4 тексела и записать в них. См. PDF-страницу 150 https://www.khronos.org/registry/OpenGL/specs/gl/glspec13.pdf, чтобы узнать, какой алгоритм использует GL для определения четырех текселей для ЛИНЕЙНОЙ фильтрации.

Итак, части вашего шейдера будут выглядеть так:

#extension GL_ARB_shader_image_load_store : enable

layout(binding = 0, r8) writeonly uniform image2D maskImage;
...
ivec2 texel = ivec2(equirectangularTexturePos * textureSize(tex, 0));
imageStore(maskImage, texel, vec4(1.0, 0.0, 0.0, 0.0));

Чтобы привязать изображение к точке привязки изображения, вы должны использовать это в своей основной программе:

glBindImageTexture(0, maskTex, 0, false, 0, GL_WRITE_ONLY, GL_R8);

Также не забудьте очистить текстуру перед запуском шейдера. После того, как шейдер запустился и заполнил доступ к текселам уровня 0 вашей текстуры маски, вам необходимо добавить соответствующий барьер синхронизации, чтобы дальнейшие вызовы GL, исходящие из этой текстуры, видели обновленные значения текстуры. Вы делаете это через:

glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

Теперь вы можете использовать эту текстуру любым способом и использовать ее, например, с другим шейдером.

person Kai Burjack    schedule 01.11.2018
comment
Большое спасибо за ваш ответ. Я сделал, как вы сказали, но не могу получить доступ к маске в хост-программе. Я использовал glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, ширина, высота, 0, GL_R8, GL_UNSIGNED_BYTE, maskImagePtr); чтобы указать изображение текстуры, где maskImagePtr относится к началу массива беззнаковых символов, который я выделил для маски. Позже, после запуска шейдера, когда я пытаюсь прочитать маску из maskImagePtr, в массиве ничего не изменилось. Вам нужно буферизовать изображение текстуры в maskImagePtr? Я думал, что это должно напрямую изменить данные в *maskImagePtr. - person Navid Mahmoudian; 02.11.2018
comment
Вам не нужна клиентская память для поддержки текстуры (оставьте последний аргумент glTexImage2D равным NULL). Кроме того, GL_R8 не является допустимым значением для параметра format glTexImage2D. Вместо этого используйте GL_RED. Ваш драйвер OpenGL, скорее всего, вызвал бы некоторые ошибки OpenGL. Вы должны проверить это либо с помощью контекста отладки и обратного вызова отладки сообщения, либо с помощью вызова glGetError(). - person Kai Burjack; 02.11.2018
comment
Мне нужна маска для последующего анализа на процессоре. Поэтому мне нужно вернуть его обратно в хост-программу. Как я могу это сделать? - person Navid Mahmoudian; 02.11.2018
comment
На самом деле мне маска нужна в основном на процессоре после процесса рендеринга. Так что, если есть способ записи прямо из фрагментного шейдера в эту память хоста, я думаю, что это было бы более эффективно, потому что равнопрямоугольные изображения большие и копирование всех данных каждый раз занимает много времени, но если есть возможность писать напрямую в памяти хоста, поскольку количество выбранных пикселей меньше общего количества пикселей, это было бы более эффективно. - person Navid Mahmoudian; 02.11.2018