Sign in to follow this  
metalcrusader

Angle between Light and Normal II

Recommended Posts

HELLo again! I still want to calculate the angle between a normal and a lightsource. My first approach was to transform the normal in the space of the lightvector - the Eyespace. But this approach seems to be camera-depending. Because of that i let the normal as it is but transform the lightvector to the objectspace. For doing that, i needed to inverse the modelviewmatrix. In another Forum i found some code, which does that. Here it is:
void Matrix_4x4_Inverse(float Matrix_in_rhs[16], float Matrix_out_rhs[16])
{
	// Rotation
	Matrix_out_rhs[ 0] = Matrix_in_rhs[ 0]; Matrix_out_rhs[ 1] = Matrix_in_rhs[ 4]; Matrix_out_rhs[ 2] = Matrix_in_rhs[ 8];
	Matrix_out_rhs[ 4] = Matrix_in_rhs[ 1]; Matrix_out_rhs[ 5] = Matrix_in_rhs[ 5]; Matrix_out_rhs[ 6] = Matrix_in_rhs[ 9];
	Matrix_out_rhs[ 8] = Matrix_in_rhs[ 2]; Matrix_out_rhs[ 9] = Matrix_in_rhs[ 6]; Matrix_out_rhs[10] = Matrix_in_rhs[10];	
	Matrix_out_rhs[ 3] = 0.0f;
	Matrix_out_rhs[ 7] = 0.0f;
	Matrix_out_rhs[11] = 0.0f;
	Matrix_out_rhs[15] = 1.0f;	
	// Translation (Translation is minus the dot of Tranlation and Rotations)
	Matrix_out_rhs[12] = -(Matrix_in_rhs[12] * Matrix_in_rhs[ 0]) - (Matrix_in_rhs[13] * Matrix_in_rhs[ 1]) - (Matrix_in_rhs[14] * Matrix_in_rhs[ 2]);
	Matrix_out_rhs[13] = -(Matrix_in_rhs[12] * Matrix_in_rhs[ 4]) - (Matrix_in_rhs[13] * Matrix_in_rhs[ 5]) - (Matrix_in_rhs[14] * Matrix_in_rhs[ 6]);
	Matrix_out_rhs[14] = -(Matrix_in_rhs[12] * Matrix_in_rhs[ 8]) - (Matrix_in_rhs[13] * Matrix_in_rhs[ 9]) - (Matrix_in_rhs[14] * Matrix_in_rhs[10]);
}

After invering the modelviewmatrix i multiply it with my lightvector to get it in objectspace. Now I can calculate the dot-product between them - but again it is camera-depending!!!??? Does anyone know why??? Here comes the rest of my code:
bool flaeche4::isBackface( )
{
	GLdouble MVmatrix[16];
	glGetDoublev( GL_MODELVIEW_MATRIX, MVmatrix);
	
	float mvMatrix[16];
	for (int i = 0; i < 16; i++)
	{
		mvMatrix[i] = (float)MVmatrix[i];
	}
	
	float invMvMatrix[16];
	Matrix_4x4_Inverse( mvMatrix, invMvMatrix);

	GLfloat lightPos[4];	
	glGetLightfv(GL_LIGHT1, GL_POSITION, lightPos);	
	Vektor4 light; light.x = lightPos[0]; light.y = lightPos[1]; light.z = lightPos[2]; light.w = lightPos[3]; 
	
	Vektor4 norm = this->berechneNormale();
	
	Vektor4 objSplight; // in Objectspace transformed light
	objSplight.x = light.x * invMvMatrix[0]  + light.y * invMvMatrix[4]  + light.z * invMvMatrix[8]  + light.w * invMvMatrix[12];
	objSplight.y = light.x * invMvMatrix[1]  + light.y * invMvMatrix[5]  + light.z * invMvMatrix[9]  + light.w * invMvMatrix[13];
	objSplight.z = light.x * invMvMatrix[2]  + light.y * invMvMatrix[6]  + light.z * invMvMatrix[10] + light.w * invMvMatrix[14];
	objSplight.w = light.x * invMvMatrix[3]  + light.y * invMvMatrix[7]  + light.z * invMvMatrix[11] + light.w * invMvMatrix[15];
	
	Vektor4 center = (this->a).plusVec( this->c); // get the Center of the surface
	center.x = center.x / center.w; center.y = center.y / center.w; center.z = center.z / center.w; center.w = center.w / center.w;
	Vektor4 diff = objSplight.minusVec( center);
	
	float lightLang = sqrt( objSplight.x * objSplight.x + objSplight.y * objSplight.y + objSplight.z * objSplight.z); 
	float diffLang = sqrt( diff.x * diff.x + diff.y * diff.y + diff.z * diff.z );
	
	double top = norm.x * diff.x + norm.y * diff.y + norm.z * diff.z;
	
	double bottom = lightLang * diffLang;
	
	double dotProdukt = top / bottom;
	
	if ( dotProdukt >= 0) 
	{ 
		return false;
	}
	else return true;
}


Share this post


Link to post
Share on other sites
If you want to convert a vector or normal from one object in the scene to another object within the scene, the modelview matrix is not what you want to use. The modelview matrix converts a vector from object space to camera space (hence your camera dependancy). To convert a vector v from the coordinate system of an object A to the coordinate system of an object B you'll need to do the following:
- Convert v to world space:
v_world = A.model_matrix * v
- convert v_world to the coodrinate system of B:
v_in_B = B.model_matrix.inverse() * v_world

if v is already in world coordinates, step one can be skipped.

Now the real problem: in OpenGL they made the (IMO unfortunate) decision to combine model and view matrix into one. This means that there is no way to obtain a pure model matrix with glGetFloatv(...). Fortunately, you can convert to view space as well:
- Convert v to view space:
v_view = A.modelview_matrix * v
- convert v_view to the coodrinate system of B:
v_in_B = B.modelview_matrix.inverse() * v_view

if v is already in world coordinates, step one can *not* be skipped, otherwise the view transform would not be cancelled out and you get camera dependant behavior again. Use the view matrix instead of the modelview matrix of A.

Tom

[Edited by - dimebolt on October 21, 2005 5:48:04 AM]

Share this post


Link to post
Share on other sites
What means this->a and this->c of the plane class?

If I see it correct, the objSplight is a position (point) and not a direction vector?! If so, your dot prouct isn't correct.

(Besides the above, you may return a boolean value directly, as in
return dotProdukt<0;
instead of translating the one boolean to another.)

@dimebolt: I agree it would be better to do these computations in model space, but I remember that it was a need to do it in eye space. However, as long as only one camera is in action, also computations in the camera co-ordinate frame can be seen as camera independent, since the camera is "canceled out" (being in view standard co-ordinates).

Share this post


Link to post
Share on other sites
Quote:
Original post by haegarr
@dimebolt: I agree it would be better to do these computations in model space, but I remember that it was a need to do it in eye space. However, as long as only one camera is in action, also computations in the camera co-ordinate frame can be seen as camera independent, since the camera is "canceled out" (being in view standard co-ordinates).

Agreed, maybe you read my post before I edited it? My second solution uses the modelview twice, which cancels out the view matrix. I edited it a bit more to match your point :)

Tom

Share this post


Link to post
Share on other sites
@dimebolt: so much agreements :-) hopefully, also metalcrusader does agree ...

Ups, aren't the matrices of OpenGL in column major order? That is
0 4 8 12
1 5 9 13
2 6 10 14
3 7 11 15
right? If so, the computation of objSplight as the transformation of light is also not correct.

Share this post


Link to post
Share on other sites
Thanks!
@haegarr:
1. I fixed my light-transformation so it considers the column major order.
2. this->a is one point of my plane( flaeche) and this->c is the point that lies diagonally to a.

@dimebolt: you said:
Fortunately, you can convert to view space as well:
- Convert v to view space:
v_view = A.modelview_matrix * v
- convert v_view to the coodrinate system of B:
v_in_B = B.modelview_matrix.inverse() * v_view

I think i´ve done exactly that! v_view is my lightvector. It is defined in viewspace (or not?). So i have to multiply it by the inverse of the modelviewmatrix of B ( my plane).
My problem is, that i define my objects directly in the world coordinates. So not every plane´s origin is 0/0/0/1. I use that coordinates to calculate the normal. Do i have a modelview-matrix for my plane anyway? And how do i get it?

Share this post


Link to post
Share on other sites
@metalcrusader

I still believe that you compute the product not correctly from the plane's normal and the direction to the light. I expect something like this:
cos<norm,diff> := norm dot diff / ( |norm| * |diff| )
but I see something like this:
cos<norm,diff> := norm dot diff / ( |objSplight| * |diff| )

Another little optimization: Normalizing center by 4 divisions isn't that fast. Since you've added two points, the center.w has to be 2, and hence it would be more efficient to multiply center.x, center.y, and center.z by 0.5f, and to set center.w to 1.

Share this post


Link to post
Share on other sites
Quote:
Original post by metalcrusader
I think i´ve done exactly that! v_view is my lightvector. It is defined in viewspace (or not?).

I cannot judge that without your rendering code, but most likely it's not.
Assuming your code conceptually looks something like this:

SetupProjectionMatrix();
SetupViewMatrix();
SetupLight(); // contains a glLightfv(light, GL_POSITION, ...) call
for object in all_objects:
glPushMatrix();
MatrixMult(object.model_matrix); // contains call to glMultMatrix or glTranslate/glRotate/glScale
DrawObject(object);
glPopMatrix()

In this case the light is in world coordinates and you will need to transform it.
If the light was defined in view space, SetupLight() would appear before SetupViewMatrix() and will move with the camera. If that's what you do, the outcome is supposed to be dependant on the camera (as the light is 'attached' to it).

I'll augment the above pseudo code with code to get the light position in object space:

SetupProjectionMatrix();
SetupViewMatrix();

// get current mv matrix:
light_mv_matrix = GetModelView();

SetupLight();
for object in all_objects:
glPushMatrix();
MatrixMult(object.model_matrix);

// get obj mv:
obj_mv_matrix = GetModelView();
// multiply light_position with light_mv, and then with obj_mv
light_in_obj = obj_mv_matrix.inverse() * light_mv_matrix * light_position;

DrawObject(object);
glPopMatrix()



I would really advice you to write some more standard vector4 and matrix4x4 functions to do stuff like getting the length of a vector, dot product etc. That would really improve readability.

Tom

EDIT: and I agree with haegarr once more, that the angle between v1 and v2 should be something like: angle = acos(dot(v1, v2) / (v1.length() * v2.length()));

Share this post


Link to post
Share on other sites
Quote:
Original post by dimebolt
I would really advice you to write some more standard vector4 and matrix4x4 functions to do stuff like getting the length of a vector, dot product etc. That would really improve readability.


Yeah, not only that, it also avoids careless mistakes since it prevents to write down such (more or less) complex stuff over and over again.

Share this post


Link to post
Share on other sites
@ haegarr:
i fixed the dot - product and use your optimization.
@dimebolt:
i set up the light before defining any matrices: Here is an extract of my main:

...
glutCreateWindow (argv[0]);
init ();
glutReshapeFunc (reshape);
glutDisplayFunc(display);
glutMainLoop();



my init():

...
glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST);
GLfloat lmodel_ambient[] = { 1.0, 1.0, 1.0, 1.0};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

glLightfv( GL_LIGHT1, GL_POSITION, lightPosition1);
glLightfv( GL_LIGHT1, GL_AMBIENT, ambientLight1);
glLightfv( GL_LIGHT1, GL_DIFFUSE, diffuseLight1);
glLightfv( GL_LIGHT1, GL_SPECULAR, diffuseLight1);

glEnable (GL_LIGHTING);
glEnable (GL_LIGHT1);
glEnable(GL_COLOR_MATERIAL);



my reshape()

glViewport (0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gluPerspective(60, 1, 1, 20);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();



and in the display function there are the viewing transformations:

glLoadIdentity();
gluLookAt( xPos, hoch, zPos, xPos, hoch, zPos-1, 0.0, 1.0, 0.0);
glRotatef(spin, 0.0, 1.0, 0.0); // so i can rotate the camera.
glPushMatrix();


... so my light should be camera-independent. But sadly it isn´t. It moves as i rotate the camera...

Share this post


Link to post
Share on other sites
Quote:
Original post by metalcrusader
@dimebolt:
i set up the light before defining any matrices:

... so my light should be camera-independent.


No, quite the opposite. If I may quote my previous post:
Quote:
Original post by dimebolt
If the light was defined in view space, SetupLight() would appear before SetupViewMatrix() and will move with the camera. If that's what you do, the outcome is supposed to be dependant on the camera (as the light is 'attached' to it).

Since you setup light *before* the view matrix, it is effectively defined in camera space (and therefore, attached to it). If the light is supposed to be independant of the camera (i.e. in world space), you should take your light code from the init function and place it after the loading of the view-matrix.

Tom

Share this post


Link to post
Share on other sites
I'll try to paraphrase what dimebolt means (hopefully I find the correct words):

The matrix pushed first onto the modelview stack is applied last to incoming geometry, and the matrix pushed last is applied first. So the translational part, then the rotational part of the "view" matrix are pushed first (assuming the standard order T*R). These matrices are to be the inverse of the camera's frame in global system, since they should transform globally defined geometry into the view standard co-ordinates. All this is done in the given case by gluLookAt(...). Your display function then multiplies another heading rotation, say H, so that your overall view matrix is T*R*H.

So, all geometry following now are expected to be "global" and is transformed into the view co-ordinates accordingly. That is, now the "world" could be rendered and is seen through the camera. If, on the other hand, something is rendered _before_ the view matrix is in place, it does not look like seen through the camera, since the camera's frame isn't applied to it. As a consequence, it looks like being attached to the camera instead of to the world.

Share this post


Link to post
Share on other sites
After reading your posts it should work after moving the lightdefinition behind the gluLookAt call. But it doesnt work either! I stuff all in my display function:

gluLookAt( xPos, hoch, zPos, xPos, hoch, zPos-1, 0.0, 1.0, 0.0);
glRotatef(spin, 0.0, 1.0, 0.0);
glLightfv( GL_LIGHT1, GL_POSITION, lightPosition1);
glLightfv( GL_LIGHT1, GL_AMBIENT, ambientLight1);
glEnable (GL_LIGHTING); glEnable (GL_LIGHT1);
// make the lights visible: (correct?)
glPushMatrix();
glTranslatef( (float)lightPosition1[0], (float)lightPosition1[1], (float)lightPosition1[2]);
glutWireCube(0.1);
glPopMatrix();
glFlush();
Vektor4 a(0.0, 0.0, 0.0, 1.0); Vektor4 b(1.0, 0.0, 0.0, 1.0); Vektor4 c(1.0, 1.0, 0.0, 1.0); Vektor4 d(0.0, 1.0, 0.0, 1.0);
glPushMatrix();
glTranslatef( 0.0, 2.0, 0.0);
glPopMatrix();
glTranslatef(0.0, 2.0, 0.0);
glBegin( GL_POLYGON);
glVertex4f(a);
glVertex4f(b);
glVertex4f(c);
glVertex4f(d);
glEnd();
flaeche4 plane(a,b,c,d);
plane.isBackface();
glPopMatrix();
glFlush ();



But as i said- the lightvector between the lightsource and the plane is still camera dependant. I think it has also something to do with my call of isBackface(). The values for the plane are the un-transformed coords - how can i use the transformed ones?
The troublemaker is the rotator of the scene ( only when i rotate the scene i get wrong values).

Share this post


Link to post
Share on other sites
Possibly I don't understand something in principal. Can you make some images of your sequence accessible? It would be nice to understand what you mean w/ "the light still depends on the camera".

So long, here comes once more something to think about:

Since all your geometry is given in global frame, they can be used for computations as are. Nothing need to be transformed. However, if you push your light into OpenGL, and later read it back and use it, instead of using your original from the code, you have to consider this:
Quote:
Manual page
The position is transformed by the modelview matrix when glLight is called (just as if it were a point), and it is stored in eye coordinates.

I've tried it out, and actually the read back parameters were altered by the model-view trafo.

In a previous posting, one can see that you've used the read back light position and reverse transformed it into the global frame. Now you want to transform the quad into the view frame. I think that isn't the correct way, since it makes the things only more complicated.

[EDIT: Furthurmore, you must be sure that the modelview matrix you use for reverse trafo is actually the inverse one of the one used previously when invoked glLight, or the things will go terribly wrong!]

Please, consider to make your computations totally in model space (or, in your case, in global space). That is: Use your lightingPosition1, defined in global frame, and _not_ the glGetLight thing. As a consequence, drop the inverse trafo stuff in flaeche4::isBackface.

All you need is the normal of the plane in global frame, and the difference vector from the plane to the light source (also in global frame).

---

A nice thing most ignored: "A Coordinate Free Geometry ADT" of Mann, Litke, and DeRose of the drawbacks and way out of using geometry of different frames.

Share this post


Link to post
Share on other sites
To go a step furthur, I'll post some code here. Please notice that I haven't compiled/run it, since I don't have all of your classes available. (Notice furthur that the code reflects my opinion of the OOP way.)

Invocation is to be done w/ an arbitrary direction vector that isn't degenerated. In your case, the difference vector from the plane to the light is wanted. E.g. so (written w.r.t. clarification, not optimization):


bool flaeche4::isBackface( const Vector4& globalVector ) {
assert(globalVector.w == 0); // must be a direction vector!
assert(globalVector.length() > 0); // must not disappear!
const Vektor4 norm = this->berechneNormale(); // assuming unit length!
double nomin = norm.dot(globalVector);
double denom = globalVector.length();
return (nomin / denom) < 0;
}

Vector4 centerPoint = myFlaeche4->getCenter();
Vector4 difference = lightPosition1 - centerPoint;
bool isBackface = myFlaeche4->isBackface( difference );



Share this post


Link to post
Share on other sites
Because i don´t know how to post pictures i try to explain my scene: i have only one cube and one lightsource on the left side of it. I want to color the sides facing the light red , and the backfaces green. The problem was when i rotate my view, red faces become green and vice versa. But now, without any transformation-shit it works. Also when rotating the camera the colors are fix. The only thing is that frontfaces are seen as backfaces and vice versa - but i think i can fix that. Thank you many, many times!!!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this