In my 3D game i am planning to compose every object from many seperate meshes. Every human for example will have a skeleton and every joint will have a mesh. This way i could build a human only using one small cube mesh. I would upload all the matrix data for each cube instance to the shader and then draw all cubes with one draw call. The plan is to be able to draw basically one third of my whole 3D world with just one draw call.
But the problem is with the textures. I obviously dont want all the cubes using the same texture, but i cant change the data i send to the shaders during one draw call. So i would need some kind of set of textures and then i could pass an index value for each instance inside the array of instance datas. What is the most efficient way to do this? I am talking about the best balance between high performance and usability.
This is what i know about the options i have:
-
Texture atlas: stores multiple images inside of one large texture and using the uv-Coordinates i can choose an area on the atlas i would like to use.
Pros: easy to implement, best performance(?)
Cons: annoying to set up uv coordinates for different atlasses -
Texture Array: an array of seperate textures which can be indexed in the shader.
Pros: good usability
Cons: is not supported on some platforms -
3D Texture: basically a stack of 2D textures and using the depth coordinate you can choose which slice you want to use as a texture
Pros: good performance, easy to use
Cons: All textures have to be the same size
I am thinking a 3D texture is what i want to go for and i have already tried to implement it, but ive had troubles with adding the Data of 2D textures together to make one 3D texture, and the indexing fragment shader causes a crash of my graphics drivers:
#version 430
uniform sampler2D sampler[10];
in Vertex{
vec4 rawPosition;
vec2 uv;
vec4 normal;
}vertexIn;
in InstanceData{
unsigned int textureUnitIndex;
}instance;
out vec4 color;
void main(){
unsigned int index = instance.textureUnitIndex;
color = texture(sampler[index], vertexIn.uv); // CAUSES CRASH
Just to make this complete, this is how i loaded the 3D texture:
//BEFORE THIS I ADDED ALL FILE PATHS OF THE TEXTURES I WANT TO LOAD TO vector<Texture>internalTextures
void TextureSet::loadTextures()
{
std::vector<unsigned char*> setData; //FINAL DATA OF THE TEXTURE SET
for (unsigned int i = 0; i < internalTextures.size(); ++i) {
//LOAD ALL PREVIOUSLY ADDED TEXTURES USING SOIL LIBRARY
internalTextures[i].texData = SOIL_load_image(internalTextures[i].texturePath.c_str(), &internalTextures[i].width, &internalTextures[i].height, &internalTextures[i].channels, SOIL_LOAD_RGBA);
//MAKE SURE TO SAVE THE LARGEST SIZE OUT OF ALL INTERNAL TEXTURES
if (internalTextures[i].width > width || internalTextures[i].height > height) {
width = internalTextures[i].width;
height = internalTextures[i].height;
}
setData.push_back(internalTextures[i].texData);
}
glCreateTextures(GL_TEXTURE_3D, 1, &setID);
glActiveTexture(GL_TEXTURE0 + setID-1);
glBindTexture(GL_TEXTURE_3D, setID);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, width, height, internalTextures.size(), GL_FALSE, GL_RGBA, GL_UNSIGNED_BYTE, &setData[0]);
//glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, width, height, GL_FALSE, GL_RGBA, GL_UNSIGNED_BYTE, texData);
glGenerateMipmap(GL_TEXTURE_3D);
glBindTexture(GL_TEXTURE_3D, 0);
//FREE ALL THE POINTERS WITH THE TEXTURE DATA
for (unsigned int i = 0; i < internalTextures.size(); ++i) {
SOIL_free_image_data(internalTextures[i].texData);
}
}
Is there a more efficient way than 3D textures or is there something i got wrong about the options?
how do i correctly assemble and index a 3D texture?