OpenGL 实现简单的光照效果

功能要求

  1. 显示默认的 Teapot 模型
  2. 为 Teapot 模型建立 Smooth Shading 效果。

实现提示

  1. 利用 OpenGL 的 API 初始化 material property, light source, lighting model, depth buffer 等信息。
  2. 使用 OpenGL Shader 实现。

开发环境

硬件

所用机器型号为 VAIO Z Flip 2016

  • Intel(R) Core(TM) i7-6567U CPU @3.30GHZ 3.31GHz
  • 8.00GB RAM

软件

  • Windows 10, 64-bit (Build 17763) 10.0.17763
  • Visual Studio Code 1.39.2
    • Remote - WSL 0.39.9:配合 WSL,在 Windows 上获得 Linux 接近原生环境的体验。
  • Windows Subsystem for Linux [Ubuntu 18.04.2 LTS]:WSL 是以软件的形式运行在 Windows 下的 Linux 子系统,是近些年微软推出来的新工具,可以在 Windows 系统上原生运行 Linux。
    • gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)

实验原理

为在场景中增加光照,需要执行以下步骤:

设置光源

光源种类

  1. 环境光:环境光是一种无处不在的光。环境光源放出的光线被认为来自任何方向。因此,当你仅为场景指定环境光时,所有的物体无论法向量如何,都将表现为同样的明暗程度。
  2. 点光源:由这种光源放出的光线来自同一点,且方向辐射自四面八方。
  3. 平行光:平行光又称镜面光,这种光线是互相平行的。从手电筒、太阳等物体射出的光线都属于平行光。
  4. 聚光灯:这种光源的光线从一个锥体中射出,在被照射的物体上产生聚光的效果。使用这种光源需要指定光的射出方向以及锥体的顶角$\alpha$。

光源成分

对于每一种光源,都有漫射光和平行光两种成分。在 OpenGL 中,环境光也被作为一种特殊的光源的成分来看待。漫射光是指在光源中能够被漫反射的光的颜色成分(白色则包含所有颜色),而平行光是指光源中所有能够被镜面反射的光的颜色成分。通过指定这两种成分的颜色,就能决定光源是平行光源还是点光源。

OpenGL 可以同时为我们提供 8 个有效的光源。也就是说,我们最多可以同时启用 8 个光源。它们分别是GL_LIGHT0GL_LIGHT1GL_LIGHT2……其中,GL_LIGHT0是最特殊的一个光源。我们可以为GL_LIGHT0指定环境光成分。

光源位置

对于点光源和平行光源,我们常常需要指定光源的位置来产生需要的效果。方法是调用glLightfv函数。

光照模型

OpenGL 的光照模型是用来模拟现实生活中的光照的。

材质设定

材质颜色

OpenGL 用材料对光的红、绿、蓝三原色的反射率来近似定义材料的颜色。象光源一样,材料颜色也分成环境、漫反射和镜面反射成分,它们决定了材料对环境光、漫反射光和镜面反射光的反射程度。在进行光照计算时,材料对环境光的反射率与每个进入光源的环境光结合,对漫反射光的反射率与每个进入光源的漫反射光结合,对镜面光的反射率与每个进入光源的镜面反射光结合。对环境光与漫反射光的反射程度决定了材料的颜色,并且它们很相似。对镜面反射光的反射率通常是白色或灰色(即对镜面反射光中红、绿、蓝的反射率相同)。镜面反射高光最亮的地方将变成具有光源镜面光强度的颜色。例如一个光亮的红色塑料球,球的大部分表现为红色,光亮的高光将是白色的。

材质定义

材质的定义与光源的定义类似。

材质 RGB 值和光源 RGB 值的关系

材质的颜色与光源的颜色有些不同。对于材质,R、G、B 值为材质对光的 R、G、B 成分的反射率。比如,一种材质的R=1.0, G=0.5, B=0.0,则材质反射全部的红色成分,一半的绿色成分,不反射蓝色成分。也就是说,若 OpenGL 的光源颜色为(LR,LG,LB),材质颜色为(MR,MG,MB),那么,在忽略所有其他反射效果的情况下,最终到达眼睛的光的颜色为(LR*MR,LG*MG,LB*MB)。同样,如果有两束光,相应的值分别为(R1,G1,B1)(R2,G2,B2),则 OpenGL 将各个颜色成分相加,得到(R1+R2,G1+G2,B1+B2),若任一成分的和值大于 1(超出了设备所能显示的亮度)则约简到1.0

实现效果

Windows 下运行teapot.exe,或 Linux 下运行teapot.out,得到如下结果。

1

源代码teapot.c

#include <GL/glut.h>
void display()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glutSolidTeapot(1);
	glFlush();
}
void reshape(GLsizei w, GLsizei h)
{
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	if (w <= h)
		glOrtho(-1.5, 1.5, -1.5 * (GLfloat)h / (GLfloat)w, 1.5 * (GLfloat)h / (GLfloat)w, -10.0, 10.0);
	else
		glOrtho(-1.5 * (GLfloat)w / (GLfloat)h, 1.5 * (GLfloat)w / (GLfloat)h, -1.5, 1.5, -10.0, 10.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}
int main(int argc, char **argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(600, 600);
	glutInitWindowPosition(100, 100);
	glutCreateWindow("17341163_WuK_CG_HW6");
	glClearColor(0, 0, 0, 0);
	glShadeModel(GL_SMOOTH);
	GLfloat
		mat_specular[] = {1.0, 1.0, 1.0, 1.0},
		mat_shininess[] = {50.0},
		light_position[] = {1.0, 1.0, 1.0, 0.0},
		white_light[] = {1.0, 1.0, 1.0, 1.0},
		Light_Model_Ambient[] = {0.2, 0.2, 0.2, 1.0};
	glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
	glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, Light_Model_Ambient);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_DEPTH_TEST);
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutMainLoop();
}

编译指令

gcc teapot.c -o teapot.out -lGL -lGLU -lglut
./teapot.out