This question already has answers here:
In a fragment shader, why can't I use a flat input integer to index a uniform array of sampler2D?
(1 answer)
Why are glsl variables not working as expected?
(2 answers)
Why are Uniform variables not working in GLSL?
(1 answer)
Closed 2 years ago.
So I've managed to successfully implement batching into my engine, but have come across some weird behavior with the array of samplerCubes in my fragment shader. The batch renderer works fine with 2 texture units - I can successfully draw cubes with the right textures if I only bind 2 textures, but as soon as I add a 3rd to the mTextures vector, and use glUniform1i for that index in the samplerCube array, the wrong texture is displayed, even though the texture id (TexID) is correct (I checked this in the fragment shader).
The issue appears to be with my understanding of OpenGL as for some reason, the texture what should be displayed by textureCubes[2] (fragment shader uniform) is displayed by textureCubes[1], and textureCubes[2] displays the same texture as textureCubes[0]. What should be displayed by textureCubes[1] just doesn't exist with the 3rd texture bound.
Here's my code:
Main.cpp
#include "Window.h"
#include "Block.h"
#include "BatchRenderer.h"
#include "Shader.h"
#include "Camera.h"
void Submit(gfx::BatchRenderer* renderer, float height) //temporary to test batching
{
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 16; j++)
{
gfx::Block* block = new gfx::Block(j % 2 == 0 ? (i % 2 == 0 ? gfx::BlockType::DIRT : gfx::BlockType::GRASS) : gfx::BlockType::COBBLE, math::Vec3f(i * 5.0, height, j * 5.0));
renderer->Submit(block);
}
}
}
int main()
{
gfx::Window* window = new gfx::Window("MineClone", 800, 800);
gfx::Shader* shader = new gfx::Shader();
shader->FromFile(GL_VERTEX_SHADER, "Resources/ModelTest.vert");
shader->FromFile(GL_FRAGMENT_SHADER, "Resources/ModelTest.frag");
shader->RefreshProgram();
math::Mat4f projection = math::Mat4f::Perspective(70.0, window->GetWidth() / window->GetHeight(), 0.1, 1000.0);
gfx::Camera* camera = new gfx::Camera(projection, 0.05, 0.0015);
gfx::BatchRenderer* renderer = new gfx::BatchRenderer();
gfx::TextureCube* grass = new gfx::TextureCube("Resources/top.png", "Resources/dirt.png", "Resources/sides.png");
renderer->Submit(grass);
gfx::TextureCube* dirt = new gfx::TextureCube("Resources/dirt.png");
renderer->Submit(dirt);
gfx::TextureCube* cobble = new gfx::TextureCube("Resources/cobble.png");
renderer->Submit(cobble);
Submit(renderer, 0.0);
Submit(renderer, 5.0);
Submit(renderer, 10.0);
Submit(renderer, 15.0);
Submit(renderer, 20.0);
Submit(renderer, 25.0);
Submit(renderer, 30.0);
Submit(renderer, 35.0);
while (!window->IsClosed())
{
window->Update();
if (window->GetInputHandler()->IsKeyDown(VK_ESCAPE))
window->SwitchMouseState();
if (window->IsSynced())
camera->Update(window->GetInputHandler());
shader->Bind();
math::Mat4f projection = camera->GetProjection();
shader->SetUniform("projection", projection);
math::Mat4f view = camera->GetView();
shader->SetUniform("view", view);
shader->SetUniform("cubeTextures[0]", 0);
shader->SetUniform("cubeTextures[1]", 1);
shader->SetUniform("cubeTextures[2]", 2);
renderer->Render();
shader->Unbind();
}
return 0;
}
BatchRenderer.cpp
#include "BatchRenderer.h"
namespace gfx
{
BatchRenderer::BatchRenderer()
: mMesh(NULL)
{
}
BatchRenderer::~BatchRenderer()
{
mBlocks.clear();
mTextures.clear();
delete mMesh;
}
void BatchRenderer::Submit(Block* block)
{
MeshData data = block->GetMesh();
mMeshData.vertices.insert(mMeshData.vertices.end(),
data.vertices.begin(),
data.vertices.end());
for (int i = 0; i < 36; i++)
{
data.indices[i] += mBlocks.size() * 8;
}
mMeshData.indices.insert(mMeshData.indices.end(),
data.indices.begin(),
data.indices.end());
mMesh = Mesh::Make(mMeshData);
mBlocks.push_back(block);
}
void BatchRenderer::Submit(TextureCube* texture)
{
mTextures.push_back(texture);
}
void BatchRenderer::Render()
{
for (int i = 0; i < mTextures.size(); i++)
{
mTextures[i]->Bind(i);
}
mMesh->Render();
for (int i = 0; i < mTextures.size(); i++)
{
mTextures[i]->Unbind(i);
}
}
}
TextureCube.cpp
#include "TextureCube.h"
namespace gfx
{
TextureCube::TextureCube(std::string paths[6])
: Texture(TextureEnum::TEXTURE_CUBE)
{
glGenTextures(1, &mHandle);
glBindTexture(GL_TEXTURE_CUBE_MAP, mHandle);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
for (int i = 0; i < 6; i++)
{
std::vector<byte> pixels;
lodepng::decode(pixels, mWidth, mHeight, paths[i].c_str());
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
}
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
TextureCube::TextureCube(std::string path)
: Texture(TextureEnum::TEXTURE_CUBE)
{
glGenTextures(1, &mHandle);
glBindTexture(GL_TEXTURE_CUBE_MAP, mHandle);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
std::vector<byte> pixels;
lodepng::decode(pixels, mWidth, mHeight, path.c_str());
for (int i = 0; i < 6; i++)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
}
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
TextureCube::TextureCube(std::string top, std::string bottom, std::string sides)
: Texture(TextureEnum::TEXTURE_CUBE)
{
glGenTextures(1, &mHandle);
glBindTexture(GL_TEXTURE_CUBE_MAP, mHandle);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
std::vector<byte> topPixels;
lodepng::decode(topPixels, mWidth, mHeight, top.c_str());
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, topPixels.data());
std::vector<byte> bottomPixels;
lodepng::decode(bottomPixels, mWidth, mHeight, bottom.c_str());
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, bottomPixels.data());
std::vector<byte> sidesPixels;
lodepng::decode(sidesPixels, mWidth, mHeight, sides.c_str());
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, sidesPixels.data());
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, sidesPixels.data());
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, sidesPixels.data());
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, sidesPixels.data());
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
TextureCube::~TextureCube()
{
glDeleteTextures(1, &mHandle);
mHandle = NULL;
}
void TextureCube::Bind(uint32_t slot)
{
glBindTextureUnit(slot, mHandle);
}
void TextureCube::Unbind(uint32_t slot)
{
glBindTextureUnit(slot, 0);
}
}
ModelTest.vert
#version 450 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec3 offset;
layout (location = 3) in float textureID;
uniform mat4 projection;
uniform mat4 view;
out vec3 TexPos;
out flat float TexID;
void main()
{
gl_Position = projection * view * vec4(position, 1.0);
TexPos = position - offset;
TexID = textureID;
}
ModelTest.frag
#version 450 core
out vec4 FragColour;
in vec3 TexPos;
in flat float TexID;
uniform samplerCube cubeTextures[3];
void main()
{
vec3 norm = normalize(TexPos);
int id = int(TexID);
FragColour = texture(cubeTextures[id], norm);
}
For anyone looking for a solution, it took me a while but I found it (I'm relatively new to OpenGL). The issue was I was setting the texture sampler slots individually using glUniform1i. To fix this, as cubeTextures is an array of samplerCubes, I created a struct for sampler data containing an int array and a length, and set the texture sampler slots using glUniform1iv.
Related
I'm trying to make a skybox that can change with a button.
My solution for this is to bind my skyboxes and load my textures in the following way:
//SkyBox
GLuint skyboxVBO, skyboxVAO;
glGenVertexArrays(1, &skyboxVAO);
glGenBuffers(1, &skyboxVBO);
glBindVertexArray(skyboxVAO);
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid *)0);
// Load textures
vector<const GLchar*> facesN;
facesN.push_back("SkyBox/night/right.tga");
facesN.push_back("SkyBox/night/left.tga");
facesN.push_back("SkyBox/night/top.tga");
facesN.push_back("SkyBox/night/bottom.tga");
facesN.push_back("SkyBox/night/back.tga");
facesN.push_back("SkyBox/night/front.tga");
GLuint cubemapTextureN = TextureLoading::LoadCubemap(facesN);
vector<const GLchar*> faces;
faces.push_back("SkyBox/day/right.tga");
faces.push_back("SkyBox/day/left.tga");
faces.push_back("SkyBox/day/top.tga");
faces.push_back("SkyBox/day/bottom.tga");
faces.push_back("SkyBox/day/back.tga");
faces.push_back("SkyBox/day/front.tga");
GLuint cubemapTexture = TextureLoading::LoadCubemap(faces);
Then, in the program loop I draw the skybox with the following:
// Draw skybox as last
glDepthFunc(GL_LEQUAL); // Change depth function so depth test passes when values are equal to depth buffer's content
SkyBoxshader.Use();
view = glm::mat4(glm::mat3(camera.GetViewMatrix())); // Remove any translation component of the view matrix
glUniformMatrix4fv(glGetUniformLocation(SkyBoxshader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(SkyBoxshader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
// skybox cube
glBindVertexArray(skyboxVAO);
glActiveTexture(GL_TEXTURE1);
if (daytime == 1.0f) {
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
}
else {
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTextureN);
}
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glDepthFunc(GL_LESS); // Set depth function back to default
In theory, whenever I press the button that changes the value of daytime, the skybox should change its textures to cubemapTextureN and, with another press of the button, change back to cubemapTexture. Instead, it just draws the texture that was loaded last, cubemapTexture in this case.
I already tested daytime and it does change, so it's not that.
Also, I tried loading cubemapTextureN after cubemapTexture and by doing so cubemapTextureN gets drawn instead, but still no change. I'm assuming somehow while loading the second textures, the first ones get updated, which shouldn't happen since they have different names, unless i'm understanding it wrong.
Easy solution would be to just make models of the entire skyboxes and work with that, but if possible I want to see if I can use this solution.
Edit:
These are the Shaders for the skybox.
Fragment shader:
#version 330 core
in vec3 TexCoords;
out vec4 color;
uniform samplerCube skybox;
void main()
{
color = texture(skybox, TexCoords);
}
Vertex shader:
#version 330 core
layout (location = 0) in vec3 position;
out vec3 TexCoords;
uniform mat4 projection;
uniform mat4 view;
void main()
{
vec4 pos = projection * view * vec4(position, 1.0);
gl_Position = pos.xyww;
TexCoords = position;
}
And their use:
Shader SkyBoxshader("Shaders/SkyBox.vs", "Shaders/SkyBox.frag");
Also, if this is usefull, this is the header with the definition of TextureLoading, which contains LoadCubemap
#pragma once
// GLEW
#include <GL/glew.h>
// Other Libs
#include "stb_image.h"
// Other includes
#include "Model.h"
#include <vector>
class TextureLoading
{
public:
static GLuint LoadTexture(GLchar *path)
{
unsigned int textureID;
glGenTextures(1, &textureID);
int width, height, nrComponents;
unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);
if (data)
{
GLenum format;
if (nrComponents == 1)
format = GL_RED;
else if (nrComponents == 3)
format = GL_RGB;
else if (nrComponents == 4)
format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
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_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
else
{
std::cout << "Failed to load texture" << path << std::endl;
stbi_image_free(data);
}
return textureID;
}
static GLuint LoadCubemap(vector<const GLchar * > faces)
{
GLuint textureID;
glGenTextures(1, &textureID);
int width, height, nrChannels;
for (unsigned int i = 0; i < faces.size(); i++)
{
unsigned char *data = stbi_load(faces[i], &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
}
else
{
std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;
stbi_image_free(data);
}
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
return textureID;
}
};
So I was trying to learn OpenGL from learnopengl.com and I'm currently on the topic of text rendering and I thought about combining scalable font textures in my program to one big texture, but for some reason, I get an exception bc of glCopyImageSubData(...).
First I try to measure how big the texture should be and then copy textures I already created to one big texture, and I was playing with this function for quite a while now but I can't find a solution.
Here is the original code with texture used for each face.
I tried to create fbo and attach a texture to it, but after research, I found this function that was far more clear to me so I decided to use it instead.
So I added xoffset to Character structure:
struct Character {
GLuint textureID;
glm::ivec2 size;
glm::ivec2 bearing;
GLuint advance;
GLuint xoffset;
};
I add this offset to each face in the first for loop:
Character character = {
texture,
glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
face->glyph->advance.x,
font_texture_width
};
font_texture_width += character.size.x;
characters.insert(std::pair<GLchar, Character>(c, character));
And then I try to paste each face texture to fontTexture:
glGenTextures(1, &fontTexture);
glBindTexture(GL_TEXTURE_2D, fontTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED,
font_texture_width, 100, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
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);
std::cout << glGetError();
for (GLubyte c = 0; c < 128; ++c)
glCopyImageSubData( characters[c].textureID, GL_TEXTURE_2D, 0, 0, 0, 0,
fontTexture, GL_TEXTURE_2D, 0, characters[c].xoffset, 0, 0,
characters[c].size.x, characters[c].size.y, 0);
Here is modyfied renderText function:
void renderText(Shader &s, std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color)
{
s.use();
s.setVec3("textColor", color);
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(VAO);
std::string::const_iterator c;
glBindTexture(GL_TEXTURE_2D, fontTexture);
for (c = text.begin(); c != text.end(); ++c)
{
Character ch = characters[*c];
GLfloat xpos = x + ch.bearing.x * scale;
GLfloat ypos = y - (ch.size.y - ch.bearing.y) * scale;
GLfloat w = ch.size.x * scale;
GLfloat h = ch.size.y * scale;
GLfloat vertices[6][4] = {
{ xpos + characters[*c].xoffset, ypos + h, 0.0, 0.0 },
{ xpos + characters[*c].xoffset, ypos, 0.0, 1.0 },
{ xpos + w + characters[*c].xoffset, ypos, 1.0, 1.0 },
{ xpos + characters[*c].xoffset, ypos + h, 0.0, 0.0 },
{ xpos + w + characters[*c].xoffset, ypos, 1.0, 1.0 },
{ xpos + w + characters[*c].xoffset, ypos + h, 1.0, 0.0 }
};
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
x += (ch.advance >> 6) * scale;
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
Exception is being thrown where I call glCopyImageSubData.
Here is my whole program: https://pastebin.com/vAeeX3Xh
EDIT:
Now I figured out that it would be better to use glTexSubImage2D instead, so this is how it looks like (instead of this block of code with glCopyImageSubData)
glGenTextures(1, &fontTexture);
glBindTexture(GL_TEXTURE_2D, fontTexture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
face->glyph->bitmap.width * 250,
face->glyph->bitmap.rows * 250,
0,
GL_RED,
GL_UNSIGNED_BYTE,
NULL
);
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);
for (GLubyte c = 0; c < 128; ++c)
{
if (FT_Load_Char(face, c, FT_LOAD_RENDER))
{
std::cout << "ERROR::FREETYPE: Failed to load Glyph" << std::endl;
continue;
}
glTexSubImage2D(GL_TEXTURE_2D, 0, characters[c].xoffset, 0, characters[c].size.x, characters[c].size.y, GL_RED, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer);
}
Now when I render this texture it looks like this:
https://imgur.com/a/A0gDy6T
So my problem is that I try to load multiple textures at start up and then store all of the ID's so that I can bind them to use them. Now I know that the ID's are being stored correctly because I can debug it and see in both assigning and usage that the ID's are the same. Just for every binding it uses the last texture that I load. Here is my code:
GLuint TextureLoader::LoadTexture (const char* fileName,Material& material,int width,int height) {
GLuint textureImage;
FILE* textureFile;
textureFile = fopen(fileName, "rb");
unsigned char* imageData;
if (textureFile == NULL) {
return 0;
}
imageData = (unsigned char*)malloc(width * height * 3);
char header[54];
fread(header,1,54,textureFile);
fread(imageData, width * height * 3, 1, textureFile);
fclose(textureFile);
for (int i = 0; i < width * height; ++i) {
int nextIndex = i * 3;
unsigned char a = imageData[nextIndex];
unsigned char b = imageData[nextIndex+2];
imageData[nextIndex] = b;
imageData[nextIndex+2] = a;
}
glEnable(GL_TEXTURE_2D);
glGenTextures( 1, &textureImage );
glBindTexture( GL_TEXTURE_2D, textureImage );
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE );
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);
/*glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_REPEAT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_REPEAT );
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height,GL_RGB, GL_UNSIGNED_BYTE, imageData );*/
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, imageData);
glGenerateMipmap(GL_TEXTURE_2D);
free(imageData);
return textureImage;
}
Here is my usage of this code:
if (showTexture) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, material.texture);
}
Here is my load call:
green1.texture = TextureLoader::LoadTexture("/Users/hewitt/Desktop/OpenGLImages/face6.bmp",green1, 256, 256);
And then I just use:
glTexCoord2f(1.0f,0.0f);
to draw depending on the corner it changes. And this draws the one texture correctly it just repeats the texture even when I glBind a different number.
Any help is greatly appreciated.
Edit ----------
glBegin(mode);
Spatial::Vector3 normal = Mesh::calculateNormal (pointGroup);
glNormal3f(normal.x, normal.y, normal.z);
Material material = mesh.getMaterial();
if (showTexture) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, material.texture);
}
int counter = 0;
for (Spatial::Vector3 point : pointGroup) {
if (showTexture == false) {
Material::Colour colour = material.getColour();
glColor3f(colour.red, colour.green, colour.blue);
} else {
if (counter == 0) {
glTexCoord2f(1.0f,0.0f);
} else if (counter == 1) {
glTexCoord2f(1.0f,1.0f);
} else if (counter == 2) {
glTexCoord2f(0.0f,1.0f);
} else if (counter == 3) {
glTexCoord2f(0.0f,0.0f);
}
}
glVertex3f(point.x,point.y,point.z);
counter ++;
}
glEnd();
You cannot call glEnable or glBindTexture while in between glBegin and glEnd. You must bind your texture before glBegin. In fact the set of OpenGL functions you can call between glBegin and glEnd is limited to those transferring vertex attributes (glVertex, glColor, glTexCoord, etc...).
Note that the immediate mode API (that is glBegin/glEnd and friends) is legacy and was deprecated for more than 10 years. I suggest you switch to the modern OpenGL pipeline, that will sort many things out for you.
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 am seeing a strange issue with a multi pass, multi RT ping-pong render
I am using 3 shaders and an FBO, I get no errors at any stage. I am using opengl 4, so I am not using glBegin/glEnd, I use my own matrices for transformation and i am using vertex buffers for rendering
The general idea of this test is to render 3 different colours to three separate fbo attachments (I realise that it is not efficient rendering the vbo twice in the first pass but this is just to help me understand a problem in some other source) I then bind a separate set of textures to the fbo and use the first set as the samplers. The final pass is to blend the results into the back buffer
What is odd is that the output is both quads appear red, when the first should be red and the second green (as these values come from the second shader).
If i change the function SamplePix in the second shader to always take from the second sampler both appear green, and if lookups up from the third then all appears yellow, but it seems to only read from one.
For now I can't seem to explain why I can read from each sampler ok but the loop doesn't seem to lookup correctly (in shader 2)
Also if I switch to rendering the texture sampled in shader 2 i see a black quad in the second pass (image 2)
Hopefully someone can throw some light on the problem. (I have also tried adding a sync fence object after the first pass but this didn't make a difference)
Texture Sample:
// initial setup of shaders and vertex buffers ommitted for now (in separate source files)
// fbo setup like so
const int DepthLayers = 3;
bool InitFBO()
{
// width and height 768pixels
for (int j=0; j<DepthLayers; j++)
{
const int idx = j + (DepthLayers*i);
glGenTextures(1, &g_FBOManager.m_fboTex[ idx ]);
glBindTexture(GL_TEXTURE_2D, g_FBOManager.m_fboTex[ idx ]);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//no mipmaps
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_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_RGBA8, g_XMLSettings->WWidth(), g_XMLSettings->WHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
//depth
glGenTextures(1, &g_FBOManager.m_fboDepthTex[ idx ]);
glBindTexture(GL_TEXTURE_2D, g_FBOManager.m_fboDepthTex[ idx ]);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//no mipmaps
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_DEPTH_TEXTURE_MODE, GL_INTENSITY);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE/* GL_COMPARE_R_TO_TEXTURE*/);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, g_XMLSettings->WWidth(), g_XMLSettings->WHeight(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glBindTexture(GL_TEXTURE_2D, g_FBOManager.m_fboDepthTex[idx]);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,GL_TEXTURE_2D, g_FBOManager.m_fboDepthTex[idx], 0);
glBindTexture(GL_TEXTURE_2D, g_FBOManager.m_fboTex[idx]);
// attach a texture to FBO color attachment point
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, g_FBOManager.m_fboTex[idx], 0);
// check FBO status
pglBindFramebufferEXT( GL_FRAMEBUFFER_EXT, g_FBOManager.FboId(i) );
if(!CheckFramebufferStatus())
{
return false;
}
}
BindFrameBuffer(-1); // unbind fbo
if (GLGETERROR("1", fileBuf, lineBuf)) ; // check for glErrors
return true; // all ok
}
// code to init second set of textures before the render loop
const int w = g_XMLSettings->WWidth(); //res: 768 square
const int h = g_XMLSettings->WHeight();
glGenTextures(bufCount, &ttex[0]);
for (int i=0; i<bufCount; ++i)
{
glBindTexture(GL_TEXTURE_2D, ttex[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
}
const int bufCount = 3;
void EnableTexture1()
{
glActiveTextureARB(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, g_BackTexture);
glUniform1iARB(g_pPassAShader->m_clr_idx, 0);
}
void RenderLoop()
{
GLenum buffers[] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT };
glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glUseProgramObjectARB(g_pPassAShader->m_glContext);
EnableTexture1();
//... code omitted to set transform matrix and supply to shader
if (!BindFrameBuffer(0))
assert(0);
const GLuint DTex = g_FBOManager.GetDepthTexture(0);
glBindTexture(GL_TEXTURE_2D, DTex);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,GL_TEXTURE_2D, DTex, 0);
for (UINT i=0; i<bufCount; ++i)
{
glBindTexture(GL_TEXTURE_2D, g_FBOManager.GetTexture(i));
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + i, GL_TEXTURE_2D, g_FBOManager.GetTexture(i), 0);
if (!CheckFramebufferStatus())
assert(0);
}
glDrawBuffers(bufCount,buffers);
// this is very important! if we don't clear it won't get written to
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_BLEND);
EnableTexture1();
RenderVertexCache(0);
//... code omitted to set transform matrix and supply to shader
// render the second quad to other FBO tex
RenderVertexCache(0);
DisableShader(0);
BindFrameBuffer(-1); // unbinds fbo
//pass 2
if (!BindFrameBuffer(0))
assert(0);
// bind other set of textures to the fbo
for (UINT i=0; i<bufCount; ++i)
{
glBindTexture(GL_TEXTURE_2D,ttex[i]);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + i, GL_TEXTURE_2D, ttex[i], 0);
if (!CheckFramebufferStatus())
assert(0);
}
glDrawBuffers(bufCount,buffers);
// this is very important! if we don't clear it won't get written to
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// bind our set from prev pass to the samplers as input
glUseProgramObjectARB(g_pPassBShader->m_glContext);
for (UINT i=0; i<bufCount; ++i)
{
glActiveTextureARB(GL_TEXTURE0+i);
glBindTexture(GL_TEXTURE_2D,g_FBOManager.GetTexture(i));
glUniform1iARB(g_pPassBShader->m_intex_idx[i], i);
}
VertexBufferManager2::RenderUsingOrthoEx(g_pPassBShader); // renders a full screen quad
DisableShader(0);
BindFrameBuffer(-1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// pass 3
glUseProgramObjectARB(g_pPassCShader->m_glContext);
for (UINT i=0; i<bufCount; ++i)
{
glActiveTextureARB(GL_TEXTURE0+i);
glBindTexture(GL_TEXTURE_2D, ttex[i]);
glUniform1iARB(g_pPassCShader->m_intex_idx[i], i);
}
VertexBufferManager2::RenderUsingOrthoEx(g_pPassCShader);
if (GLGETERROR("at1", fileBuf, lineBuf)) ; // reports any glErrors
}
// the three shaders (fragment shaders only)
// vertex shaders transfer uv's and transform vertices (version set to 410 in all)
// shader for pass1
//--------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------
#version 410
uniform sampler2D g_ClrMap;
in vec2 g_InterpUV; // supplied by vshader
out vec4 g_FBOLayers[ 3 ];
void main(void)
{
vec4 tex = texture(g_ClrMap, g_InterpUV); // if we set fbo from texture only first texture renders
g_FBOLayers[0] = vec4(1,0,0,.5);
g_FBOLayers[1] = vec4(0,1,0,1);
g_FBOLayers[2] = vec4(1,1,0,1);
}
// shader pass 2
//--------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------
#version 410
uniform sampler2D g_inLayers0;
uniform sampler2D g_inLayers1;
uniform sampler2D g_inLayers2;
in vec2 g_InterpUV ;
out vec4 g_FBOLayers[ 3 ];
vec2 fTextureSize;
vec4 SamplePix(in int buf, in vec2 tCoords, in float lod)
{
if (buf==1)
return textureLod(g_inLayers1, tCoords, lod);
else if (buf==2)
return textureLod(g_inLayers2, tCoords, lod);
return textureLod(g_inLayers0, tCoords, lod);
}
void main(void)
{
ivec2 iCoords = ivec2(int(gl_FragCoord.x),int(gl_FragCoord.y));
ivec2 iTextureSize = textureSize(g_inLayers0,0);
fTextureSize = vec2(float(iTextureSize.x), float(iTextureSize.y));
vec2 coords = vec2(gl_FragCoord.x/iTextureSize.x,gl_FragCoord.y/iTextureSize.y);
for(int i=0; i<3; ++i)
{
g_FBOLayers[i] = vec4(0.,0.,0.,0.);
}
int j = 0;
for(; j < 2; ++j)
{
g_FBOLayers[j] = SamplePix(j,coords,0);
}
}
// shader pass 3
//--------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------
#version 410
uniform sampler2D g_inLayers0;
uniform sampler2D g_inLayers1;
uniform sampler2D g_inLayers2;
in vec2 g_InterpUV ;
out vec4 g_PixColour;
vec4 tFetch(in int buf, in ivec2 tCoords, in int lod)
{
if (buf==1)
return texelFetch(g_inLayers1, tCoords, lod);
if (buf==2)
return texelFetch(g_inLayers2, tCoords, lod);
return texelFetch(g_inLayers0, tCoords, lod);
}
void main(void)
{
ivec2 iCoords = ivec2(int(gl_FragCoord.x),int(gl_FragCoord.y));
ivec2 iTextureSize = textureSize(g_inLayers0,0);
vec2 coords = vec2(gl_FragCoord.x/iTextureSize.x,gl_FragCoord.y/iTextureSize.y);
vec4 colour = vec4(0.,0.,0.,0.);
int i = 0;
for(; i <2; ++i) // 3rd texture omitted for now
{
vec4 texel = tFetch(i,iCoords,0);
if(texel.a + colour.a <= 1.0)
{
colour.rgb += (texel.rgb*texel.a);
colour.a += texel.a;
}
else
{
texel.a -= (texel.a + colour.a) - 1.0;
colour.rgb += (texel.rgb*texel.a);
colour.a += texel.a;
break;
}
}
g_PixColour = colour;
}