Текстуры OpenGL выглядят просто черными - PullRequest
1 голос
/ 08 ноября 2010

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

Вот что я делаю до сих пор:

Следующий класс абстрагирует представление DevIL для изображения.

#include "Image.h"

Image::Image()
{
    ilGenImages(1, &this->imageId);
}

Image::~Image()
{
    ilDeleteImages(1, &this->imageId);
}

ILint Image::getWidth()
{
    return this->width;
}

ILint Image::getHeight()
{
    return this->height;
}

ILint Image::getDepth()
{
    return this->depth;
}

ILint Image::getBpp()
{
    return this->bpp;
}

ILint Image::getFormat()
{
    return this->format;
}

ILubyte* Image::getData()
{
    return ilGetData();
}

bool Image::loadFromFile(wchar_t *filename)
{
    // Load the image from file.
    ILboolean retval = ilLoadImage(filename);
    if (!retval) {
        ILenum error;
        while ((error = ilGetError()) != IL_NO_ERROR) {
            wcout << error << L" " << iluErrorString(error);
        }
        return false;
    }

    this->width = ilGetInteger(IL_IMAGE_WIDTH);
    this->height = ilGetInteger(IL_IMAGE_HEIGHT);
    this->depth = ilGetInteger(IL_IMAGE_DEPTH);
    this->bpp = ilGetInteger(IL_IMAGE_BPP);
    this->format = ilGetInteger(IL_IMAGE_FORMAT);

    return true;
}

bool Image::convert()
{
    ILboolean retval = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
    if (!retval) {
        ILenum error;
        while ((error = ilGetError()) != IL_NO_ERROR) {
            wcout << error << L" " << iluErrorString(error);
        }
        return false;
    }
    return true;
}

bool Image::scale(ILint width, ILint height, ILint depth)
{
    ILboolean retval = iluScale(width, height, depth);
    if (!retval) {
        ILenum error;
        while ((error = ilGetError()) != IL_NO_ERROR) {
            wcout << error << L" " << iluErrorString(error);
        }
        return false;
    }
    return true;
}

void Image::bind()
{
    ilBindImage(this->imageId);
}

Этот класс абстрагирует представление текстуры для OpenGL.

#include "Texture.h"

Texture::Texture(int width, int height)
{
    glGenTextures(1, &this->textureId);

    this->width = width;
    this->height = height;
}

int Texture::getWidth()
{
    return this->width;
}

int Texture::getHeight()
{
    return this->height;
}

void Texture::initFilter()
{
    // We will use linear interpolation for magnification filter.
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // We will use linear interpolation for minifying filter.
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

}

void Texture::unpack()
{
    glPixelStoref(GL_UNPACK_ALIGNMENT, 1);
}

void Texture::bind()
{
    glBindTexture(GL_TEXTURE_2D, this->textureId);
}

Texture::~Texture()
{
    glDeleteTextures(1, &this->textureId);
}

Следующий класс содержит процесс загрузки текстуры.

#include "TextureLoader.h"

void TextureLoader::initialize()
{
    if (ilGetInteger(IL_VERSION_NUM) < IL_VERSION) {
        debug("Wrong DevIL version detected.");
        return;
    }

    ilInit();
    ilutRenderer(ILUT_OPENGL);
}

Texture* TextureLoader::createTexture(wchar_t *filename, Color *color)
{
    // Generate some space for an image and bind it.
    Image *image = new Image();
    image->bind();

    bool retval = image->loadFromFile(filename);
    if (!retval) {
        debug("Could not load image from file.");
        return 0;
    }

    retval = image->convert();
    if (!retval) {
        debug("Could not convert image from RGBA to unsigned byte");
    }

    int pWidth = getNextPowerOfTwo(image->getWidth());
    int pHeight = getNextPowerOfTwo(image->getHeight());
    int size = pWidth * pHeight;

    retval = image->scale(pWidth, pHeight, image->getDepth());
    if (!retval) {
        debug("Could not scale image from (w: %i, h: %i) to (w: %i, h: %i) with depth %i.", image->getWidth(), image->getHeight(), pWidth, pHeight, image->getDepth());
        return 0;
    }

    // Generate some space for a texture and bind it.
    Texture *texture = new Texture(image->getWidth(), image->getHeight());
    texture->bind();

    // Set the interpolation filters.
    texture->initFilter();

    // Unpack pixels.
    texture->unpack();

    ILubyte *imageData = image->getData();

    TextureLoader::setColorKey(imageData, size, new Color(0, 0, 0));
    TextureLoader::colorize(imageData, size, new Color(255, 0, 0));

    debug("bpp: %i", image->getBpp());
    debug("width: %i", image->getWidth());
    debug("height: %i", image->getHeight());
    debug("format: %i", image->getFormat());

    // Map image data to texture data.
    glTexImage2D(GL_TEXTURE_2D, 0, image->getBpp(), image->getWidth(), image->getHeight(), 0, image->getFormat(), GL_UNSIGNED_BYTE, imageData);

    delete image;

    return texture;
}

void TextureLoader::setColorKey(ILubyte *imageData, int size, Color *color)
{
    for (int i = 0; i < size * 4; i += 4)
    {
        if (imageData[i] == color->r && imageData[i + 1] == color->g && imageData[i + 2] == color->b)
        {
            imageData[i + 3] = 0;
        }
    }
}

void TextureLoader::colorize(ILubyte *imageData, int size, Color *color)
{
    for (int i = 0; i < size * 4; i += 4)
    {
        int rr = (int(imageData[i]) * int(color->r)) >> 8;
        int rg = (int(imageData[i + 1]) * int(color->g)) >> 8;
        int rb = (int(imageData[i + 2]) * int(color->b)) >> 8;
        int fak = int(imageData[i]) * 5 - 4 * 256 - 138;

        if (fak > 0)
        {
            rr += fak;
            rg += fak;
            rb += fak;
        }

        rr = rr < 255 ? rr : 255;
        rg = rg < 255 ? rg : 255;
        rb = rb < 255 ? rb : 255;

        imageData[i] = rr > 0 ? (GLubyte) rr : 1;
        imageData[i + 1] = rg > 0 ? (GLubyte) rg : 1;
        imageData[i + 2] = rb > 0 ? (GLubyte) rb : 1;
    }
}

Последний класс делает рисунок.

#include "Texturizer.h"

void Texturizer::draw(Texture *texture, float x, float y, float angle)
{
    // Enable texturing.
    glEnable(GL_TEXTURE_2D);

    // Bind the texture for drawing.
    texture->bind();

    // Enable alpha blending.
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    int width = texture->getWidth();
    int height = texture->getHeight();

    // Create centered dimension vectors.
    b2Vec2 vertices[4];
    vertices[0] = 0.5f * b2Vec2(- width, - height);
    vertices[1] = 0.5f * b2Vec2(+ width, - height);
    vertices[2] = 0.5f * b2Vec2(+ width, + height);
    vertices[3] = 0.5f * b2Vec2(- width, + height);

    b2Mat22 matrix = b2Mat22();
    matrix.Set(angle);

    glBegin(GL_QUADS);
    for (int i = 0; i < 4; i++) {
        float texCoordX = i == 0 || i == 3 ? 0.0f : 1.0f;
        float texCoordY = i < 2 ? 0.0f : 1.0f;
        glTexCoord2f(texCoordX, texCoordY);

        // Rotate and move vectors.
        b2Vec2 vector = b2Mul(matrix, vertices[i]) + meter2pixel(b2Vec2(x, y));
        glVertex2f(vector.x, vector.y);
    }
    glEnd();

    glDisable(GL_BLEND);
    glDisable(GL_TEXTURE_2D);
}

И последнее, но не менее важное: следующий метод инициализирует OpenGL (и запускает инициализацию DevIL):

void GraphicsEngine::initialize(int argc, char **argv)
{
    // Initialize the window.
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
    glutInitWindowSize(WIDTH, HEIGHT);

    // Set shading model.
    glShadeModel(GL_SMOOTH);

    // Create the window.
    this->mainWindow = glutCreateWindow(TITLE);

    // Set keyboard methods.
    glutKeyboardFunc(&onKeyDownCallback);
    glutKeyboardUpFunc(&onKeyUpCallback);
    glutSpecialFunc(&onSpecialKeyDownCallback);
    glutSpecialUpFunc(&onSpecialKeyUpCallback);

    // Set mouse callbacks.
    glutMouseFunc(&onMouseButtonCallback);
#ifdef FREEGLUT
    glutMouseWheelFunc(&onMouseWheelCallback);
#endif
    glutMotionFunc(&onMouseMotionCallback);
    glutPassiveMotionFunc(&onMousePassiveMotionCallback);

    // Set display callbacks.
    glutDisplayFunc(&onDrawCallback);
    glutReshapeFunc(&onReshapeCallback);

    // Set a timer to control the frame rate.
    glutTimerFunc(FRAME_PERIOD, onTimerTickCallback, 0);

    // Set clear color.
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

    Camera::getInstance()->subscribe(this);

    // Initialize texture loader.
    TextureLoader::initialize();
}

Образ, который я использую, уже работал для другого проекта OpenGL / DevIL, поэтому он не может быть источником проблемы.

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

#include "Blobby.h"

Blobby::Blobby()
{
    this->isJumping = false;
    this->isRotating = false;
    this->isWalking = false;
    this->isDucking = false;
    this->isStandingUp = false;
    this->isOnGround = false;
    this->isTouchingWall = false;
    this->angle = 0;
    this->direction = DIRECTION_UNKNOWN;
    this->wallDirection = DIRECTION_UNKNOWN;

    // Create a red blobby texture.
    this->texture = TextureLoader::createTexture(L"D:/01.bmp", new Color(255, 0, 0));

    ContactListener::getInstance()->subscribe(this);
}

void Blobby::draw()
{
    GraphicsEngine::drawString(35, 40, "isOnGround     = %s", this->isOnGround ? "true" : "false");
    GraphicsEngine::drawString(35, 55, "inJumping      = %s", this->isJumping ? "true" : "false");
    GraphicsEngine::drawString(35, 70, "isRotating     = %s", this->isRotating ? "true" : "false");
    GraphicsEngine::drawString(35, 85, "isTouchingWall = %s (%i)", this->isTouchingWall ? "true" : "false", this->wallDirection);

    Texturizer::draw(this->texture, this->getBody(0)->GetPosition().x, this->getBody(0)->GetPosition().y, this->getBody(0)->GetAngle());

    AbstractEntity::draw(); // draws debug information... not important
}

Обратный вызов таймера OpenGL вызывает метод шага, который заканчивается здесь:

void Simulator::step()
{
    // Update physics.
    this->gameWorld->step();

    b2Vec2 p = Camera::convertWorldToScreen(meter2pixel(this->cameraBlobby->getBody(0)->GetPosition().x), 300.0f);
    if (p.x < 300) {
        Camera::getInstance()->setViewCenter(Camera::convertScreenToWorld(400 - (300 - int(p.x)), 300));
    } else if (p.x > 500) {
        Camera::getInstance()->setViewCenter(Camera::convertScreenToWorld(400 + (int(p.x) - 500), 300));
    }


    for (unsigned int i = 0; i < this->gameWorld->getEntityCount(); i++) {
        IEntity *entity = this->gameWorld->getEntity(i);
        entity->draw();
    }
}

IEntity - это чистый виртуальный класс (то есть интерфейс), AbstractEntity реализует этот интерфейс и добавляет глобальные методы. Blobby наследуется от AbstractEntity и добавляет подпрограммы, которые специально для этого объекта мира.

EDIT: Я загрузил более свежую версию кода (весь проект, включая зависимости) здесь: http://upload.visusnet.de/uploads/BlobbyWarriors-rev19.zip (~ 9,5 МБ)

Ответы [ 3 ]

3 голосов
/ 08 ноября 2010

Я не знаком с DevIL, но ... вы предоставляете правильный диффузный цвет для ваших вершин? Если освещение включено, есть ли какие-либо источники света, указывающие на квадроцикл? Камера смотрит на переднюю сторону четырехугольника?

EDIT:

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

Вы звоните glColor3i(255, 255, 255), и он устанавливает диффузный цвет (почти) черный, как и ожидалось. glColor3i не не принимает значения цвета в целевом диапазоне (вычисление или кадровый буфер). Возможные значения масштабируются до всего диапазона типа int. Это означает, что максимальное значение (1,0 в плавающей запятой) представлено как MAX_INT (2 147 483 647) 0 - 0, а -1,0 - MIN_INT (-2 147 483 648). Предоставленное вами значение 255 составляет около 0,000000118, что почти равно нулю.

Я полагаю, вы намеревались использовать одну из следующих (полностью эквивалентных) форм:

glColor3f(1.0, 1.0, 1.0), glColor3ub(255, 255, 255),

glColor3i(2147483647, 2147483647, 2147483647).

0 голосов
/ 09 ноября 2010

У меня была такая проблема давным-давно, я думаю, тогда это была проблема с размерами текстуры, не являющимися показателем степени 2 (128x128, 512x512 и т. Д.). Я уверен, что они уже исправили это, но это может быть что-то, чтобы попробовать.

0 голосов
/ 08 ноября 2010

Что находится в матрице b2Mat22?Может ли быть так, что умножение на эту матрицу приводит к тому, что ваши вершины рисуются по часовой стрелке, потому что я думаю, что в этом случае спина вашего квадрата будет обращена к вам, а текстура может быть на другой (невидимой) стороне.

...