C ++ OpenGL glTexImage2D Нарушение прав доступа - PullRequest
1 голос
/ 31 марта 2012

Я пишу приложение, используя OpenGL (freeglut и glew).

Я также хотел текстуры, поэтому я провел некоторое исследование формата файла Bitmap и написал структуру для основного заголовка и другую для заголовка DIB (информационный заголовок).

Тогда я начал писать загрузчик. Он автоматически связывает текстуру с OpenGL. Вот функция:

static unsigned int ReadInteger(FILE *fp)
{
    int a, b, c, d;

    // Integer is 4 bytes long.
    a = getc(fp);  
    b = getc(fp);  
    c = getc(fp);  
    d = getc(fp);

    // Convert the 4 bytes to an integer.
    return ((unsigned int) a) + (((unsigned int) b) << 8) +
           (((unsigned int) c) << 16) + (((unsigned int) d) << 24);
}

static unsigned int ReadShort(FILE *fp)
{
    int a, b;

    // Short is 2 bytes long.
    a = getc(fp);  
    b = getc(fp);

    // Convert the 2 bytes to a short (int16).
    return ((unsigned int) a) + (((unsigned int) b) << 8);
}

    GLuint LoadBMP(const char* filename)
{
    FILE* file;

    // Check if a file name was provided.
    if (!filename)
        return 0;

    // Try to open file.
    fopen_s(&file, filename, "rb");

    // Return if the file could not be open.
    if (!file)
    {
        cout << "Warning: Could not find texture '" << filename << "'." << endl;
        return 0;
    }

    // Read signature.
    unsigned char signature[2];
    fread(&signature, 2, 1, file);

    // Use signature to identify a valid bitmap.
    if (signature[0] != BMPSignature[0] || signature[1] != BMPSignature[1])
    {
        fclose(file);
        return 0;
    }

    // Read width and height.
    unsigned long width, height;
    fseek(file, 16, SEEK_CUR); // After the signature we have 16bytes until the width.
    width = ReadInteger(file);
    height = ReadInteger(file);

    // Calculate data size (we'll only support 24bpp).
    unsigned long dataSize;
    dataSize = width * height * 3;

    // Make sure planes is 1.
    if (ReadShort(file) != 1)
    {
        cout << "Error: Could not load texture '" << filename << "' (planes is not 1)." << endl;
        return 0;
    }

    // Make sure bpp is 24.
    if (ReadShort(file) != 24)
    {
        cout << "Error: Could not load texture '" << filename << "' (bits per pixel is not 24)." << endl;
        return 0;
    }

    // Move pointer to beggining of data. (after the bpp we have 24 bytes until the data)
    fseek(file, 24, SEEK_CUR);

    // Allocate memory and read the image data.
    unsigned char* data = new unsigned char[dataSize];

    if (!data)
    {
        fclose(file);
        cout << "Warning: Could not allocate memory to store data of '" << filename << "'." << endl;
        return 0;
    }

    fread(data, dataSize, 1, file);

    if (data == NULL)
    {
        fclose(file);
        cout << "Warning: Could no load data from '" << filename << "'." << endl;
        return 0;
    }

    // Close the file.
    fclose(file);

    // Create the texture.
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); //NEAREST);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 

    gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, width, height, GL_BGR_EXT, GL_UNSIGNED_BYTE, data);

    return texture;
}

Я знаю, что данные растрового изображения правильно читаются, потому что я вывел их данные на консоль и сравнил с изображением, открытым в краске.

Проблема вот в этой строке:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dibheader.width,
    dibheader.height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

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

Необработанное исключение в 0x008ffee9 в GunsGL.exe: 0xC0000005: Местоположение чтения нарушения прав доступа 0x00af7002.

Это разборка, где происходит ошибка:

movzx       ebx,byte ptr [esi+2]

Это не ошибка моего загрузчика, потому что я скачал другие загрузчики. Загруженный загрузчик, который я использовал, был этот от NeHe.

РЕДАКТИРОВАТЬ: (ОБНОВЛЕНИЕ КОДА ВЫШЕ)

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

void __cdecl _lock (
        int locknum
        )
{

        /*
         * Create/open the lock, if necessary
         */
        if ( _locktable[locknum].lock == NULL ) {

            if ( !_mtinitlocknum(locknum) )
                _amsg_exit( _RT_LOCK );
        }

        /*
         * Enter the critical section.
         */

        EnterCriticalSection( _locktable[locknum].lock );
}

На линии:

EnterCriticalSection( _locktable[locknum].lock );

Кроме того, вот снимок экрана одного из тех случаев, когда приложения не аварийно завершают работу (текстура явно не правильная): http://i.stack.imgur.com/4Mtso.jpg

Edit2:

Обновлен код с новым рабочим. (Ответ, помеченный как ответ, не содержит всего, что нужно для этого, но это было жизненно важно)

Ответы [ 2 ]

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

Попробуйте glPixelStorei(GL_UNPACK_ALIGNMENT, 1) перед вашим glTexImage2D() звонком.

2 голосов
/ 31 марта 2012

Я знаю, заманчиво читать двоичные данные, как это

BitmapHeader header;
BitmapInfoHeader dibheader;
/*...*/
// Read header.
fread(&header, sizeof(BitmapHeader), 1, file);

// Read info header.
fread(&dibheader, sizeof(BitmapInfoHeader), 1, file);

но вы действительно не должны делать это таким образом. Зачем? Поскольку структура памяти в структурах может быть дополнена в соответствии с ограничениями выравнивания (да, я знаю о прагмах упаковки), размер типа используемого компилятора может не соответствовать размеру данных в двоичном файле, и последний, но не менее важный метод может не совпадать .

Всегда считывайте двоичные данные в промежуточный буфер, из которого вы извлекаете поля четко определенным образом с точно заданными смещениями и вводом.

// Allocate memory for the image data.
data = (unsigned char*)malloc(dibheader.dataSize);

Если это C ++, используйте оператор new. Если это C, то не приводите от void * к типу значения L, это плохой стиль и может охватывать полезные предупреждения компилятора.

// Verify memory allocation.
if (!data)
{
    free(data);

Если data равен NULL, вы не должны освобождать его.

// Swap R and B because bitmaps are BGR and OpenGL uses RGB.
for (unsigned int i = 0; i < dibheader.dataSize; i += 3)
{
    B = data[i]; // Backup Blue.
    data[i] = data[i + 2]; // Place red in right place.
    data[i + 2] = B; // Place blue in right place.
}

OpenGL действительно поддерживает выравнивание BGR. Параметр формата сюрприз GL_BGR

// Generate texture image.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dibheader.width, dibheader.height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

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

...