• 3D Computer Grapihcs Using OpenGL


    本节我们将尝试利用三角形制作一个“走马灯”效果。

    一个三角形如图示方式,从左向右依次移动。

    先看一下代码:

    MyGlWindow.cpp

     1 #include <glglew.h>
     2 #include "MyGlWindow.h"
     3 #include <iostream>
     4 #include <fstream>
     5 
     6 float triangleWidth = 0.1f;
     7 float bytesPerTriangle = sizeof(GLfloat) * 18;
     8 uint triangleIndex = 0;
     9 uint maxTriangleCount = 20;
    10 
    11 void MyGlWindow::sendDataToOpenGL()
    12 {
    13     //GLfloat verts[] =
    14     //{
    15     //    -1.0f, -1.0f, +0.5f,//Vertex 0
    16     //    +1.0f, +0.0f, +0.0f,//Color  0
    17     //    +0.0f, +1.0f, -0.5f,//Vertex 1
    18     //    +0.0f, +1.0f, +0.0f,//Color  1
    19     //    +1.0f, -1.0f, +0.5f,//Vertex 2
    20     //    +0.0f, +0.0f, +1.0f,//Color  2
    21 
    22     //    -1.0f, +1.0f, +0.5f,//Vertex 3
    23     //    +0.5f, +0.3f, +0.1f,//Color  3
    24     //    +0.0f, -1.0f, -0.5f,//Vertex 4
    25     //    +0.1f, +0.4f, +0.2f,//Color  4
    26     //    +1.0f, +1.0f, +0.5f,//Vertex 5
    27     //    +1.0f, +0.5f, +0.2f,//Color  5
    28     //};
    29 
    30     GLuint vertexBufferID;
    31     glGenBuffers(1, &vertexBufferID);
    32     glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
    33     glBufferData(GL_ARRAY_BUFFER, maxTriangleCount * bytesPerTriangle, NULL, GL_STATIC_DRAW);
    34 
    35     //GLushort indices[] =
    36     //{
    37     //    0,1,2,
    38     //    3,4,5,
    39     //};
    40     //GLuint indexBufferID;
    41     //glGenBuffers(1, &indexBufferID);
    42     //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
    43     //glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    44 
    45     glEnableVertexAttribArray(0);
    46     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, 0);
    47 
    48     glEnableVertexAttribArray(1);
    49     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (char*)(sizeof(GLfloat) * 3));
    50 }
    51 
    52 void MyGlWindow::installShaders()
    53 {
    54     //未修改,省略...
    55 }
    56 
    57 void MyGlWindow::initializeGL()
    58 {
    59     glewInit();
    60     glEnable(GL_DEPTH_TEST);
    61     sendDataToOpenGL();
    62     installShaders();
    63 }
    64 
    65 void MyGlWindow::paintGL()
    66 {
    67     glClear(GL_DEPTH_BUFFER_BIT);
    68     glViewport(0, 0, width(), height());
    69     //glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
    70     sendAnotherTriangle();
    71     glDrawArrays(GL_TRIANGLES, (triangleIndex-1)*3, triangleIndex * bytesPerTriangle);
    72 }
    73 
    74 void MyGlWindow::sendAnotherTriangle()
    75 {
    76     if (triangleIndex == maxTriangleCount)
    77         return;
    78     GLfloat xVal = -1 + triangleIndex * triangleWidth;
    79     GLfloat newTriangle[] = 
    80     {
    81         xVal, 1.0f, 0.0f,
    82         1.0f, 0.0f, 0.0f,
    83 
    84         xVal + triangleWidth, 1.0f, 0.0f,
    85         0.0f, 1.0f, 0.0f,
    86 
    87         xVal, 0.0f, 0.0f,
    88         0.0f, 0.0f, 1.0f,
    89     };
    90 
    91     glBufferSubData(GL_ARRAY_BUFFER, bytesPerTriangle * triangleIndex, bytesPerTriangle, newTriangle);
    92 
    93     triangleIndex++;
    94 }
    95 
    96 std::string MyGlWindow::ReadShaderCode(const char* fileName)
    97 {
    98     //未修改,省略...
    99 }

    MyGlWindow.h

     1 #pragma once
     2 #include <QtOpenGLqgl.h>
     3 #include <string>
     4 class MyGlWindow :public QGLWidget
     5 {
     6 protected:
     7     void sendDataToOpenGL();
     8     void installShaders();
     9     void initializeGL();
    10     void paintGL();
    11     std::string ReadShaderCode(const char* fileName);
    12     void sendAnotherTriangle();
    13 };

    重点看cpp文件里的变化。

    先定义了几个变量(其实也可以定义成常量),方便后面使用,他们分别是:

    • float triangleWidth = 0.1f 表示三角形的宽度
    • float bytesPerTriangle = sizeof(GLfloat) * 18 表示每个三角形包含的顶点信息数据字节数,一个三角形使用了3个顶点,每个顶点有6个GLfloat类型数据
    • uint triangleIndex = 0 表示当前绘制的三角形的索引
    • uint maxTriangleCount = 20 “走马灯”最多有多少个三角形

    此前我们是在sendDataToOpenGL()函数中创建一个verts数组,把所有的数据一次性发送到OpenGL中进行绘制,本次我们需要动态改变绘制的内容,所以就不事先将数据一次性发送了。首先删除掉sendDataToOpenGL函数中的verts数组。(13-28行)

    另外也不需要所以数组了,也把索引数组相关的内容删除掉。(35-43行以及69行)

    33行也做了修改,首先我们要给VertexArrayBuffer分配足够的空间,所以第二个参数改成了maxTriangleCount * bytesPerTriangle,提供20个三角形需要的空间。而我们在这个阶段不需要提供任何数据(后面会讲如何提供),所以第三个参数直接给个空值NULL。

    在71行绘制Array之前,我们调用了一个新添加的函数sendAnotherTriangle(),这个函数的前半部分(78-89行)是准备数据,准备每次走马灯要绘制的三角形的数据,由数据内容也能看出来,主要区别就是位置向右移动了。

    重点是91行的函数

    glBufferSubData(GL_ARRAY_BUFFER, bytesPerTriangle * triangleIndex, bytesPerTriangle, newTriangle);

    glBufferSubData 这个OpenGL函数的作用是“部分填充” Array Buffer。可以对比观察33行的glBufferData(一次性全部填充), 名字只是多了一个Sub,但是两者的参数还是有些区别的。

    • 第一个参数和glBufferData是一样的,表示设置哪个绑定点的数据。
    • 第二个参数是一个绘制的起始位置,因为我们右了一个三角形的索引triangleIndex,所以起始值就是它乘以每个三角形的字节数。
    • 第三个参数表示每个元素的长度,正好使用我们一开始定义的每个三角形的字节数 bytesPerTriangle
    • 第四个参数是数据本身

    71行绘制Array Buffer, 注意第二个参数是每个三角形的起始点。

    完成后编译运行,发现画面并没有变化,主要原因是画面没有重绘,为了激活重绘,最简单的办法就是让窗口失去焦点和得到焦点,也就是可以在opengl窗口和其他任意窗口之间点击切换。

    但是看到的效果仍然不是我们期望的。效果如下:

    这是什么原因呢?

    原因是OpenGL使用了双重缓存。一个Front Buffer, 一个Back Buffer。

    绘制工作都是在Back Buffer上进行的,以免用户看到绘制的过程,绘制好以后,会和Front Buffer进行一次交换。这就是为什么我们看到了似乎有两副不同的图在反复切换。

    另外,我们明确指定了每次只绘制一个三角形,但是为什么之前的三角形都保存下来了?

    原因是我们没有进行一次“清理”。

    找到MyGlWindow.cpp的67行,我们对它进行如下修改:

    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

    既然要清理Depth Buffer,我们顺便使用一个"位或" 运算符把 Color Buffer也添加上。

    这样修改以后,就可以实现"走马灯"效果了!(效果就不截图了,动图太难弄了)

  • 相关阅读:
    ehcache memcache redis 三大缓存男高音
    tomcat启用压缩的方式
    Linux rpm 命令参数使用详解[介绍和应用]
    rpm常用命令及rpm参数介绍
    RPM 命令大全
    BZOJ2298: [HAOI2011]problem a(带权区间覆盖DP)
    BZOJ2037: [Sdoi2008]Sue的小球(区间DP)
    HDU3507 Print Article(斜率优化DP)
    线性代数学习笔记(几何版)
    HDU 2065 "红色病毒"问题(生成函数)
  • 原文地址:https://www.cnblogs.com/AnKen/p/8343895.html
Copyright © 2020-2023  润新知