计算机图形学·19 Shadings in OpenGL

本文是记录专业课计算机图形学的部分笔记,参考教材为Angel的第八版交互式计算机图形学------基于WebGL 2.0的自顶向下方法。

1、OpenGL使用改进的Phong光照模型(Blinn光照模型)对每个顶点计算颜色,考虑表面材料属性、光源属性、光照模型属性这三个影响光照的因素。具体的,在OpenGL中应用明暗处理的步骤为①启用明暗处理功能,并选择模式;②指定法向量;③指定材料属性;④指定光源。

2、在OpenGL中法向量是状态的一部分,相当于指定glcoclor。一般使用glNormal*()和glNormal*()设置,比如glNormal3f(x, y, z); glNormal3fv(p); 。一般来讲,我们想让法向量为单位向量,这样余弦计算就非常直接(注意放缩并不保持其长度),glEnable(GL_NORMALIZE) 可以使OpenGL自动进行单位化,当然是以损失效率为代价。

3、OpenGL的光照模型参数包括4个部分:①全局环境光强度;②观察点位于场景、中近处还是无限远处;③物体的正反面是否执行不同的光照计算;④镜面光颜色分量合成方式,镜面光颜色是否从环境光和漫反射颜色中分离出来,并在纹理操作后再应用。比如:

复制代码
void glLightModel {if} (GLenum pname, TYPE param);
void glLightModel {if}v (GLenum pname, TYPE * param);

两个参数:第一个表示要设置的项目,第二个参数表示要设置成的值。

4、接下来我们具体看glLightModel ()中的参数。

①GL_LIGHT_MODEL_LOCAL_VIEWER 表示是否在计算中应用无穷远视点的假设来简化计算。(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_FALSE); 是缺省值,在计算中应用无穷远视点的假设简化计算,如果是GL_TRUE则要使用近视点(将视点放置在(0,0,0)处,实际并不能移动视点,只是效果更真实,因为视点位置/方向能影响高光计算)。

②GL_LIGHT_MODEL_TWO_SIDED 表示单面还是双面光照处理。(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); 是缺省值,单独对多边形的外面进行明暗处理,如果是GL_TRUE则启用双面光照。

③GL_LIGHT_MODEL_AMBIENT设置整个场景中的全局环境光强,与0-7个光源没有关系。(GL_LIGHT_MODEL_AMBIENT, (0.2,0.2,0.2,1.0) ); 是缺省值,这些数值产生白色环境光。

④GL_LIGHT_MODEL_COLOR_CONTROL表示颜色计算方式,镜面光是否与漫反射和环境光分开计算。(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR); 是缺省值,表示按通常顺序操作,先计算所有光照包括镜面光、漫反射和环境光,再计算纹理。如果是GL_SEPATATE_SPECULAR_COLOR表示将GL_SPECULAR属性分离出来,先计算光照的漫反射和环境光部分,待纹理操作完成后再计算镜面光。可以使画面效果更为逼真,不过如果本身没有任何纹理操作,分离就没有任何意义。

5、明暗处理计算由下述命令启用glEnable(GL_LIGHTING),如果光照被激活,glColor()命令将被忽略。此外,还必须单独激活每个光源,最多同时使用八个光源(GL_LIGHT0, GL_LIGHT1, ... , or GL_LIGHT7):

而对OpenGL中的每个光源,都有环境光、漫射光和镜面光项,相关参数有:光源颜色项(GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR)、位置/方向(GL_POSITION)、衰减项(GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION, GL_QUADRATIC_ATTENUATION)、聚光灯参数(GL_SPOT_CUTOFF, GL_SPOT_DIRECTION, GL_SPOT_EXPONENT)。


值得注意的是,GL_LIGHT0参数缺省值不同于其他光源,其缺省值为GL_DIFFUSE (1.0,1.0,1.0,1.0) 白光、GL_SPECULAR (1.0,1.0,1.0,1.0) 白光。而其他光源光源的缺省值为GL_DIFFUSE (0.0,0.0,0.0,1.0) 黑色、GL_SPECULAR (0.0,0.0,0.0,1.0) 黑色

6、OpenGL中光照的位置是以齐次坐标的形式给定的,如果w = 1.0, 指定的是一个有限位置(点);如果w = 0.0, 指定的是一个平行光源,所给定的是入射光方向,其­缺省为(0.0, 0.0, 1.0, 0.0)有限位置。不过需要注意,光强反比于距离的因子(a + bd + cd2),其默认值为a=1.0、b=c=0.0,改变方法如:

复制代码
glLightf(GL_LIGHT0,GLCONSTANT_ATTENUATION, 0.8);
glLightf(GL_LIGHT0,GL_LINEAR_ATTENUATION, 1.0);
glLightf(GL_LIGHT0,GL_QUADRATIC_ATTENUATION, 0.0);

7、①对全局环境光,环境光依赖于每个光源,比如在白屋中的红灯会使生成红色环境光,当灯被关闭后这种成分就消失。OpenGL中也可以定义一个对测试非常有用的全局环境光(与0-7个光源没有关系):

复制代码
GLfloat global_ambient[]={0.2, 0, 0, 1}; // 少量红光
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);

②对聚光灯:

③而对移动光源,光源是几何对象,它的位置或方向受模型视图矩阵的影响。把光源的位置和方向设置函数放置在不同的地方,可以达到不同的效果:

(1)固定光源,移动对象:初始化光源位置,设置变换后,再定义对象

(2)和对象一起移动光源:所有的变换设置在光源位置和对象定义之前调用

(3)固定对象,移动光源:先定义对象,设置变换后,再定义光源位置

(4)分别移动光源和对象:采用矩阵堆栈

8、来看下面的例子。①如果先给出gluLookAt变换矩阵, 再定义光源位置,那么相当于光跟着地走。

②而如果先定义光源位置,再给出gluLookAt变换矩阵,那么光跟着人走。

③如果想要光源与圆环体对象分开移动,并单独移动光源,则代码:

复制代码
static GLdouble spin;
void display(void){
    GLfloat light_pos[]={0.0,0.0,1.5,1.0};
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
          gluLookAt(0.0,0.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0);
          glPushMatrix();
                 glRotated(spin,1.0,0.0,0.0);  //旋转光源
                 glLightfv(GL_LIGHT0,GL_POSITION,light_pos);
          glPopMatrix();
          glutSolidTorus(0.275,0.85,8,15);
     glPopMatrix();
     glFlush();

}

9、此外,材料属性也是OpenGL状态的一部分,与简单光照模型中的各项是匹配的。glMaterialv() 可以设置材料属性的函数:

复制代码
void glMaterial{if}(GLenum face, GLenum pname, TYPE param);
void glMaterial{if}v(GLenum face, GLenum pname, TYPE *param);

有GL_FRONT, GL_BACK, GL_FRONT_AND_BACK这几个face,参数则有:

GL_AMBIENT,缺省为(0.2, 0.2, 0.2, 1.0) 缺省考量 真实感

GL_DIFFUSE,缺省为(0.8, 0.8, 0.8, 1.0)

GL_SPECULAR,缺省为(0.0, 0.0, 0.0, 1.0)

GL_SHININESS,缺省为0.0, 唯一的非向量参数, [0.0,128.0]

GL_EMISSION,缺省为(0.0, 0.0, 0.0, 1.0),这项表示自发光(本身不是光源)

复制代码
GLfloat ambient[] = {0.2, 0.2, 0.2, 1.0};  //RGBA模式
GLfloat diffuse[] = {1.0, 0.8, 0.0, 1.0};
GLfloat specular[] = {1.0, 1.0, 1.0, 1.0};
GLfloat shine = 100.0  //镜面指数
glMaterialf (GL_FRONT, GL_AMBIENT, ambient);
glMaterialf (GL_FRONT, GL_DIFFUSE, diffuse);
glMaterialf (GL_FRONT, GL_SPECULAR, specular);
glMaterialf (GL_FRONT, GL_SHININESS, shine);

10、默认状态下只把对象的前面(几何定义好)进行明暗处理,对于凸对象这种处理结果是正确的。如果设置进行两面光照,那么OpenGL就会对曲面的双面进行明暗处理(每一面都可以具有自己的绘制属性,用glMaterialf分别指定)。

11、GL_EMISSION是特殊的参数(发射光项),在OpenGL中可以用材料的该项来模拟一个光源(本身不是光源)。该项的颜色不受任何其它光源或者变换的影响,自发光但不是光源,是材料性质。

12、通常当启用光照进行明暗处理后,原来的glColor*()命令失去原有的作用。如果调用了glEnable(GL_COLOR_MATERIAL),那么就会使光照模型中的某几种光根据glColor*()中的指定,而不是计算来确定颜色。这可以用来专门改变材质某种光照下的颜色属性分量:

void glColorMaterial(GLenum face, GLenum mode); 其中face的取值有 GL_FRONT、GL_BACK 与GL_FRONT_AND_BACK (默认值),mode的取值有 GL_EMISSION, GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR 与 GL_AMBIENT_AND_DIFFUSE (默认值)。例如代码:

复制代码
glEnable(GL_COLOR_MATERIAL);   // 启动阶段
glColorMaterial(GL_FRONT, GL_DIFFUSE);

/* now glColor* changes diffuse reflection */
glColor3f(0.2, 0.5, 0.8);
/* draw some objects here */
glColorMaterial(GL_FRONT, GL_SPECULAR);
/* glColor* no longer changes diffuse reflection */
/* now glColor* changes specular reflection */
glColor3f(0.9, 0.0, 0.2);
/* draw other objects here */

glDisable(GL_COLOR_MATERIAL);  //不需要使用时,确保禁用!

13、由于材料属性为状态的一部分,因此如果对许多表面采用大量的不同材料,那么系统的性能就会大打折扣。因此由于使用glColorMaterial()时不用计算得到材质的颜色分量,这种方法比glMaterial更加有效。在编程时,可以通过定义一个材料结构,利用它在初始化时设置所有材料,从而净化代码,并支持通过指针选择材料,比如:

复制代码
typedef struct materialStruct {
   GLfloat ambient[4];
   GLfloat diffuse[4];
   GLfloat specular[4];
   GLfloat shineness;
} MaterialStruct;

14、另外,对多边形着色,OpenGL对每个顶点进行明暗处理的计算,顶点的颜色变为顶点的明暗效果。默认状态下(glShadeModel(GL_SMOOTH);),多边形内部的颜色是顶点颜色的线性插值。如果调用了 glShadeModel(GL_FLAT); 那么第一个顶点的颜色确定整个多边形的颜色。

相关推荐
陈奕昆1 小时前
n8n实战营Day2:复杂逻辑控制·HTTP请求+条件分支节点实操
网络·人工智能·python·网络协议·n8n
丝斯20111 小时前
AI学习笔记整理(22)—— AI核心技术(深度学习6)
人工智能·笔记·学习
liushangzaibeijing1 小时前
用 bert-base-chinese 做一个能上线的 AI 应用
人工智能·bert-base
初夏睡觉1 小时前
全排列题解
算法·深度优先·图论
依米s2 小时前
2021年人工智能大会核心议题《智联世界 众智成城》
人工智能·waic·人工智能大会+
数字冰雹2 小时前
数据中心运维新革命:图观数字孪生引擎的实战应用
人工智能·数据可视化
i***58672 小时前
Java开发的AI应用框架简述——LangChain4j、Spring AI、Agent-Flex
java·人工智能·spring
在下赵某人2 小时前
概率数据结构的设计原理与误差分析
数据结构·算法·哈希算法
CoderYanger2 小时前
递归、搜索与回溯-综合练习:19.目标和
java·算法·leetcode·1024程序员节