I'm trying to render an video through OpenGL in GTK3 on linux. These Vertex and Fragment shaders were used with succes on QT. Actually all the opengl function calls are the same from working examples on Qt.
Anyone knows why? Is it possible that GLEW coordinates are different from coordinates in QT OpenGL?
The video is in YUV420P format, so that's why the fragment shader does matrix multiplication. Is it possible that my fragment coordinates are wrong?
Anyways, here's the video:
My Vertex Shader:
#version 130
attribute vec4 vertexIn;
attribute vec2 textureIn;
varying vec2 textureOut;
void main(void)
{
gl_Position = vertexIn;
textureOut = textureIn;
}
My Fragment Shader:
#version 130
varying vec2 textureOut;
uniform sampler2D tex_y;
uniform sampler2D tex_u;
uniform sampler2D tex_v;
void main(void)
{
vec3 yuv;
vec3 rgb;
yuv.x = texture2D(tex_y, textureOut).r;
yuv.y = texture2D(tex_u, textureOut).r - 0.5;
yuv.z = texture2D(tex_v, textureOut).r - 0.5;
rgb = mat3(1.0, 1.0, 1.0,
0.0, -0.39465, 2.03211,
1.13983, -0.58060, 0.0) * yuv;
gl_FragColor = vec4(rgb, 1.0);
}
My rendering code:
static const GLfloat ver[] = {
-1.0f,-1.0f,
1.0f,-1.0f,
-1.0f, 1.0f,
1.0f, 1.0f
};
static const GLfloat tex[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
void OpenGLArea::init()
{
std::cout << "OpenGLArea init" << std::endl;
set_size_request(640, 360);
Singleton::instance()->getStream("cam1").mediaStream->ffmpegDecoder->setVideoReceiver(this);
}
void OpenGLArea::receiveVideo(unsigned char **videoBuffer, int frameWidth, int frameHeight)
{
this->frameWidth = frameWidth;
this->frameHeight = frameHeight;
//Before first render, datas pointer isn't even created yet
if (!firstFrameReceived)
{
buffer[0] = new unsigned char[frameWidth * frameHeight]; //Y
buffer[1] = new unsigned char[frameWidth * frameHeight / 4]; //U
buffer[2] = new unsigned char[frameWidth * frameHeight / 4]; //V
firstFrameReceived = true;
}
else
{
memcpy(buffer[0], videoBuffer[0], frameWidth * frameHeight);
memcpy(buffer[1], videoBuffer[1], frameWidth * frameHeight / 4);
memcpy(buffer[2], videoBuffer[2], frameWidth * frameHeight / 4);
}
//glDraw();
}
void OpenGLArea::glInit()
{
int frameWidth = 640;
int frameHeight = 360;
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
Shader vertex_shader(ShaderType::Vertex, "vertex.shader");
Shader fragment_shader(ShaderType::Fragment, "fragment.shader");
program = new Program();
program->attach_shader(vertex_shader);
program->attach_shader(fragment_shader);
program->link();
glGenTextures(3, texs);//TODO: delete texture
//Y
glBindTexture(GL_TEXTURE_2D, texs[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, frameWidth, frameHeight, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//U
glBindTexture(GL_TEXTURE_2D, texs[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, frameWidth / 2, frameHeight / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//V
glBindTexture(GL_TEXTURE_2D, texs[2]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, frameWidth / 2, frameHeight / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
void OpenGLArea::glDraw()
{
program->use();
glVertexAttribPointer(A_VER, 2, GL_FLOAT, 0, 0, ver);
glEnableVertexAttribArray(A_VER);
glVertexAttribPointer(T_VER, 2, GL_FLOAT, 0, 0, tex);
glEnableVertexAttribArray(T_VER);
unis[0] = glGetAttribLocation(program->get_id(), "tex_y");
unis[1] = glGetAttribLocation(program->get_id(), "tex_u");
unis[2] = glGetAttribLocation(program->get_id(), "tex_v");
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texs[0]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth, frameHeight, GL_RED, GL_UNSIGNED_BYTE, buffer[0]);
glUniform1i(unis[0], 0);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, texs[1]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth / 2, frameHeight / 2, GL_RED, GL_UNSIGNED_BYTE, buffer[1]);
glUniform1i(unis[1], 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, texs[2]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth / 2, frameHeight / 2, GL_RED, GL_UNSIGNED_BYTE, buffer[2]);
glUniform1i(unis[2], 2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
class GLWindow : public Gtk::Window
{
public:
GLWindow()
{
vbox = new Gtk::VBox;
drawing_area = new OpenGLArea();
vbox->pack_start(*drawing_area, true, true);
add(*vbox);
}
private:
Gtk::Button *button;
Gtk::VBox *vbox;
OpenGLArea *drawing_area;
};
Also, I'm only getting image updates when I resize or refocus on the screen. It's possible that I'm forgetting to call some function when the video is updated. Anyone knows which function is this?
ps: OpenGLArea is subclass of Gtk::DrawingArea
UPDATE:
I just noticed that in the lines
unis[0] = glGetAttribLocation(program->get_id(), "tex_y");
unis[1] = glGetAttribLocation(program->get_id(), "tex_u");
unis[2] = glGetAttribLocation(program->get_id(), "tex_v");
unis[i] always have the same value: 4294967295, so glGetAttribLocation isn't returning anything
GLEW is a library that is used for retrieving the function pointers to OpenGL functions. It's needed for OGL version > 1.1.
So, don't think of Glew as the culprit of your vertices issue. If you need to change the order of vertices is due to Winding order.
The order also may be the cause of drawing a texture upside-down.
glGetAttribLocation is used for attributes. In your VS these are vertexIn and textureIn.
For uniforms (your tex_XXX) you must use glGetUniformLocation
For the resizing issue, this question may be useful.
In short, you must connect a callback for the "configure-event" and use glViewport
Related
Any idea, why decoded YUV -> RGB(shader conversion) has that extra green edge on the right side?
Almost any 1080X1920 video seems to have this issue.
A screen recording of the issue is uploaded here https://imgur.com/a/JtUZq4h
Once I manually scale up the texture width, I can see it fills up to the viewport, but it would be nice if I could fix the actual cause. Is it some padding that's part of YUV colorspace? What else could it be?
My model is -1 to 1, filling the entire width
The texture coordinates are also 0 to 1 ratio
float vertices[] = {
-1.0, 1.0f, 0.0f, 0.0, // top left
1.0f, 1.0f, 1.0, 0.0, // top right
-1.0f, -1.0f, 0.0f, 1.0f, // bottom left
1.0f, -1.0f, 1.0f, 1.0f // bottom right
};
Fragment Shader
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;
precision highp float;
uniform sampler2D textureY;
uniform sampler2D textureU;
uniform sampler2D textureV;
uniform float alpha;
uniform vec2 texScale;
void main()
{
float y = texture(textureY, TexCoord / texScale).r;
float u = texture(textureU, TexCoord / texScale).r - 0.5;
float v = texture(textureV, TexCoord / texScale).r - 0.5;
vec3 rgb;
//yuv - 709
rgb.r = clamp(y + (1.402 * v), 0, 255);
rgb.g = clamp(y - (0.2126 * 1.5748 / 0.7152) * u - (0.0722 * 1.8556 / 0.7152) * v, 0, 255);
rgb.b = clamp(y + (1.8556 * u), 0,255);
FragColor = vec4(rgb, 1.0);
}
Texture Class
class VideoTexture {
public:
VideoTexture(Decoder *dec) : decoder(dec) {
glGenTextures(1, &texture1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, texture1);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, decoder->frameQueue.first().linesize[0], decoder->frameQueue.first().height, 0, format, GL_UNSIGNED_BYTE, 0);
glGenerateMipmap(GL_TEXTURE_2D);
glGenTextures(1, &texture2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, texture2);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, decoder->frameQueue.first().linesize[1], decoder->frameQueue.first().height / 2, 0, format, GL_UNSIGNED_BYTE, 0);
glGenerateMipmap(GL_TEXTURE_2D);
glGenTextures(1, &texture3);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, texture3);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, decoder->frameQueue.first().linesize[2], decoder->frameQueue.first().height / 2, 0, format, GL_UNSIGNED_BYTE, 0);
glGenerateMipmap(GL_TEXTURE_2D);
}
void Render(Shader *shader, Gui *gui) {
if (decoder->frameQueue.isEmpty()) {
return;
}
glActiveTexture(GL_TEXTURE0);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, decoder->frameQueue.first().linesize[0], decoder->frameQueue.first().height, format, GL_UNSIGNED_BYTE, decoder->frameQueue.at(currentFrame).data[0]);
glBindTexture(GL_TEXTURE_2D, texture1);
shader->setInt("textureY", 0);
glActiveTexture(GL_TEXTURE1);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, decoder->frameQueue.first().linesize[1], decoder->frameQueue.first().height / 2, format, GL_UNSIGNED_BYTE, decoder->frameQueue.at(currentFrame).data[1]);
glBindTexture(GL_TEXTURE_2D, texture2);
shader->setInt("textureU", 1);
glActiveTexture(GL_TEXTURE2);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, decoder->frameQueue.first().linesize[2], decoder->frameQueue.first().height / 2, format, GL_UNSIGNED_BYTE, decoder->frameQueue.at(currentFrame).data[2]);
glBindTexture(GL_TEXTURE_2D, texture3);
shader->setInt("textureV", 2);
}
~VideoTexture() {
printf("\nVideo texture destructor");
glDeleteTextures(1, &texture1);
glDeleteTextures(1, &texture2);
glDeleteTextures(1, &texture3);
}
private:
GLuint texture1;
GLuint texture2;
GLuint texture3;
GLint internalFormat = GL_RG8;
GLint format = GL_RED;
int currentFrame = 0;
Decoder *decoder;
}
I am working on a 2D game engine and I want to implement text rendering. I want to use freetype. I have the following code:
GLuint va;
glGenVertexArrays(1, &va);
GLuint vb;
glGenBuffers(1, &vb);
glBindVertexArray(va);
glBindBuffer(GL_ARRAY_BUFFER, vb);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
FT_Face testface;
FT_Error error;
error = FT_New_Face(ftlib, "arial.ttf", 0, &testface);
if (error == FT_Err_Unknown_File_Format)
{
std::cout << "Font format not supported" << std::endl;
}
else if (error)
{
std::cout << "Something else" << "\n";
}
error = FT_Set_Pixel_Sizes(testface, 0, 48);
if (error)
{
std::cout << "Problem with set px szes occ\n";
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (unsigned char c = 0; c < 128; c++)
{
if (FT_Load_Char(testface, c, FT_LOAD_RENDER))
{
std::cout << "Error. Failed to load glyph "<<c<<"\n";
continue;
}
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
testface->glyph->bitmap.width,
testface->glyph->bitmap.rows,
0,
GL_RED,
GL_UNSIGNED_BYTE,
testface->glyph->bitmap.buffer
);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
Character newchar = {
texture,
glm::ivec2(testface->glyph->bitmap.width, testface->glyph->bitmap.rows),
glm::ivec2(testface->glyph->bitmap_left, testface->glyph->bitmap_top),
testface->glyph->advance.x
};
characters.insert(std::pair<char, Character>{c, newchar});
}
FT_Done_Face(testface);
//glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glUseProgram(text_shader_program);
glUniform4f(glGetUniformLocation(text_shader_program, "textColor"), 1.f, 1.f, 1.f, 1.f);
glActiveTexture(GL_TEXTURE0);
std::string::const_iterator c;
std::string text = "TEST";
glBindVertexArray(va);
float x = 0.f;
for (c = text.begin(); c != text.end(); c++)
{
Character ch = characters[*c];
float xpos = x + ch.bearing.x;
float ypos = 0.f - (ch.size.y - ch.bearing.y);
float w = ch.size.x;
float h = ch.size.y;
float vertices[6][4] = {
{xpos, ypos + h, 0.f, 0.f},
{xpos, ypos, 0.f, 1.f},
{xpos + w, ypos, 1.f, 1.f},
{xpos, ypos + h, 0.f, 0.f},
{xpos + w, ypos, 1.f, 1.f},
{xpos + w, ypos + h, 1.f, 0.f}
};
glBindTexture(GL_TEXTURE_2D, ch.textureID);
glBindBuffer(GL_ARRAY_BUFFER, vb);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
x += (ch.advance >> 6);
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
I initialize the library (before the above code happens) with
FT_Error error = FT_Init_FreeType(&ftlib);
if (error)
{
std::cout << "????" << std::endl;
}
The face and library initializes properly, glyphs are loaded, but nothing is drawn. I don't use camera, so all the coordinates are in space -1 to 1 if that matters.
I have the following shaders for text rendering:
vertexshader
#version 330 core
layout(location=0) in vec4 vertex;
out vec2 TextCoords;
void main() {
gl_Position = vec4(vertex.xy, 0.0, 1.0);
TextCoords = vertex.zw;
}
fragmentshader
#version 330 core
in vec3 TextCoords;
out vec4 color;
uniform sampler2D text;
uniform vec4 textColor;
void main(){
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TextCoords).r);
color = textColor * sampled;
}
Update:
I changed the line FT_Set_Pixel_Sizes(testface, 0, 48) to FT_Set_Pixel_Sizes(testface, 0, 1) and I got a huge gray rectangle where the text should be.
Has anyone got any ideas?
The problem seems to be the result of not using z-coordinate. The problem is, when using coordinate space -1 to 1, the text is rendering in too big scale to be seen or recognized. The solution is to use the z-coordinate as a static camera view, so for 500x500 window the borders are -250.f and 250.f
I need some help with rendering inverted scene to framebuffer. I want to make a mirror effect.
What i have in result:
Initializing frame buffer:
GLuint FramebufferName = 0;
glGenFramebuffers(1, &FramebufferName);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
GLuint renderedTexture;
glGenTextures(1, &renderedTexture);
glBindTexture(GL_TEXTURE_2D, renderedTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SCREEN_WIDTH, SCREEN_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderedTexture, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glReadBuffer(GL_NONE);
Draw scene to frame buffer:
mView = camera.getViewMatrix();
mProjection = perspective(45.0f, 4.0f / 3.0f, 0.1f, 300.0f) * scale(mat4(1.0f), vec3(1.0f, -1.0f, 1.0f));
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FramebufferName);
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glCullFace(GL_FRONT);
glEnable(GL_CULL_FACE);
mWorld = rotate(mWorld, -15.0f, vec3(1, 0, 0));
mWorld = rotate(mWorld, -30.0f, vec3(0, 1, 0));
cubemap.Draw(mView, mProjection, mWorld);
glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glUseProgram(program);
mWorld = translate(mat4(1.0f), vec3(0, -10, 0));
glUniformMatrix4fv(mvp_object, 1, GL_FALSE, value_ptr(mProjection * mView * mWorld * scale(mat4(1.0f), vec3(10.0f))));
cannon.Draw(program);
mWorld = mat4(1.0f);
mWorld = translate(mat4(1.0f), vec3(50, -10, 0));
mWorld = rotate(mWorld, 90.0f, vec3(0, 1, 0));
glUniformMatrix4fv(mvp_object, 1, GL_FALSE, value_ptr(mProjection * mView * mWorld * scale(mat4(1.0f), vec3(10.0f))));
cannon.Draw(program);
mWorld = mat4(1.0f);
mWorld = translate(mat4(1.0f), vec3(0, -10, 50));
mWorld = rotate(mWorld, 140.0f, vec3(0, 1, 0));
glUniformMatrix4fv(mvp_object, 1, GL_FALSE, value_ptr(mProjection * mView * mWorld * scale(mat4(1.0f), vec3(10.0f))));
cannon.Draw(program);
glDisable(GL_DEPTH_TEST);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
I saw some tutorials where used glScalef(1.0, -1.0, 1.0) to invert scene.
glScalef was used in old versions of opengl. How can i invert scene in new versions of opengl?
I've multiplied projection matrix on scale(mat4(1.0f), vec3(1.0, -1.0, 1.0)) for this effect. Is it right?
Here is the vertex shader written in GLSL.
void main(void)
{
// Clean up inaccuracies
vec2 Pos = sign(gl_Vertex.xy);
gl_Position = vec4(Pos.xy, 0.0, 1.0);
// Image-space
gl_TexCoord[0].xy = vec2(-Pos.x, Pos.y) * 0.5 + 0.5; // x texture coord is inverted
}
Pos refers to screen aligned quad for framebuffer output.
Fragment shader
uniform sampler2D framebufferSampler;
void main(void)
{
gl_FragColor = texture2D(framebufferSampler, gl_TexCoord[0].xy);
}
Screen aligned quad can be drawn
glBegin(GL_QUADS)
{
glVertex2f(-1.0f, -1.0f);
glVertex2f(1.0f, -1.0f);
glVertex2f(1.0f, 1.0f);
glVertex2f(-1.0f, 1.0f);
}
glEnd();
or using vertex arrays.
GLfloat vertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
1.0f, 1.0f,
-1.0f, 1.0f
};
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, (void*)vertices);
glDrawArrays(GL_QUADS, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
texture that is used to be wraped to the quad, is the framebuffer info with previous rendered content (multipass shader technique).
You can also do so with others 2D texture coords or images instead of framebuffer and screen align quads.
Original image.
Inverted image.
More info can be found in Learn OpenGL.
Could some one explain for me how can I render multiple objects with different textures in OpenGL?
I think that I'm nearly to the final result but at the moment I got stuck here and I don't know what I need to do next. Really need some helps!
At the moment this is what I have:
drawSphere(): draw an UV sphere based on number of longs and lats
int numberOfVerices = 0;
for(int i = 0; i <= lats; i++) {
double lat0 = M_PI * (-0.5 + (double) (i - 1) / lats);
double z0 = sin(lat0);
double zr0 = cos(lat0);
double lat1 = M_PI * (-0.5 + (double) i / lats);
double z1 = sin(lat1);
double zr1 = cos(lat1);
for(int j = 0; j <= longs; j++) {
double lng = 2 * M_PI * (double) (j - 1) / longs;
double x = cos(lng);
double y = sin(lng);
glNormal3f(x * zr0, y * zr0, z0);
vertices.push_back(x * zr0);
vertices.push_back(y * zr0);
vertices.push_back(z0);
indices.push_back(numberOfVerices);
numberOfVerices++;
vertices.push_back(x * zr1);
vertices.push_back(y * zr1);
vertices.push_back(z1);
indices.push_back(numberOfVerices);
numberOfVerices++;
}
indices.push_back(GL_PRIMITIVE_RESTART_FIXED_INDEX);
}
SetupGeometry(): this method is used to bind vertices and texture coordinates.
drawSphere(300, 300);
glGenBuffers(1, &vboVertex);
glBindBuffer(GL_ARRAY_BUFFER, vboVertex);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), &vertices[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0);
glGenBuffers(1, &vboTexture);
glBindBuffer(GL_ARRAY_BUFFER, vboTexture);
glBufferData(GL_ARRAY_BUFFER, texture.size() * sizeof(GLfloat), &texture[0], GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glGenBuffers(1, &vboIndex);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndex);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW);
numsToDraw = indices.size();
nums = indices.size();
SetupShader(): create shader and compile shader
char text[1000];
int length;
vertexSource = filetobuf("space/sphere.vert");
fragmentSource = filetobuf("space/sphere.frag");
vertexShader = glCreateShader(GL_VERTEX_SHADER);
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vertexShader, 1, (const GLchar**) &vertexSource, 0);
glShaderSource(fragmentShader, 1, (const GLchar**) &fragmentSource, 0);
fprintf(stderr, "Compiling vertex shader....\n");
glCompileShader(vertexShader);
fprintf(stderr, "Compiling fragment shader....\n");
glCompileShader(fragmentShader);
fprintf(stderr, "Done....\n");
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glBindAttribLocation(shaderProgram, 0, "in_Position");
glBindAttribLocation(shaderProgram, 1, "Texture_Coord");
printf("Linking program ... \n");
glLinkProgram(shaderProgram);
glGetProgramInfoLog(shaderProgram, 1000, &length, text);
if(length > 0){
fprintf(stderr, "Validate Shader Program\n%s\n", text );
}
glUseProgram(shaderProgram);
SetupTexture(char * filename): loading image, generate 2 arrays of textures, one to GL_TEXTURE0 and other one to GL_TEXTURE1
GLuint texture[2];
glGenTextures(2, texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture[0]);
data = stbi_load(fileName, &w, &h, &n, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glUniform1i(glGetUniformLocation(shaderProgram, "texture_Sun"), 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
stbi_image_free(data);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture[1]);
data = stbi_load(fileName1, &w, &h, &n, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glUniform1i(glGetUniformLocation(shaderProgram, "texture_Earth"), 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
stbi_image_free(data);
Render(int i): rendering 2 objects, which is sphere.
glClearColor(0.0, 0.0, 0.0, 1.0);/* Make our background black */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndex);
glPushMatrix();
glLoadIdentity();
glm::mat4 Projection = glm::perspective(50.0f, 5.0f / 3.0f, 1.0f, 100.0f);
glm::mat4 View = glm::lookAt(
glm::vec3(0, 5, 2),
glm::vec3(0, 0, 0),
glm::vec3(0, 1, 0)
);
/* Animations */
GLfloat angle = (GLfloat) (i);
View = glm::translate(View, glm::vec3(2.0f, 0.0f, 0.0f));
View = glm::rotate(View, angle * 0.5f, glm::vec3(0.0f, 0.0f, 1.0f));
/* ******* */
glm::mat4 Model = glm::mat4(1.0f);
glm::mat4 MVP = Projection * View * Model;
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "mvpMatrix"), 1, GL_FALSE, glm::value_ptr(MVP));
glDrawElements(GL_QUAD_STRIP, numsToDraw, GL_UNSIGNED_INT, NULL);
glPopMatrix();
glPushMatrix();
glLoadIdentity();
glm::mat4 Projection1 = glm::perspective(50.0f, 5.0f / 3.0f, 1.0f, 100.0f);
glm::mat4 View1 = glm::lookAt(
glm::vec3(0, 5, 2),
glm::vec3(0, 0, 0),
glm::vec3(0, 1, 0)
);
/* Animations */
GLfloat angle1 = (GLfloat) (i);
View1 = glm::translate(View1, glm::vec3(-2.0f, 0.0f, 0.0f));
View1 = glm::rotate(View1, angle1 * -0.5f, glm::vec3(0.0f, 0.0f, 1.0f));
/* ******* */
glm::mat4 Model1 = glm::mat4(1.0f);
MVP = Projection1 * View1 * Model1;
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "mvpMatrix"), 1, GL_FALSE, glm::value_ptr(MVP));
glDrawElements(GL_QUAD_STRIP, nums, GL_UNSIGNED_INT, NULL);
glPopMatrix();
VertexShader:
#version 330 core
precision highp float;
attribute vec3 in_Position;
attribute vec3 in_Position1;
varying vec4 Texture_Coord;
uniform mat4 mvpMatrix;
void main(void){
gl_Position = mvpMatrix * vec4(in_Position, 1.0);
Texture_Coord = vec4(in_Position, 1.0);
}
FragmentShader:
#version 330 core
precision highp float;
varying vec4 Texture_Coord;
uniform sampler2D texture_Sun;
uniform sampler2D texture_Earth;
out vec4 FragColor;
void main(void){
vec2 longLat = vec2((atan(Texture_Coord.y, Texture_Coord.x)/3.1415926 + 1) * 0.5, (asin(Texture_Coord.z) / 3.1415926 + 0.5));
FragColor = texture2D(texture_Sun, longLat);
FragColor = texture2D(texture_Earth, longLat);
}
Main codes in main:
SetupGeomtry();
SetupShader();
SetupTexture("images/Earth.jpg", "images/sun.jpg");
/*
* Main loop
*/
int i = 0;
while(!glfwWindowShouldClose(window)){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Render(i+=1);
glfwSwapBuffers(window);
glfwPollEvents();
Sleep(10);
}
Normally you would load your textures (i.e. buffer them to GPU):
GLuint texture[2];
glGenTextures(2, texture);
glBindTexture(GL_TEXTURE_2D, texture[0]);
data = stbi_load(fileName, &w, &h, &n, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
stbi_image_free(data);
glBindTexture(GL_TEXTURE_2D, texture[1]);
data = stbi_load(fileName1, &w, &h, &n, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
stbi_image_free(data);
then before drawing each of your objects bind the relevant texture for said object:
// already selected the shader programme for both Sun and Earth
GLint textureLocation = glGetUniformLocation(shaderProgram, "texture_CelestialBody");
// optionally remember more locations for multi texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glUniform1i(textureLocation, 0);
// optionally bind more textures for multi texture shader...
// draw Sun now
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture[1]);
glUniform1i(textureLocation, 0);
// optionally bind more textures for multi texture shader...
// draw Earth now
then the texture_CelestialBody identifies the texture for both the Sun and the Earth - i.e. the fragment shader would not discriminate between the two.
I am trying to use deferred shading to implement SSAO and I have problems to access my textures in the deferred fragment shader. The code is in C++/Qt5 and makes use of Coin3D to generate the rest of the UI (but this shouldn't really matter here).
The fragment shader of the deferred pass is:
#version 150 compatibility
uniform sampler2D color;
uniform sampler2D position;
uniform sampler2D normal;
uniform vec3 dim;
uniform vec3 camPos;
uniform vec3 camDir;
void main()
{
// screen position
vec2 t = gl_TexCoord[0].st;
// the color
vec4 c = texture2D(color, t);
gl_FragColor = c + vec4(1.0, t.x, t.y, 1.0);
}
The code for running the deferred pass is
_geometryBuffer.Unbind();
// push state
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glPushAttrib(GL_DEPTH_BUFFER_BIT |
GL_COLOR_BUFFER_BIT |
GL_LIGHTING_BIT |
GL_SCISSOR_BIT |
GL_POLYGON_BIT |
GL_CURRENT_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDisable(GL_LIGHTING);
glDisable(GL_COLOR_MATERIAL);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_CULL_FACE);
}
// bind shader
// /!\ IMPORTANT to do before specifying locations
_deferredShader->bind();
_CheckGLErrors("deferred");
// specify positions
_deferredShader->setUniformValue("camPos", ...);
_deferredShader->setUniformValue("camDir", ...);
_geometryBuffer.Bind(GBuffer::TEXTURE_TYPE_NORMAL, 2);
_deferredShader->setUniformValue("normal", GLint(2));
_geometryBuffer.Bind(GBuffer::TEXTURE_TYPE_POSITION, 1);
_deferredShader->setUniformValue("position", GLint(1));
_geometryBuffer.Bind(GBuffer::TEXTURE_TYPE_DIFFUSE, 0);
_deferredShader->setUniformValue("color", GLint(0));
_CheckGLErrors("bind");
// draw screen quad
{
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glColor3f(0, 0, 0);
glVertex2f(-1, -1);
glTexCoord2f(1, 0);
glColor3f(0, 0, 0);
glVertex2f( 1, -1);
glTexCoord2f(1, 1);
glColor3f(0, 0, 0);
glVertex2f( 1, 1);
glTexCoord2f(0, 1);
glColor3f(0, 0, 0);
glVertex2f(-1, 1);
glEnd();
}
_deferredShader->release();
// for debug
_geometryBuffer.Unbind(2);
_geometryBuffer.Unbind(1);
_geometryBuffer.Unbind(0);
_geometryBuffer.DeferredPassBegin();
_geometryBuffer.DeferredPassDebug();
// pop state
{
glPopAttrib();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
I know that the textures have been correctly processed in the geometry buffer creation because I can dump them into files and get the expected result.
The deferred pass doesn't work. The shader compiled correctly and I get the following result on screen:
And the last part of my code (DeferredPassBegin/Debug) is to draw the FBO to the screen (as shown in screenshot) as a proof that the GBuffer is correct.
The current result seems to mean that the textures are not correctly bound to their respective uniform, but I know that the content is valid as I dumped the textures to files and got the same results as shown above.
My binding functions in GBuffer are:
void GBuffer::Unbind()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}
void GBuffer::Bind(TextureType type, uint32_t idx)
{
glActiveTexture(GL_TEXTURE0 + idx);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _textures[static_cast<uint32_t>(type)]);
}
void GBuffer::Unbind(uint32_t idx)
{
glActiveTexture(GL_TEXTURE0 + idx);
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
}
Finally, the textures are 512/512, and I created them in my GBuffer with:
WindowWidth = WindowHeight = 512;
// Create the FBO
glGenFramebuffers(1, &_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
const uint32_t NUM = static_cast<uint32_t>(NUM_TEXTURES);
// Create the gbuffer textures
glGenTextures(NUM, _textures);
glGenTextures(1, &_depthTexture);
for (unsigned int i = 0 ; i < NUM; i++) {
glBindTexture(GL_TEXTURE_2D, _textures[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, WindowWidth, WindowHeight, 0, GL_RGBA, GL_FLOAT, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i + _firstIndex, GL_TEXTURE_2D, _textures[i], 0);
}
// depth
glBindTexture(GL_TEXTURE_2D, _depthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depthTexture, 0);
GLenum buffers[NUM];
for(uint32_t i = 0; i < NUM; ++i){
buffers[i] = GLenum(GL_COLOR_ATTACHMENT0 + i + _firstIndex);
}
glDrawBuffers(NUM, buffers);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
printf("FB error, status: 0x%x\n", status);
return _valid = false;
}
// unbind textures
glBindTexture(GL_TEXTURE_2D, 0);
// restore default FBO
glBindFramebuffer(GL_FRAMEBUFFER, 0);
How can I debug farther at this stage?
I know that the texture data is valid, but I can't seem to bind it to the shader correctly (but I have other shaders that use textures loaded from files and which work fine).
--- Edit 1 ---
As asked, the code for DeferredPassBegin/Debug (mostly coming from this tutorial )
void GBuffer::DeferredPassBegin() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
}
void GBuffer::DeferredPassDebug() {
GLsizei HalfWidth = GLsizei(_texWidth / 2.0f);
GLsizei HalfHeight = GLsizei(_texHeight / 2.0f);
SetReadBuffer(TEXTURE_TYPE_POSITION);
glBlitFramebuffer(0, 0, _texWidth, _texHeight,
0, 0, HalfWidth, HalfHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
SetReadBuffer(TEXTURE_TYPE_DIFFUSE);
glBlitFramebuffer(0, 0, _texWidth, _texHeight,
0, HalfHeight, HalfWidth, _texHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
SetReadBuffer(TEXTURE_TYPE_NORMAL);
glBlitFramebuffer(0, 0, _texWidth, _texHeight,
HalfWidth, HalfHeight, _texWidth, _texHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
Arghk!!!
So I expected that texture parameters were not mandatory, but as I looked at some code, I just tried to specify my texture parameters. When generating the FBO textures, I use now
for (unsigned int i = 0 ; i < NUM; i++) {
glBindTexture(GL_TEXTURE_2D, _textures[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, WindowWidth, WindowHeight, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i + _firstIndex, GL_TEXTURE_2D, _textures[i], 0);
}
And with this change, I get the expected result (with only c in the fragment shader, and similar correct results if I switch to visualizing the normal / position).
Conclusion: one must specify the texture parameters for deferred shading to work (at least with the graphics setup of my application / machine).