这是计算机图形学基础的课后题:
编制OpenGL程序,显示包含一个球体和正四面体,使用有衰减的聚光灯进行光照,球面和正四面体具有不同的材质,分别调整球面和正四面体的材质属性,以及聚光灯的衰减系数(包括沿光线路径和聚光灯光锥体内的衰减),观察结果。
OpenGL给的光照的相关函数真的多,分类非常细,需要根据光照计算公式以及函数说明去尝试并体会不同光照打在不同材质的物体上的效果。可以通过改变其中的参数来体会那么多函数分别起什么作用,我们就是用以下的函数和参数来控制衰减。
分别是聚光灯方向、聚光灯角度、聚光灯光强度随偏离中轴角度的衰减
常数衰减、线性衰减、二次方衰减的系数
最开始全都使用默认衰减系数的效果:
当我们增加聚光灯光锥体内的衰减时:
当我们减少光随距离的增长而衰弱的系数时:
但是我发现设置了光随距离的增长而衰弱的系数后聚光灯光锥体内的衰减就不起作用了,并不知道为什么,望高手解答。同样还会发现球体上会有明显的锯齿现象。
我们是通过键盘交互来实现这些系数的调整,具体看代码:
#include <stdio.h>
#include <windows.h>
#include <GL/freeglut.h>
//摄像机离物体的距离
float G_fDistance = 3.6f;
//物体的旋转角度
float G_fAngle_horizon = 0.0;
float G_fAngle_vertical = 0.0f;
//聚光光照参数
float G_vLit0Position[4] = { 0.0f, 1.0, 1.0, 1.0f };
float G_vLit0Ambient[4] = { 0.0f, 0.8f, 0.8f, 1.0f };
float G_vLit0Diffuse[4] = { 0.0f, 0.8f, 0.8f, 1.0f };
float G_vLit0Specular[4] = { 0.0f, 0.5f, 0.5f, 1.0f };
float spot_direction[]={ 0.0 ,0.0,-1.0 };
//球体和正四面体的高光材质参数
float G_vMaterialSpecu1[4] = { 0.8f, 0.8f, 0.8f, 1.0f };
float G_vMaterialSpecu2[4] = { 0.8f, 0.8f, 0.8, 1.0f };
//聚光灯光强度随偏离中轴角度的衰减
GLfloat vSpotExp=0.0;
//聚光灯随照射的角度范围(vLitCutoff*2)
GLfloat vLitCutoff=10.0;
//聚光灯光强度随距离的衰减
GLfloat Kc=1.0,Ki=0.0,Kq=0.0;//常数衰减、线性衰减、二次方衰减的参数(初始化都是默认值)
void myinit(void);
void myReshape(GLsizei w, GLsizei h);
void display(void);
//响应键盘输入, 从而设定物体移近移远以及旋转的回调函数
void processSpecialKeys(int key, int x, int y);
void processNormalKeys(unsigned char key,int x,int y);
//主函数
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
//初始化OPENGL显示方式
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA);
// glutInitDisplayMode (GLUT_SINGLE | GLUT_RGBA);
//设定OPENGL窗口位置和大小
glutInitWindowSize (1000, 1000);
glutInitWindowPosition (100, 100);
//打开窗口
glutCreateWindow ("聚光灯参数的改变");
//调用初始化函数
myinit();
//设定窗口大小变化的回调函数
glutReshapeFunc(myReshape);
//设定键盘控制的回调函数
glutSpecialFunc(processSpecialKeys);
glutKeyboardFunc(processNormalKeys);
//开始OPENGL的循环
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
//用户初始化函数
void myinit(void)
{
//your initialization code
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glShadeModel(GL_SMOOTH);
//混合物体自身的颜色
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT);
}
//窗口大小变化时的回调函数
void myReshape(GLsizei w, GLsizei h)
{
//设定视区
glViewport(0, 0, w, h);
//设定透视方式
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0,(GLfloat)w/(GLfloat)h,1.0,30.0);
}
//每桢OpenGL都会调用这个函数,用户应该把显示代码放在这个函数中
void display(void)
{
//设置清除屏幕的颜色,并清除屏幕和深度缓冲
glClearColor(0.0f,0.0f,0.0f,0.0f);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//设置光照
glShadeModel(GL_SMOOTH);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glLightfv(GL_LIGHT0, GL_POSITION, G_vLit0Position);
glLightfv(GL_LIGHT0, GL_AMBIENT, G_vLit0Ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, G_vLit0Diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, G_vLit0Specular);
glLightfv( GL_LIGHT0,GL_SPOT_DIRECTION,spot_direction );
//聚光灯角度
glLightf( GL_LIGHT0,GL_SPOT_CUTOFF,vLitCutoff);
//聚光灯随距离中轴角度的变化的衰减
glLightf( GL_LIGHT0,GL_SPOT_EXPONENT,vSpotExp);
//聚光灯随距离的变化的衰减
glLightf( GL_LIGHT0,GL_CONSTANT_ATTENUATION,Kc);
glLightf( GL_LIGHT0,GL_LINEAR_ATTENUATION,Ki);
glLightf( GL_LIGHT0,GL_QUADRATIC_ATTENUATION,Kq);
//视角的变化
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0,0.0,-G_fDistance);
glRotatef(G_fAngle_horizon, 0.0f, 1.0f, 0.0f);
glRotatef(G_fAngle_vertical, 1.0f, 0.0f, 0.0f);
//gluLookAt(0.0,G_fAngle_vertical,G_fAngle_horizon,0.0,0.0,0.0,0.0,1.0,-1.0);
gluLookAt(0.0,2.0,2.0,0.0,0.0,0.0,0.0,1.0,-1.0);
//球面
glPushMatrix();
//物体材质
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, G_vMaterialSpecu1);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 60.0f);
glTranslatef(-1.0,0.0,0.0);
glColor3f(1.0,1.0,0.0);
glutSolidSphere(0.5,20,20);
glPopMatrix();
//正四面体
glPushMatrix();
//物体材质
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, G_vMaterialSpecu2);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 60.0f);
glTranslatef(1.0,0.0,0.0);
glColor3f(0.0,0.0,1.0);
glutSolidTetrahedron();
glPopMatrix();
//交换前后缓冲区
glutSwapBuffers();
}
void processSpecialKeys(int key, int x, int y)
{
switch(key) {
case GLUT_KEY_LEFT:
G_fAngle_horizon -= 1.0f;
break;
case GLUT_KEY_RIGHT:
G_fAngle_horizon += 1.0f;
break;
case GLUT_KEY_UP:
G_fAngle_vertical -= 1.0f;
break;
case GLUT_KEY_DOWN:
G_fAngle_vertical += 1.0f;
break;
}
glutPostRedisplay();
}
void processNormalKeys(unsigned char key,int x,int y)
{
switch(key) {
case 97: //"a"
vSpotExp+=10.0;
break;
case 100: //"d"
vSpotExp-=10.0;
break;
case 119: //"w"
vLitCutoff+=1.0;
break;
case 115: //"s"
vLitCutoff-=1.0;
break;
case 99: //"c"
Kc+=0.1;
break;
case 67: //"C"
Kc-=0.1;
break;
case 105: //"i"
Ki+=0.1;
break;
case 73: //"I"
Ki-=0.1;
break;
case 113: //"q"
Kq+=0.1;
break;
case 81: //"Q"
Kq-=0.1;
break;
case 27: //"esc"
exit(0);
}
glutPostRedisplay();
}