3D Computer Grapihcs Using OpenGL - 07 Passing Data from Vertex to Fragment Shader
上節的最後我們實現了兩個綠色的三角形,而綠色是直接在Fragment Shader中指定的。
這節我們將為這兩個三角形進行更加自由的著色——五個頂點各自使用不同的顏色。
要實現這個目的,我們分兩步進行,首先
在頂點數組裏增加數據用來表示顏色
修改sendDataToOpenGL()函數中的verts數組:
1 GLfloat verts[] = 2 { 3 +0.0f, +0.0f, //Vertex 0 4 +1.0, +0.0, +0.0f, //Color 0 5 +1.0f, +1.0f, //Vertex 16 +0.0, +1.0, +0.0f, //Color 1 7 -1.0f, +1.0f, //Vertex 2 8 +0.0, +0.0, +1.0f, //Color 2 9 -1.0f, -1.0f, //Vertex 3 10 +0.5f, +0.3f, +0.1f,//Color 3 11 +1.0f, -1.0f, //Vertex 4 12 +0.1f, +0.4f, +0.2f,//Color 4 13 };
增加了5*3=15個元素,穿插在每個頂點位置後,表示顏色的r,g,b值。現在每5個數據描述一個頂點,前兩個表示頂點位置,後三個表示顏色。
為了把這些數據輸入到GPU,我們還需要啟用第二個通道。(我們之前只啟用了一個通道0)
修改sendDataToOpenGL()函數:
1 void MyGlWindow::sendDataToOpenGL() 2 { 3 GLfloat verts[] = 4 { 5 +0.0f, +0.0f, //Vertex 0 6 +1.0, +0.0, +0.0f, //Color 0 7 +1.0f, +1.0f, //Vertex 1 8 +0.0, +1.0, +0.0f, //Color 1 9 -1.0f, +1.0f, //Vertex 2 10 +0.0, +0.0, +1.0f, //Color 2 11 -1.0f, -1.0f, //Vertex 3 12 +1.0f, +1.0f, +0.0f,//Color 3 13 +1.0f, -1.0f, //Vertex 4 14 +0.0f, +1.0f, +1.0f,//Color 4 15 }; 16 17 GLuint vertexBufferID; 18 glGenBuffers(1, &vertexBufferID); 19 glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID); 20 glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); 21 22 GLushort indices[] = 23 { 24 0,1,2, 25 0,3,4, 26 }; 27 GLuint indexBufferID; 28 glGenBuffers(1, &indexBufferID); 29 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID); 30 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); 31 32 glEnableVertexAttribArray(0); 33 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, 0); 34 35 glEnableVertexAttribArray(1); 36 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (char*)(sizeof(GLfloat) * 2)); 37 }
註意第33行,函數glVertexAttribPointer函數第三個參數變成了sizeof(GLfloat)*5,原因是Stride變成了5個float,如前所述,5個元素描述一個頂點。
另外增加了第35和36行,35行開啟了通道1,這個1和前面的0是和Shader中的layout (location = x)對應的,稍後進行詳解。
第36行glVertexAttribPoint函數的第一個參數對應35行的1, 第二個參數表示三個元素為一組。
需要特別註意的是最後一個參數,它表示本組數據的起始偏移值,本組數據的第一個位置在代碼中的第六行,它前面有2個元素,所以這裏是sizeof(GLfloat)*2,但是由於該函數第四個參數的類型是char*類型,我們只能強制轉換為char * 。
然後我們需要
修改Shader
1 const char* vertexShaderCode = 2 " #version 430 \r\n" 3 " \r\n" 4 " in layout(location=0) vec2 position; \r\n" 5 " in layout(location=1) vec3 vertexColor; \r\n" 6 " \r\n" 7 " out vec3 passingColor; \r\n" 8 " \r\n" 9 " void main() \r\n" 10 " { \r\n" 11 " gl_Position= vec4(position,0.0,1.0); \r\n" 12 " passingColor= vertexColor; \r\n" 13 " } \r\n" 14 " \r\n" 15 " \r\n"; 16 17 const char* fragmentShaderCode = 18 " #version 430 \r\n" 19 " \r\n" 20 " in vec3 passingColor; \r\n" 21 " out vec4 finalColor; \r\n" 22 " \r\n" 23 " \r\n" 24 " void main() \r\n" 25 " { \r\n" 26 " finalColor = vec4(passingColor,1.0); \r\n" 27 " } \r\n" 28 " \r\n" 29 " \r\n";
先看Vertex Shader。
增加了第5行。
仔細看一下第4行和第5行,對比sendDataToOpenGL()函數的33和36行, location=0指定了通道0, vec2說明了需要二維向量(2個float),33行函數的前兩個參數也分別是0(表示通道0),2(表示兩個元素),兩者是相對應的。
ShaderCode的第5行和sendDataToOpenGL()的36行也是對應的。
再對應verts[]數組對比著看,就會發現其工作原理。verts數組每五個元素為一組,每組的前兩個元素將被發送到通道0,後三個元素將被發送到通道1。
另外Vertex Shader還增加了第7行,這裏使用了out關鍵字定義了一個三維向量passingColor,說明這個參數將被發送出去,傳遞到渲染管線的下一個環節。而在main中,我們把剛剛從通道2接受到的數據(存在vertexColor裏)傳遞給passingColor。所以說相當於我們沒有對通道2傳遞進來的數據做任何處理,直接發送到下一個環節。
觀察Fragment Shader,我們增加了20行,註意看這行和Vertex Shader的第7行非常相似,除了第一個關鍵字改成了in。
這種匹配是GLSL的一種固定模式,上遊的out 變量會傳遞給下遊的in變量,只要兩者保持一致性。
在Fragment Shader的main中,我們把最終的顏色輸出改成了 vec4(passingColor,1.0),也就是我們把這個顏色表現了出來。
縱觀全局,我們這次做的修改將把verts數組中新增的一些信息傳遞先傳遞到Vertex Shader中,然後傳遞到FragmentShader中,最終輸出成頂點的顏色。
編譯運行我們看到兩個彩色的三角形:
3D Computer Grapihcs Using OpenGL - 07 Passing Data from Vertex to Fragment Shader