Reputation: 1592
I'm asked to make a simple Propeller using GL_TRIANGLE_STRIP
. However, writing the structure wasn't hard, but making the lighting to work properly is causing me trouble as I'm using GL_TRIANGLE_STRIP
. Before I used GL_POLYGON
and making lighting for shapes (e.g. rectangles) was very easy as I just specify glNormal3f
once for each shape.
Question: Based on some internet search, apparently I need to specify the normal vector for vortex not each shape. So is my current lighting (the way that I am specifying the normal vectors) correct?
#include <GL/glut.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#define PI 3.14159265
static GLfloat lpos[] = { 0.0, 0.0, 4.0, 1.0 };
static GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };
static GLfloat black[] = { 0.0, 0.0, 0.0, 1.0 };
static GLfloat red[] = { 1.0, 0.0, 0.0, 1.0 };
static GLfloat yellow[] = { 1.0, 1.0, 0.0, 1.0 };
static float alpha = 0.0;
static float beta = PI / 6.0;
static float zoom = 10.0;
static bool lightSource = true;
float twistConstant = 0;
float rotateConstant = 0;
float numberOfObj = 3;
float numberOfTriangles = 1;
static GLdouble cpos[3];
void DrawTopTriangleSet(){
glMaterialfv(GL_FRONT, GL_EMISSION, black);
glMaterialfv(GL_BACK, GL_EMISSION, black);
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, yellow);
glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, yellow);
glBegin(GL_TRIANGLE_STRIP);
for (int i = 180; i >= 0; i = i - numberOfTriangles){
glNormal3f(0, cos(i*PI / 180), sin(i*PI / 180));
glVertex3f(i*PI / 180, 0, 0.5*sin(i*PI / 180));
glVertex3f(i*PI / 180, 0.5*sin(i*PI / 180), twistConstant*-sin(i*PI / 180));
}
glEnd();
}
void DrawBottomTriangleSet(){
glMaterialfv(GL_FRONT, GL_EMISSION, black);
glMaterialfv(GL_BACK, GL_EMISSION, black);
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, yellow);
glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, yellow);
glBegin(GL_TRIANGLE_STRIP);
for (int i = 180; i >= 0; i = i - numberOfTriangles){
glNormal3f(0, -cos(i*PI / 180), sin(i*PI / 180));
glVertex3f(i*PI / 180, -0.5*sin(i*PI / 180), twistConstant*sin(i*PI / 180));
glVertex3f(i*PI / 180, 0, 0.5*sin(i*PI / 180));
}
glEnd();
}
void DrawBackTriangleSet(){
glMaterialfv(GL_FRONT, GL_EMISSION, black);
glMaterialfv(GL_BACK, GL_EMISSION, black);
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, yellow);
glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, yellow);
glBegin(GL_TRIANGLE_STRIP);
for (int i = 180; i >= 0; i = i - numberOfTriangles){
glNormal3f(0, -cos(i*PI / 180), -sin(i*PI / 180));
glVertex3f(i*PI / 180, 0.5*sin(i*PI / 180), twistConstant*-sin(i*PI / 180));
glVertex3f(i*PI / 180, -0.5*sin(i*PI / 180), twistConstant*sin(i*PI / 180));
}
glEnd();
}
void DrawInsideTriangleSet(){
glMaterialfv(GL_FRONT, GL_EMISSION, black);
glMaterialfv(GL_BACK, GL_EMISSION, black);
glBegin(GL_TRIANGLE_STRIP);
for (int i = 180; i >= 0; i = i - numberOfTriangles){
glVertex3f(i*PI / 180, 0.5*sin(i*PI / 180), twistConstant*-sin(i*PI / 180));
glVertex3f(i*PI / 180, 0, 0.5*sin(i*PI / 180));
glVertex3f(i*PI / 180, -0.5*sin(i*PI / 180), twistConstant*sin(i*PI / 180));
}
glEnd();
}
void writemessage()
{
printf(" X => x++ <= Move light source in direction of +X\n");
printf(" Y => y++ <= Move light source in direction of +Y\n");
printf(" Z => z++ <= Move light source in direction of +Z\n");
printf("\n");
printf("^X => x-- <= Move light source in direction of -X\n");
printf("^Y => y-- <= Move light source in direction of -Y\n");
printf("^Z => z-- <= Move light source in direction of -Z\n");
printf("\n");
printf(" ^ => Move camera up\n");
printf(" > => Move camera right\n");
printf(" < => Move camera left\n");
printf(" down arrow => Move camera down\n");
printf("\n");
printf(" t => More Twist\n");
printf(" f => Less Twist\n");
printf("\n");
printf(" q => More Propeller\n");
printf(" f => Less Propeller\n");
printf("\n");
printf(" w => More Triangles\n");
printf(" s => Less Triangles\n");
printf("\n");
printf(" 0 => Toggling light source\n");
printf("\n");
printf(" r => Rotates Propeller\n");
printf("\n");
printf(" You can not move the light source when the light source is off !!!");
}
void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, (GLfloat)w / (GLfloat)h, 0.01, 20.0);
glMatrixMode(GL_MODELVIEW);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
cpos[0] = zoom * cos(beta) * sin(alpha);
cpos[1] = zoom * sin(beta);
cpos[2] = zoom * cos(beta) * cos(alpha);
gluLookAt(cpos[0], cpos[1], cpos[2], 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
if (lightSource == true){
glLightfv(GL_LIGHT0, GL_POSITION, lpos);
glMaterialfv(GL_FRONT, GL_EMISSION, white);
glPushMatrix();
glTranslatef(lpos[0], lpos[1], lpos[2]);
glutSolidSphere(0.1, 10, 8);
glPopMatrix();
glMaterialfv(GL_FRONT, GL_EMISSION, black);
}
glRotatef(rotateConstant, 0, 0, 1);
for (int i = 0; i < numberOfObj; i++){
glPushMatrix();
glRotatef(i * 360 / numberOfObj, 0, 0, 1);
DrawTopTriangleSet();
DrawBottomTriangleSet();
DrawBackTriangleSet();
DrawInsideTriangleSet();
glPopMatrix();
}
// Cone
glMaterialfv(GL_FRONT, GL_EMISSION, black);
glMaterialfv(GL_BACK, GL_EMISSION, black);
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, red);
glPushMatrix();
glTranslated(0, 0, -1.5);
glutSolidCone(1, 2, 50, 50);
glPopMatrix();
// Back of Cone
glMaterialfv(GL_FRONT, GL_EMISSION, black);
glMaterialfv(GL_BACK, GL_EMISSION, black);
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, red);
glBegin(GL_POLYGON);
glNormal3f(0, 0, 1);
for (int i = 0; i <= 360; i++)
{
glVertex3f(cos(i*PI / 180) * 1, sin(i*PI / 180) * 1, -1.5);
}
glEnd();
glutSwapBuffers();
glFlush();
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 27:
exit(0);
break;
case 'x':
if (lightSource == true)
lpos[0] = lpos[0] + 0.2;
glutPostRedisplay();
break;
case 'X':
if (lightSource == true)
lpos[0] = lpos[0] - 0.2;
glutPostRedisplay();
break;
case 'y':
if (lightSource == true)
lpos[1] = lpos[1] + 0.2;
glutPostRedisplay();
break;
case 'Y':
if (lightSource == true)
lpos[1] = lpos[1] - 0.2;
glutPostRedisplay();
break;
case 'z':
if (lightSource == true)
lpos[2] = lpos[2] + 0.2;
glutPostRedisplay();
break;
case 'Z':
if (lightSource == true)
lpos[2] = lpos[2] - 0.2;
glutPostRedisplay();
break;
case '+':
if (zoom != 1.5)zoom = zoom - 0.5;
glutPostRedisplay();
break;
case '-':
if (zoom != 15)zoom = zoom + 0.5;
glutPostRedisplay();
break;
case '0':
if (lightSource == true){
glDisable(GL_LIGHT0);
lightSource = false;
}
else{
glEnable(GL_LIGHT0);
lightSource = true;
}
glutPostRedisplay();
break;
case 't':
if (twistConstant <= 1){
twistConstant = twistConstant + 0.05;
glutPostRedisplay();
}
break;
case 'f':
if (twistConstant >= 0){
twistConstant = twistConstant - 0.05;
glutPostRedisplay();
}
break;
case 'r':
rotateConstant = rotateConstant + 2;
glutPostRedisplay();
break;
case 'q':
if (numberOfObj <= 6){
numberOfObj++;
glutPostRedisplay();
}
break;
case 'a':
if (numberOfObj >= 0){
numberOfObj--;
glutPostRedisplay();
}
break;
case 's':
if (numberOfTriangles <90){
numberOfTriangles++;
glutPostRedisplay();
}
break;
case 'w':
if (numberOfTriangles > 1){
numberOfTriangles--;
glutPostRedisplay();
}
break;
default:
break;
}
}
void specialkey(GLint key, int x, int y)
{
switch (key) {
case GLUT_KEY_RIGHT:
alpha = alpha + PI / 180;
if (alpha > 2 * PI) alpha = alpha - 2 * PI;
glutPostRedisplay();
break;
case GLUT_KEY_LEFT:
alpha = alpha - PI / 180;
if (alpha < 0) alpha = alpha + 2 * PI;
glutPostRedisplay();
break;
case GLUT_KEY_UP:
if (beta < 0.45*PI) beta = beta + PI / 180;
glutPostRedisplay();
break;
case GLUT_KEY_DOWN:
if (beta > -0.05*PI) beta = beta - PI / 180;
glutPostRedisplay();
break;
default:
break;
}
}
int main(int argc, char** argv)
{
writemessage();
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(1200, 800);
glutInitWindowPosition(0, 0);
glutCreateWindow(argv[0]);
glClearColor(0.0, 0.0, 0.0, 0.0);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glEnable(GL_LIGHTING);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glEnable(GL_LIGHT0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 5.0, 10.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutSpecialFunc(specialkey);
glutMainLoop();
return 0;
}
Upvotes: 2
Views: 550
Reputation: 54652
I don't think your normals are quite correct. For example for this part of the geometry:
glNormal3f(0, cos(i*PI / 180), sin(i*PI / 180));
glVertex3f(i*PI / 180, 0, 0.5*sin(i*PI / 180));
glVertex3f(i*PI / 180, 0.5*sin(i*PI / 180), twistConstant*-sin(i*PI / 180));
Since twistConstant
is part of the vertex coordinate calculation, it looks very suspicious that it's not also part of the normal calculation. I don't think it's possible for the normal to not depend on the constant value if it changes how the vertices are calculated.
Let's try and calculate the normal for this case. This is just for illustration of the process, it's quite likely that I won't get this exactly right without trying it out.
For parameterized surfaces, you normally calculate the normal as the cross product of the two gradient vectors (one for each parameter). Using a
short for the value of the angle I * PI / 180
, and c
short for twistConstant
, the two vertices from the code above are:
| a | | a |
v1 = | 0 | v2 = | 0.5 * sin(a) |
| 0.5 * sin(a) | | - c * sin(a) |
The gradient for parameter a
at vertex v1
is then:
| 1 |
dv1 / da = | 0 |
| 0.5 * cos(a) |
The way I read your code, the surface is flat in the direction between these two vertices, so we can use the difference between the two vectors as the second gradient:
| 0 |
v2 - v1 = | 0.5 * sin(a) |
| - (c + 0.5) * sin(a) |
The cross product between these two gradient vectors is then:
| 1 | | 0 | | -0.25 * sin(a) * cos(a) |
| 0 | x | 0.5 * sin(a) | = | (c + 0.5) * sin(a) |
| 0.5 * cos(a) | | - (c + 0.5) * sin(a) | | 0.5 * sin(a) |
This would then be the normal at vertex v1
. The normal at v2
will be different, which makes sense because the blade is twisted.
Beyond the math, you're also missing to specify normals in DrawInsideTriangleSet()
:
glVertex3f(i*PI / 180, 0.5*sin(i*PI / 180), twistConstant*-sin(i*PI / 180));
glVertex3f(i*PI / 180, 0, 0.5*sin(i*PI / 180));
glVertex3f(i*PI / 180, -0.5*sin(i*PI / 180), twistConstant*sin(i*PI / 180));
The first call was probably supposed to be glNormal3f()
instead of glVertex3f()
.
Upvotes: 3