OpenGL学习心得

半程总结:
OpenGL是API(Application Programming Interface),但是并没有具体去定义接口函数如何去定义。而是通过规范化输入输出完成相同的功能。
OpenGL是一个巨大的状态机,一系列的变量描述着OpenGL如何运作。OpenGL的状态被称为上下文(context)。可以通过设置选项、操作缓冲的方法去控制context。可以通过状态设置函数来对OpenGL的上下文进行改变。
OpenGL的工作流:首先创建出一个对象,使用id来保存它的引用。然后将对象绑定到上下文(context)的目标位置。接着设置窗口选项。最后对绑定到目标位置的对象解绑。那么设置的内
容就会被保存到所引用的对象中。一旦我们再次绑定时,这些设置又会重新生效。如下所示:
    // 创建对象
    unsigned int objectId = 0;
    glGenObject(1, &objectId);
    // 绑定对象至上下文
    glBindObject(GL_WINDOW_TARGET, objectId);
    // 设置当前绑定到 GL_WINDOW_TARGET 的对象的一些选项
    glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, 800);
    glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, 600);
    // 将上下文对象设回默认
    glBindObject(GL_WINDOW_TARGET, 0);
OpenGL中对象的好处:可以绑定多个不同的对象,那么不同对象绑定到上下文的目标位置就会有不同的效果。所以只需要创建出不同的对象,将其绑定到合适的上下文目标位置,就能够绘制
不同的效果。
OpenGL的实现:GLFW,它提供了一些渲染物体所需的最低限度的接口。它允许用户创建OpenGL上下文、定义窗口参数以及处理用户输入。enough!!!
OpenGL中函数指针的解决:因为OpenGL中函数不能在编译时被确定,而是需要在运行阶段确定函数位置。所以需要管理函数指针,使用GLAD开源库。
OpenGL中的窗口对象:GLFWwindow类型的指针去接收glfwCreateWindow函数。
OpenGL中上下文的创建:glfwMakeContextCurrent函数。创建完窗口window我们就可以通知GLFW将我们窗口的上下文设置为当前线程的主上下文了。
OpenGL中的视口(viewport):视口就是需要渲染窗口的尺寸大小,只有确定了视口才能知道如何根据窗口的大小显示数据和坐标。视口是真实物理坐标地址,而窗口是逻辑地址。一般进行二维三维变换是在逻辑上进行的,但是最后都会转换到视口上。任何时候视口坐标都是和窗口坐标一一对应的,由计算机进行显示 。
OpenGL中的坐标范围: -1~1 。那么就会根据创建的视口大小进行转换,比如在视口中定义的是(0,0,800,600)。那么就会将-1到1的值映射到0~800和0~600之间。
OpenGL中的回调函数:在函数中去调用另外的函数。就是某些变量的参数发生改变,在另外的函数中去改变因变量。而在主函数中去调用变量改变时需要去调用回调函数的函数。why_callback?
因为回调函数能够提高程序的执行效率,假如需要监视某个变量参数的变化,那么就需要循环或者经常性地去检测判断。而回调函数则解决了这个问题。
OpenGL中的渲染循环:渲染循环(Render Loop),如下所示:
    while(!glfwWindowShouldClose(window))
    {
             glfwSwapBuffers(window);  //交换颜色缓冲,相当于它是存储了渲染窗口中像素值的缓冲,在迭代中被用来绘制,作为显示输出到屏幕上。
                //双缓冲的机制:渲染指令在后缓冲中被执行,在本次迭代中将前缓冲与后缓冲进行交换(swap)。
             glfwPollEvents();    //检测是否有触发事件地产生
    }
OpenGL中的输入操作:定义函数在渲染循环中,如果输入了什么,就在该次迭代(每一帧)中做出相应的改变。
OpenGL中的状态函数:一般是gl开头,而不是glfw。可以分为状态设置以及状态使用。如下所示:
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);  //状态设置函数,设置上下文的状态
    glClear(GL_COLOR_BUFFER_BIT);  //状态使用函数,使用当前的状态来清除颜色。
OpenGL中的图形渲染管线(Graphics Pipeline),主要有两部分:第一部分把你的3D坐标转换为2D坐标,第二部分是把2D坐标转变为实际的有颜色的像素。
OpenGL中的着色器:因为要做的事情是如何将3D空间的物体最后渲染成2D像素输出。它首先接收一组顶点数据,然后通过一系列的着色器处理最终输出2D像素。每个着色器的输出都是下个
着色器的输入。那么这些阶段很容易在GPU上进行并行化执行。GPU显卡上有成千上万的小处理核心,所以可以并行执行这些程序。我们将这些程序成为着色器shader。
OpenGL着色器语言:GLSL。
OpenGL的渲染流程:输入顶点数据(顶点包括了不同的顶点属性)-->顶点着色器(将3D坐标转换为另外形式的3D坐标,需要自定义)-->图元装配(指定顶点数据要渲染的类型,
有GL_POINTS、GL_TRIANGLES、GL_LINE_STRIP等)-->几何着色器-->光栅化阶段(将图元映射为最终屏幕输出的颜色,已经转换到屏幕空间坐标中了,并且生成供片段着色器使用的片段)
-->片段着色器(根据片段生成屏幕中像素的最终颜色,需自定义)-->测试与混合
OpenGL中的片段:片段指渲染一个像素所需要的数据(例如位置信息,采样器以及其他信息),所以片段是一个数据集合。需要在片段着色器中进行处理。
OpenGL输入数据的处理:只有输入的顶点数据范围在-1到1之间(标准化设备坐标),才会进行数据处理。超过该范围,不会处理显示。
OpenGL中的数据存储:通过VBO(顶点缓冲对象)进行GPU存储,能够一次性发送顶点数据到GPU上。先创建对象,在上下文目标位置绑定对象。设置选项,解绑对象。VBO对象中存储了
设置的选项,可以通过VBO对象来进行状态的改变。当然也可以绑定其他的对象设置不同的选项。
OpenGL中的顶点着色器:需要声明所有的输入顶点属性。还需要设置顶点着色器的输出。需要经过着色器对象创建、编译、判断等步骤。
OpenGL中的片段着色器:需要输出4分量的颜色值,也可以输入变量。
OpenGL中的着色器程序:创建着色器程序对象,对顶点着色器对象和片段着色器对象进行链接,同时激活glUseProgram(shaderProgram);
OpenGL中的链接顶点属性:该部分需要指明输入的顶点数据中的顶点属性与顶点着色器中哪个顶点属性相对应。所以,必须在渲染前指定OpenGL该如何解释顶点数据。OpenGL中的缓冲
开始的位置是顶点数据中第一个元素的位置(因为是紧密排布)。如下所示:
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
OpenGL中的顶点属性:指的是一个顶点所对应的属性(位置,颜色,索引)。每个顶点属性从一个VBO管理的内存中获得它的数据,而具体是从哪个VBO中获取则需要找到之前定义的VBO对象。
OpenGL中的VAO,顶点数组对象。VAO对象是为了解决VBO对象中,顶点属性过多或者需要绘制多个物体时需要。一个顶点数组对象会储存以下这些内容:
    glEnableVertexAttribArray和glDisableVertexAttribArray的调用。
    通过glVertexAttribPointer设置的顶点属性配置。
    通过glVertexAttribPointer调用与顶点属性关联的顶点缓冲对象。
所以VAO的作用就是解决绑定正确缓冲对象VBO,再进行顶点属性指针的配置(因为顶点属性指针的配置需要绑定正确的VBO)。那么在VAO中存储的是VBO对象以及配置的顶点属性指针。
这样就不需要去重复配置顶点属性指针以及绑定正确的VBO。一个储存了我们顶点属性配置和应使用的VBO的顶点数组对象(VAO)。一般当你打算绘制多个物体时,你首先要生成/配置所有的
VAO(和必须的VBO及属性指针),然后储存它们供后面使用。当我们打算绘制物体的时候就拿出相应的VAO,绑定它,绘制完物体后,再解绑VAO。
OpenGL中绘制三角形:使用glDrawArrays(GL_TRIANGLES,索引起始位置,绘制顶点数)。使用的状态为GL_TRIANGLES
OpenGL中EBO(元素缓冲对象):举例(如果绘制矩形,可以使用两个三角形去组合绘制,但是有6个顶点,实际只需要4个。有50%的额外计算)。所以最好的方式是直接定义4个顶点的
位置,指定一定的绘制顺序去绘制。EBO对象是元素缓冲对象,存储了要绘制顶点的索引(索引绘制)。实际上定义了4个顶点,但是再indices中存储了每个三角形的绘制索引(共6个);
OpenG中EBO对应的绘制方式:glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);  注意VAO也能存储EBO对象以及顶点属性配置。
OpenGL中VBO与EBO的解绑:在配置完VBO对象所对应的顶点属性时,可以在VAO解绑之前解绑。但是不要解绑EBO,否则EBO属性就 不存在了。
OpenGL中shader中的着色器:需要输入顶点属性。一般着色器中最少可以输入16个顶点属性。
OpenGL中的顶点着色器:需要输入特定的数据,否则性能会下降,能够直接接收顶点数据中的顶点属性变量(使用layout链接)。
OpenGL中数据的传输:在不同的着色器中设置相同的变量名。但是一个输出,一个是输入。就能够进行数据的传输。
OpenGL中的uniform变量:是全局变量。能够在任何时候被任何着色器访问,并且uniform变量能够存储其数据,直到被更新或重置。
OpenGL中uniform变量赋值:首先需要有着色器程序对象,然后对该着色器程序对象中的uniform进行索引/位置的确定。得到变量的位置后,进行glUniform赋值。uniform对于设置一个在渲染
迭代中会改变的属性是一个非常有用的工具,它也是一个在程序和着色器间数据交互的很好工具。
OpenGL中加入新的顶点属性:需要更新顶点属性的步长以及偏移量。
****************************************************************************************************************************************************************************************************
重要!OpenGL中的片段着色器中的(片段插值)。当渲染一个三角形时,光栅化(Rasterization)阶段通常会造成比原指定顶点更多的片段。光栅会根据每个片段在三角形形状上所处相对位置决定
这些片段的位置。基于这些位置,它会插值(Interpolate)所有片段着色器的输入变量。
****************************************************************************************************************************************************************************************************
文件的读取:使用<ifstream>对象进行读取,将其读入到<stringstream>对象中。当然这还不是string类型,需要使用.str()进行转换转换为string对象。着色器中使用的是C语言风格的字符串,所以
使用.c_str()进行转换。
OpenGL中的纹理坐标:通常纹理坐标的范围是(0,0)左下角到(1,1)右上角。为了能够将纹理映射到对应的形状上,需要对每个顶点关联一个纹理坐标。将纹理坐标传入片段着色器后,进行
片段插值操作。只要给顶点着色器传递纹理坐标就行了,接下来它们会被传片段着色器中,它会为每个片段进行纹理坐标的插值。
OpenGL中的纹理环绕方式、纹理过滤(当进行放大(Magnify)和缩小(Minify)操作的时候可以设置纹理过滤的选项),多级渐远纹理
OpenGL中加载与创建纹理:使用stb_img.h进行图像的读取保存为data数据。接着创建纹理对象,进行上下文目标状态绑定,生成纹理对象glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
将data数据传入,生成纹理对象。
OpenGL纹理的应用:在片段着色器中定义全局的采样器,uniform sample2d load_texture;使用texture(load_texture,tex_cood);进行纹理贴图。texture函数会使用之前设置的纹理参数对相应的颜色值进行采样。
这个片段着色器的输出就是纹理的(插值)纹理坐标上的(过滤后的)颜色。
OpenGL中的纹理单元:通常需要对纹理进行进行位置赋值(因为是uniform类型)。由于可以在片段着色器中设置多个纹理采样器,那么就可以设置多个纹理。每个纹理位置值被称为纹理单元。
一个纹理的默认纹理单元是0,它是默认的激活纹理单元。所以只有一个纹理对象时,不需要进行纹理位置赋值。纹理单元的主要目的是让我们在着色器中可以使用多于一个的纹理。
***************************************************************************************************************************
OpenGL纹理单元的使用:在进行渲染时,绑定纹理单元之前,需要激活纹理单元glActiveTexture(GL_TEXTURE0);
激活纹理单元之后,接下来的glBindTexture函数调用会绑定这个纹理到当前激活的纹理单元,纹理单元GL_TEXTURE0默认总是被激活,所以我们在前面的例子里当我们使用glBindTexture的时候,无需激活任何纹理单元。
OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从GL_TEXTURE0到GL_TEXTRUE15。它们都是按顺序定义的,所以我们也可以通过GL_TEXTURE0 + 8的方式获得GL_TEXTURE8,这在当我们需要循环一些纹理单元的时候会很有用。
***************************************************************************************************************************
OpenGL中矩阵与向量之间相乘:将顶点数据定义为向量,然后使用矩阵对其及操作;矩阵与向量之间操作。
OpenGL中的缩放(scale)、旋转(rotate)与位移(translate)
OpenGL中的坐标转换:主要分为 五部分。1、局部坐标(物体自身大小上的坐标,内部)2、model matrix对物体进行世界坐标转换  3、view matrix进行观察坐标转换,将世界坐标系下的物体转换到相对于观察者(位置,朝向,上向量) 
4、projection matrix 投影矩阵,将坐标转换到裁剪空间(标准化设备坐标,范围-1到1,其余坐标被裁剪掉) 5、将裁剪坐标转换到屏幕坐标(根据视口 viewport将-1到1的坐标映射变换到设置的参数范围内,并将转换后的坐标送到光栅器,转换为片段)
OpenGL的局部空间、世界空间、观察空间、裁剪空间
OpenGL的裁剪空间:需要将空间中的3D点投影到2D的坐标。有正交投影以及透视投影,正交投影不满足进大远小。需要定义近平面以及远平面的长宽以及长方体的近平面以及远平面。而透视投影相当于将视锥体的内容投影到二维中,透视投影中的w分量
离屏幕距离越远的点的话w越大。形成近大远小的视觉效果。(需要定义可视范围,长宽比,近平面以及远平面)。被投影到裁剪空间后,才进行透视除法(-1~1)以及视口变换。
OpenGL中的右手坐标系:特别说明z轴的正方向向外,负方向向里。而负方向也是摄像机的观察方向。
OpenGL中立方体的定义:6个面,每个面2个三角形,每个三角形3个顶点。如果不使用索引绘制,则需要36个顶点。
OpenGL的Z缓冲:由于OpenGL是一个一个绘制三角形的,所以会出现覆盖的问题。可以通过状态使用来清除深度缓冲,以免被覆盖。
OpenGL中绘制出更多的立方体:因为立方体的GPU存储以及顶点属性已经被配置好,所以只要绑定正确的VAO即可。再去使用model matrix矩阵更新位置绘制其他的立方体。
OpenGL中的摄像机:位置信息、摄像机方向(特别说明,摄像机正方向被设定为z的负轴,但是方向向量被定义为摄像机位置向量-目标位置向量),右轴(需要假定一个上向量,与方向向量叉乘得到,可以根据右手定则来),上轴(根据右手定则方向向量叉乘右向量)
OpenGL中的LookAt矩阵:将三个相互垂直的坐标轴和位置向量整合到一个矩阵中。创建出Lookat矩阵,需要摄像机位置,目标位置以及上向量。
OpenGL中摄像机自由移动:自由移动的前提是,摄像机的方向不能改变。所以需要将lookat矩阵定义为view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp)的形式;保证无论我们怎么移动,摄像机都会注视着目标方向。这样只要在cameraPos 
的基础上去更新cameraFront或者右向量的值即可。(W,S,A,D)
OpenGL中的移动速度:通常不同设备的刷新率是不一致的,所以需要确保移动速度是一致的。一般来说需要得到每一帧的渲染时间,渲染时间越大,那么移动的速度也应该越大。所以需要定义当前帧时间以及上一帧时间。
OpenGL中的视角移动:需要结合欧拉角来实现。需要使用到俯仰角Pitch以及偏移角Yaw。首先在俯仰角,y是sin分量 ,x,z是cos分量。在偏移角下,z是sin分量,而x是xos分量。
OpenGL中视角移动实现步骤:首席按需要获得鼠标的偏移量,将偏移量更新到俯仰角以及偏移角。最后还有转化到摄像机上向量以及右向量。
OpenGL中的颜色:有光源和被照物。物体所呈现的颜色是光源颜色分量与被照物颜色分量的相乘。一般来说对光源立方体可以使用另外的VAO进行VBO顶点位置的存储以及属性配置。
OpenGL中的基础光照:环境光照(直接影响,间接影响:环境光照)、漫反射光照以及镜面光照。
    环境光照:通常是一个常数量对光源颜色进行影响
    漫反射光照:需要直到片段的法向量信息,以及入射向量(可以将光源位置-片段位置,注意是在世界坐标系下,所以需要将片段变换到世界坐标系中)计算直接的余弦值(需要归一化)需要将法向量通过法线矩阵将其转换到世界坐标系中,以应对物体的不均匀缩放。
    镜面光照:镜面光照是在漫反射的基础上加入了可视角。只要在一定的范围内才能可见高光。在漫反射基础上,在片段着色器增加了观察点,也是全局变量。计算出观察方向与反射方向之间的夹角余弦值。
最后将三个分量组成了基础光照,与物体颜色分量相乘。
OpenGL中的材质:之前计算物体最终的颜色没有考虑物体的材质信息。所以需要定义一个结构体来描述其中的材质信息。just like this:
    #version 330 core
    struct Material {
            vec3 ambient;
            vec3 diffuse;
            vec3 specular;
            float shininess;
        }; 
    uniform Material material;
将这些材质属性分别对环境光照、漫反射光照以及镜面光照分量进行相乘,就能够得到不同物体材质对于光照分量的影响。
OpenGL中光的属性: 光源对环境光、漫反射和镜面光分量也分别具有不同的强度。需要为每个光照分量分别指定一个强度向量。创建了一些光照属性来影响各个光照分量。我们希望为光照属性创建类似材质结构体的东西:
    #version 330 core
    struct Light{
        vec3 position;

            vec3 ambient;
            vec3 diffuse;
            vec3 specular;
        }; 
    uniform Light light;
OpenGL中的光照贴图:为了实现更加逼真的效果。需要对立方体进行贴图,有漫反射贴图(diffuse map)以及镜面光贴图(specular map)。
OpenGL中的漫反射贴图:需要在物体材质中的Material类中设置一个全局变量uniform diffuse.然后在漫反射与环境光计算中,使用texture(material.diffuse,texturecood)得到每个像素漫反射结果。
OpenGL中的镜面光贴图:相当于是纹理单元,也是在Material类中设置一个全局变量uniform specular。然后计算镜面光反射时使用material.specular分量。
OpenGL中的投光物:有定向光、点光源。定向光中包含平行光(与光源的位置没有关系,即在很远处入射的光,总是平行的。所以光源的位置和被投物的位置相对关系不重要)
OpenGL中的点光源:点光源是处于世界中某一个位置的光源,它会朝着所有方向发光,但光线会随着距离逐渐衰减。衰减的公式包含常数项、一次项、二次项。具体的实现需要在Light结构体中分别定义三个分量。然后
计算出光源位置与物体片段之间的距离,计算出衰减量。然后对环境光、漫反射光以及镜面反射光乘上该衰减量。
OpenGL中的聚光:类似于手电筒的效果。在一定范围内有light光源颜色分量,范围外没有。
 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值