一.什么是openGL
OpenGL被定義為“圖形硬件的一種軟件接口”。從本質上說,它是一個3D圖形和模型庫,具有高度的可移植性,具有非常快的速度。
二.管線
管線這個術語描述了opengl渲染的整個過程。openGL采用cs模型:c是cpu,s是GPU,c給s的輸入是vertex信息和Texture信息,s的輸出是顯示器上顯示的圖像。下面這2個圖比較清楚的講解了opengl的渲染管線。


相信沒有opengl基礎的應該看不懂,下面會簡單的介紹這個流程,再看下文之前,建議先看GPU大百科全書系列文章,對理解opengl工作原理絕對有幫助。
三.管線詳解
下面這個圖取自《OpenGL ES 3.0編程指南》,此流程為可編程管線。

1.VBO/VAO(頂點緩沖區對象或頂點數組對象):
VBO/VAO(到底是啥,下回講解)是cpu提供給GPU的頂點信息,包括了頂點的位置、顏色(只是頂點的顏色,和紋理的顏色無關)、紋理坐標(用於紋理貼圖)等頂點信息。
2.VertexShader(頂點着色器):
頂點着色器是處理VBO/VAO提供的頂點信息的程序。VBO/VAO提供的每個頂點都執行一遍頂點着色器。Uniforms(一種變量類型)在每個頂點保持一致,Attribute每個頂點都不同(可以理解為輸入頂點屬性)。執行一次VertexShader輸出一個Varying和gl_positon。

3.PrimitiveAssembly(圖元裝配):
頂點着色器下一個階段是圖元裝配,圖元(prmitive)是三角形、直線或者點精靈等幾何對象。這個階段,把頂點着色器輸出的頂點組合成圖元。
4.rasterization(光柵化):
光柵化是將圖元轉化為一組二維片段的過程,然后,這些片段由片段着色器處理(片段着色器的輸入)。這些二維片段代表着可在屏幕上繪制的像素。用於從分配給每個圖元頂點的頂點着色器輸出生成每個片段值的機制稱作插值(Interpolation)。這句不是人話的話解釋了一個問題,就是從cpu提供的分散的頂點信息是如何變成屏幕上密集的像素的,圖元裝配后頂點可以理解成變為圖形,光柵化時可以根據圖形的形狀,插值出那個圖形區域的像素(紋理坐標v_texCoord、顏色等信息)。注意,此時的像素並不是屏幕上的像素,是不帶有顏色的。接下來的片段着色器完成上色的工作。
5.FragmentShader(片段着色器):
片段着色器為片段(像素)上的操作實現了通用的可編程方法,光柵化輸出的每個片段都執行一遍片段着色器,對光柵化階段生成每個片段執行這個着色器,生成一個或多個(多重渲染)顏色值作為輸出。

6.Per-Fragment Operations(逐片段操作)
在此階段,每個片段上執行如下功能:

(1)pixelOwnershipTest(像素歸屬測試):
這個用來確定幀緩沖區中位置(x,y)的像素是不是歸當前上下文所有。例如,如果一個顯示幀緩沖區窗口被另一個窗口所遮蔽,則窗口系統可以確定被遮蔽的像素不屬於此opengl的上下文,從而不顯示這些像素。
(2)ScissorTest(剪裁測試):
如果該片段位於剪裁區域外,則被拋棄
(3)StencilTest and DepthTest(模板和深度測試):
深度測試比較好理解,若片段着色器返回的深度小於緩沖區中的深度,則舍棄。模板測試沒有用過,不清楚具體功能,猜測功能應該和名字一樣,模板形狀內可通過。
(4)Blending(混合):
將新生成的片段顏色值與保存在幀緩沖區的顏色值組合起來,產生新的RGBA。
(5)dithering(抖動):
不知道這個是神馬作用?
最后把產生的片段放到幀緩沖區(前緩沖區或后緩沖區或FBO)中,若不是FBO,則屏幕繪制緩沖區中的片段,產生屏幕上的像素。
7.固定管線
固定管線是沒有shader參與的OpenGL繪制管線,OpenGL3.0已經廢除了這個功能。固定管線的流程如下所示:

代碼示例:
#include <iostream>
#include <GLUT/GLUT.h>
#include <OpenGL/gl.h>
void RenderScene(void) {
glClear(GL_COLOR_BUFFER_BIT);
glShadeModel(GL_SMOOTH);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glBegin(GL_TRIANGLES);
glColor3ub((GLubyte)255, (GLubyte)0, (GLubyte)0);
glVertex3f(0, 200, 0);
glColor3ub((GLubyte)0, (GLubyte)255, (GLubyte)0);
glVertex3f(200, -70, 0);
glColor3ub((GLubyte)0, (GLubyte)0, (GLubyte)255);
glVertex3f(-200, -70, 0);
glEnd();
glutSwapBuffers();
}
void SetupRC(void) {
glClearColor(0, 0, 0, 1);
}
void changeSize(int w, int h) {
GLfloat windowHeight, windowWidth;
if (h == 0) {
h = 1;
}
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
windowHeight = windowWidth = 250;
glOrtho(-windowWidth, windowWidth, -windowHeight, windowHeight, -1, 1);
}
int main(int argc, char * argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(600, 600);
glutCreateWindow("Simple");
const GLubyte * info = glGetString(GL_VERSION);
std::cout << ( const char *)info << std::endl;
glutReshapeFunc(changeSize);
glutDisplayFunc(RenderScene);
SetupRC();
glutMainLoop();
return 0;
}
效果為:

8.可編程管線:
OpenGL 2.0引入了可編程管線,shader得到支持,流程如下圖所示:

要得到相同的效果,代碼如下:
#include <iostream>
#include <GLUT/GLUT.h>
#include <OpenGL/gl.h>
GLuint createShader(GLenum type, const char * shaderSrc) {
GLuint shader;
GLint compiled;
shader = glCreateShader(type);
if (shader == 0) {
return 0;
}
glShaderSource(shader, 1, &shaderSrc, NULL);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char * infoLog = (char *)malloc(sizeof(char) * infoLen);
glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
std::cout << "Error compiling shader: " << infoLog << std::endl;
free(infoLog);
}
glDeleteShader(shader);
return 0;
}
return shader;
}
GLuint programObject;
bool init() {
//todo 讀文件
char * vShaderStr =
"attribute vec4 vPositon; \n"
"attribute vec4 aColor; \n"
"varying vec4 vertexColor; \n"
"void main() { \n"
" gl_Position = gl_ModelViewProjectionMatrix * vPositon; \n"
" vertexColor = aColor; \n"
"}"
;
char * fShaderStr =
"varying vec4 vertexColor;"
"void main() {"
" gl_FragColor = vertexColor;"
"}"
;
GLuint vertexShader;
GLuint fragmentShader;
GLint linked;
vertexShader = createShader(GL_VERTEX_SHADER, vShaderStr);
fragmentShader = createShader(GL_FRAGMENT_SHADER, fShaderStr);
programObject = glCreateProgram();
if (programObject == 0) {
return false;
}
glAttachShader(programObject, vertexShader);
glAttachShader(programObject, fragmentShader);
glLinkProgram(programObject);
glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
if (!linked) {
GLint infoLen = 0;
glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char * infoLog =(char *)malloc(sizeof(char) * infoLen);
glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
std::cout << "Error linking program: " << infoLog << std::endl;
free(infoLog);
}
glDeleteProgram(programObject);
return false;
}
glClearColor(0, 0, 0, 0);
return true;
}
void RenderScene(void) {
GLfloat vVerties[] = {
0, 200, 0,
200, -70, 0,
-200, -70, 0
};
GLfloat vertexColor[] = {
1,0,0,1,
0,1,0,1,
0,0,1,1
};
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(programObject);
GLint attributePos = glGetAttribLocation(programObject, "vPositon");
glVertexAttribPointer(attributePos, 3, GL_FLOAT, GL_FALSE, 0, vVerties);
glEnableVertexAttribArray(attributePos);
GLint colorPos = glGetAttribLocation(programObject, "aColor");
glVertexAttribPointer(colorPos, 4, GL_FLOAT, GL_FALSE, 0, vertexColor);
glEnableVertexAttribArray(colorPos);
glDrawArrays(GL_TRIANGLES, 0, 3);
glutSwapBuffers();
}
void changeSize(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLfloat windowHeight, windowWidth;
windowHeight = windowWidth = 250;
glOrtho(-windowHeight, windowHeight, -windowWidth, windowWidth, -1, 1);
}
int main(int argc, char * argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(600, 600);
glutCreateWindow("Simple");
glutReshapeFunc(changeSize);
glutDisplayFunc(RenderScene);
init();
glutMainLoop();
return 0;
}
