3D Computer Grapihcs Using OpenGL - 10 Color Buffer
本節我們將嘗試利用三角形制作一個“走馬燈”效果。
一個三角形如圖示方式,從左向右依次移動。
先看一下代碼:
MyGlWindow.cpp
1 #include <gl\glew.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 uintmaxTriangleCount = 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 <QtOpenGL\qgl.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也添加上。
這樣修改以後,就可以實現"走馬燈"效果了!(效果就不截圖了,動圖太難弄了)
3D Computer Grapihcs Using OpenGL - 10 Color Buffer