预备知识
材料
材料设置
void glMaterialfv(
GLenum face, //设置材质的面片,是前面,后面,还是双面
GLenum pname, //材质的类型,是环境光材质,漫反射,反射,高光,辐射光等
GLfloat *param //参数,跟光的参数一样,表示对应的值。
);
- face参数
指出将材质应用于物体的哪面
GL_FRONT
GL_BACK
GL_FRONT_AND_BACK
- pname参数可选项
GL_AMBIENT//环境光颜色
GL_DIFFUSE//漫反射颜色
GL_SPECULAR//镜面反射颜色
GL_SHININESS//镜面反射指数高光
GL_EMISSION//辐射光颜色
- param
颜色参数表示对该种色光的反射程度
1表示完全反射,0表示完全吸收
材质状态
材质会被保存在状态里面,所以如果不改变材质的话,默认会用最近用过的材质。
在使用某种材质的前文中需要设定好材质属性,再进行绘制。
这一点和之前所学的glTranslatef
,glScalef
,glRotatef
有一定相似之处
思考
发现在给物体设置材质的时候,在本次实验里面设定GL_FRONT
还是GL_FRONT_AND_BACK
没有差别,猜想是因为这次的物体在查看的时候显示出来的全是FRONT面
光照
光照设置
void glLightfv(
GLenum light, //指定光源
GLenum pname, //材质的类型,是环境光材质,漫反射,反射,高光,辐射光等
const GLfloat *param //参数,跟光的参数一样,表示对应的值。
);
- light参数
OpenGL里面最多可设置八个光源,GL_LIGHT0、GL_LIGHT1、……、GL_LIGHT7
light参数便是指定对某一光源进行属性设置
- pname参数可选项
GL_AMBIENT//环境光颜色,默认值(0,0,0,1)
GL_DIFFUSE//漫反射颜色,默认值(1,1,1,1)
GL_SPECULAR//镜面反射颜色,默认值(1,1,1,1)
GL_POSITION//光源位置,默认值(0,0,1,0)
GL_SPOT_CUTOFF//角度,对于聚光灯设定
GL_SPOT_DIRECTION//光照方向,对于聚光灯设定
GL_SPOT_EXPONENT//聚集度,对于聚光灯设定
- param
设置pname项的具体参数
其中GL_POSITION
的第四个参数w
若w=0,表示定向光源,也即光源位于无穷远处,照在物体上时可当成平行光
若w=1,表示定点光源,光源位于特定的某个点处
光源开启
光源和材质不一样,材质只需要直接指定即可,但是光源需要“开启”,才能显示出作用
在设置光源之前,需要开启光照模式
glEnable(GL_LIGHTING);
设置完某一光源后,想要在结果里面显示出有光源的效果,需要像打开灯的开关一样打开它
glEnable(GL_LIGHT0);
实验要求
使用Visual Studio C++编译已有项目工程。
模型尺寸不做具体要求。要求修改代码达到以下要求:
1.通过设置材料使得桌面和四条腿的颜色各不相同,分别为:(1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 1, 1), (0, 0, 1);
2.通过设置材料使得茶壶为金黄色;
3.添加按键处理,移动场景中的光源,并能改变光源的颜色(在两种颜色间切换,颜色自己定义);
4.修改茶壶的镜面反射系数,使之对光源呈现高光;
5.在场景中添加一个聚光光源,其照射区域正好覆盖茶壶,并能调整改聚光光源的照射角度和朝向。
具体做法
一)设置材质
根据实验要求对茶壶,桌面,四条桌腿分别设置不同的材质
//设置材质参数
GLfloat specular[] = { 0.6f,0.6f,0.6f,1 };
GLfloat diffuse0[] = { 0.85f,0.65f,0.2f,1 };
GLfloat diffuse1[] = { 1,0,0,1 };
GLfloat diffuse2[] = { 0,1,0,1 };
GLfloat diffuse3[] = { 1,1,0,1 };
GLfloat diffuse4[] = { 0,1,1,1 };
GLfloat diffuse5[] = { 0,0,1,1 };
茶壶的镜面反射与漫反射设置不同的颜色
//茶壶
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);//正反面的镜面反射颜色
glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, 50);//镜面反射系数
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse0);//正反面的漫反射颜色
桌面与每条桌腿自身的镜面反射与漫反射设置成相同颜色,不考虑高光
//桌面
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse1);//正反面的镜面反射颜色
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse1);//正反面的漫反射颜色
二)设置光照
设定点光源与聚光灯的一系列参数
/* 点光源 */
GLfloat light0_pos[] = { 5,5,5,1 };//Position
GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };//color
GLfloat purple[] = { 1.0, 0, 1.0, 1.0 };//color
/*聚光灯*/
GLfloat light1_pos[] = { 0.0f, 5.0f, 0.0f, 1.0f };
GLfloat light_dir[] = { 0,-1,0 };
GLfloat angle = 5.0f;
配置光源并打开
- 点光源
/* 点光源 */
glLightfv(GL_LIGHT0, GL_POSITION, light0_pos);//位置
//环境光可在白光和紫光之间切换
if (isWhite) {
glLightfv(GL_LIGHT0, GL_AMBIENT, white);//环境光属性
}
else {
glLightfv(GL_LIGHT0, GL_AMBIENT, purple);//环境光属性
}
glLightfv(GL_LIGHT0, GL_DIFFUSE, white);//漫反射属性
glLightfv(GL_LIGHT0, GL_SPECULAR, white);//镜面反射属性
glEnable(GL_LIGHT0);
- 聚光灯
/* 聚光灯 */
glLightfv(GL_LIGHT1, GL_POSITION, light1_pos);//位置
if (isWhite) {
glLightfv(GL_LIGHT1, GL_AMBIENT, white);//环境光属性
}
else {
glLightfv(GL_LIGHT1, GL_AMBIENT, purple);//环境光属性
}
glLightfv(GL_LIGHT1, GL_DIFFUSE, white);//漫反射属性
glLightfv(GL_LIGHT1, GL_SPECULAR, white);//镜面反射属性
glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, angle);//设置聚光灯角度
glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light_dir);//设置聚光灯方向
glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, 2);//设置聚光灯聚集度
glEnable(GL_LIGHT1);
三)设置按键功能
光照颜色切换
使用l按键来切换,isWhite
是事先定义好的一个全局变量
//切换光照颜色,白色or紫色
case 'l': {
isWhite = !isWhite;
break;
}
光源位置调整
考虑到按键数量有限,若是给点光源位置,聚光灯位置,聚光灯朝向分别设置6个按键分别控制上下前后左右的调整,按键数可能不够也非常不好管理。所以想让这6个按键彼此共用,一方面需要管理的按键数量减少,另一方面能够尽可能保证按键与其功能有一定意义上的关联,便于记忆与理解。
共用的话就需要做好切换,因此引入两个全局变量 isLight0
,isPos
调整
-
isLight0
在true与false间切换,true代表当前控制的是点光源,false表示聚光灯 -
isPos
在true与false间切换,true代表当前控制的是position,false表示direction(由于只有聚光灯有direction,所以只有当isLight0=false;
时才会进行对isPos
的判断
按键5与按键0分别进行isLight0
,isPos
的控制
//移动控制,移动环境光or聚光灯
case '5': {
isLight0 = !isLight0;
break;
}
//移动控制,改变聚光灯角度or位置
case '0': {
isPos = !isPos;
break;
}
数字键盘上的按键824613分别表示上下左右前后
//光照左移
case '4': {
if (isLight0) //移动环境光
{
light0_pos[0] -= 0.2f;
}
else //移动聚光灯
{
if (isPos) {
light1_pos[0] -= 0.2f;
}
else
{
light_dir[0] -= 0.05f;
}
}
break;
}
聚光灯角度调整
聚光灯角度需要设计一个范围,只能在该范围内放大减小
angle
是预先定义好的全局变量
用按键+-控制角度变大变小
//增大聚光灯角度
case '+': {
if (angle <= 89.8) {
angle += 0.2f;
}
break;
}
//减小聚光灯角度
case '-': {
if (angle >= 0.2) {
angle -= 0.2f;
}
break;
}
}
实验结果
附源码参考
// glutEx1.cpp : 定义控制台应用程序的入口点。
//
#include <stdlib.h>
#include "glut.h"
float fTranslate;
float fRotate;
float fScale = 1.0f; // set inital scale value to 1.0f
bool bPersp = false;
bool bAnim = false;
bool bWire = false;
bool isWhite = true;
bool isLight0 = true;
bool isPos = true;
int wHeight = 0;
int wWidth = 0;
/* 点光源 */
GLfloat light0_pos[] = { 5,5,5,1 };//Position
GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };//color
GLfloat purple[] = { 1.0, 0, 1.0, 1.0 };//color
/*聚光灯*/
GLfloat light1_pos[] = { 0.0f, 5.0f, 0.0f, 1.0f };
GLfloat light_dir[] = { 0,-1,0 };
GLfloat angle = 5.0f;
void Draw_Leg();
void Draw_Triangle() // This function draws a triangle with RGB colors
{
//设置材质参数
GLfloat specular[] = { 0.6f,0.6f,0.6f,1 };
GLfloat diffuse0[] = { 0.85f,0.65f,0.2f,1 };
GLfloat diffuse1[] = { 1,0,0,1 };
GLfloat diffuse2[] = { 0,1,0,1 };
GLfloat diffuse3[] = { 1,1,0,1 };
GLfloat diffuse4[] = { 0,1,1,1 };
GLfloat diffuse5[] = { 0,0,1,1 };
//茶壶
glPushMatrix();
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);//正反面的镜面反射颜色
glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, 50);//镜面反射系数
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse0);//正反面的漫反射颜色
glTranslatef(0, 0, 4+1);
glRotatef(90, 1, 0, 0);
glutSolidTeapot(1);
glPopMatrix();
//桌面
glPushMatrix();
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse1);//正反面的镜面反射颜色
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse1);//正反面的漫反射颜色
glTranslatef(0, 0, 3.5);
glScalef(5, 4, 1);
glutSolidCube(1.0);
glPopMatrix();
//桌腿1
glPushMatrix();
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse2);//正反面的镜面反射颜色
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse2);//正反面的漫反射颜色
//glMaterialfv(GL_BACK, GL_SPECULAR, diffuse2);//正反面的镜面反射颜色
//glMaterialfv(GL_BACK, GL_DIFFUSE, diffuse2);//正反面的漫反射颜色
glTranslatef(1.5, 1, 1.5);
Draw_Leg();
glPopMatrix();
//桌腿2
glPushMatrix();
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse3);//正反面的镜面反射颜色
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse3);//正反面的漫反射颜色
glTranslatef(-1.5, 1, 1.5);
Draw_Leg();
glPopMatrix();
//桌腿3
glPushMatrix();
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse4);//正反面的镜面反射颜色
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse4);//正反面的漫反射颜色
glTranslatef(1.5, -1, 1.5);
Draw_Leg();
glPopMatrix();
//桌腿4
glPushMatrix();
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse5);//正反面的镜面反射颜色
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse5);//正反面的漫反射颜色
glTranslatef(-1.5, -1, 1.5);
Draw_Leg();
glPopMatrix();
}
void Draw_Leg()
{
glScalef(1, 1, 3);
glutSolidCube(1.0);
}
void updateView(int width, int height)
{
glViewport(0,0,width,height); // Reset The Current Viewport
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
float whRatio = (GLfloat)width/(GLfloat)height;
if (bPersp) {
gluPerspective(45.0f, whRatio,0.1f,100.0f);
//glFrustum(-3, 3, -3, 3, 3,100);
} else {
glOrtho(-3 ,3, -3, 3,-100,100);
}
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
}
void reshape(int width, int height)
{
if (height==0) // Prevent A Divide By Zero By
{
height=1; // Making Height Equal One
}
wHeight = height;
wWidth = width;
updateView(wHeight, wWidth);
}
void idle()
{
glutPostRedisplay();
}
float eye[] = {0, 0, 8};
float center[] = {0, 0, 0};
void key(unsigned char k, int x, int y)
{
switch(k)
{
case 27:
case 'q': {exit(0); break; }
//切换投影模式
case 'p': {bPersp = !bPersp; break; }
//旋转
case ' ': {bAnim = !bAnim; break;}
//切换线框模式
case 'o': {bWire = !bWire; break;}
//整体左移
case 'a': {
eye[0] += 0.2f;
center[0] += 0.2f;
break;
}
//整体右移
case 'd': {
eye[0] -= 0.2f;
center[0] -= 0.2f;
break;
}
//整体上移
case 'w': {
eye[1] -= 0.2f;
center[1] -= 0.2f;
break;
}
//整体下移
case 's': {
eye[1] += 0.2f;
center[1] += 0.2f;
break;
}
//整体前移
case 'z': {
eye[2] -= 0.2f;
center[2] -= 0.2f;
break;
}
//整体后移
case 'c': {
eye[2] += 0.2f;
center[2] += 0.2f;
break;
}
//切换光照颜色,白色or紫色
case 'l': {
isWhite = !isWhite;
break;
}
//移动控制,移动环境光or聚光灯
case '5': {
isLight0 = !isLight0;
break;
}
//移动控制,改变聚光灯角度or位置
case '0': {
isPos = !isPos;
break;
}
//光照左移
case '4': {
if (isLight0) //移动环境光
{
light0_pos[0] -= 0.2f;
}
else //移动聚光灯
{
if (isPos) {
light1_pos[0] -= 0.2f;
}
else
{
light_dir[0] -= 0.05f;
}
}
break;
}
//光照右移
case '6': {
if (isLight0) //移动环境光
{
light0_pos[0] += 0.2f;
}
else //移动聚光灯
{
if (isPos) {
light1_pos[0] += 0.2f;
}
else
{
light_dir[0] += 0.05f;
}
}
break;
}
//光照上移
case '8': {
if (isLight0) //移动环境光
{
light0_pos[1] += 0.2f;
}
else //移动聚光灯
{
if (isPos) {
light1_pos[1] += 0.2f;
}
else
{
light_dir[1] += 0.05f;
}
}
break;
}
//光照下移
case '2': {
if (isLight0) //移动环境光
{
light0_pos[1] -= 0.2f;
}
else //移动聚光灯
{
if (isPos) {
light1_pos[1] -= 0.2f;
}
else
{
light_dir[1] -= 0.05f;
}
}
break;
}
//光照前移
case '1': {
if (isLight0) //移动环境光
{
light0_pos[2] += 0.2f;
}
else //移动聚光灯
{
if (isPos) {
light1_pos[2] += 0.2f;
}
else
{
light_dir[2] += 0.05f;
}
}
break;
}
//光照后移
case '3': {
if (isLight0) //移动环境光
{
light0_pos[2] -= 0.2f;
}
else //移动聚光灯
{
if (isPos) {
light1_pos[2] -= 0.2f;
}
else
{
light_dir[2] -= 0.05f;
}
}
break;
}
//增大聚光灯角度
case '+': {
if (angle <= 89.8) {
angle += 0.2f;
}
break;
}
//减小聚光灯角度
case '-': {
if (angle >= 0.2) {
angle -= 0.2f;
}
break;
}
}
updateView(wHeight, wWidth);
}
void redraw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); // Reset The Current Modelview Matrix
gluLookAt(eye[0], eye[1], eye[2],
center[0], center[1], center[2],
0, 1, 0); // 场景(0,0,0)的视点中心 (0,5,50),Y轴向上
if (bWire) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
else {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
/* 点光源 */
glLightfv(GL_LIGHT0, GL_POSITION, light0_pos);//位置
//环境光可在白光和紫光之间切换
if (isWhite) {
glLightfv(GL_LIGHT0, GL_AMBIENT, white);//环境光属性
}
else {
glLightfv(GL_LIGHT0, GL_AMBIENT, purple);//环境光属性
}
glLightfv(GL_LIGHT0, GL_DIFFUSE, white);//漫反射属性
glLightfv(GL_LIGHT0, GL_SPECULAR, white);//镜面反射属性
glEnable(GL_LIGHT0);
/* 聚光灯 */
glLightfv(GL_LIGHT1, GL_POSITION, light1_pos);//位置
if (isWhite) {
glLightfv(GL_LIGHT1, GL_AMBIENT, white);//环境光属性
}
else {
glLightfv(GL_LIGHT1, GL_AMBIENT, purple);//环境光属性
}
glLightfv(GL_LIGHT1, GL_DIFFUSE, white);//漫反射属性
glLightfv(GL_LIGHT1, GL_SPECULAR, white);//镜面反射属性
glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, angle);//设置聚光灯角度
glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light_dir);//设置聚光灯方向
glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, 2);//设置聚光灯聚集度
glEnable(GL_LIGHT1);
// glTranslatef(0.0f, 0.0f,-6.0f); // Place the triangle at Center
glRotatef(fRotate, 0, 1.0f, 0); // Rotate around Y axis
glRotatef(-90, 1, 0, 0);
glScalef(0.2, 0.2, 0.2);
Draw_Triangle(); // Draw triangle
if (bAnim) fRotate += 0.5f;
glutSwapBuffers();
}
int main (int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
glutInitWindowSize(480,480);
int windowHandle = glutCreateWindow("Simple GLUT App");
glutDisplayFunc(redraw);
glutReshapeFunc(reshape);
glutKeyboardFunc(key);
glutIdleFunc(idle);
glutMainLoop();
return 0;
}