前言
本文是關于OpenGL ES的系統(tǒng)性學習過程涂佃,記錄了自己在學習OpenGL ES時的收獲。
這篇文章的目標是學習OpenGL ES著色器語言咆贬。
環(huán)境是Xcode8.1+OpenGL ES 2.0
目前代碼已經放到github上面,OpenGL ES入門02-OpenGL ES著色器
歡迎關注我的 OpenGL ES入門專題
概述
著色器(Shader)是運行在GPU上的小程序。這些小程序為圖形渲染管線的某個特定部分而運行缴阎。從基本意義上來說,著色器只是一種把輸入轉化為輸出的程序简软。著色器也是一種非常獨立的程序蛮拔,因為它們之間不能相互通信;它們之間唯一的溝通只有通過輸入和輸出替饿。
效果
著色器語言
著色器是使用一種叫GLSL的類C語言寫成的语泽。GLSL是為圖形計算量身定制的,它包含一些針對向量和矩陣操作的有用特性视卢。
著色器分類
頂點著色器 是一個可編程的處理單元踱卵,執(zhí)行頂點變換、紋理坐標變換据过、光照惋砂、材質等頂點的相關操作,每頂點執(zhí)行一次绳锅。替代了傳統(tǒng)渲染管線中頂點變換西饵、光照以及紋理坐標的處理。
attribute vec3 position;
attribute vec3 color;
varying vec3 outColor;
void main()
{
gl_Position = vec4(position, 1.0);
outColor = color;
}
片元著色器 是一個處理片元值及其相關聯(lián)數據的可編程單元鳞芙,片元著色器可執(zhí)行紋理的訪問眷柔、顏色的匯總期虾、霧化等操作,每片元執(zhí)行一次驯嘱。
precision mediump float;
varying vec3 outColor;
void main()
{
gl_FragColor = vec4(outColor, 1.0);
}
數據類型
類型 | 說明 |
---|---|
float | 浮點型 |
bool | 布爾型 |
int | 整形 |
vec2 | 包含了2個浮點數的向量 |
vec3 | 包含了3個浮點數的向量 |
vec4 | 包含了4個浮點數的向量 |
ivec2 | 包含了2個整數的向量 |
ivec3 | 包含了3個整數的向量 |
ivec4 | 包含了4個整數的向量 |
bvec2 | 包含了2個布爾數的向量 |
bvec3 | 包含了3個布爾數的向量 |
bvec4 | 包含了4個布爾數的向量 |
mat2 | 2*2維矩陣 |
mat3 | 3*3維矩陣 |
mat4 | 4*4維矩陣 |
sampler1D | 1D紋理采樣器 |
sampler2D | 2D紋理采樣器 |
sampler3D | 3D紋理采樣器 |
samplerCube | Cube紋理采樣器 |
常量
const 可以用來修飾任何基本數據類型镶苞。通常const變量在聲明的同時要進行初始化,結構體字段不能使用const修飾嗎鞠评,但是變量可以茂蚓,并通過構造器進行初始化。包含數組的數組和結構體不能聲明為常量剃幌,因為它們不能被初始化聋涨。
onst vec4 color = vec4 (1.0, 1.0, 1.0, 1.0);
存儲修飾符
attribute 變量(屬性變量)只能用于頂點著色器中,不能用于片元著色器负乡。 一般用該變量來表示一些頂點數據牍白,如:頂點坐標、紋理坐標敬鬓、顏色等淹朋。
uniforms 是一種從CPU中的應用向GPU中的著色器發(fā)送數據的方式,但uniform和頂點屬性有些不同钉答。首先础芍,uniform是全局的(Global)。全局意味著uniform變量必須在每個著色器程序對象中都是獨一無二的数尿,而且它可以被著色器程序的任意著色器在任意階段訪問仑性。第二,無論你把uniform值設置成什么右蹦,uniform會一直保存它們的數據诊杆,直到它們被重置或更新。
samplers 一種特殊的 uniform何陆,用于呈現紋理晨汹。sampler 可用于頂點著色器和片元著色器。
varying 變量(易變變量)是從頂點著色器傳遞到片元著色器的數據變量贷盲。頂點著色器可以使用易變變量來傳遞需要插值的顏色淘这、法向量、紋理坐標等任意值巩剖。 在頂點與片元shader程序間傳遞數據是很容易的铝穷,一般在頂點shader中修改varying變量值,然后片元shader中使用該值佳魔,當然,該變量在頂點及片元這兩段shader程序中聲明必須是一致的 鞠鲜。例如:上面代碼中應用程序中由頂點著色器傳入片元著色器中的vColor變量断国。
精度修飾符
precision 可以用來確定默認精度修飾符朦佩。類型可以是int或float或采樣器類型,precision-qualifier可以是lowp, mediump, 或者highp语稠。任何其他類型和修飾符都會引起錯誤。如果type是float類型弄砍,那么該精度(precision-qualifier)將適用于所有無精度修飾符的浮點數聲明(標量仙畦,向量,矩陣)音婶。如果type是int類型慨畸,那么該精度(precision-qualifier)將適用于所有無精度修飾符的整型數聲明(標量,向量)衣式。包括全局變量聲明寸士,函數返回值聲明,函數參數聲明碴卧,和本地變量聲明等弱卡。沒有聲明精度修飾符的變量將使用和它最近的precision語句中的精度。
precision highp float;
precision highp int;
precision lowp sampler2D;
precision lowp samplerCube;
內建變量
gl_Position 頂點著色器內建變量住册,表示變換后點的空間位置婶博。 頂點著色器從應用程序中獲得原始的頂點位置數據,這些原始的頂點數據在頂點著色器中經過平移荧飞、旋轉凡人、縮放等數學變換后,生成新的頂點位置叹阔。新的頂點位置通過在頂點著色器中寫入gl_Position傳遞到渲染管線的后繼階段繼續(xù)處理挠轴。
*** gl_PointSize*** 頂點著色器內置變量,設置柵格化點的直徑,也就是點的大小,通常用于點精靈耳幢,粒子等繪制岸晦。
gl_FragColor 片元著色器內置變量,用來保存片元著色器計算完成的片元顏色值帅掘,此顏色值將送入渲染管線的后繼階段進行處理委煤。
加載著色器
static GLuint createGLShader(const char *shaderText, GLenum shaderType)
{
GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &shaderText, NULL);
glCompileShader(shader);
int compiled = 0;
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);
if (infoLog) {
glGetShaderInfoLog (shader, infoLen, NULL, infoLog);
GLlog("Error compiling shader: %s\n", infoLog);
free(infoLog);
}
}
glDeleteShader(shader);
return 0;
}
return shader;
}
1、創(chuàng)建著色器修档,通過glCreateShader創(chuàng)建著色器碧绞,type為著色器的類型GL_VERTEX_SHADER 和 GL_FRAGMENT_SHADER
glCreateShader (GLenum type) //type: GL_VERTEX_SHADER 或者 GL_FRAGMENT_SHADER
2、添加著色器源程序吱窝,將著色器源碼關聯(lián)到一個著色器對象shader上讥邻。string是一個有count行GLchar類型的字符串組成的數組迫靖,用來表示著色器的源代碼數據。string可以以NULL結尾兴使,也可以不是系宜。如果length為NULL則string給出的每行都是以NULL結尾,否則length中必須有count個表示string長度的元素盹牧。(也就是說字符串以NULL結尾我們不用指定長度汰寓,否則必須制定每行的長度)
glShaderSource (GLuint shader, GLsizei count, const GLchar* const *string, const GLint* length)
3有滑、編譯著色器源程序
glCompileShader (GLuint shader)
4毛好、刪除著色器
glDeleteShader (GLuint shader)
5苛秕、容錯處理想帅,通過glGetShaderiv獲取編譯狀態(tài),通過glGetShaderInfoLog獲取錯誤信息旨剥。
#define GL_COMPILE_STATUS 0x8B81
#define GL_INFO_LOG_LENGTH 0x8B84
glGetShaderiv (GLuint shader, GLenum pname, GLint* params)
glGetShaderInfoLog (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog)
創(chuàng)建著色器程序
GLuint createGLProgram(const char *vertext, const char *frag)
{
GLuint program = glCreateProgram();
GLuint vertShader = createGLShader(vertext, GL_VERTEX_SHADER);
GLuint fragShader = createGLShader(frag, GL_FRAGMENT_SHADER);
if (vertShader == 0 || fragShader == 0) {
return 0;
}
glAttachShader(program, vertShader);
glAttachShader(program, fragShader);
glLinkProgram(program);
GLint success;
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
GLint infoLen;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
GLchar *infoText = (GLchar *)malloc(sizeof(GLchar)*infoLen + 1);
if (infoText) {
memset(infoText, 0x00, sizeof(GLchar)*infoLen + 1);
glGetProgramInfoLog(program, infoLen, NULL, infoText);
GLlog("%s", infoText);
free(infoText);
}
}
glDeleteShader(vertShader);
glDeleteShader(fragShader);
glDeleteProgram(program);
return 0;
}
glDetachShader(program, vertShader);
glDetachShader(program, fragShader);
glDeleteShader(vertShader);
glDeleteShader(fragShader);
return program;
}
1、創(chuàng)建著色器程序
glCreateProgram (void)
2衩椒、裝配著色器
glAttachShader (GLuint program, GLuint shader)
3毛萌、鏈接著色器程序
glLinkProgram (GLuint program)
4、卸載著色器程序
glDetachShader (GLuint program, GLuint shader)
5膏秫、使用著色器程序
glUseProgram (GLuint program)
6缤削、容錯處理
#define GL_LINK_STATUS 0x8B82
#define GL_INFO_LOG_LENGTH 0x8B84
glGetProgramiv (GLuint program, GLenum pname, GLint* params)
glGetProgramInfoLog (GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog)
三角形繪制
- (void)setupGLProgram
{
NSString *vertFile = [[NSBundle mainBundle] pathForResource:@"vert.glsl" ofType:nil];
NSString *fragFile = [[NSBundle mainBundle] pathForResource:@"frag.glsl" ofType:nil];
_program = createGLProgramFromFile(vertFile.UTF8String, fragFile.UTF8String);
glUseProgram(_program);
}
- (void)setupVertexData
{
// 需要加static關鍵字亭敢,否則數據傳輸存在問題
static GLfloat vertices[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
GLint posSlot = glGetAttribLocation(_program, "position");
glVertexAttribPointer(posSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(posSlot);
//顏色數據
static GLfloat colors[] = {
0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f
};
GLint colorSlot = glGetAttribLocation(_program, "color");
glVertexAttribPointer(colorSlot, 3, GL_FLOAT, GL_FALSE, 0, colors);
glEnableVertexAttribArray(colorSlot);
}
- (void)render
{
glClearColor(1.0, 1.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
// 在這里初始化數據帅刀,可以加static關鍵字劝篷,也可以不加
// GLfloat vertices[] = {
// 0.0f, 0.5f, 0.0f,
// -0.5f, -0.5f, 0.0f,
// 0.5f, -0.5f, 0.0f
// };
// GLint posSlot = glGetAttribLocation(_program, "position");
// glVertexAttribPointer(posSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
// glEnableVertexAttribArray(posSlot);
//
// GLfloat colors[] = {
// 0.0f, 1.0f, 0.0f,
// 0.0f, 1.0f, 0.0f,
// 0.0f, 1.0f, 0.0f
// };
// GLint colorSlot = glGetAttribLocation(_program, "color");
// glVertexAttribPointer(colorSlot, 3, GL_FLOAT, GL_FALSE, 0, colors);
// glEnableVertexAttribArray(colorSlot);
[self setupVertexData];
glDrawArrays(GL_TRIANGLES, 0, 3);
//將指定 renderbuffer 呈現在屏幕上娇妓,在這里我們指定的是前面已經綁定為當前 renderbuffer 的那個哈恰,在 renderbuffer 可以被呈現之前着绷,必須調用renderbufferStorage:fromDrawable: 為之分配存儲空間锌云。
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
indx 指定要修改的頂點著色器中頂點變量id桑涎;
size 指定每個頂點屬性的組件數量。必須為1娃胆、2里烦、3或者4禁谦。
type 指定數組中每個組件的數據類型∩フ海可用的符號常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT触趴,初始值為GL_FLOAT;
normalized 指定當被訪問時爽冕,固定點數據值是否應該被歸一化(GL_TRUE)或者直接轉換為固定點值(GL_FALSE)披蕉;
stride 指定連續(xù)頂點屬性之間的偏移量没讲。如果為0,那么頂點屬性會被理解為:它們是緊密排列在一起的徙缴。初始值為0于样;
ptr 頂點數據指針。
參考鏈接
https://www.khronos.org/opengles/sdk/docs/reference_cards/OpenGL-ES-2_0-Reference-card.pdf
http://www.cnblogs.com/kesalin/archive/2012/11/25/opengl_es_tutorial_02.html