1

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?

6
  • I'm not entirely sure what you're trying to achieve, but if you want to mix the texture and colour equally, you could do a weighted sum; 0.5 * texture() + 0.5 * colour. Commented May 19, 2014 at 17:33
  • Hi, I want to mix all colors and textures, that are in the objects material file, so that the final object looks like it should. I am not sure how to combine all those values, that can occur in the .obj file. Also most of the time, not all three textures are set. So sometimes there are empty. But I don't know how to detect, in the shader, if a texture uniform has a value. So basically I want to know how to render every possible .obj File correctly. With all it's possible attributes. Thanks a lot for help! Commented May 19, 2014 at 18:52
  • Unfortunately, GLSL doesn't support querying openGL settings. You'll have to resort to setting those settings using uniforms. Also, you do NOT want to use if statements for checking such things. Since your program is executed millions of times a second, an if statement will cause a state push/pop, which is really, really slow. Commented May 19, 2014 at 20:16
  • (cotd) To enable and disable stuff, use multiplication/addition instead. For example, if you want to switch between textures and base colour you'd do (texture2D() * texturesEnabled) + (gl_Color * (1 - texturesEnabled)); Commented May 19, 2014 at 20:18
  • (cotd) The values you see in the materials file refer to the various components of the Phong lighting model (wikipedia has a good article on it). They are used for lighting calculations. If you don't want to use lighting, it's common to use the diffuse colour/texture to define the colour. In that case you'd have to use something I mentioned initially; a weighted sum of the colour and texture colours. There's a ton of Phong shaders that work out of the box, which do proper lighting calculations. Commented May 19, 2014 at 20:20

1 Answer 1

1

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();
} 
Sign up to request clarification or add additional context in comments.

8 Comments

Thanks for help! I am trying to use you shader, but I am faling to use glMaterial and glLight. Look at this code: const GLfloat blue[4] = { 0.3f, 0.3f, 1.0f, 1.0f }; glMaterialfv(GL_FRONT, GL_DIFFUSE, blue); This code gives me an GL_INVALIO_OPERATION id=1282 error. Does it work with openGL > 3.3?
I have managed to do it using uniforms. Lightning works now! Thanks! :-) What did you mean by "turning textures on and off"? How can I do this? Also, if I wanted multible lights, would I just add the return values of the lighting() function and give the light source index as an paramenter into the function? Like so: gl_FragColor = texture2D(diffuseTexture, gl_TexCoord[0].st) * lighting(0) + lighting(1) ... + lighting(n);
@Dafen: Not every model has textures, which you have to take into account in your shader. The best way you could do this is to use a uniform float that you set to 1 or 0 depending on whether you'd like to enable or disable textures, respectively. Then you multiply that uniform by the texture2d() call to have its value included into the calculation. For multiple lights, you'd indeed do the function call for different values of gl_LightSource[i]. Note that lighting calculations do become significantly more expensive as a result of that. You want to use your resources where they matter ;)
Thanks! I understand why I should multiply the uniform float with the texture2d(), but how do i prevent the shader from generating an error when the texture uniform is empty? Also why can't I use glMaterial() and glLight()? I just get an GL_INVALID_OPERATION error. :( Thanks for your help!
I am now using uniform buffer and an array of structs. That works. It seems that glLight() is not avalibale in OpenGL > 3.3.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.