Чтение пикселей из UTexture2D не представляет особой сложности, действительно, этот пост на Unreal AnswerHub почти идеально описывает, как это сделать. Однако некоторые моменты отсутствуют, и можно было бы пойти в случае, если вызов RawImageData-›Lock(LOCK_READ_ONLY) вернет nullptr. Когда это происходит, это мешает нам читать пиксели и, более того, потенциально может вызвать огромный сбой, если ситуация не была ожидаема.

Мы быстро рассмотрим, как читать пиксели из UTexture2D и как избежать ситуации, когда nullptr возвращается при блокировке изображения.

Чтение пикселей.

Как указано в сообщении AnswerHub, чтение пикселей UTexture2D можно разделить на следующие шаги:

  • Доступ к MIP-карте 0 текстуры (т. е. с тем же разрешением, что и исходная текстура)
  • Доступ к BulkData этого MIP-карты
  • Блокировка его для доступа к пикселям
  • Разблокировка

С точки зрения кода это можно записать так:

const FColor* FormatedImageData = static_cast<const FColor*>( MyTexture2D->PlatformData->Mips[0].BulkData.LockReadOnly());
for(int32 X = 0; X < MyTexture2D->SizeX; X++) {
  for (int32 Y = 0; Y < MyTexture2D->SizeY; Y++) {
    FColor PixelColor = FormatedImageData[Y * MyTexture2D->SizeX + X]; // Do the job with the pixel
  }
}
MyTexture2D->PlatformData->Mips[0].BulkData.Unlock();

В этом примере кода мы можем отметить два интересных момента.

Во-первых, результат LockReadOnly(), который возвращает массив байтов текстуры, приводится к массиву FColor. Его также можно привести к массиву uint8, если это так, FormatedImageData[0] будет представлять красный канал первого пикселя, FormatedImageData[1] будет зеленый канал первого пикселя и так далее. Выполняя преобразование FColor*, мы получаем FormatedImageData[0], содержащий все цветовые каналы первого пикселя.

Во-вторых, возвращаемый массив представляет собой массив 1D, представляющий 2D-текстуру, мы должны преобразовать наши 2D-координаты (X, Y) в 1D-координаты. Это делается с помощью следующей формулы: Y * MyTexture2D-›SizeX + X, которая используется для доступа к пикселю (X, Y) в массиве FormatedImageData.

Предотвращение возврата LockReadOnly nullptr.

При вызове LockReadOnly мы можем получить в ответ nullptr, если текстура не имеет определенного формата.

Действительно, насколько я знаю, текстура должна иметь следующие параметры:

  • CompressionSettings установлен на VectorDisplacementmap
  • MipGenSettings в NoMipmaps
  • SRGB в ложь

В общем случае будем считать, что эти условия не выполняются. Итак, что мы будем делать, так это сохранять текущие настройки текстуры, применять настройки выше, читать пиксели, восстанавливать предыдущие настройки.

Вот пример кода для сохранения и применения новых настроек:

TextureCompressionSettings OldCompressionSettings = MyTexture2D->CompressionSettings;
TextureMipGenSettings OldMipGenSettings = MyTexture2D->MipGenSettings;
bool OldSRGB = MyTexture2D->SRGB;
MyTexture2D->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap;
MyTexture2D->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;
MyTexture2D->SRGB = false;
MyTexture2D->UpdateResource();

Вызов UpdateResource() применяет новые настройки. Затем мы можем прочитать пиксели, используя код, представленный в первой части этой статьи.

Как только пиксели прочитаны и текстура разблокирована, мы можем повторно применить старые настройки:

Texture->CompressionSettings = OldCompressionSettings; 
Texture->MipGenSettings = OldMipGenSettings; 
Texture->SRGB = OldSRGB; 
Texture->UpdateResource();

Вывод.

Когда мы интегрируем все примеры кода, подробно описанные в этой статье, мы получаем следующий код:

TextureCompressionSettings OldCompressionSettings = MyTexture2D->CompressionSettings;
TextureMipGenSettings OldMipGenSettings = MyTexture2D->MipGenSettings; 
bool OldSRGB = MyTexture2D->SRGB; 
MyTexture2D->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap; 
MyTexture2D->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps; 
MyTexture2D->SRGB = false; 
MyTexture2D->UpdateResource(); 
const FColor* FormatedImageData = static_cast<const FColor*>( MyTexture2D->PlatformData->Mips[0].BulkData.LockReadOnly());
for(int32 X = 0; X < MyTexture2D->SizeX; X++) {
  for (int32 Y = 0; Y < MyTexture2D->SizeY; Y++) {
    FColor PixelColor = FormatedImageData[Y * MyTexture2D->SizeX + X];
  }
}
MyTexture2D->PlatformData->Mips[0].BulkData.Unlock(); 
Texture->CompressionSettings = OldCompressionSettings; 
Texture->MipGenSettings = OldMipGenSettings; 
Texture->SRGB = OldSRGB; 
Texture->UpdateResource();

Этот код был протестирован, и мы надеемся, что он будет работать для вас. Тем не менее, может быть какой-то неуправляемый случай, и в конечном итоге вы можете найти другую обязательную настройку при чтении пикселей (документация Unreal на самом деле не является исчерпывающей). Если это так, не стесняйтесь обращаться к нам, чтобы мы могли улучшить эту статью.

Первоначально опубликовано на isaratech.com 22 ноября 2017 г.