win32编程:Win32 OpenGL编程(11) 光照

  最近跟风看了思维导图用XMind为此节画了个思维导图事实上感觉这种书说起来不定完全没有用但是为了显示有用似乎说太过夸张了基本上我认可图胜千言吧另外XMind还算比较好用特别是上传然后共享方式比较方便:)基础版还开源

Win32 OpenGL编程(11) 光照

    图片看不清楚?请点击这里查看原图(大图)

  上面这种图用于解释概念就略显简单但是用于梳理脉络复习时回忆概念还算是比较有用

  上帝说要有光就有了光---- 旧约•创世纪

  使用OpenGL时员可以暂时充当下上帝角色(其实犯了妄自称上帝戒律)你对电脑说要有光于是显示时候就有了光呵呵当然时候需要用电脑语言

  相对来说光照在OpenGL中是属于那种概念理解比较简单但是使用却比较复杂功能的光照属于比较容易影响显示效果原因所以光照使用方法太多导致可以使用定制手段相当多这也就加深了使用复杂度基本上遵循理解核心概念需要时再去查文档理解每个具体参数使用和意义是种不错学习方式当然要想精通恐怕对各种光照应用情况都得有个了解才行上述思维导图就是核心概念个简要列表以下以此图展开来讨论

  为啥需要光

  上帝为啥创造光?上帝没有光看不见东西吗?或者上帝认为黑暗是不好不想呆在黑暗的中只有上帝知道

  那我们为啥需要光呢?我们世界有上帝创造而OpenGL本质上是对现实世界种建模既然现实世界有太阳有光那么模拟现实世界显示OpenGL如何能没有光呢?(以上纯属无聊废话)

  以前画3D图形时候我有意将物体每个面都设置成区别颜色(比如以前那个 3角锥)这样我们才能够较为清楚看出来这是个3D图形为什么会这样说呢?那我们先看看事实上假如我没有那样做我们看到是啥这里以Win32 OpenGL编程(6) 踏入3D世界glSmoothColorPyramid例为修改蓝本(原来源代码见\2009-10-21\glSmoothColorPyramid\)将其4面颜色全部设置为红色

  即将其源代码中表示颜色改为如下形式:

 GLfloat fPyramidColors = { 1.0, 0.0, 0.0,
    1.0, 0.0, 0.0,
    1.0, 0.0, 0.0,
    1.0, 0.0, 0.0};


  我们看看效果

Win32 OpenGL编程(11) 光照

    图片看不清楚?请点击这里查看原图(大图)

  事实上即便是整个 3角锥正以3D方式不停旋转我们看到效果也类似于张 3角形红纸片不停晃悠而已3D效果荡然无存很显然这是有问题不能说个纯色物体3D效果会受到影响吧?这算是我们需要光最简单层了个 3角锥还好说我们看看个球此例利用了glut具体画实心体以后不再全部利用自己复杂手工代码来创造那简陋 3角锥了作为些概念讲解那样 3角锥已经有点过于简单了(当然可以用时候还是会用)利用了glut画球就很简单了(事实上不用也不难)只需要设置好红色然后用

glutSolidSphere(0.5, 30, 16);

  就能画出下面这种半径为窗口宽度1/4

Win32 OpenGL编程(11) 光照

  有人能看出来上面那个是球?我们看到仅仅是个圆而已数学中我们也常常会将某个方向图形抽象成个圆但是现实中我们还是很容易看出个球就是个球而不是个圆为啥呢?光啊光照存在个球在我们眼里事实上不会是如上面那样纯红色就整个球都是纯红色会根据光照方向强度等原因区别导致各个部位显示区别下面是个加上了光照红色球体例子(上传U酷后颜色变了-_-!可能是U酷在上传后进行了2次压缩编码导致颜色混乱光源移动效果基本需要真实体验可以下载源代码编译后运行)

  主要源代码如下:

//OpenGL化开始
void SceneInit( w, h)
{
    GLenum err = glewInit;
    (err != GLEW_OK)
    {
        MessageBox(NULL, _T("Error"), _T("Glew init failed."), MB_OK);
        exit(-1);
    }
    GLfloat mat_specular = { 1.0, 0.0, 0.0, 1.0 };
    GLfloat mat_shininess = { 50.0 };
    GLfloat mat_ambient = { 1.0, 0.0, 0.0, 1.0 };
    GLfloat mat_dfuse = { 1.0, 0.0, 0.0, 1.0 };
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_dfuse);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glColor3f(0.0, 0.0, 0.0);
}
//这里进行所有绘图工作
void SceneShow(GLvoid)        
{
     GLfloat light_position = { 1.0, 0.0, -3.0, 0.0 };
     GLfloat angle = 1.0;
    glClear(GL_COLOR_BUFFER_BIT);    // 清空颜色缓冲区
    
    glPushMatrix;
    glRotatef(angle, 0.0, 1.0, 0.0);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glPopMatrix;
    angle  1.0;
    glutSolidSphere(0.5, 30, 16);
    glFlush;
}  


  这里启用光照顺序就像是上面那个思维导图中表示下解释下各步骤:

  1.定义物体法线

  这步其实很重要但是我们在这个例子中看不到原因在于glutSolidSphere已经完成了这样定义法线定义有点类似我们以前确定物体表面时用逆/正时针顶点指定顺序来确定个物体正背面法线定义了光线入射时物体相对于光源方向这算是个额外小题目以后有机会再论及可以参考OpenGL 编程指南第2.5节基本上知道法线概念后需要就是glNormal*但是法线计算并不总是那么容易比如此例球体还好此例是使用现成省了此步

  2.创建和选择光源并设置位置

glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);


  两句表示启用了第0号光源此光源默认颜色为白色位置为(0.0,0.0,1.0, 0.0)我们利用了其默认颜色

  glLight*是专门光照配置控制

  OpenGL Programming Guide:

    glLight —  light source parameters
    C Specication
    void glLightf(    GLenum      light,
         GLenum      pname,
         GLfloat      param);
    void glLighti(    GLenum      light,
         GLenum      pname,
         GL      param);
    Parameters
    light
                            Species a light.
                            The number of lights depends on the implementation,
                            but at least eight lights are supported.
                            They are identied by symbolic names of the form GL_LIGHT
                            i,
                            where i ranges from 0 to the value of GL_MAX_LIGHTS - 1.
    pname
                            Species a single-valued light source parameter for light.
                            GL_SPOT_EXPONENT,
                            GL_SPOT_CUTOFF,
                            GL_CONSTANT_ATTENUATION,
                            GL_LINEAR_ATTENUATION, and
                            GL_QUADRATIC_ATTENUATION are accepted.
    param
                            Species the value that parameter pname of light source light
                            will be  to.


  事实上远远不如看起来那么简单上面说了光照属于比较容易影响显示效果原因所以光照使用方法很多上述pname参数指定n 个值就可见但是本例中使用方式却很简单仅仅使用此来设定0号光源位置而已光照分类其实包括环境光散射光镜面光发射光(实际指物体发射颜色emissive color)等属性这里不详述了

  glLightfv(GL_LIGHT0, GL_POSITION, light_position);

  光照模型位置指定是使用了模型视图所以我们也可以用模型变换方式来指定光源位置就像光源是个普通物体此例中使用了旋转模型视图旋转了光源位置旋转方式是以Y轴为旋转轴看到效果也就是如视频所示类似月相变化动画

  3.创建和选择光照模型

  光照在OpenGL中有两种模型种叫无限远观察者模式种叫本地观察者模式通过glLightModel*设置默认为无限远观察者模型这里没有修改即使用了默认值区别在于是否考虑到观察者位置对物体观察角度区别引起变化相对来说本地观察者模型会更加逼真但是有额外光照计算性能也会受到影响按照OpenGL管理默认选择往往是性能更优而不是效果更好但是就这么简单例子我看不出区别

  OpenGL Programming Guide:

    glLightModel —  the lighting model parameters
    C Specication
    void glLightModelf(    GLenum      pname,
         GLfloat      param);
    void glLightModeli(    GLenum      pname,
         GL      param);
    Parameters
    pname
                            Species a single-valued lighting model parameter.
                            GL_LIGHT_MODEL_LOCAL_VIEWER,
                            GL_LIGHT_MODEL_COLOR_CONTROL, and
                            GL_LIGHT_MODEL_TWO_SIDE are accepted.
    param
                            Species the value that param will be  to.


  我们可以通过

  glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);

  切换至本地观察者模型

  4.定义物体材料属性

  光是定义了光源而不定义物体如何反射光在OpenGL中是不行物体材料属性决定了其将会如何对光进行反应和光源属性对应也有几种和反射光相关材料属性可以设置我们通过glMateriall*来设置材料属性

  OpenGL Programming Guide:

    glMaterial — specy material parameters for the lighting model
    C Specication
    void glMaterialfv(    GLenum      face,
         GLenum      pname,
         const GLfloat *      params);
    void glMaterialiv(    GLenum      face,
         GLenum      pname,
         const GL *      params);
    Parameters
    face
                            Species which face or faces are being updated.
                            Must be one of
                            GL_FRONT,
                            GL_BACK, or
                            GL_FRONT_AND_BACK.
    pname
                            Species the material parameter of the face or faces that is being updated.
                            Must be one of
                            GL_AMBIENT,
                            GL_DIFFUSE,
                            GL_SPECULAR,
                            GL_EMISSION,
                            GL_SHININESS,
                            GL_AMBIENT_AND_DIFFUSE, or
                            GL_COLOR_INDEXES.
    params
                            Species a poer to the value or values that pname will be  to.




  上例中:

GLfloat mat_specular = { 1.0, 0.0, 0.0, 1.0 };
GLfloat mat_shininess = { 50.0 };
GLfloat mat_ambient = { 1.0, 0.0, 0.0, 1.0 };
GLfloat mat_dfuse = { 1.0, 0.0, 0.0, 1.0 };
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_dfuse);


  这样8行定义了4中材料属性并且我们都是使用 GL_FRONT定义正面受到光照(即默认背面无光照)GL_SPECULAR表示镜面颜色GL_SHININESS表示镜面指数GL_AMBIENT表示环境颜色GL_DIFFUSE表示散射颜色其中散射颜色和环境颜色影响物体所反色散射光和环境光颜色(和光源属性对应)有直接光照时人体主要感知还是以散射颜色为主无光照时环境颜色才会占重要地位镜面颜色决定镜面反射产生亮点GL_SHININESS参数设置亮点大小和亮度发射光颜色会让物体微微发光(此例未用)比较复杂上述文字仅仅大概描述了个概念具体参数还是参考OpenGL Programming Guide 并且实际调整区别数值体验效果为好其中OpenGL Programming Guide书中推荐Nate RobinOpenGL教程教学中有两个例子和光照有关能够体验数值改变时显示效果假如大家不想自己编程调试可以使用教程中lightpositionlightmateria两个例子体验 



Tags:  win32编程

延伸阅读

最新评论

发表评论