如果这篇博客帮助到你,可以请我喝一杯咖啡~
CC BY 4.0 (除特别声明或转载文章外)
功能要求
- 显示默认的 Teapot 模型
- 为 Teapot 模型建立 Smooth Shading 效果。
实现提示
- 利用 OpenGL 的 API 初始化 material property, light source, lighting model, depth buffer 等信息。
- 使用 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)
实验原理
为在场景中增加光照,需要执行以下步骤:
设置光源
光源种类
- 环境光:环境光是一种无处不在的光。环境光源放出的光线被认为来自任何方向。因此,当你仅为场景指定环境光时,所有的物体无论法向量如何,都将表现为同样的明暗程度。
- 点光源:由这种光源放出的光线来自同一点,且方向辐射自四面八方。
- 平行光:平行光又称镜面光,这种光线是互相平行的。从手电筒、太阳等物体射出的光线都属于平行光。
- 聚光灯:这种光源的光线从一个锥体中射出,在被照射的物体上产生聚光的效果。使用这种光源需要指定光的射出方向以及锥体的顶角$\alpha$。
光源成分
对于每一种光源,都有漫射光和平行光两种成分。在 OpenGL 中,环境光也被作为一种特殊的光源的成分来看待。漫射光是指在光源中能够被漫反射的光的颜色成分(白色则包含所有颜色),而平行光是指光源中所有能够被镜面反射的光的颜色成分。通过指定这两种成分的颜色,就能决定光源是平行光源还是点光源。
OpenGL 可以同时为我们提供 8 个有效的光源。也就是说,我们最多可以同时启用 8 个光源。它们分别是GL_LIGHT0
,GL_LIGHT1
,GL_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
,得到如下结果。
源代码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