1、本节实现的内容
上一节课,我们建立了简单的坐标系,同时也显示了一个正方体,但正方体的颜色为纯红色,好像一个平面物体一样,我们这节课就可以加一些光照,并创建更多的模型,使这些物体变得更加立体一些。更多详见https://blog.csdn.net/zhooyu主页内容。
2、光照的认识
在OpenGL中光的种类大致可以分为平行光源、点光源和聚光灯光源三种。
2.1、平行光
从他们的名字也很好理解,平行光是从远方照射过来的光,有点类似咱们现实生活中,太阳从远处照过来的光,可以简单的理解为太阳光的每一条光线都是平行照射过来的。平行光最主要的特点,就是在一个场景中多个物体,平行光折射他们的角度基本保持一致,光照面的方向也就一致。
2.2、点光源
点光源,有点类似于现实生活中的蜡烛🕯,它照射出来的是散射的光,在一个场景中的多个物体,我的位置不同,照射的光面角度也不同,而且会根据与蜡烛的远近距离不同,光线照射的明暗程度也不一样。类似的还有山洞中的火把的效果,人物在黑夜里出行的时候,随着伙伴位置的不停改变,正为物体的光照面也会不停的发生改变,同时火把只能照射到人物周围的物体,离人物距离越近光照效果就越强,离人物距离越远光照强度就越差。
2.3、聚光灯
聚光灯有点类似于我们现实生活中的手电筒,手电筒照出去的是一个范围光,光照出去只能照到一个大致圆环范围内的物体,而且手电筒的光是有方向的,人们可以不断改变手电筒光照射的方向。同时我们手电筒可以调整光圈的大小,像我们旋转手电筒头,可以让光圈放大或者缩小。
3、简单平行光的设置
首先我们可以先加一个平行光,当然,这个光可以有不同的颜色,我们这里暂时模拟太阳光,让它产生一个简单白色光。这里要注意一点,GL_POSITION属性的值(x,y,z,w),w为零表示无限远,x/w,y/w,z/w分别表示光源位置。同时,我们这里使用的是GL_LIGHT0号光源,系统默认定义了GL_LIGHT0至GL_LIGHT7号光源供用户使用。
cpp
//设置并打开自定义光源
void OpenLightExample()
{
//GL_POSITION属性的值(x,y,z,w),w为零表示无限远,x/w,y/w,z/w表示光源位置
GLfloat user_light_position[]={1.0f,1.0f,-1.0f,0.0f};
//GL_AMBIENT光源发出的光,经过非常多次反射,遗留在整个光照环境中的强度(RGBA)
GLfloat user_light_ambient[]={0.2f,0.2f,0.2f,1.0f};
//GL_DIFFUSE光源发出的光,照射到粗糙表面时,经过漫反射所得到的光强度(RGBA)
GLfloat user_light_diffuse[]={1.0f,1.0f,1.0f,1.0f};
//GL_DIFFUSE光源发出的光,照射到光滑表面时,经过镜面反射所得到的光强度(RGBA)
GLfloat user_light_specular[]={1.0f,1.0f,1.0f,1.0f};
//加载设置
glLightfv(GL_LIGHT0,GL_POSITION,user_light_position);
glLightfv(GL_LIGHT0,GL_AMBIENT,user_light_ambient);
glLightfv(GL_LIGHT0,GL_DIFFUSE,user_light_diffuse);
glLightfv(GL_LIGHT0,GL_SPECULAR,user_light_specular);
//开启光源和光照处理功能
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
}
cpp
//关闭光源和光照处理功能
void CloseLightExample()
{
glDisable(GL_LIGHT0);
glDisable(GL_LIGHTING);
}
4、添加平行光
在定义了以上光线操作函数后,我们就可以在任何需要添加光照的地方添加以下代码,由于Opengl是状态机,因此,光照打开后会一直生效,直到关闭相应的光源为止。
cpp
//打开自定义平行光
OpenLightExample();
//在此显示的物体将会收到光照影响
......
//关闭自定义平行光源
CloseLightExample();
当我们添加我们太阳光后,发现我们的立方体并没有产生背光照的效果,我们的立体模型都变成了灰白色,这主要是我们没有设定物体的材质。就好比我们告诉计算机现在有了光线,但是没有告诉计算机你现在显示的立方体对光照的效果怎样,就是说你的立方体是一个金属反光材质,还是一个毛绒玩具的不反光材质?如果反光材质,它的反光程度有多少,是闪眼睛的那种反光,还是只有轻微反光的效果。
5、材质问题简单的解决办法
我们这里鉴于让大家能很容易的上手,就先暂时不研究具体的材质问题(嗯,复杂的材质设置会给我们带来更多的光照效果,功能非常强大,我们后期再根据需要进行阐述),我们这里就力求用最简单的语言,最容易理解的方式,去了解整个游戏程序设计的思路,需要更多还可以了解我的主页https://blog.csdn.net/zhooyu。
cpp
//默认打开颜色材质
glEnable(GL_COLOR_MATERIAL);
//添加显示3D场景物体,如立方体等
......
//默认打开颜色材质
glDisable(GL_COLOR_MATERIAL);
至此,我们的立方体显示出了应该有的光照效果,立方体各个平面之间产生了比较分明的棱线,圆也出现了渐变的关照效果,特别是正二十面体的不同面效果更漂亮。当然,我们这里并没有介绍法线的概念,光照和法线离不开,但是系统自带的以上模型已经自带法线设置,我们后期遇到具体问题在描述法线的设置和作用。
我们已经多添加了几个系统自带图形查看光照效果,包括一个圆球体、正二十面体和环状体,这样光照效果就好了很多。以下补充其他立体模型的生成代码:
cpp
//显示球体
glPushMatrix();
glTranslatef(-5,5,0);
glColor3f(0.0f,0.0f,1.0f);
glutSolidSphere(2.0f,30.0f,30.0f);
glPopMatrix();
//显示圆环体
glPushMatrix();
glTranslatef(-3,2,5);
glRotatef(90,1.0f,0.0f,0.0f);
glColor3f(1.0f,0.0f,1.0f);
glutSolidTorus(0.5f,1.0f,30.0f,30.0f);
glPopMatrix();
//显示二十面体
glPushMatrix();
glTranslatef(0,2,0);
glColor3f(0.0f,0.6f,1.0f);
glutSolidIcosahedron();
glPopMatrix();
6、长方体地面的添加
我们显示了以上物体后,总是没有脚踏实地的,虽然现在有了三维的坐标参照,但是总感觉脚下悬空的。我们还是去创建一个地面,这样可以感觉心里踏实,这里由于没有系统自带的长方体,要自定义创建一个长方体。当然,我们现在加的只是一个感官上的地面,后期还要添加玩家在地面上的碰撞检查,我落在地面上,可以顺着地形的不同不断改变能力的高度,遇到障碍物我无法通过,如果遇到了峡谷,人物也可以自由落体势掉落到峡谷底部,但不会掉落无尽深渊的效果。更多详见https://blog.csdn.net/zhooyu主页内容。
cpp
//显示地面
if(true)
{
//长方体的八个顶点坐标
int vertices[8][4]={
{-10,-1,-10},{-10,-1,+10},{+10,-1,+10},{+10,-1,-10},
{-10,-5,-10},{-10,-5,+10},{+10,-5,+10},{+10,-5,-10},
};
//长方体六个面上顶点显示顺序
int indices[6][4]={{3,2,1,0},{0,1,5,4},{1,2,6,5},{2,3,7,6},{3,0,4,7},{4,5,6,7}};
//逐个显示面长方体六个面
for(int i=0;i<6;i++)
{
//设置各个面的颜色
if(i==0)
{
glColor3f(0.0f,1.0f,0.0f);
}
else
{
glColor3f(0.8f,0.5f,0.0f);
}
//显示各个面
glBegin(GL_QUADS);
//显示各个顶点
for(int j=0;j<4;j++)
{
glVertex3f(vertices[indices[i][j]][0],vertices[indices[i][j]][1],vertices[indices[i][j]][2]);
}
glEnd();
}
}
这里我们需要显示一个底面积很大,高度不高的长方体,并放置到我们大致脚下的位置。我们先不用VAO和VBO的高级方式,这些高级操作方式可以更有效的操作GPU显存,对我们后期的性能非常重要,随后再详细说明,我们现在用最简单的方式显示。这更多的是为了方便大家查看。
我们的游戏画面稍微不那么单调,但是我们还需要添加后期的纹理,物体材质,加载上下起伏但平滑的地图模型,加载3D建模制作各种物体模型,操作界面的各种按键、窗口控件,来丰富我们的游戏内容。