Normal Matrix Issue

Hi there,

I’m working on some normal mapping and I have an issue with the normal matrix.

If I pass the matrix as an uniform to my custom shader, like so:

shader.setUniformMatrix4f("normalMatrix", ofGetCurrentNormalMatrix());

And interpolate it on the vertex shader, like this:

interpolatedNormal = normalize(mat3(normalMatrix) * vec3(normal));

It does not work.


But if I do the all the work on the vertex, everything works fine:

mat4 normalMatrix =  transpose(inverse(modelViewMatrix));
interpolatedNormal = normalize(mat3(normalMatrix) * vec3(normal)); 

Thanks!

what do you mean with it does not work? the only difference that i can think of is that the normal matrix returned by ofGetCurrentNormalMatrix is the transposed of the inverse of the view matrix, not the modelview. that makes the normal matrix not be affected by calls like ofTranslate, ofRotate… which were making the light position change before but apart from that the calculation is the corrent and we use it for the calculation of lights in the programmable renderer without problem

That was the issue. I was applying an ofNode:rotate() to the mesh in setup, and everything would go crazy. With no rotation, everything works fine.

But… I still want transformations. Like so, shouldn’t this work?

normalMatrix = mat4ToMat3(ofGetCurrentMatrix(OF_MATRIX_MODELVIEW));
normalMatrix.invert();
normalMatrix.transpose();
    
shader.setUniformMatrix3f("normalMatrix", normalMatrix);

ofMatrix3x3 ofApp::mat4ToMat3(ofMatrix4x4 mat4) {
    return ofMatrix3x3(mat4._mat[0][0], mat4._mat[0][1], mat4._mat[0][2], mat4._mat[1][0], mat4._mat[1][1], mat4._mat[1][2], mat4._mat[2][0], mat4._mat[2][1], mat4._mat[2][2]);
}

you can use a 4x4 matrix for the normal matrix no need to convert it to mat3

True, maybe I’m not converting properly. I will check it with no conversion when I get back home.

But also, why is OF using “the transposed of the inverse of the view matrix, not the modelview”?

if you use the modelview to calculate lights the lights appear as if they were affected by those transformations which is not correct. for example this:

light.setPosition(100,100,100);
ofPushMatrix();
ofTranslate(-100,-100,-100);
ofDrawBox(0,0,0,50);

the lighting will look as if the light position was 0,0,0 cause the normal matrix would be calculated from the modelview, the matrix for the object not for the camera, which is what you usually need. we could have a different normal matrix for the model, perhaps even in ofNode so you can get the normal matrix for each object that is in the scene but the global matrix makes more sense as the one calculated for the camera not for the objects

But that’s because of how the custom shader OF uses the normal matrix, right?

I ask this because, around the web (three.js and in forums) and in the openGL books that I own, people seem to use modelView instead of view.

I’ve been using openGL/GLSL for more 2D generative textures. I’m now starting with the 3D transformations, lights, maps, etc.

we found that problem first in openGL 2 actually where the normal matrix is not even needed but we need to pass the view matrix when setting the light positions otherwise the lights seem to move when doing any kind of transformation. in general if you work in eye space (which is the most common) you need the normal matrix relative to the camera transformation, the view matrix not the modelview.

With the examples/samples I’ve copied from the books, if I use the view matrix it does not work. The light goes to the wrong side, the normal map is a mess, etc.

If I use the modelView, I get a bit of movement, which is noticeable on the specular/shininess. But Is not problematic, for now.

I was trying to use the OF shader from the ofMaterial so that I could apply mappings and other stuff to learn and compare results. Copy/paste the vertex and just the spotlight from the fragment, double checked uniforms and seem to be all okay, but this happens :

That weird colors on the right, caused by the diffuse. Do you know why?

i’d say that’s because culling is not enabled so it’s lighting both the back and front faces. you can call:

glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);

there’s a problem with some primitives in OF where the culling is reversed because of the way we create the mesh, we’ll fix it in the next release but by now, if that doesn’t work try with glCullFace(GL_BACK)

I tried that, but it doesn’t solve the issue. If I use a texture, the area where the light is near, gets darker than the rest.

But, if I use the OF’s Light and Material, everything works as expected. So, probably I’m not enabling something or wrongly calculating the position of the light.

no idea then. btw you can use ofLight with your own materials. ofLight is just a node so you can use that to get it’s position and direction and the rest of parameters from the getters. or you can also call ofLightsData() which returns a vector of weak pointers to the ofLight::Data of evey light in the scene

Yes, I was already doing an ofLight object, since you guys already did the work. :smile:

But there’s something that I’ really missing out. Can you help me? I copied the shader from the ofMaterial with only the point light. I removed the rest and the loop and array for >1 lights. The GPU doesn’t return any errors, I tested the texcoords, normals, uniforms, etc, and everything seems to be working. Except the shading it self.

I’m I forgetting to enable something?

void ofApp::setup(){
 
    ofSetColor(255);
    ofSetBackgroundColor(0);
    ofSetVerticalSync(true);
    // Before Mipmaps
    ofDisableArbTex();
    
    ofEnableDepthTest();
//    ofEnableLighting();
    
    earthDayTexture.enableMipmap();
    ofLoadImage(earthDayTexture, "textures_low/8081-earthmap10k.jpg");
    earthDayTexture.generateMipmap();
    
    shader.load("shaders/phong_ofShader");
    
    earth.set(200, 300);
    earth.rotate(190, 0, 1, 0);
    earth.rotate(-20, 1, 0, 0);

    easyCamera.setVFlip(true);
    easyCamera.setDistance(600);
    
    
    // For the light
    lightRadius = 300.f;
    lightCenter = ofVec3f(0.f);
       
    // GUI
    gui.setup("Esfera");
    gui.add(frameRate.set("Framerate", "0"));
    
//    pointLight.setup();
//    pointLight.enable();
//    pointLight.setPointLight();
    pointLight.setAmbientColor(ofFloatColor(0.12, 0.09, 0.14, 1.0f));
    pointLight.setDiffuseColor(ofFloatColor(0.5, 0.24, 0.0f, 1.0f));
    pointLight.setSpecularColor(ofFloatColor(0.0f, 0.0f, 0.35, 1.0f));
    pointLight.setAttenuation(0.000001f, 0.000001f, 0.000001f);
    
    materialLight.setAmbientColor(ofFloatColor(0.0561f, 0.0561f, 0.0561f));
    materialLight.setDiffuseColor(ofFloatColor(0.02f, 0.04f, 0.0f));
    materialLight.setSpecularColor(ofFloatColor(0.4,0.2,0.3,1.0));
    materialLight.setEmissiveColor(ofFloatColor(0.02f, 0.04f, 0.0f));
    materialLight.setShininess(5);
    
}

//--------------------------------------------------------------
void ofApp::update(){
    
    frameRate = ofToString((int)ofGetFrameRate());
    flow += ofGetLastFrameTime();
    
    lightPosition = ofVec4f(cos(flow * .05f) * lightRadius + lightCenter.x,
                            0.f,
                            sin(flow * .05f) * lightRadius + lightCenter.z,
                            0.f);
    
    pointLight.setPosition(lightPosition.x, lightPosition.y, lightPosition.z);
}

//--------------------------------------------------------------
void ofApp::draw(){
    
    ofEnableDepthTest();
    
    easyCamera.begin();
//    materialLight.begin();

    shader.begin();
    
    shader.setUniformMatrix4f("normalMatrix", ofGetCurrentNormalMatrix());

    shader.setUniformTexture("earthDayTexture", earthDayTexture, 1);

    shader.setUniform4f("light.position", lightPosition * ofGetCurrentViewMatrix());
    shader.setUniform4f("light.ambient", pointLight.getAmbientColor());
    shader.setUniform4f("light.diffuse", pointLight.getDiffuseColor());
    shader.setUniform4f("light.specular", pointLight.getSpecularColor());

    shader.setUniform1f("light.constantAttenuation", pointLight.getAttenuationConstant());
    shader.setUniform1f("light.linearAttenuation", pointLight.getAttenuationLinear());
    shader.setUniform1f("light.quadraticAttenuation", pointLight.getAttenuationQuadratic());

    shader.setUniform4f("global_ambient", ofFloatColor(0.2, 0.2, 0.2, 1.0));
    shader.setUniform4f("mat_ambient", materialLight.getAmbientColor());
    shader.setUniform4f("mat_diffuse", materialLight.getDiffuseColor());
    shader.setUniform4f("mat_specular", materialLight.getSpecularColor());
    shader.setUniform4f("mat_emissive", materialLight.getEmissiveColor());
    shader.setUniform1f("mat_shininess", materialLight.getShininess());
    
//    shader.setUniform4f("mat_ambient", 0.0561f, 0.0561f, 0.0561f, 1.0);
//    shader.setUniform4f("mat_diffuse", 0.02f, 0.04f, 0.0f, 1.0);
//    shader.setUniform4f("mat_specular", 0.4,0.2,0.3,1.0);
//    shader.setUniform4f("mat_emissive", 0.02f, 0.04f, 0.0f, 1.0);
//    shader.setUniform1f("mat_shininess", 5.f);
  
    earth.draw();

    shader.end();
//    materialLight.end();

    // Assistance
    // ofDrawAxis(600);
    ofDrawBox(lightPosition, 5);

    easyCamera.end();
  
    ofDisableDepthTest();
} 

(Note: some of the stuff that is commented, it’s just me testing how OF binds and such.)

When I use ofLight, ofMaterial, bind the texture and no shader, everything works fine… :confused: But this, does not.