It seems every time I try to get texturing working in my OpenGL apps I miss something obvious, and I end up spending many hours debugging. Well it happened again and even though I've been trying to compare my code with an older working app for quite some time, I'm still missing something because the texture just isn't displaying at all. The mesh shows up black. I'm not getting any OpenGL errors.
I would be very grateful for some help narrowing down the problem.
Here is part of my code, I'm pretty sure my mistake is in these areas of the code:
OpenGL.cpp:
bool OpenGL::enable_attribs_and_uniforms(GLuint program_id)
{
glGenVertexArrays(1, &this->vao_id);
glBindVertexArray(this->vao_id);
this->pos_a_loc = glGetAttribLocation(program_id, "pos_a");
this->uv_a_loc = glGetAttribLocation(program_id, "uv_a");
if (this->pos_a_loc == -1 || /*this->color_a_loc == -1 ||*/ this->uv_a_loc == -1)
{
std::cerr << "OpenGL Warning: Failed to get attribute locations, probably because at least one attribute isn't being used in the shader program. Attributes get optimized out if they don't affect the output of the shader program in any way.\n\n";
}
glEnableVertexAttribArray(this->pos_a_loc);
glEnableVertexAttribArray(this->uv_a_loc);
this->p_mat_u_loc = glGetUniformLocation(program_id, "p_mat_u");
this->v_mat_u_loc = glGetUniformLocation(program_id, "v_mat_u");
this->m_mat_u_loc = glGetUniformLocation(program_id, "m_mat_u");
this->mvp_mat_u_loc = glGetUniformLocation(program_id, "mvp_mat_u");
this->tex_samp_u_loc = glGetUniformLocation(program_id, "tex_samp_u");
if (this->p_mat_u_loc == -1 || this->v_mat_u_loc == -1 ||
this->m_mat_u_loc == -1 || this->mvp_mat_u_loc == -1 ||
this->tex_samp_u_loc == -1)
{
std::cerr << "OpenGL Warning: Failed to get all uniform locations, probably because at least one uniform isn't being used in the shader program. Uniforms get optimized out if they don't affect the output of the shader program in any way.\n\n";
}
check_gl_error();
return true;
}
void OpenGL::fill_buffers(std::vector<Object*>& objs)
{
std::vector<Object*>::iterator it;
for (it = objs.begin(); it != objs.end(); ++it)
{
GLuint vb_pos_id;
glGenBuffers(1, &vb_pos_id);
glBindBuffer(GL_ARRAY_BUFFER, vb_pos_id);
glBufferData(GL_ARRAY_BUFFER, (*it)->v_pos.size() * sizeof(glm::vec3), &(*it)->v_pos[0], GL_STATIC_DRAW);
this->vb_pos_ids.push_back(vb_pos_id);
(*it)->tex_filenames[0] = "some_texture.png";
SDL_Surface* tex_surface = IMG_Load((*it)->tex_filenames[0].c_str());
if (!tex_surface)
{
std::cerr << "Texture Error: Couldn't open texture file: " << (*it)->tex_filenames[0] << "\n\n";
return;
}
GLuint tex_samp_id;
glGenTextures(1, &tex_samp_id);
glBindTexture(GL_TEXTURE_2D, tex_samp_id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_surface->w, tex_surface->h, 0,
GL_RGBA, GL_UNSIGNED_BYTE, tex_surface->pixels);
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_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
this->tex_samp_ids.push_back(tex_samp_id);
GLuint vb_uv_id;
glGenBuffers(1, &vb_uv_id);
glBindBuffer(GL_ARRAY_BUFFER, vb_uv_id);
glBufferData(GL_ARRAY_BUFFER, (*it)->v_uv.size() * sizeof(glm::vec2), &(*it)->v_uv[0], GL_STATIC_DRAW);
this->vb_uv_ids.push_back(vb_uv_id);
}
check_gl_error();
return;
}
void OpenGL::draw(std::vector<Object*>& objs) // draws GL_TRIANGLES
{
std::vector<Object*>::iterator objs_it;
std::vector<GLuint>::iterator vb_pos_it;
std::vector<GLuint>::iterator vb_col_it;
std::vector<GLuint>::iterator vb_uv_it;
unsigned int i = 0;
for (objs_it = objs.begin(), vb_pos_it = this->vb_pos_ids.begin(),
vb_col_it = this->vb_col_ids.begin(), vb_uv_it = this->vb_uv_ids.begin();
objs_it != objs.end(); ++objs_it, ++vb_pos_it, ++vb_col_it, ++vb_uv_it, ++i)
{
(*objs_it)->m_mat = glm::rotate((*objs_it)->m_mat, 1.0f, glm::vec3(0.0f, 1.0f, 0.0f));
this->mvp_mat = this->p_mat * this->v_mat * (*objs_it)->m_mat;
// send attribs to vertex shader
glBindBuffer(GL_ARRAY_BUFFER, (*vb_pos_it));
glVertexAttribPointer(this->pos_a_loc, 3, GL_FLOAT, GL_FALSE, 0, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, this->tex_samp_ids[i]);
glBindBuffer(GL_ARRAY_BUFFER, (*vb_uv_it));
glVertexAttribPointer(this->uv_a_loc, 2, GL_FLOAT, GL_FALSE, 0, 0);
// send uniforms to vertex shader
glUniformMatrix4fv(this->p_mat_u_loc, 1, GL_FALSE, &this->p_mat[0][0]);
glUniformMatrix4fv(this->v_mat_u_loc, 1, GL_FALSE, &this->v_mat[0][0]);
glUniformMatrix4fv(this->m_mat_u_loc, 1, GL_FALSE, &(*objs_it)->m_mat[0][0]);
glUniformMatrix4fv(this->mvp_mat_u_loc, 1, GL_FALSE, &this->mvp_mat[0][0]);
glUniform1i(this->tex_samp_u_loc, 0);
// draw
glDrawArrays(GL_TRIANGLES, 0, (*objs_it)->v_pos.size());
check_gl_error();
glGetError(); // clear GL error before next iteration
}
return;
}
simple.vsh:
#version 330 core
in vec3 pos_a;
in vec2 uv_a;
uniform mat4 p_mat_u;
uniform mat4 v_mat_u;
uniform mat4 m_mat_u;
uniform mat4 mvp_mat_u;
out vec2 uv_v;
void main()
{
gl_Position = mvp_mat_u * vec4(pos_a, 1.0);
uv_v = uv_a;
}
simple.fsh:
#version 330 core
smooth in vec2 uv_v;
uniform sampler2D tex_samp_u;
out vec4 color_o;
void main()
{
color_o = texture2D(tex_samp_u, uv_v);
}
Well, it turned out the code wasn't the problem, I was exporting from Blender incorrectly. I guess the downvote was appropriate.
This code works to display the texture, but it shows up flipped the wrong way, so I'll have to flip it after loading the image, and I still have to figure out how to get Blender's collada exporter to export with -Z forward, Y up orientation... but that's not for this question.
Related
I've been playing with textures for some time and i just can't get it to work
Here is the code
#include "App.h"
std::array <float, 4> background_col = {0.25, 0.25, 0.25, 1.0} ;
std::string name = "OpenGL" ;
std::string icon = "data/images/dot.png" ;
const int width = 800 ;
const int height = 800 ;
bool anti_aliasing = true ;
bool FPS_60_CAP = true ;
MVP mvp;
void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods)
{
}
void App::run()
{
Window window(true, anti_aliasing);
window.make_Window(glfwCreateWindow(width, height, name.c_str(), NULL, NULL), key_callback, icon);
srand(time(NULL));
mvp.view = glm::ortho(0.0f, (float)width, 0.0f, (float)height);
Renderer renderer;
Player player;
// redesighn with errors in mind
// 1. TODO: Implement Text
// 2. TODO: Implement UI
std::vector<float> vertexes = {
200, 200, 1, 1, 1, 1, 0, 0,
200, 600, 1, 1, 1, 1, 0, 1,
600, 600, 1, 1, 1, 1, 1, 1,
600, 200, 1, 1, 1, 1, 1, 0
};
std::vector<uint32_t> indexes = {0, 1, 2, 3};
while (!glfwWindowShouldClose(window.window))
{
glClearColor(background_col[0], background_col[1], background_col[2], background_col[3]);
glClear(GL_COLOR_BUFFER_BIT);
warn(0);
Shader shader("shaders/text.shader");
shader.useShader();
warn(1);
Texture tex("data/images/dot.png");
tex.Bind();
warn(2);
shader.uniformMat4Float("u_projection_matrix", mvp.projection * mvp.view * mvp.model);
shader.uniformInt({0}, "tex");
warn(3);
VBO vbo;
VAO vao;
IBO ibo;
warn(4);
vbo.write(vertexes.data(), vertexes.size() * sizeof(float));
ibo.write(indexes.data(), indexes.size() * sizeof(uint32_t));
warn(5);
ibo.bind();
vbo.bind();
vao.bind();
warn(6);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (const void *)(sizeof(float) * 0));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (const void *)(sizeof(float) * 2));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (const void *)(sizeof(float) * 6));
warn(7);
glDrawElements(GL_QUADS, indexes.size(), GL_UNSIGNED_INT, nullptr);
warn(8);
ibo.unbind();
vbo.unbind();
vao.unbind();
warn(9);
tex.unBind();
tex.~Texture();
warn(10);
vbo.~VBO();
vao.~VAO();
ibo.~IBO();
warn(11);
shader.deleteShader();
warn(12);
glfwSwapBuffers(window.window);
glfwSwapInterval(FPS_60_CAP);
glfwPollEvents();
}
alcCloseDevice(player.Device);
window.~Window();
}
the texture class:
#include "Core.h"
Texture::Texture(std::string path)
{
this->path = path;
this->bpp = 0;
this->height = 0;
this->width = 0;
this->localbuffer = nullptr;
this->TexID = 0;
stbi_set_flip_vertically_on_load(1);
localbuffer = stbi_load(path.c_str(), &width, &height, &bpp, 4);
glGenTextures(1, &TexID);
glBindTexture(GL_TEXTURE_2D, TexID);
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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, localbuffer);
glBindTexture(GL_TEXTURE_2D, 0);
if (localbuffer) { stbi_image_free(localbuffer); }
else { info("h"); }
}
void Texture::Bind(uint32_t slot)
{
glActiveTexture(GL_TEXTURE0 + slot);
}
void Texture::unBind()
{
glBindTexture(GL_TEXTURE_2D, 0);
}
Texture::~Texture()
{
glDeleteTextures(1, &TexID);
}
and the shader:
#shader vertex
#version 400 core
layout (location = 0) in vec4 Pos;
layout (location = 1) in vec4 vColor;
layout (location = 2) in vec2 vTexCoord;
out vec4 color;
out vec2 TexCoord;
uniform mat4 u_projection_matrix;
void main()
{
gl_Position = u_projection_matrix * Pos;
color = vColor;
TexCoord = vTexCoord;
}
#shader fragment
#version 400 core
in vec4 color;
in vec2 TexCoord;
uniform sampler2D tex;
void main()
{
vec4 texcol = texture(tex, TexCoord);
gl_FragColor = texcol;
}
it just stays blank and displays this error
corrupted double-linked list
it is important to mention this error happens on the second frame
The code that I know works because I've got textures to display before correctly is:
the Shader class
VAO VBO and IBO
EDIT: after the fix from rafix07 it doesn't crash anymore it just doesn't display
EDIT2:
I've implemented glDebugMessageCallback, and it gives me this error:
source: 33352
type: 33361
id: 1
severity: 33387
length: 130
GL ERROR!: Shader Stats: SGPRS: 24 VGPRS: 24 Code Size: 72 LDS: 0 Scratch: 0 Max Waves: 10 Spilled SGPRs: 0 Spilled VGPRs: 0 PrivMem VGPRs: 0
Remove all explicit invocations of destructors, like:
tex.~Texture();
You get corrupted double-linked list because you deallocate a resource twice: first time by calling destructor explicitly and the second one when destructor is called at the end of scope within object is defined.
{
Texture tex;
// ...
tex.~Texture(); // texture is deallocated and id texture is dangling reference
// <-- dtor of tex is called implicitly, and the same id texture is deleted again leading to crash
}
I generate a PointCloud in my program, and now, I want to be able to click on a point in this point cloud rendered to my screen using OpenGL.
In order to do so, I used the trick of giving to each pixel in an offscreen render a colour based on its index in the VBO. I use the same camera for my offscreen render and my onscreen render so they move together, and when I click, I get values of my offscreen render to retrieve the position in the VBO to get the point I clicked on. This is the theory since when I click, I have only (0,0,0). I believe that means my FBO is not well renderer but I'm not sure whether it is that or if the problem comes from somewhere else...
So here are the steps. clicFBO is the FBO I'm using for offscreen render, and clicTextureColorBuf is the texture in which I write in the FBO
glGenFramebuffers(1, &clicFBO);
glBindFramebuffer(GL_FRAMEBUFFER, clicFBO);
glGenTextures(1, &clicTextureColorBuf);
glBindTexture(GL_TEXTURE_2D, clicTextureColorBuf);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, clicTextureColorBuf, 0);
GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, DrawBuffers);
After that, I wrote a shader that gives to each point the color of its index in the VBO...
std::vector<cv::Point3f> reconstruction3D; //Will contain the position of my points
std::vector<float> indicesPointsVBO; //Will contain the indexes of each point
for (int i = 0; i < pts3d.size(); ++i) {
reconstruction3D.push_back(pts3d[i].pt3d);
colors3D.push_back(pt_tmp);
indicesPointsVBO.push_back(((float)i / (float)pts3d.size() ));
}
GLuint clicVAO, clicVBO[2];
glGenVertexArrays(1, &clicVAO);
glGenBuffers(2, &clicVBO[0]);
glBindVertexArray(clicVAO);
glBindBuffer(GL_ARRAY_BUFFER, clicVBO[0]);
glBufferData(GL_ARRAY_BUFFER, reconstruction3D.size() * sizeof(cv::Point3f), &reconstruction3D[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glEnable(GL_PROGRAM_POINT_SIZE);
glBindBuffer(GL_ARRAY_BUFFER, clicVBO[1]);
glBufferData(GL_ARRAY_BUFFER, indicesPointsVBO.size() * sizeof(float), &indicesPointsVBO[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
and the vertex shader:
layout (location = 0) in vec3 pos;
layout (location = 1) in float col;
out float Col;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform int pointSize;
void main()
{
gl_PointSize = pointSize;
gl_Position = projection * view * model * vec4(pos, 1.0);
Col = col;
}
And the Fragment:
#version 330 core
layout(location = 0) out vec4 FragColor;
in float Col;
void main()
{
FragColor = vec4(Col, Col, Col ,1.0);
}
And this is how I render this texture:
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 1.0f, 100.0f);
glBindFramebuffer(GL_FRAMEBUFFER, clicFBO);
clicShader.use();
glDisable(GL_DEPTH_TEST);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
clicShader.setMat4("projection", projection);
clicShader.setMat4("view", view);
model = glm::mat4();
clicShader.setMat4("model", model);
clicShader.setInt("pointSize", pointSize);
glBindVertexArray(clicVAO);
glDrawArrays(GL_POINTS, 0, (GLsizei)reconstruction3D.size());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
And then, when I click, I Use this piece of Code:
glBindFramebuffer(GL_FRAMEBUFFER, clicFBO);
glReadBuffer(GL_COLOR_ATTACHMENT0);
int width = 11, height = 11;
std::array<GLfloat, 363> arry{ 1 };
glReadPixels(Xpos - 5, Ypos - 5, width, height, GL_RGB, GL_UNSIGNED_BYTE, &arry);
for (int i = 0; i < 363; i+=3) { // It's 3 time the same number anyways for each number
std::cout << arry[i] << " "; // It gives me only 0's
}
std::cout << std::endl << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, clicFBO);
I know the error might be really stupid but I still have some problems with how OpenGL works.
I put what I thought was necessary to understand the problem (without extending too much), but if you need more code, I can write it too.
I know this is not a question in which you can say Yes or No and it's more like debugging my program, but since I really don't find from where the problem comes from, I'm looking toward someone who can explain to me what I did wrong. I do not necessarily seek the solution itself, but clues that could help me understand where my error is ...
Using a framebuffer object FBO to store a "object identifier" is a cool method. But also want to see the objects, right? Then you must render also to the default frame buffer (let me call it "defFB", which is not a FBO).
Because you need to render to two different targets, you need one of these techniques:
Draw objects twice (e.g. with two glDrawArrays calls), one to the FBO and a second one to the defFB.
Draw to two FBO's images at once and later blit one of then (with colors) to the defFB.
For the first technique you may use a texture attached to a FBO (as you currently do). Or you can use a "Renderbuffer" and draw to it.
The second approach needs a second "out" in the fragment shader:
layout(location = 0) out vec3 color; //GL_COLOR_ATTACHMENT0
layout(location = 1) out vec3 objID; //GL_COLOR_ATTACHMENT1
and setting the two attachments with glDrawBuffers.
For the blit part, read this answer.
Note that both "out" have the same format, vec3 in this example.
A fail in your code is that you set a RGB texture format and also use this format at glReadPixels, but your "out" in the FS is vec4 instead of vec3.
More concerns are:
Check the completeness with glCheckFramebufferStatus
Using a "depth attachment" to the FBO may be needed, even it will not be used for reading.
Disabling the depth test will put all elements if the frame. Your point-picking will select the last drawn, not the nearest.
I found the problem.
There were 2 failures in my code :
The first one is that in OpenGL, there is an Y inversion between the image and the framebuffer. So in order to pick the good point, you have to flip Y using the size of the viewport : I did it like this :
GLint m_viewport[4];
glGetIntegerv(GL_VIEWPORT, m_viewport);
int YposTMP = m_viewport[3] - Ypos - 1;
The second one is the use of
glReadPixels(Xpos - 2, Ypos - 2, width, height, GL_RGB, GL_UNSIGNED_BYTE, &pixels[0]);, the 6th parameter must be GL_FLOAT since the datas i'm returning are float.
Thanks all!
Best regards,
R.S
I have drawn a box. I added the basic class that deals vao and vbo.
class BasicObject_V2
{
public:
BasicObject_V2(GLuint typeOfPrimitives = 0){
this->typeOfPrimitives = typeOfPrimitives;
switch (this->typeOfPrimitives){
case GL_QUADS: numVertsPerPrimitive = 4; break;
}}
void allocateMemory(){
glGenVertexArrays(1, &vaoID);
glBindVertexArray(vaoID);
glGenBuffers(1, &vboID);
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vertexAttributes), &vertices[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 48, (const void*)0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 48, (const void*)12);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 48, (const void*)24);
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 48, (const void*)40);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
}
GLuint vboID;
GLuint vaoID;
GLuint typeOfPrimitives;
GLuint numVertsPerPrimitive;
int getNumberOfVertices(){
return vertices.size();
}
struct vertexAttributes{
glm::vec3 pos;
glm::vec3 normal;
glm::vec4 color;
glm::vec2 texCoord;
};
std::vector<vertexAttributes> vertices;
};
Here is the my Box-class.
class Box_V2 : public BasicObject_V2 {
public:
Box_V2(float width = 1, float height = 1, float length = 1) : BasicObject_V2(GL_QUADS) {
float wHalf = width / 2.0f;
float hHalf = height / 2.0f;
float lHalf = length / 2.0f;
vertexAttributes va;
glm::vec3 corners[8];
corners[0] = glm::vec3(-wHalf, -hHalf, lHalf);
corners[1] = (...)
glm::vec3 normals[6];
normals[0] = glm::vec3(0, 0, 1); // front
//(...)
// FRONT
va.pos = corners[0];
va.normal = normals[0];
va.texCoord = glm::vec2(0.0, 0.0);
vertices.push_back(va);
//...
allocateMemory();
}
};
Also I have set the texture for it (acoording to the opengl superbible example), but it does work at all.
static GLubyte checkImage[128][128][4];
void makeCheckImage(void)
{
int i, j, c;
for (i = 0; i<128; i++) {
for (j = 0; j<128; j++) {
c = ((((i & 0x8) == 0) ^ ((j & 0x8)) == 0)) * 255;
checkImage[i][j][0] = (GLubyte)c;
checkImage[i][j][1] = (GLubyte)c;
checkImage[i][j][2] = (GLubyte)c;
checkImage[i][j][3] = (GLubyte)255;
}}}
void init(){
glClearColor(0.7, 0.4, 0.6, 1);
//Create a Texture
makeCheckImage();
//Texure
glGenTextures(4, tex);
//glCreateTextures(GL_TEXTURE_2D, 4, tex);//4.5
glTextureStorage2D(tex[0], 0, GL_RGBA32F, 2, 2);
glBindTexture(GL_TEXTURE_2D, tex[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGBA, GL_FLOAT, checkImage);
//glTextureSubImage2D(tex[0], 0, 0, 0, 2, 2, GL_RGBA, GL_FLOAT, checkImage);//4.5
//load the shader
programID = loadShaders("vertex.vsh", "fragment.fsh");
glUseProgram(programID);
//create a box
box_v2 = new Box_V2();
glBindTexture(GL_TEXTURE_2D, tex[0]);
}}
void drawing(BasicObject* object){
glBindVertexArray(object->vaoID);
glDrawArrays(object->typeOfPrimitives, 0, object->getNumberOfVertices() * object->numVertsPerPrimitive);
glBindVertexArray(0);
}
This is my shaders:
VS:
#version 450 core
layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec4 color;
layout(location = 3) in vec2 texCoord;
uniform mat4 rotate;(...)
out vec4 v_normal;
out vec4 v_color;
out vec4 v_pos;
out vec2 v_texCoord;
void main(){
v_normal = rotate*vec4(normal,1);//*model;
v_color=color;
v_texCoord=texCoord;
gl_Position = rotate*scale*trans*vec4(pos.xyz, 1);
}
And FS:
#version 450 core
in vec4 v_color;
in vec2 v_texCoord;
uniform sampler2D ourTexture;
out vec4 outColor;
void main(){
//outColor=texelFetch(ourTexture, ivec2(gl_FragCoord.xy), 0); //4.5
outColor=texture(ourTexture,v_texCoord*vec2(3.0, 1.0));
}
Now it draws a black box without any texture on it. I'm new in OpenGL and I can't find where is the problem.
There are a lot of things wrong with your code. It looks like you tried to write it one way, then tried to write it another way, leading to some kind of Frankenstein's code where it's not really one way or the other.
glGenTextures(4, tex);
//glCreateTextures(GL_TEXTURE_2D, 4, tex);//4.5
glTextureStorage2D(tex[0], 0, GL_RGBA32F, 2, 2);
This is a perfect example. You use the non-DSA glGenTextures call, but you immediately turn around and use the DSA glTextureStorage2D call on it. You cannot do that. Why? Because tex[0] hasn't been created yet.
glGenTextures only allocates the name for the texture, not its state data. Only by binding the texture does it get contents. That's why DSA introduced the glCreate* functions; they allocate both the name and state for the object. This is also why glCreateTextures takes a target; that target is part of the texture object's state.
In short, the commented out call was correct all along. Looking at your code as written, the glTextureStorage2D call fails with an OpenGL error (FYI: turn on KHR_debug to see when errors happen).
So let's look at what your code would look like if we take out the failure parts:
glGenTextures(4, tex);
glBindTexture(GL_TEXTURE_2D, tex[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGBA, GL_FLOAT, checkImage);
There are two problems here. First, as BDL mentioned, your data is described incorrectly. You're passing GLubytes, but you told OpenGL you were passing GLfloats. Don't lie to OpenGL.
Second, there's the internal format. Because you used an unsized internal format (and because you're not in OpenGL ES land), the format your implementation chooses will be unsigned normalized. It won't be a 32-bit floating point format. If you want one of those, you must explicitly ask for it, with GL_RGBA32F.
Furthermore, because you allocated mutable storage for your texture (ie: because glTextureStorage2D failed), you now have a problem. You only allocated one mipmap level, but the default filtering mode will try to fetch from multiple mipmaps. And you never set the texture's mipmap range to only sample from the first. So your texture is not complete. An immutable storage texture would have made the mipmap range match what you allocated, but mutable storage textures aren't so nice.
You should always set your mipmap range when creating mutable storage textures:
glTexParameteri(GL_TEXTURE2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE2D, GL_TEXTURE_MAX_LEVEL, 0); //It's a closed range.
Lastly, you bind the texture to unit 0 for rendering (mostly by accident, since you never call glActiveTexture). But you never bother to inform your shader of this, by setting the ourTexture uniform value to 0.
There are several Problems with your code:
Data type doesn't match data
The data comes in an unsigned byte format (GLubyte checkImage[128][128][4]) with a size of of 4 bytes per pixel (1 byte per channel), but you tell OpenGL that it is in GL_FLOAT format, which would require 16 bytes per pixel (4 bytes per channel).
Data storage
See answer from 246Nt.
Mipmaps/Filters
Per default OpenGL has the minification filter set to GL_LINEAR_MIPMAP_LINEAR which requries the user to supply valid mipmaps for the texture. One can either change this filter to GL_LINEAR or GL_NEAREST which do not require mipmaps (see glTexParameteri), or generate mipmaps (glGenerateMipmaps).
I want to draw a cube and a sphere and apply a different texture to each.
I use blender to create the scene and then export to an obj file which then includes the vertices, normals, uvs and faces for both objects as well as the textures.
I have created a routine which loads all the data from the obj file. This all works as I can load the objects and display them etc but with only one texture. As I say I have gone through pages and pages of code and posts and 99% only deal with 1 texture to 1 object and those that deal with multiple textures only deal with one object or are in a very old version of openGL.
The one thing I haven't tried is uniform sample2D arrays in the fragment shader but I haven't found an explanation on that so haven't tried it.
My code that I have below:
ObjLoader *obj = new ObjLoader();
string _filepath = "objects\\" + _filename;
//bool res = obj->loadObjWithStaticColor(_filepath.c_str(), _vertices, _normals, vertex_colors, _colors, 1.0);
bool res = obj->loadObjWithTextures(_filepath.c_str(), _objects, _textures);
program = InitShader("shaders\\vshader.glsl", "shaders\\fshader.glsl");
glUseProgram(program);
GLuint vao_world_objects;
glGenVertexArrays(1, &vao_world_objects);
glBindVertexArray(vao_world_objects);
//GLuint vbo_world_objects;
//glGenBuffers(1, &vbo_world_objects);
//glBindBuffer(GL_ARRAY_BUFFER, vbo_world_objects);
NumVertices = _objects[_objects.size() - 1]._stop + 1;
for (size_t i = 0; i < _objects.size(); i++)
{
_vertices.insert(_vertices.end(), _objects[i]._vertices.begin(), _objects[i]._vertices.end());
_normals.insert(_normals.end(), _objects[i]._normals.begin(), _objects[i]._normals.end());
_uvs.insert(_uvs.end(), _objects[i]._uvs.begin(), _objects[i]._uvs.end());
}
GLuint _vSize = _vertices.size() * sizeof(point4);
GLuint _nSize = _normals.size() * sizeof(point4);
GLuint _uSize = _uvs.size() * sizeof(point2);
GLuint _totalSize = _vSize + _uSize; // normals + vertices + uvs
GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, _vSize, &_vertices[0], GL_STATIC_DRAW);
GLuint uvbuffer;
glGenBuffers(1, &uvbuffer);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glBufferData(GL_ARRAY_BUFFER, _uSize, &_uvs[0], GL_STATIC_DRAW);
TextureID = glGetUniformLocation(program, "myTextureSampler");
TextureObjects = new GLuint[_textures.size()];
glGenTextures(_textures.size(), TextureObjects);
for (size_t i = 0; i < _textures.size(); i++)
{
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, TextureObjects[i]);
// Give the image to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _textures[i].width, _textures[i].height, 0, GL_BGR, GL_UNSIGNED_BYTE, _textures[i]._tex_data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
for (size_t i = 0; i < _objects.size(); i++)
{
if (i == 0)
{
glActiveTexture(GL_TEXTURE0);
}
else
{
glActiveTexture(GL_TEXTURE1);
}
glBindTexture(GL_TEXTURE_2D, TextureObjects[i]);
GLuint _v_size = _objects[i]._vertices.size() * sizeof(point4);
GLuint _u_size = _objects[i]._uvs.size() * sizeof(point2);
GLuint vPosition = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(vPosition);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
if (i == 0)
{
glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
}
else
{
glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(_v_size));
}
GLuint vUV = glGetAttribLocation(program, "vUV");
glEnableVertexAttribArray(vUV);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
if (i == 0)
{
glVertexAttribPointer(vUV, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
}
else
{
glVertexAttribPointer(vUV, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(_u_size));
}
if (i == 0)
{
glUniform1i(TextureID, 0);
}
else
{
glUniform1i(TextureID, 1);
}
}
_scale = Scale(zoom, zoom, zoom);
_projection = Perspective(45.0, 4.0 / 3.0, 0.1, 100.0);
_view = LookAt(point4(Camera.x, Camera.y, Camera.z, 0), point4(0, 0, 0, 0), point4(0, 1, 0, 0));
_model = mat4(1.0); // identity matrix
_mvp = _projection * _view * _model;
MVP = glGetUniformLocation(program, "MVP");
theta = glGetUniformLocation(program, "theta");
Zoom = glGetUniformLocation(program, "Zoom");
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_CULL_FACE);
glClearColor(1.0, 1.0, 1.0, 1.0);
I understand that I have to switch between the active textures when drawing an object but I can't figure out how.
UPDATE
#immibis Ok I tried to do that yesterday but it didn't work but it was late and I was highly frustrated. SO just to get my thinking correct here, do I have to create a buffer every time (glGenBuffer) and then fill it, activate texture and then glDrawArrays or do I just create the buffer and then fill it every time with the different vetices and uvs for each object, set the offsets and then call glDrawArray for each object?
When I tried this originally I didn't know where the
glGetAttribLocation / glEnableVertexAttribArray /glBindBuffer
should go. So if I understand correctly every time I do a transformation like rotating around the x axis then buffers have to be filled etc so the code needs to go in the display function. Is that correct?
SOLVED
Ok so thanx to immibus' comments, it got me looking in a different direction. I was staring the whole time into how the data was pumped into the arrays that I never even looked at glDrawArrays. I was searching the web again and I came across a piece of code in a tutorial and the person explained glDrawArrays and I saw that you can tell it what to draw.
So then this became easy, as I originally thought it was supposed to be. I changed my code back to pumping everything in the buffers and since I have a start and stop property on my objects returned from my loader it was real easy to tell glDrawArrays what to do.
Thank you.
I'm trying to do a compute pass where I render to a texture that will be used in a draw pass later on. My initial implementation was based on shader storage buffer objects and was working nicely. But I want to apply a computation method that is going to take advantage of the blend hardware of the GPU so I started porting the SSBO implementation to RTT one. Unfortunately the code has stopped working. Now when I read back the texture it is getting wrong values.
Here is my texture and frame buffer setup code:
glGenFramebuffers(1, &m_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
// Create render textures
glGenTextures(NUM_TEX_OUTPUTS, m_renderTexs);
m_texSize = square_approximation(m_numVertices);
cout << "Textures size: " << glm::to_string(m_texSize) << endl;
GLenum drawBuffers[NUM_TEX_OUTPUTS];
for (int i = 0 ; i < NUM_TEX_OUTPUTS; ++i)
{
glBindTexture(GL_TEXTURE_2D, m_renderTexs[i]);
// 1st 0: level, 2nd 0: no border, 3rd 0: no initial data
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_texSize.x, m_texSize.y, 0, GL_RGBA, GL_FLOAT, 0);
// XXX: do we need this?
// Poor filtering. Needed !
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
// 0: level
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, m_renderTexs[i], 0);
drawBuffers[i] = GL_COLOR_ATTACHMENT0 + i;
}
glDrawBuffers(NUM_TEX_OUTPUTS, drawBuffers);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
cout << "Error when setting frame buffer" << endl;
// throw exception?
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
And this is the code to start the compute pass:
m_shaderProgram.use();
// setup openGL
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glViewport(0, 0, m_texSize.x, m_texSize.y); // setup viewport (equal to textures size)
// make a single patch have the vertex, the bases and the neighbours
glPatchParameteri(GL_PATCH_VERTICES, m_maxNeighbours + 5);
// Wait all writes to shader storage to finish
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
glUniform1i(m_shaderProgram.getUniformLocation("curvTex"), m_renderTexs[2]);
glUniform2i(m_shaderProgram.getUniformLocation("size"), m_texSize.x, m_texSize.y);
glUniform2f(m_shaderProgram.getUniformLocation("vertexStep"), (umax - umin)/divisoes,
(vmax-vmin)/divisoes);
// Bind buffers
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
glBindBufferBase(GL_UNIFORM_BUFFER, m_mvp_location, m_mvp_ubo);
// Make textures active
for (int i = 0; i < NUM_TEX_OUTPUTS; ++i)
{
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, m_renderTexs[i]);
}
// no need to pass index array 'cause ibo is bound already
glDrawElements(GL_PATCHES, m_numElements, GL_UNSIGNED_INT, 0);
I then read back the textures using the following:
bool readTex(GLuint tex, void *dest)
{
glBindTexture(GL_TEXTURE_2D, tex);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, dest);
glBindTexture(GL_TEXTURE_2D, 0);
// TODO: check glGetTexImage return values for error
return true;
}
for (int i = 0; i < NUM_TEX_OUTPUTS; ++i)
{
if (m_tensors[i] == NULL) {
m_tensors[i] = new glm::vec4[m_texSize.x*m_texSize.y];
}
memset(m_tensors[i], 0, m_texSize.x*m_texSize.y*sizeof(glm::vec4));
readTex(m_renderTexs[i], m_tensors[i]);
}
Finally, the fragment shader code is:
#version 430
#extension GL_ARB_shader_storage_buffer_object: require
layout(pixel_center_integer) in vec4 gl_FragCoord;
layout(std140, binding=6) buffer EvalBuffer {
vec4 evalDebug[];
};
uniform ivec2 size;
in TEData {
vec4 _a;
vec4 _b;
vec4 _c;
vec4 _d;
vec4 _e;
};
layout(location = 0) out vec4 a;
layout(location = 1) out vec4 b;
layout(location = 2) out vec4 c;
layout(location = 3) out vec4 d;
layout(location = 4) out vec4 e;
void main()
{
a= _a;
b= _b;
c= _c;
d= _d;
e= _e;
evalDebug[gl_PrimitiveID] = gl_FragCoord;
}
The fragment coordinates are correct (each fragment is pointing to a x,y coordinate in the texture), so are all the input values (_a to _e), but I do not see them outputted correctly to the textures when reading back. I also tried accessing the texture in the shader to see if it was only a read-back error, but my debug SSBO returned all zeroes.
Am I missing some setup step?
I've tested both on linux and windows (titan and 540M geforces) and I'm using openGL 4.3.
As derhass pointed out in the comments above, the problem was with the texture format. I assumed that by passing GL_FLOAT as the data type it would use 32bit floats for each of the RGBA channels. It was not so.
As derhass said, the data type parameter here does not change the texture format. I had to change the internalFormat parameter to what I wanted (GL_RGBA32F) so that it would work as expected.
So, after changing glTexImage2D call to:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_texSize.x, m_texSize.y, 0, GL_RGBA, GL_FLOAT, 0);
I was able to correctly render the results to the texture and read it back. :)