Вопрос или проблема
Я написал функцию для рисования прямоугольника и хочу добавить текстуру. Однако OpenGL не может применить ее, если она была инициализирована не в функции рисования.
class Material {
public:
Material(const char* filename) {
int width, height, channels;
unsigned char* data = stbi_load(filename, &width, &height, &channels, STBI_rgb_alpha);
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
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_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
~Material() { release(); }
Material(const Material &) = delete;
Material &operator=(const Material &) = delete;
Material(Material &&other) : texture(other.texture) {
other.texture = 0;
}
Material &operator=(Material &&other) {
if(this != &other) {
release();
std::swap(texture, other.texture);
}
}
GLuint texture = 0;
private:
void release() {
glDeleteTextures(1, &texture);
texture = 0;
}
};
class SRender {
public:
Material* mat = new Material("01.png");
void drawRect () const;
};
float vertex[] = {0,0,0, 0,1,0, 1,1,0, 1,0,0};
float texCord[] = {0,0, 0,1, 1,1, 1,0};
void SRender::drawRect() const {
//Material* mat2 = new Material("01.png");
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mat->texture);
glColor3f(1, 1, 1);
glPushMatrix();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertex);
glTexCoordPointer(2, GL_FLOAT, 0, texCord);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glPopMatrix();
}
int WINAPI WinMain(...)
{
SRender render;
/// Код
while (!bQuit)
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glPushMatrix();
render.drawRect();
glPopMatrix();
SwapBuffers(hDC);
Sleep(1);
}
}
Если я использую mat
в glBindTexture
, он рисует белый прямоугольник, но если я использую mat2
, это работает. Я не хочу инициализировать его каждый раз, когда я что-то рисую. Что здесь не так?
Ответ или решение
Ваш вопрос касается проблемы отображения текстуры в OpenGL, когда вы пытаетесь использовать уже инициализированный объект Material
для наложения текстуры на прямоугольник. Давайте разберемся с тем, почему это происходит и как можно исправить ситуацию.
Проблема
В вашем коде вы создаете объект Material
, в котором происходит загрузка текстуры и генерация текстурного объекта OpenGL. Вероятной причиной того, что текстура не отображается, когда вы используете указатель mat
, заключается в том, что объект Material
не сохраняет состояние текстуры правильно, или текстура не связывается корректно при вызове функции drawRect()
.
Когда вы используете mat2
, вы создаете новый объект Material
перед каждым вызовом отрисовки. Это означает, что каждый раз происходит новая загрузка текстуры, и, вероятно, настройки текстуры (например, параметры фильтрации и обертки) применяются корректно, поэтому текстура и отображается.
Возможные решения
- Проверьте инициализацию текстуры:
Убедитесь, что текстура успешно загружается в объектеMaterial
. Вы должны проверить, чтоdata
не равноnullptr
после вызоваstbi_load()
. Если оно равноnullptr
, это означает, что возникла ошибка при загрузке изображения.
if (!data) {
std::cerr << "Ошибка загрузки текстуры: " << stbi_failure_reason() << std::endl;
}
-
Убедитесь в контексте OpenGL:
Убедитесь, что вы вызываете функции OpenGL в правильном контексте. Возможно, объектMaterial
инициализируется, когда контекст OpenGL не активен, либо происходит попытка использования текстуры в неподходящий момент. -
Правильное связывание текстуры:
В функцииdrawRect()
вы связываете текстуру с глобальным идентификатором текстуры. Убедитесь, что до этого момента вы вызываетеglBindTexture(GL_TEXTURE_2D, mat->texture);
, гдеmat
указывает на корректно инициализированную текстуру. -
Проверка состояния OpenGL:
После отрисовки можно использоватьglGetError()
, чтобы проверить, не возникли ли ошибки OpenGL, вы можете добавить проверку перед и после ваших OpenGL вызовов. Это поможет выявить, нет ли других проблем, которые могут мешать корректной отрисовке. -
Инициализация в конструкторе:
Если вы не хотите постоянно создавать новые экземплярыMaterial
, инициализируйте текстуру один раз в конструкторе классаSRender
или используйте метод для загрузки текстур, чтобы избегать излишнего использования ресурсов.
Пример кода
Вот как можно немного изменить ваш класс SRender
, чтобы гарантировать правильную инициализацию текстуры и ее использование:
class SRender {
public:
Material* mat;
SRender(const char* filename) {
mat = new Material(filename);
}
~SRender() {
delete mat;
}
void drawRect() const {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mat->texture);
glColor3f(1, 1, 1);
glPushMatrix();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertex);
glTexCoordPointer(2, GL_FLOAT, 0, texCord);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glPopMatrix();
}
};
Заключение
Если вы следите за инициализацией текстур, связыванием их перед рисованием и удостоверяетесь, что контекст OpenGL активен в нужные моменты, ваша текстура должна отображаться корректно. Если проблема сохраняется, рассмотрите возможность использования методов отладки и проверки состояния OpenGL для выявления других возможных ошибок в вашем коде.