OpenGL绘制光照和材质效果 |
您所在的位置:网站首页 › m=0的轨道是实函数可以描述几何图形 › OpenGL绘制光照和材质效果 |
OpenGL绘制光照和材质效果
本次任务主要实践三维空间的平移旋转、透视投影、光照及材质。有关阴影效果,课程的本节内容仅讲述了手动操作模型矩阵来绘制的阴影平面,而主流的阴影绘制方法是利用纹理和贴图来渲染阴影。 1. 将三个物体同轴排列前面几个小节已经绘制了平面五角星、三维彩色立方体以及递归细分四面体法的三维球体,本节的要求首先是将三个物体同轴排列,使之在视景体中可见并设置透视投影来观察三者。为了更好地观察空间中的物体,本代码中额外绘制了三维坐标架,并绘制出了XOY平面,相关代码如下: void frame() { // 绘制三维坐标架 glColor3f(0.3f, 0.3f, 0.3f); glLineWidth(0.75); glBegin(GL_LINES); glVertex3f(-100.0f, 0.0f, 0.0f); glVertex3f(100.0f, 00.0f, 0.0f); glVertex3f(0.0f, -100.0f, 0.0f); glVertex3f(0.0f, 100.0f, 0.0f); glVertex3f(0.0f, 0.0f, -100.0f); glVertex3f(0.0f, 0.0f, 100.0f); glEnd(); // 绘制XOY平面 glColor4f(0.8f, 0.8f, 0.8f, 0.5f); glNormal3f(0.0f, 0.0f, 1.0f); glBegin(GL_QUADS); glVertex3f(8.0f, 8.0f, 0.0f); glVertex3f(-8.0f, 8.0f, 0.0f); glVertex3f(-8.0f, -8.0f, 0.0f); glVertex3f(8.0f, -8.0f, 0.0f); glEnd(); }绘制好坐标架后,三个物品依次排列时就可以清晰地看出其空间关系。三个物体绘制的具体代码不再赘述,以下是display函数中将三个物体依次排列的相关代码: void display() { // 设置背景为白色 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 加载单位阵 glLoadIdentity(); // 设置相机的位置和视角 gluLookAt(7, 4, 4, 0.0, 0.0, 0.0, 0, 0, 1); // 绘制坐标架以及XOY平面 frame(); // 平移坐标系,让三个待绘制物品的底部排列在X轴上 glTranslatef(0.0f, 0.0f, 1.0f); // 在中心绘制三维彩色立方体,并绘制简单阴影 glTranslatef(-0.75f, -0.75f, -0.75f); color_cube(1.5f); cube_shadow(0.0f, 0.0f, 6.0f, 1.5f); glTranslatef(0.75f, 0.75f, 0.75f); // 在左侧(Y轴负半轴)绘制红色球面 glTranslatef(0.0f, -4.0f, 0.0f); sphere(); glTranslatef(0.0f, 4.0f, 0.0f); // 在右侧(Y轴正半轴)绘制平面的五角星 glTranslatef(0.0f, 4.0f, 0.0f); pentagram(); glTranslatef(0.0f, -4.0f, 0.0f); // 刷新帧缓存 glutSwapBuffers(); }在不添加光照和阴影效果时,三个物品依次排列绘制出的效果如下: 可以看出,我们将三个物体的中心排列在同一高度,但由于使用了透视投影,靠右侧的五角星在视觉上要比靠左侧的球体更大,但实际上五角星的外接圆半径与球体半径是相同的,均为1。但由于没有使用光照效果,球体看上去只是一个圆(透视投影下被拉成椭圆)。 2. 光照、材质与简单阴影效果光照和材质效果可以在init()函数中设置,此外在使用光照时,需要在绘制图像过程中增加每一点法向量的设置。指定法线向量的方式与指定颜色的方式有雷同之处。在指定颜色时,只需要指定每一个顶点的颜色,OpenGL就可以自行计算顶点之间的其它点的颜色。并且,颜色一旦被指定,除非再指定新的颜色,否则以后指定的所有顶点都将以这一向量作为自己的颜色。在指定法线向量时,只需要指定每一个顶点的法线向量,OpenGL会自行计算顶点之间的其它点的法线向量。并且,法线向量一旦被指定,除非再指定新的法线向量,否则以后指定的所有顶点都将以这一向量作为自己的法线向量。使用glColor*()函数可以指定颜色,而使用glNormal*()函数则可以指定法线向量。 在OpenGL中,仅仅支持有限数量的光源。使用GL_LIGHT0表示第0号光源,GL_LIGHT1表示第1号光源,依次类推。OpenGL至少会支持8个光源,即GL_LIGHT0到GL_LIGHT7。每一个光源都可以设置其属性,这一动作是通过glLight*()函数完成的。glLight*()函数具有三个参数,第一个参数指明是设置哪一个光源的属性,第二个参数指明是设置该光源的哪一个属性,第三个参数则是指明把该属性值设置成多少。GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR这三个属性表示了光源所发出的光的反射特性(以及颜色),每个属性由四个值表示,分别代表了颜色的R, G, B, A值。GL_AMBIENT表示该光源所发出的光,经过非常多次的反射后,最终遗留在整个光照环境中的强度(颜色)。GL_DIFFUSE表示该光源所发出的光,照射到粗糙表面时经过漫反射,所得到的光的强度(颜色)GL_SPECULAR表示该光源所发出的光,照射到光滑表面时经过镜面反射,所得到的光的强度(颜色)。GL_POSITION属性表示光源所在的位置。由四个值(X, Y, Z, W)表示。如果第四个值W为零,则表示该光源位于无限远处,前三个值表示了它所在的方向。这种光源称为方向性光源,通常,太阳可以近似的被认为是方向性光源。如果第四个值W不为零,则X/W, Y/W, Z/W表示了光源的位置。这种光源称为位置性光源。对于位置性光源,设置其位置与设置多边形顶点的方式相似,各种矩阵变换函数例如:glTranslate*()、glRotate*()等在这里也同样有效。方向性光源在计算时比位置性光源快了不少,因此,在视觉效果允许的情况下,应该尽可能的使用方向性光源。 材质与光源相似,也需要设置众多的属性。不同的是,光源是通过glLight*()函数来设置的,而材质则是通过glMaterial*()函数来设置的。glMaterial*()函数有三个参数。第一个参数表示指定哪一面的属性。可以是GL_FRONT、GL_BACK或者GL_FRONT_AND_BACK。分别表示设置“正面”“背面”的材质,或者两面同时设置。第二、第三个参数与glLight*函数的第二、三个参数作用类似。GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR这三个属性与光源的三个对应属性类似,每一属性都由四个值组成。GL_SHININESS属性只有一个值,称为“镜面指数”,取值范围是0到128。该值越小,表示材质越粗糙,点光源发射的光线照射到上面,也可以产生较大的亮点。该值越大,表示材质越类似于镜面,光源照射到上面后,产生较小的亮点。GL_EMISSION属性由四个值组成,表示一种颜色。OpenGL认为该材质本身就微微的向外发射光线,以至于眼睛感觉到它有这样的颜色,但这光线又比较微弱,以至于不会影响到其它物体的颜色。 void init() { // 计算五角星有关数据 get_pentagram(); // 设置逆时针排列的点围成的平面为正面 glFrontFace(GL_CCW); // 设置不绘制背面,节省算力同时不会出现背面覆盖正面的情况 glCullFace(GL_BACK); glEnable(GL_CULL_FACE); // 启用抗锯齿(使线平滑) glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 设置材质和光照的信息 // 有关光照与材质:https://blog.csdn.net/timidsmile/article/details/7017197 GLfloat mat_ambient[4] = { 1.0f, 1.0f, 1.0f, 0.0f }; GLfloat mat_diffuse[4] = { 1.0f, 1.0f, 1.0f, 0.0f }; GLfloat mat_specular[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat mat_shininess[4] = { 100.0f }; GLfloat light_ambient[4] = { 0.2f, 0.2f, 0.2f, 0.0f }; GLfloat light_diffuse[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat light_specular[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat light_position[4] = { 0.0f, 6.0f, 0.0f, 1.0f }; // 设置正向面的材质和光源的光照 glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); glLightfv(GL_LIGHT0, GL_POSITION, light_position); // 设置颜色材料,使光照模式下仍然可以显示原本的颜色 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); glEnable(GL_COLOR_MATERIAL); // 启用平滑着色功能 glShadeModel(GL_SMOOTH); // 启用光照功能 glEnable(GL_LIGHTING); // 启用0号光源 glEnable(GL_LIGHT0); // 启用检测深度 glEnable(GL_DEPTH_TEST); // 环境颜色设置为白色 glClearColor(1.0, 1.0, 1.0, 1.0); }可以看出,在(0, 0, 6)处设置一点光源,从上方照射三个物体,物体的侧面由于受到光照较少,会产生出阴影的质感。下面是变换光源位置以及光源三种属性后的不同效果。为了更好地展现光照效果,此处将环境颜色设置成深色。 从Z轴负向,即底部向上照射三个物体: 从顶部向下照射三个物体,但去除了环境光,只保留漫反射和镜面反射: 光照之后的任务要求即为绘制阴影。阴影是一种高级光照渲染,会为物体进一步增加立体感。阴影一般由深度贴图渲染实现,本节的课件中介绍了一种简单的绘制阴影的方法,即手动操作变换矩阵,将原物品直接映射到一个平面上。这种方法需要绘制原物品两次,并且需要手动计算阴影变换矩阵,不太灵活,代码耦合性较高且手动计算复杂。下面仅实现了立方体的阴影: 添加阴影后效果如下,但可以看出阴影也是绘制出的多面体,同样也要受到全局光照的影响,其原本的颜色在光照下变亮,显然不符合阴影的效果。如果要修改,需要改动多面体本身的绘制代码,单独添加与光照有关的设置,非常麻烦,本代码中没有涉及,仅仅实践了简单的阴影绘制方法而已: |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |