梳理 Opengl ES 3.0 (二)剖析一個GLSL程序


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);

這樣,一條通信信道就算建立起來了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM