I'm working with SDL2.0, and using a (semi modern) opengl (3.1). I'm looking to add a text overlay to my application, and to render TTF in the application. How would I go about this using modern OpenGL?
EDIT:
As per the suggestion of genpfault, I've tried using the SDL_TTF library, but All I'm getting is garbage on screen http://i.stack.imgur.com/FqyCT.png
I've attached a gist of my shaders, which are very simple for this program, and also the snipped I'm using to load the text into surface, and to bind it to the texture. I'm not trying to do anything crazy here at all. Is there anything I'm doing wrong you can see? I'm not really too sure how to debug shaders etc.
https://gist.github.com/anonymous/7284430
I spent too much time with a black screen before I figured out the actually text data was in the alpha channel.
GLuint shader_program_text;
void drawText()
{
//Render the message to an SDL_Surface, as that's what TTF_RenderText_X returns
text_font = TTF_OpenFont("bpl_binary/waltographUI.ttf", 50);
SDL_Color color = {255, 0, 0, 0};
SDL_Surface* sdl_surface = TTF_RenderText_Blended(text_font, "hello world", color);
GLuint texture_id;
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, sdl_surface->w, sdl_surface->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, sdl_surface->pixels);
glBindTexture(GL_TEXTURE_2D, 0);
SDL_FreeSurface(sdl_surface);
TTF_CloseFont(text_font);
//glDrawPixels(sdl_surface->w, sdl_surface->h, GL_RGBA, GL_UNSIGNED_BYTE, sdl_surface->pixels);
//FILE* file = fopen("output.txt", "w+");
//for(int h=0; h<sdl_surface->h; h++)
//{
//
// for(int w=0; w<sdl_surface->w; w++)
// {
// unsigned int xxx = ((unsigned int*)sdl_surface->pixels)[h*sdl_surface->w + w];
// /*if(xxx != 0)
// fprintf(file, "x", xxx);
// else
// fprintf(file, " ", xxx);*/
// fprintf(file, "%08x ", xxx);
// }
// fprintf(file, "\n");
//}
//fclose(file);
//MULTIPLY WITH ALPHA TO ACTUALLY SEE SOMETHING
glUseProgram(shader_program_text);
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_id);
glBegin(GL_TRIANGLE_STRIP);
//texcoord; position;
glVertexAttrib2f(1, 0, 1); glVertexAttrib2f(0, -1, -1); //top left
glVertexAttrib2f(1, 1, 1); glVertexAttrib2f(0, +1, -1); //top right
glVertexAttrib2f(1, 0, 0); glVertexAttrib2f(0, -1, +1); //bottom left
glVertexAttrib2f(1, 1, 0); glVertexAttrib2f(0, +1, +1); //bottom right
glEnd();
glDisable(GL_TEXTURE_2D);
glUseProgram(0);
}
Use the pixels member of the SDL_Surface returned by a TTF_Render*() call to populate a texture.
Related
I'm learning OpenGL from the MakingGamesWithBen series and I'm writing a simple asteroid shooter based on his engine. I have created a system that randomly positions the asteroid sprites with random sizing, and selects a random texture path from an std::vector, and passes the path to the asteroid constructor. The sprites are drawn, however only the first texture is drawn. I've read that I need to bind those textures and switch to the relevant glActiveTexture; from my code below, how would I go about this?
void MainGame::prepareTextures() {
//compile shaders and get Texlocations
initShaders("Shaders/background.vert", "Shaders/background.frag");
GLint TexLoc = _colorProgram.getUniformLocation("bgTexture");
glActiveTexture(GL_TEXTURE0);
}
m_asteroid[i].draw():
glm::vec4 uv(0.0f, 0.0f, 1.0f, 1.0f);
//convert m_imgNum to string and remove trailing zeros
std::string strImgNum = std::to_string(m_imgNum);
strImgNum.erase(strImgNum.find_last_not_of('0') + 1, std::string::npos);
//construct filpath
std::string filePath = m_dir + strImgNum + ".png";
static Engine::GLTexture texture = Engine::ResourceManager::GetTexture(filePath, 0, 0, 32, 4);
Engine::Color color;
color.r = 255;
color.g = 255;
color.b = 255;
color.a = 255;
glm::vec4 posAndSize = glm::vec4(m_posX, m_posY, m_width, m_height);
spriteBatch.Draw(posAndSize, uv, texture.id, 0.0f, color);
Engine::ResourceManager::GetTexture():
GLTexture texture = {};
unsigned char *imageData = stbi_load(filePath.c_str(), &width, &height, &bitsPerPixel, forceBpp);
if (imageData == NULL) {
const char *loadError = stbi_failure_reason();
stbi_image_free(imageData);
fatalError(loadError);
}
//Create the texture in opengl
glGenTextures(1, &(texture.id));
glBindTexture(GL_TEXTURE_2D, texture.id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
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);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(imageData);
glBindTexture(GL_TEXTURE_2D, 0);
texture.width = width;
texture.height = height;
return texture;
renderbatch():
void SpriteBatch::renderbatch() {
glBindVertexArray(m_vao);
for (unsigned int i = 0; i < m_renderBatches.size(); i++) {
glBindTexture(GL_TEXTURE_2D, m_renderBatches[i].texture);
glDrawArrays(GL_TRIANGLES, m_renderBatches[i].offset, m_renderBatches[i].numVertices);
}
glBindVertexArray(0);
}
I can provide any other code/clarification that may be needed!
Usually when not all textures are showing up, it's probably has something to do with uploading the texture by OpenGL. You could try to debug this by checking if your textures are all uploaded properly;
std::uint32_t textureId = Engine::ResourceManager::GetTexture(filePath, 0, 0, 32, 4).id;
std::cout << "texture id, should not be 0 :" << textureId << std::endl;
This could happen if you called this function not from a thread with OpenGL context.
EDIT:
Is there any reason to use a static object here?
static Engine::GLTexture texture = Engine::ResourceManager::GetTexture(filePath, 0, 0, 32, 4);
try changing that to just
Engine::GLTexture texture = Engine::ResourceManager::GetTexture(filePath, 0, 0, 32, 4);
UPDATE:
I just replaced your spitebatch.cpp/.h with the ones from Ben's github and put a simple test in your MainGame.cpp;
m_spriteBatch.begin();
asteroids.draw(m_spriteBatch);
ship.draw(m_spriteBatch);
m_spriteBatch.end();
m_spriteBatch.renderBatch();
_colorProgram.unuse();
_window.SwapBuffers();
and I can render the two uploaded textures properly;
HTH.
I have a series of rectangles of different colours and I'm trying to add a texture to one of them. However when I apply the texture to the given rectangle, it just turns black. Below is the function I use to load the texture.
GLuint GLWidget:: LoadTexture(const char * pic, int width, int height){
GLuint Texture;
BYTE * data;
FILE * picfile;
picfile = fopen(pic, "rb");
if (picfile == NULL)
return 0;
data = (BYTE *)malloc(width * height * 3);
fread(data, width * height, 3, picfile);
fclose(picfile);
glGenTextures(1, &Texture);
glBindTexture(GL_TEXTURE_2D, Texture);
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_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB8, GL_UNSIGNED_BYTE, data);
return Texture;
}
In another function where the GL_QUADS are drawn, I then have...
GLuint myTex = LoadTexture("texture.bmp", 500, 500);
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBindTexture(GL_TEXTURE_2D, myTex);
glBegin(GL_QUADS);
glTexCoord2f(1, 1); glVertex3f(42, 10, 42);
glTexCoord2f(1, 0); glVertex3f(42, 10, -42);
glTexCoord2f(0, 0); glVertex3f(-42,10,-42);
glTexCoord2f(0, 1); glVertex3f(-42,10, 42);
glEnd();
If anyone could let me know where I am going wrong that would be great, thanks!
This call
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB8, GL_UNSIGNED_BYTE, data);
is invalid. GL_RGB8 is a valid internalFormat, but it is not a valid enum for format. Use GL_RGB, GL_UNSIGNED_BYTE as format and type if your client-side data is 3 channels with 8 but unsigned int data per channel.
Another thing is
LoadTexture("texture.bmp", 500, 500);
This suggests that you are dealing with BMP files, but your loader only deals with completely raw image data.
I'm trying to render to two textures, rendering from the first to the second, and then from the second to the first etc. The problem is that when I'm rendering the first texture to the second, it works fine but rendering the second to the first leaves a white texture, when it's supposed to be a purple one. I'm working with Qt and OpenGL.
Both textures are bound to the same FBO, and I'm switching them through glDrawBuffer(GL_COLOR_ATTACHMENT_i)
Here is my initialization code:
void GlWidget::initializeGL() {
glewInit();
src = true;
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
//glEnable(GL_CULL_FACE);
// Generate the texture to be drawn
tex = new float[256*256*4];
for(int i = 0; i < 256*256*4; i++){
if (i % 4 == 0){
tex[i] = 0.5;
}else if (i % 4 == 1){
tex[i] = 0.3;
}else if (i % 4 == 2){
tex[i] = 0.5;
}else if (i % 4 == 3){
tex[i] = 0;
}
}
glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 256, 256, 0, GL_RGBA, GL_FLOAT, tex);
glGenTextures(1, &targetTex);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, targetTex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 256, 256, 0, GL_RGBA, GL_FLOAT, NULL);
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
//Attach 2D texture to this FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, targetTex, 0);
glDrawBuffer (GL_COLOR_ATTACHMENT1);
//-------------------------
glGenRenderbuffers(1, &depth_rb);
glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_rb);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 256, 256);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER, depth_rb);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
shaderProgram.addShaderFromSourceFile(QGLShader::Vertex, ":/vertexShader.vsh");
shaderProgram.addShaderFromSourceFile(QGLShader::Fragment, ":/fragmentShader.fsh");
shaderProgram.link();
vertices << QVector3D(-1, -1, -2) << QVector3D(-1, 1, -2) << QVector3D(1, 1, -2) << QVector3D(1, -1, -2);
texCoords << QVector2D(0, 0) << QVector2D(0, 1) << QVector2D(1, 1) << QVector2D(1, 0);
}
And here is my drawing code:
void GlWidget::render_to_screen () {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
qglClearColor(QColor(Qt::blue));
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if(src){
glActiveTexture(GL_TEXTURE0);
}else{
glActiveTexture(GL_TEXTURE1);
}
shaderProgram.enableAttributeArray("textureCoordinates");
shaderProgram.enableAttributeArray("vertex");
glDrawArrays(GL_QUADS, 0, vertices.size());
shaderProgram.disableAttributeArray("vertex");
shaderProgram.disableAttributeArray("textureCoordinates");
}
void GlWidget::paintGL()
{
qDebug() << "Updating";
glBindFramebuffer(GL_FRAMEBUFFER, fb);
if(src) {
glDrawBuffer(GL_COLOR_ATTACHMENT1);
glActiveTexture(GL_TEXTURE0);
}else {
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glActiveTexture(GL_TEXTURE1);
}
src = !src;
qglClearColor(QColor(Qt::white));
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix4x4 mMatrix;
QMatrix4x4 vMatrix;
shaderProgram.bind();
shaderProgram.setUniformValue("mvpMatrix", pMatrix * vMatrix * mMatrix);
shaderProgram.setAttributeArray ("textureCoordinates", texCoords.constData ());
shaderProgram.enableAttributeArray("textureCoordinates");
shaderProgram.setAttributeArray("vertex", vertices.constData());
shaderProgram.enableAttributeArray("vertex");
glDrawArrays(GL_QUADS, 0, vertices.size());
shaderProgram.disableAttributeArray("vertex");
shaderProgram.disableAttributeArray("textureCoordinates");
render_to_screen ();
shaderProgram.release();
}
I'm supposed to be getting a blue screen with a purple quad in the center, instead I'm getting a white quad in the center. What am I doing wrong here?
I see several places in your code where you set the active texture. But setting the active texture means nothing as far as what texture unit the program will pull from. That's decided by the texture image unit set into the sampler uniform that's accessing the texture. You need to change that uniform, not the active texture.
Or better yet, just bind the other texture to the context. There's no need to set the active texture image unit; just bind the texture you want to sample from. Really, there's no point in any of the glActiveTexture calls you're making.
I'm having a problem trying to get my texture showing correctly on some geometry. Basically, I have some triangles which which describe the shape of a person's face, and then have a png which I have to load with opencv.
I have the following code
void Geometry::loadTex(const char* fileName){
RenderUtils * u = new RenderUtils();
IplImage* img = u->getImg(fileName);
GLuint texture;
// allocate a texture name
glGenTextures( 1, &texture );
// select our current texture
glBindTexture( GL_TEXTURE_2D, texture );
// select modulate to mix texture with color for shading
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
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_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, img->imageData);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
//delete [] t;
}
IplImage* RenderUtils::getImg(const char *fileName){
IplImage* img = 0;
img = cvLoadImage(fileName, CV_LOAD_IMAGE_COLOR);
if(!img) cout << "Couldn't load image " << fileName << endl;
return img;
}
void Geometry::display(){
glPushMatrix();
glTranslatef(20.0f, 20.0f, -40.f);
glRotatef(180.f, 0.0, 0.0, 1.0);
for(int i = 0; i < numTris; i+=3){
glBegin(GL_TRIANGLES);
glTexCoord3f(mean[tri[i]] / texWidth, ((mean[meanLength/2 + tri[i]] - maximumMu) * -1) / texHeight, 0 );
glVertex3f(mean[tri[i]], (mean[meanLength/2 + tri[i]] - maximumMu) * -1, 0);
glTexCoord3f(mean[tri[i+1]] / texWidth, ((mean[meanLength/2 + tri[i+1]] - maximumMu) * -1) / texHeight, 0 );
glVertex3f(mean[tri[i+1]], (mean[meanLength/2 + tri[i+1]] - maximumMu) * -1, 0);
glTexCoord3f(mean[tri[i+2]] / texWidth, ((mean[meanLength/2 + tri[i+2]] - maximumMu) * -1) / texHeight, 0 );
glVertex3f(mean[tri[i+2]], (mean[meanLength/2 + tri[i+2]] - maximumMu) * -1, 0);
glEnd();
}
glPopMatrix();
glutSwapBuffers();
glFlush();
}
So loadTex is called first, which calls load getImg, and then the display method is called from Glut.
You might want to take a look at this. It does exactly what you want.
This should be really simple, but it's consumed multi-hours of my time, and I have no clue what's going on.
I'm rendering a flat-colored full-screen quad to a texture, then reading back the result with glGetTexImage. It's GPGPU related, so I want the alpha value to behave as if it's any of the other three. I'm using an FBO, texture format GL_RGBA32F_ARB, NVidia card on a MacBook Pro with 10.5, if it matters.
I only get back the correct color if the alpha I specify is one; with any other value it appears to be blending with what's already in the framebuffer, even though I've explicitly disabled GL_BLEND. I also tried enabling blending and using glBlendFunc(GL_ONE, GL_ZERO) but the end result is the same. I can clear the framebuffer to zero before rendering, which fixes it, but I want to understand why that's necessary. As a second test, rendering two overlapping quads gives a blended result, when I just want the original 4-channel color back. Surely the solid color quad should be overwriting pixels in the framebuffer completely? I'm guessing I've misunderstood something fundamental. Thanks.
const size_t res = 16;
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB,
res, res, 0, GL_RGBA, GL_FLOAT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
GLuint fbo;
glGenFramebuffersEXT(1, &fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
glViewport(0, 0, res, res);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, res, 0, res, -1, 1);
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT);
//glEnable(GL_BLEND);
//glBlendFunc(GL_ONE, GL_ZERO);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glColor4f(0.2, 0.3, 0.4, 0.5);
for (int i=0; i<2; ++i) {
glBegin(GL_QUADS);
glVertex2i(0,0);
glVertex2i(res, 0);
glVertex2i(res, res);
glVertex2i(0, res);
glEnd();
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
std::vector<float> tmp(res*res*4);
glBindTexture(GL_TEXTURE_2D, tex);
glGetTexImage(GL_TEXTURE_2D, 0,
GL_RGBA, GL_FLOAT, &tmp.front());
const float * const x = &tmp.front();
cerr << x[0] << " " << x[1] << " " << x[2] << " " << x[3] << endl;
// prints 0.3 0.45 0.6 0.75
glDeleteTextures(1, &tex);
glDeleteFramebuffersEXT(1, &fbo);
Not really a good answer, however, some things to note:
What you're observing does not really look like blending. For one, your back-buffer is initially rgba=0, so alpha-blending against it would give 0, not 0.2 0.3 0.4 0.5 like you may observe.
my inclination was that you somehow set the same texture buffer as texture and framebuffer attachement. This is undefined in the spec (section 4.4.3). In the code snippet you provide, you do a glBindTexture(GL_TEXTURE_2D, 0) though, which should make sure it is not the case... I'll let it here in case you've missed it.