
了解OpenGL的同學(xué)應(yīng)該知道,使用固定著色器可以繪制這樣一個金字塔。今天我們來講解一下如何使用GLSL繪制這樣一個金字塔。
學(xué)習(xí)這片文章需要先了解一下上一篇文章 OpenGL ES 紋理繪制 。
我們先來準備頂點著色程序和片元著色程序的代碼。
頂點著色程序shaderv.glsl文件:
attribute vec4 position;
attribute vec4 positionColor;
attribute vec2 textureCoordinate;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
varying lowp vec4 varyColor;
varying lowp vec2 varyTextCoord;
void main()
{
varyColor = positionColor;
varyTextCoord = textureCoordinate;
vec4 transPos;
transPos = projectionMatrix * modelViewMatrix * position;
gl_Position = transPos;
}
position:頂點坐標。
positionColor:頂點顏色坐標。
textureCoordinate:紋理坐標。
projectionMatrix:投影矩陣。
modelViewMatrix: 模型視圖矩陣。
varyColor:用于將傳positionColor遞到片元著色器。varyColor名字和類型需要跟片元著色程序保持一致。
varyTextCoord:用于將傳textureCoordinate遞到片元著色器。varyTextCoord名字和類型需要跟片元著色程序保持一致。
片元著色程序shaderf.glsl文件:
precision highp float;
varying lowp vec4 varyColor;
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main()
{
vec4 weakMask = texture2D(colorMap, varyTextCoord);
vec4 mask = varyColor;
float alpha = 0.3;
vec4 tempColor = mask * (1.0 - alpha) + weakMask * alpha;
gl_FragColor = tempColor;
}
precision highp float:默認精度。
colorMap:紋理采樣器。
varyTextCoord:從頂點著色器傳遞過來的紋理坐標。
varyColor:從頂點著色器傳遞過來的顏色坐標。
tempColor:紋素和顏色轉(zhuǎn)換后的的顏色值。我們本案例繪制的是顏色和紋理混合的效果,所以需要轉(zhuǎn)換。
下面我們來看下代碼的核心流程。
我們在上一篇文章已 OpenGL ES 紋理繪制 中經(jīng)介紹過紋理填充的流程。本案例主流程其實是一樣的。
//1.設(shè)置layer
[self setupPyramidLayer];
//2.設(shè)置上下文
[self setupPyramidContext];
//3.清空renderBuffer和frameBuffer
[self deleteBuffer];
//4.設(shè)置renderBuffer
[self setupRenderBuffer];
//5.設(shè)置frameBuffer
[self setupFrameBuffer];
//6.繪制
[self draw];
前五步準備工作的代碼,跟上一篇文章 OpenGL ES 紋理繪制 中是一樣的,這里就不多介紹了。
我們重點介紹一下核心代碼第六步繪制:
6.1 基本操作
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0.0, 0.0, 0.0, 1.0);
CGFloat scale = [[UIScreen mainScreen] scale];
glViewport(self.frame.origin.x * scale, self.frame.origin.y * scale, self.frame.size.width * scale, self.frame.size.height * scale);
6.2 加載shader程序
NSString *vShaderPath = [[NSBundle mainBundle] pathForResource:@"shaderv" ofType:@"glsl"];
NSString *fShaderPath = [[NSBundle mainBundle] pathForResource:@"shaderf" ofType:@"glsl"];
if (self.program) {
glDeleteProgram(self.program);
self.program = 0;
}
//加載程序program
self.program = [self loadProgramWithShadervPath:vShaderPath shaderfPath:fShaderPath];
6.3 鏈接
glLinkProgram(self.program);
GLint linkSuccess;
glGetProgramiv(self.program, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE) {
GLchar message[512];
glGetProgramInfoLog(self.program, sizeof(message), 0, &message[0]);
NSString *messageString = [NSString stringWithUTF8String:message];
NSLog(@"Link program failed: %@", messageString);
return;
}
NSLog(@"Link program success!");
6.4 使用
glUseProgram(self.program);
到此前面的代碼都是我們上篇文章 OpenGL ES 紋理繪制 介紹過的,一樣的代碼。
6.5 處理頂點數(shù)據(jù)
我們這里繪制的金字塔,既有顏色也有圖片,所以我們的頂點坐標需要包含金字塔的 頂點坐標、顏色坐標、紋理坐標。
//頂點坐標
GLfloat attrArr[] =
{
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, //左上0
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, //右上1
-0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, //左下2
0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, //右下3
0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, //頂點4
};
//索引數(shù)組
GLuint indices[] =
{
0, 3, 2,
0, 1, 3,
0, 2, 4,
0, 4, 1,
2, 3, 4,
1, 4, 3,
};
if (self.vertices == 0) {
glGenBuffers(1, &_vertices);
}
glBindBuffer(GL_ARRAY_BUFFER, self.vertices);
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), &attrArr, GL_DYNAMIC_DRAW);
這里引入了索引數(shù)組 indices[],用于制定頂點的連接方式。我們用一張圖來標注一下金字塔的5個頂點:

索引數(shù)組
indices[]中代表了哪三個點連接成一個三角形。
6.6 設(shè)置讀取方式
GLsizei s = (GLsizei)sizeof(GLfloat)*8;
GLuint position = glGetAttribLocation(self.program, "position");
glEnableVertexAttribArray(position);
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, s, NULL);
GLuint positionColor = glGetAttribLocation(self.program, "positionColor");
glEnableVertexAttribArray(positionColor);
glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, s, (float *)NULL + 3);
GLuint textureCoordinate = glGetAttribLocation(self.program, "textureCoordinate");
glEnableVertexAttribArray(textureCoordinate);
glVertexAttribPointer(textureCoordinate, 2, GL_FLOAT, GL_FALSE, s, (float *)NULL + 6);
-
glGetAttribLocation(self.program, "position");:
從program讀取頂點數(shù)據(jù)position,注意這里的"position"一定要跟shaderv.vsh文件中的position保持一致。 -
glEnableVertexAttribArray(position);:
打開頂點數(shù)據(jù)讀取通道,默認是關(guān)閉的。 -
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);設(shè)置讀取方式:
6.7 設(shè)置投影矩陣、模型視圖矩陣
GLint projectionMatrixSlot = glGetUniformLocation(self.program, "projectionMatrix");
KSMatrix4 _projectionMatrix;
ksMatrixLoadIdentity(&_projectionMatrix);
float width = self.frame.size.width;
float height = self.frame.size.height;
float aspect = width / height;
ksPerspective(&_projectionMatrix, 30.0, aspect, 5.0f, 20.0f);
glUniformMatrix4fv(projectionMatrixSlot, 1, GL_FALSE, (GLfloat *)&_projectionMatrix.m[0][0]);
KSMatrix4 _modelViewMatrix;
ksMatrixLoadIdentity(&_modelViewMatrix);
ksTranslate(&_modelViewMatrix, 0.0, 0.0, -10.0);
KSMatrix4 _rotationMatrix;
ksMatrixLoadIdentity(&_rotationMatrix);
ksRotate(&_rotationMatrix, xDegree, 1.0, 0.0, 0.0);
ksRotate(&_rotationMatrix, yDegree, 0.0, 1.0, 0.0);
ksRotate(&_rotationMatrix, zDegree, 0.0, 0.0, 1.0);
ksMatrixMultiply(&_modelViewMatrix, &_rotationMatrix, &_modelViewMatrix);
GLuint modelViewMatrixSlot = glGetUniformLocation(self.program, "modelViewMatrix");
glUniformMatrix4fv(modelViewMatrixSlot, 1, GL_FALSE, (GLfloat *)&_modelViewMatrix.m[0][0]);
glGetUniformLocation(self.program, "projectionMatrix"):從program讀取投影矩陣projectionMatrix,注意這里的"projectionMatrix"一定要跟shaderv.glsl文件中的projectionMatrix保持一致。glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value):
location:該變量在shader中的位置。
count:被賦值的矩陣的數(shù)目(因為uniform變量可以是一個數(shù)組)。
transpose:表明在向uniform變量賦值時該矩陣是否需要轉(zhuǎn)置。
value:傳遞給uniform變量的數(shù)據(jù)的指針。xDegree, yDegree, zDegree是我們在旋轉(zhuǎn)中修改的角度。
這段代碼中KS開頭的使用到了三方庫中的API。
6.8 加載紋理
[self setupTexture:@"scence"];
該段代碼的內(nèi)容在上一篇文章 OpenGL ES 紋理繪制 中有詳解。
6.9 設(shè)置紋理采樣器 sampler2D
GLint colorMap = glGetUniformLocation(self.program, "colorMap");
glUniform1i(colorMap, 0);
//開啟剔除模式
glEnable(GL_CULL_FACE);
colorMap需要跟shaderf.fsh文件中保持一致。
6.10 繪制
glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(indices[0]), GL_UNSIGNED_INT, indices);
繪制方法跟上篇文章的有所不同。
- 參數(shù)一:繪制圖元裝配方式。
- 參數(shù)二:索引數(shù)組的個數(shù)。
- 參數(shù)三:索引數(shù)組中的類型。
- 參數(shù)四:索引數(shù)組。
6.11 從渲染緩存區(qū)顯示到屏幕上
[self.currentContext presentRenderbuffer:GL_RENDERBUFFER];
最后實現(xiàn)轉(zhuǎn)轉(zhuǎn)我們還需要創(chuàng)建個定時器修改角度 xDegree, yDegree, zDegree , 然后重新繪制。