OpenGL ES shading language 3.0 也被稱作 GLSL,是個 C風格的編程語言。
Opengl ES 3.0內部有兩種可編程處理單元,即Vertex processor和Fragment processor,分別用來處理Vertex shader executable和Fragment shader executable。注意,Opengl ES 3.0不支持Geometry Shader。上圖中,紫色部分就是可執行體了,即 executable .
先來一段Vertex shader代碼
#version 300 es //版本號 in vec4 VertexPosition; //應用層輸入逐頂點位置坐標數據 in vec4 VertexColor; //應用層輸入逐頂點顏色數據 uniform float RadianAngle; //應用層輸入數據 out vec4 TriangleColor;
mat2 rotation = mat2(cos(RadianAngle),sin(RadianAngle), -sin(RadianAngle),cos(RadianAngle));
void main()
{
gl_Position = mat4(rotation)*VertexPosition;//給內置數據賦值
TriangleColor = VertexColor;
}
再來一段Fragment shader代碼
#version 300 es precision mediump float;//配置精度 in vec4 TriangleColor; out vec4 FragColor; void main() { FragColor = TriangleColor; };
上面這兩段代碼,只是文本數據,上述的兩種processor可沒辦法直接執行,這就需要一個叫做編譯和鏈接的步驟,來將這個文本數據變成executable的數據。可以通過下圖來了解這個創建executable的過程:
需要在應用層加載和編譯shader,使用如下代碼進一步說明:
GLuint loadAndCompileShader(GLenum shaderType, const char* sourceCode)
{ // Create the shader GLuint shader = glCreateShader(shaderType); if ( shader )
{ // Pass the shader source code glShaderSource(shader, 1, &sourceCode, NULL); // Compile the shader source code glCompileShader(shader); // Check the status of compilation GLint compiled = 0; glGetShaderiv(shader,GL_COMPILE_STATUS,&compiled); if (!compiled)
{ // Get the info log for compilation failure GLint infoLen = 0; glGetShaderiv(shader,GL_INFO_LOG_LENGTH, &infoLen); if (infoLen)
{ char* buf = (char*) malloc(infoLen); if (buf)
{ glGetShaderInfoLog(shader, infoLen, NULL, buf); printf("Could not compile shader %s:" buf); free(buf); } // Delete the shader program glDeleteShader(shader); shader = 0; } } } return shader; }
使用如下代碼來執行鏈接過程:
GLuint linkShader(GLuint vertShaderID,GLuint fragShaderID){ if (!vertShaderID || !fragShaderID){ // Fails! return return 0; } // Create an empty program object GLuint program = glCreateProgram(); if (program) { // Attach vertex and fragment shader to it glAttachShader(program, vertShaderID); glAttachShader(program, fragShaderID); // Link the program glLinkProgram(program); GLint linkStatus = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); if (linkStatus != GL_TRUE) { GLint bufLength = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); if (bufLength) { char* buf = (char*) malloc(bufLength); if(buf) { glGetProgramInfoLog(program,bufLength,NULL,buf); printf("Could not link program:\n%s\n", buf);
free(buf);
}
}
glDeleteProgram(program);
program = 0;
}
}
return program;
}
前文一直提到的executable就是這個返回的句柄變量 program。這個program將會在流水線的Processor上執行。
回過頭再來分析vertex shader代碼和fragment shader代碼。
in vec4 VertexPosition; in vec4 VertexColor;
shader代碼里聲明的這兩個變量的值是從哪里獲取的呢,這就涉及了一個重要的環節,就是應用層和shader層的數據通信問題。為了方便理解,根據執行的位置不同,可以把應用層看做CPU,把shader層看做GPU。即可抽象為CPU與GPU通信,進一步抽象為客戶端C和服務端S之間的通信。下面來解釋這個通信的流程,從通信上來說,必然是要先讓客戶端找到服務器端的一個通信端口,然后客戶端建立與這個通信端口的連接,最后只要往這個連接上扔數據,這樣服務端就能收到數據了。
首先實現尋找到服務端通信端口的功能,以下代碼就幫助CPU端找到GPU端的數據通信端口
GLuint positionAttribHandle; GLuint colorAttribHandle; positionAttribHandle = glGetAttribLocation(programID, "VertexPosition"); colorAttribHandle = glGetAttribLocation(programID, "VertexColor");
以下代碼,實現了往通信通道上扔數據的功能:
// Send data to shader using queried attrib location glVertexAttribPointer(positionAttribHandle, 2, GL_FLOAT,GL_FALSE, 0, gTriangleVertices); glVertexAttribPointer(colorAttribHandle, 3, GL_FLOAT,GL_FALSE, 0, gTriangleColors);
GPU端,默認所有這些頂點屬性端口都是關閉的,因此需要打開這些通信端口:
// Enable vertex position attribute glEnableVertexAttribArray(positionAttribHandle); glEnableVertexAttribArray(colorAttribHandle);
這樣,一條通信信道就算建立起來了。