当前位置:   article > 正文

C# SharpGL-Light光源

sharpgl

写在最前面的话:光源是OpenGL里非常重要的一部分,内容比较多也比较深,未必能够全部整理的清楚,若有整理不当或需补充的地方,还望各位大虾多多指点。

结合我们身边实际的场景和名词,先简单说一下与OpenGL的Light对应关系:
1、光源:就是能够自行发光的物体,太阳是光源,灯泡、手电筒也可以是光源。光源可以分为:平行光、点光源、聚光灯三种类型,分别对应OpemGL里的方向性光源、位置性光源、聚光灯光源。上张图,便于理解三种光源:
在这里插入图片描述
因为,不同类型的光源,照射到物体上发射光的方向是不一样的,需要特别注意,仿照太阳光就用方向性光源,仿照聚光灯效果就用聚光灯光源,仿照点光源效果,就用位置性光源。注意:光源类型在OpenGL中是通过设定光源位置决定的。
2、光的颜色:光源自身发出的光的颜色,太阳光是白色,彩色光源能够发出指定颜色光。注意:光的颜色对应的就是OpenGL中的环境光颜色。
3、反射光:反射光分为镜面反射光和漫反射光。镜面反射光就是光源光照射到物体上后,物体能够将照射到物体表面上的光关不反射出去的光,就像镜子一样,反射光方向与光照方向和物体表面法向有关系。漫反射光,由于物体表面很难是绝对的平滑,光照射到物体表面后,会被反射只四面八方,此时反射出来的光就是漫反射光。
在这里插入图片描述
注意:反射光对应OpenGL中的镜面光和漫射光参数。
4、反射的光颜色:我们之所以能够看到不发光的物体,是因为这些不发光的物体能够反射照射在该物体上的光,反射出来的光的颜色=照射在物体上的光的颜色+物体本身的颜色。太阳光是白色,照射在一个红色的物体上,我们人眼看到的物体就是红色的。若光源颜色改为绿色,照射在红色物体上,我们人眼看到的物体颜色是黄色的。注意:物体本身的颜色对应到OpenGL中就是材质参数。
5、平面发线:表示垂直与物体表面的一个方向,该方向主要用于计算光照射到物体表面后,反射的效果:方向、颜色等相关因素。注意:OpenGL中的法线,一般需要用代码方式对图形中的每个顶点分别设定。

基础科普已经完成,下面就看一下SharpGL如何应用光源:
一、设定光源
设定光源函数:public void Light(LightName light, LightParameter pname, float[] parameters)
其中:
LightName:是指定设置的哪一个光源,OpenGL支持8个光源,分别是GL_LIGHT0、GL_LIGHT1、…、GL_LIGHT7。
LightParameter:是指定设定该光源哪一个属性,参数如下:
在这里插入图片描述
parameters:对应属性设定的值。
此处,重点讲一下两个属性:
1、GL_POSITION属性:表示光源所在的位置。由四个值(X, Y, Z, W)表示。
  如果第四个值W为零,则表示该光源位于无限远处,前三个值表示了它所在的方向。这种光源称为方向性光源,通常,太阳可以近似的被认为是方向性光源。如果第四个值W不为零(建议W=1),则X/W, Y/W, Z/W表示了光源的位置,这种光源称为位置性光源。下面会上范例,实际比较一下两种光源的视觉效果。
  注意:1、对于位置性光源,设置其位置与设置多边形顶点的方式相似,各种矩阵变换函数例 如:Translate、Rotate等对光源也同样有效。2、当为方向性光源时,光照方向是从(X,Y,Z)指向(0,0,0)位置。
2、聚光属性:
GL_SPOT_DIRECTION属性:有三个值(X,Y,Z),表示一个向量,及光源发射的方向。
GL_SPOT_EXPONENT属性:有一个值,表示聚光的程度,为零时表示光照范围内向各方向发射的光线强度相同,为正数时表示光照向中央集中,正对发射方向的位置受到更多光照,其它位置受到较少光照。数值越大,聚光效果就越明显。
GL_SPOT_CUTOFF属性:有一个值,表示一个角度,它是光源发射光线所覆盖角度的一半,其取值范围在0到90之间,也可以取180这个特殊值。取值为180时表示光源发射光线覆盖360度,即不使用聚光灯,向全周围发射。
GL_CONSTANT_ATTENUATION、 GL_LINEAR_ATTENUATION、GL_QUADRATIC_ATTENUATION属性:这三个属性表示了光源所发出的光线的直线传播特性 。现实生活中,光线的强度随着距离的增加而减弱,OpenGL把这个减弱的趋势抽象成函数:
衰减因子 = 1 / (k1 + k2 * d + k3 * k3 * d)
  其中d表示距离,光线的初始强度乘以衰减因子,就得到对应距离的光线强度。上述是引荐这里
简单说明一下:GL_POSITION的第四个参数W=0,光源为方向性光源。GL_POSITION的第四个参数W≠0,光源为位置性光源。位置性光源,在不设定聚光灯参数是,此时光源为点光源。位置性光源,设定聚光灯参数,此时光源为聚光灯光源。
二、开启光源
OpenGL开启光源需要操作两步操作:
1、开启OpenGL光照功能:gl.Enable(GL_LIGHTING);
2、开启指定的光源:gl.Enable(GL_LIGHT#),#表示指定的几号光源,范围:0~7;
OpenGL关闭光源也比较简单:
1、关闭OpenGL光照功能:gl.Disable(GL_LIGHTING);
2、关闭指定的光源:gl.Disable(GL_LIGHT#),#表示指定的几号光源,范围:0~7;

上程序,看效果:
初始化程序:

        //光源位置
        float[] lightPosG = { 5f, 0f, 0f, 1f };//W=1,位置性光源
        //光源漫射光
        float[] diffLightG = { 0f, 1f, 0f, 1f };
        //光源镜面光
        float[] specLightG = { 0f, 1f, 0f, 1f };
        //默认光源
        float[] defDiffLight = { 1f, 1f, 1f, 1f };
        float[] defSpecLight = { 1f, 1f, 1f, 1f };
        float[] defLightPos = { 0f, 0f, 10f, 0f };//W=0,方向性光源
        private void openGLControl1_OpenGLInitialized(object sender, EventArgs e)
        {
            SharpGL.OpenGL gl = this.openGLControl1.OpenGL;
            setLight(gl);//设定光源
            gl.ClearColor(0, 0, 0, 0);
        }
        private void setLight(OpenGL gl)
        {
             gl.LoadIdentity();
             //0号灯光,默认灯光
             //gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, defDiffLight);//环境光先不要设置
             gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, defDiffLight);
             gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, defLightPos);
 
             //2号灯光
             //gl.Light(OpenGL.GL_LIGHT2, OpenGL.GL_AMBIENT, diffLightG);//环境光不要设置,用光源0
             gl.Light(OpenGL.GL_LIGHT2, OpenGL.GL_DIFFUSE, diffLightG);
             gl.Light(OpenGL.GL_LIGHT2, OpenGL.GL_POSITION, lightPosG);

             gl.Enable(OpenGL.GL_LIGHTING);
             //gl.Enable(OpenGL.GL_LIGHT0);//开启0号光源,先关闭,等下再开启,对比下视觉效果
             gl.Enable(OpenGL.GL_LIGHT2);//开启2号光源
         }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

在空间中绘制四个球体,其中两个球体分别位于Light0的光源位置和Light2的光源位置,另外两个球体,用于显示不同光照角度的光照效果。
  绘制球体函数:

 /// <summary>
        /// 绘制球体
        /// </summary>
        /// <param name="gl"></param>
        /// <param name="x">xPos</param>
        /// <param name="y">yPos</param>
        /// <param name="z">zPos</param>
        /// <param name="radius">球体半径</param>
        /// <param name="segx">球体纬线</param>
        /// <param name="segy">球体经线</param>
        /// <param name="isLines">球体样式</param>
        private void DrawSphere(SharpGL.OpenGL gl, float x, float y, float z, double radius, int segx, int segy, bool isLines)
        {
            gl.PushMatrix();
            gl.Translate(x, y, z);
            var sphere = gl.NewQuadric();//创建几何体对象
            if (isLines)
                gl.QuadricDrawStyle(sphere, OpenGL.GL_LINES);//设置绘制样式,GL_Point 以点方式绘制,GL_Lines 以线方式绘制, GL_Fill 以填充方式绘制
            else
                gl.QuadricDrawStyle(sphere, OpenGL.GL_QUADS);
             gl.QuadricNormals(sphere, OpenGL.GLU_SMOOTH);//GLU_NONE:无,GLU_FLAT:普通,GLU_SMOOTH:平滑。设置几何体法线
             gl.QuadricOrientation(sphere, (int)OpenGL.GLU_OUTSIDE);//GLU_OUTSIDE,GLU_INSIDE。设置对齐方式
             gl.QuadricTexture(sphere, (int)OpenGL.GLU_FALSE);//GL_TRUE,GLU_FALSE。是否绑定纹理
             gl.Sphere(sphere, radius, segx, segy);//绘制球:半径,纬线,经线
             gl.DeleteQuadric(sphere);
            gl.PopMatrix();
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

注意:球体法线QuadricNormals一定要设置,不能是NONE,不然球体默认法线都是向上,光照效果会与实际效果不一致。
  编写绘制函数,在指定光源位置绘制指定颜色的球体,用对应颜色的球体代表光源:

       private void Draw(SharpGL.OpenGL gl)
        {
            //默认光源
            gl.PushMatrix();
            gl.Color(1f, 1f, 1f);
            gl.Disable(OpenGL.GL_LIGHTING);//关闭光源
            DrawSphere(gl, defLightPos[0], defLightPos[1], defLightPos[2], 1f, 10, 10, false);//球体位置与光源0位置重合,位置在(0,0,10)
            gl.Enable(OpenGL.GL_LIGHTING);//开启光源
            gl.PopMatrix();

            //绿色光源
            gl.PushMatrix();
            gl.Color(0f, 1f, 0f);
            gl.Disable(OpenGL.GL_LIGHTING);
            DrawSphere(gl, lightPosG[0], lightPosG[1], lightPosG[2], 1f, 10, 10, false);//球体位置与光源2位置重合,位置在(5,0,0)
            gl.Enable(OpenGL.GL_LIGHTING);
            gl.PopMatrix();

            //绘制球体,映射光源效果
            DrawSphere(gl, 0, 0, 0, 3, 40, 40, false);//位置在(0,0,3)
            DrawSphere(gl, 10, 0, 0, 1, 20, 20, false);//位置在(10,0,0)
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

注意:在绘制图形,指定图形颜色时,一定要先关闭光源,不然会出现图形颜色设置不成功的情况!!!
OpenGL绘制函数:

       private void openGLControl1_OpenGLDraw(object sender, RenderEventArgs args)
        {
            SharpGL.OpenGL gl = this.openGLControl1.OpenGL;
            gl.LoadIdentity();
            gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);

            gl.MatrixMode(OpenGL.GL_PROJECTION);
            gl.LoadIdentity();
            gl.Perspective(70, (double)(this.Width / this.Height), 0.01, 100);
            gl.LookAt(eyex, eyey, eyez, 0, 0, 0, 0, 0, 1);//编写KeyPress,用于键盘控制相机位置
            gl.MatrixMode(OpenGL.GL_MODELVIEW);

            gl.LoadIdentity();
            Draw(gl);//绘制函数
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

效果1:Light0未开启,Light2开启,Light2为位置性点光源:lightPosG = { 5f, 0f, 0f, 1f },Ligjt2位于两个映射球体中间,看下视觉效果:
在这里插入图片描述
效果2:Light0未开启,Light2开启,Light2为方向性光源:lightPosG = { 5f, 0f, 0f, 0f },Ligjt2位于两个映射球体中间,看下视觉效果:
在这里插入图片描述
效果3:Light0开启,Light2开启,Light2为方向性光源:lightPosG = { 5f, 0f, 0f, 0f },Ligjt2位于两个映射球体中间,看下视觉效果:
在这里插入图片描述
再此基础上,加入键盘操作,控制相机位置和光源位置:

         private void KeyPress(object sender, KeyPressEventArgs e)
         {
             if (e.KeyChar == 'w')
                 eyey = eyey + 0.5;
             else if (e.KeyChar == 's')
                 eyey = eyey - 0.5;
             else if (e.KeyChar == 'a')
                 eyex = eyex - 0.5;
             else if (e.KeyChar == 'd')
                 eyex = eyex + 0.5;
             else if (e.KeyChar == 'r')
                 eyez = eyez + 0.5;
             else if (e.KeyChar == 'f')
                 eyez = eyez - 0.5;

             if (e.KeyChar == 'i')
                 LightPosR[1] = LightPosR[1] + 0.2f;
             else if (e.KeyChar == 'k')
                 LightPosR[1] = LightPosR[1] - 0.2f;
             else if (e.KeyChar == 'j')
                 LightPosR[0] = LightPosR[0] - 0.2f;
             else if (e.KeyChar == 'l')
                 LightPosR[0] = LightPosR[0] + 0.2f;
             else if (e.KeyChar == 'o')
                 LightPosR[2] = LightPosR[2] - 0.2f;
             else if (e.KeyChar == 'p')
                 LightPosR[2] = LightPosR[2] + 0.2f;
         }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

在增加一组红色位置性点光源,看下视觉效果:
在这里插入图片描述

测试程序在这里:SharpGL测试范例

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/257728?site
推荐阅读
相关标签
  

闽ICP备14008679号