Trouble with the light. Cornell Box task.

Greetings!

I’m trying to make a Cornell Box in c++ OpenGL GLUT, but having trouble with the light.
I have 3 sources, all of them are in GL_POSITION ‘mode’

What i currently have is shown on the screen below. My trouble is that the ceiling is much darker than other walls, although they are all have same color. Floor is also lighter than walls, but its not that noticeable.

Lighting code:


static GLfloat lightpos0[] = { 0.f, 50.f, -400.f, 1.f };
static GLfloat lightpos1[] = { 20.f, 50.f, -400.f, 1.f };
static GLfloat lightpos2[] = { -20.f, 50.f, -400.f, 1.f };

glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHT1);
	glEnable(GL_LIGHT2);
	glLightfv(GL_LIGHT0, GL_POSITION, lightpos0);
	glLightfv(GL_LIGHT1, GL_POSITION, lightpos1);
	glLightfv(GL_LIGHT2, GL_POSITION, lightpos2);
	glEnable(GL_DEPTH_TEST);
	glShadeModel(GL_SMOOTH);

	//создание перспективы на сцене
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(-100, 100, -100, 100, 320., 640.);
	glMatrixMode(GL_MODELVIEW);

Walls code:

glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
	GLfloat x, y, z;

	/* пол */
	glBegin(GL_QUADS);
	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, beige_material);
	glNormal3f(0.f, 1.f, 0.f);
	x = -100.f, y = -100.f, z = -320.f;
	for (int i = 1; i <= 200; i++) {
		for (int j = 1; j <= 200; j++) {
			glVertex3f(x, y, z);
			glVertex3f(x + j, y, z);
			glVertex3f(x + j, y, z - i);
			glVertex3f(x, y, z - i);
		}
		x = -100.f;
	}

	/* левая стена */
	glNormal3f(1.f, 0.f, 0.f);
	x = -100.f, y = -100.f, z = -320.f;
	for (int i = 1; i <= 200; i++) {
		for (int j = 1; j <= 200; j++) {
			glVertex3f(x, y, z);
			glVertex3f(x, y, z - j);
			glVertex3f(x, y + i, z - j);
			glVertex3f(x, y + i, z);
		}
		z = -320.f;
	}

	/* правая стена */
	glNormal3f(-1.f, 0.f, 0.f);
	x = 100.f, y = -100.f, z = -320.f;
	for (int i = 1; i <= 200; i++) {
		for (int j = 1; j <= 200; j++) {
			glVertex3f(x, y, z);
			glVertex3f(x, y + j, z);
			glVertex3f(x, y + j, z - i);
			glVertex3f(x, y, z - i);
		}
		y = -100.f;
	}

	/* потолок */
	glNormal3f(0.f, -1.f, 0.f);
	x = -100.f, y = 100.f, z = -320.f;
	for (int i = 1; i <= 200; i++) {
		for (int j = 1; j <= 200; j++) {
			glVertex3f(x, y, z);
			glVertex3f(x, y, z - j);
			glVertex3f(x + i, y, z - j);
			glVertex3f(x + i, y, z);
		}
		z = -320.f;
	}

	/* задняя стена */
	glNormal3f(0.f, 0.f, 1.f);
	x = -100.f, y = -100.f, z = -520.f;
	for (int i = 1; i <= 200; i++) {
		for (int j = 1; j <= 200; j++) {
			glVertex3f(x, y, z);
			glVertex3f(x + j, y, z);
			glVertex3f(x + j, y + i, z);
			glVertex3f(x, y + i, z);
		}
		x = -100.f;
	}
	glEnd();

Room_items code:

glPushMatrix();
	glTranslatef(-34.f, -75.f, -430.f);
	glRotatef(20.f, 0.f, 1.f, 0.f);
	glutSolidCube(50.0);
	glPopMatrix();

	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, brown_material);
	glPushMatrix();
	glTranslatef(40.f, -85.f, -370.f);
	glRotatef(-25.f, 0.f, 1.f, 0.f);
	glutSolidCube(30.0);
	glPopMatrix();

	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, yellow_material);
	glPushMatrix();
	glTranslatef(0.f, -80.f, -470.f);
	glutSolidSphere(20.0, 100, 100);
	glPopMatrix();

Main method:

	glutInit(&argc, argv);
	glutInitWindowSize(800, 800);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_ACCUM | GLUT_DOUBLE);
	(void)glutCreateWindow("Cornell Box");
	init();
	glutDisplayFunc(display);

	GLfloat plane[4];
	GLfloat v0[3], v1[3], v2[3];
	v0[0] = -100.f;v0[1] = -100.f; v0[2] = -320.f;
	v1[0] = 100.f; v1[1] = -100.f; v1[2] = -320.f;
	v2[0] = 100.f; v2[1] = -100.f; v2[2] = -520.f;
	findplane(plane, v0, v1, v2);
	shadowmatrix(floorshadow, plane, lightpos1);

	glReadBuffer(GL_BACK);
	glutMainLoop();

I have a shadows method and MSAA method(which is display func), which I have not included, but i can if needed.
Already tried glLightModeli or switch from only GL_POSITION lights to GL_AMBIENT, GL_SPECULAR, GL_DIFFUSE and GL_POSITION lights.
Any help are much appreciated!

Screenshot:

First, you’re generating the geometry incorrectly. E.g. this:

should be:


	x = -100.f, y = -100.f, z = -320.f;
	for (int i = 1; i <= 200; i++) {
		for (int j = 1; j <= 200; j++) {
			glVertex3f(x + j,     y, z - i);
			glVertex3f(x + j + 1, y, z - i);
			glVertex3f(x + j + 1, y, z - i - 1);
			glVertex3f(x + j,     y, z - i - 1);
		}
	}

The original code generates many rectangles, each with one corner at (x,y,z) and the opposite corner at (x+i,y,z-i). I’m fairly sure that you want to move the entire quad each time, meaning that you need to add (j,0,-i) to all four vertices. The other loops have the same issue.

But aside from that, you’re not going to get anything even close to realistic lighting for this scene without using radiosity.

As for why the ceiling is darker: it’s probably because a) the lights are closer to the ceiling than to the floor and b) you don’t have any attenuation. For any given plan-view (x,z) position, the corresponding point on the ceiling (x,100,z) will result in a greater angle between the light direction and the surface normal than for the corresponding point on the floor (x,-100,z), which results in lower intensity (the diffuse component is proportional to the cosine of the angle between the light direction and the surface normal).

If the inverse-square law was taken into account, that would tend to make the ceiling brighter overall; the points on the ceiling immediately above the lights would be at 1/3 the distance of those on the floor immediately below the lights (50 units vs 150 units), and so would have 9 times the intensity.

However, that won’t change the fact that the lighting will be more even on the floor; the ceiling will have more distinct bright spots above each light, whereas the floor will be more uniform.

Thank you for a quick answer!
Indeed, my geometry code is wrong, changed it, thanks!

About ceiling. Moved light position from y=50 to y=0 (center of the room). Ceiling became normal, but corners almost disappeared (screenshot below).
Nevertheless, usually, the closer light source to the ceiling - the brighter ceiling is. Is it only achievable by radiosity or attenuation can fix this?

Using ‘real’ light attenuation (inverse square law) is a first step, but it won’t fix all.
Radiosity will take into account what you won’t get with OpenGL like this: indirect lighting (ray light after bouncing on surfaces). So depending on what you want to achieve, you’ll have to tackle a global illumination model, and in that case radiosity fits well (closed scene, diffuse materials and no transparencies).
And as a side note, it is nowadays possible to achieve some real-time radiosity using graphics API like OpenGL (plus shaders). I guess this article talks about this.

Attenuation will fix that aspect.

In reality, light intensity is inversely proportional to the square of the distance from the light source. If you surround a point light with a sphere, the emitted light will be distributed uniformly over the surface of the sphere. The area of the sphere is proportional to the square of the radius, so the amount of light per unit area is inversely proportional to the square of the radius.

It won’t fix the fact that areas with little direct illumination will appear dark. That’s what radiosity will fix.

Another thing which you need to take into account for realistic lighting is gamma. The intensity of light emitted from the monitor isn’t proportional to the value stored in the framebuffer, but to xγ, where x is is the value and γ is the monitor’s “gamma” (a gamma of 2.2 is typical, and is used for the sRGB colour standard). To get accurate results, you need to calculate linear intensities and perform gamma encoding as a final post-process step. Recent versions of OpenGL have some support for this via sRGB textures framebuffers, otherwise you have to perform additional calculations to get correct results (in particular, texture filtering and alpha blending aren’t correct by default).