I want to load .obj files with c++ and draw it's shapes and materiels to the scene with OpenGl. To do the loading I am using the tinyobjectloader by syoyo. The loading works very well and I am able to draw the obect with a static color or a static diffuse texture. So I am able to give all the vericies and texture coordinates into the shader using buffers. My Problem now is how to set the right color in the pixel shader. The shapes given by the loader class have a mesh and a meterial, which are defined as follows:
typedef struct {
std::string name;
float ambient[3];
float diffuse[3];
float specular[3];
float transmittance[3];
float emission[3];
float shininess;
float ior; // index of refraction
float dissolve; // 1 == opaque; 0 == fully transparent
// illumination model (see http://www.fileformat.info/format/material/)
int illum;
std::string ambient_texname;
std::string diffuse_texname;
std::string specular_texname;
std::string normal_texname;
std::map<std::string, std::string> unknown_parameter;
} material_t;
typedef struct
{
std::vector<float> positions;
std::vector<float> normals;
std::vector<float> texcoords;
std::vector<unsigned int> indices;
} mesh_t;
I would like to set all the colos and different textures as uniforms. For example, the diffuse color would be set like this:
GLuint diffColLocation = glGetUniformLocation(programmId, "diffuseColor");
glUniform3f(diffColLocation, this->material.diffuse[0], this->material.diffuse[1], this->material.diffuse[2]);
And the Textures like this:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, this->ambientTextureID);
glUniform1i(this->diffuseTextureUniformIndex, 0);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, this->diffuseTextureID);
glUniform1i(this->diffuseTextureUniformIndex, 0);
etc.
I was also able to load an object, where all shapes had a diffuse texture given. So no problem here. My problem ist, how to deal with unset textures. For example many objects don't have an specular texture set. Can I somehow create an default texture?
My Pixel shader looks like this at the moment:
#version 150 core
#extension GL_ARB_explicit_attrib_location: enable
// output to the (default) FBO in color buffer 0
layout (location = 0) out vec4 outColor;
in vec2 fragTextCoords;
uniform sampler2D ambientTex;
uniform sampler2D diffuseTex;
uniform sampler2D specularTex;
uniform vec3 ambientColor;
uniform vec3 diffuseColor;
uniform vec3 specularColor;
// main entry point for the fragment shader
void main() {
//vec3 tempColor ambientColor + diffuseTex * specularTex; //Is that correct?
//outcolor = tempColor * texture(diffuseTex, fragTextCoords); //Then this?
outcolor = texture(diffuseTex, fragTextCoords);
}
So only the texture is being shown and this only works if every shape has a diffuse texture. But how to I combine every color and texture together in such a way, that the object looks just like in my 3d software? What happens if one of the colors is null or when an sampler2D is null?
The values you mention are configuration values for the various materials of your model. They are used in the Phong lighting model to calculate the final colour of a pixel when rendering the model. You have to set the light and material settings in advance using the glMaterial() and glLight() functions, respectively.
Regarding turning settings on and off, try to avoid if statements at all costs unless there is no other way to accomplish what you are trying to do. You also want to avoid looking up the location of uniforms every time you want to set one. The locations do not change, and the operation is quite expensive.
On another note, if you set the texture ID of the uniform variable several times, like in your example, you don't need to keep setting it to 0 every time. The variable will retain its value, and simply refer to the last texture you set as the last one you bound with glBindTexture()
If it helps, here's the shader combo I use for phong lighting. You should be able to modify them to turn textures on or off at will quite easily. Otherwise, lighthouse3d is a great resource on getting started with GLSL.
Vertex shader:
varying vec3 normal;
varying vec3 position;
void main( void )
{
gl_Position = ftransform();
gl_TexCoord[0] = gl_MultiTexCoord0;
normal = gl_NormalMatrix * gl_Normal;
position = ( gl_ModelViewMatrix * gl_Vertex ).xyz;
}
Fragment shader:
varying vec3 normal;
varying vec3 position;
uniform sampler2D diffuseTexture;
vec4 lightSource(vec3 norm, vec3 view, gl_LightSourceParameters light)
{
vec3 lightVector = normalize(light.position.xyz - view);
vec3 reflection = normalize(lightVector - normalize(view.xyz));
float diffuseFactor = max(0, dot(norm, lightVector));
float specularDot = max(0, dot(norm, reflection));
float specularFactor = pow(specularDot, gl_FrontMaterial.shininess);
return
gl_FrontMaterial.ambient * light.ambient +
gl_FrontMaterial.diffuse * light.diffuse * diffuseFactor +
gl_FrontMaterial.specular * light.specular * specularFactor;
}
vec4 lighting()
{
// normal might be damaged by linear interpolation.
vec3 norm = normalize(normal);
return
gl_FrontMaterial.emission +
gl_FrontMaterial.ambient * gl_LightModel.ambient +
lightSource(norm, position, gl_LightSource[0]);
}
void main()
{
gl_FragColor = texture2D(diffuseTexture, gl_TexCoord[0].st) * lighting();
}