![](https://img.haomeiwen.com/i10966263/1b9f5636b75a81d9.gif)
了解OpenGL的同学应该知道,使用固定着色器可以绘制这样一个金字塔。今天我们来讲解一下如何使用GLSL绘制这样一个金字塔。
学习这片文章需要先了解一下上一篇文章 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
:纹素和颜色转换后的的颜色值。我们本案例绘制的是颜色和纹理混合的效果,所以需要转换。
下面我们来看下代码的核心流程。
我们在上一篇文章已 OpenGL ES 纹理绘制 中经介绍过纹理填充的流程。本案例主流程其实是一样的。
//1.设置layer
[self setupPyramidLayer];
//2.设置上下文
[self setupPyramidContext];
//3.清空renderBuffer和frameBuffer
[self deleteBuffer];
//4.设置renderBuffer
[self setupRenderBuffer];
//5.设置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 处理顶点数据
我们这里绘制的金字塔,既有颜色也有图片,所以我们的顶点坐标需要包含金字塔的 顶点坐标、颜色坐标、纹理坐标。
//顶点坐标
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
};
//索引数组
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);
这里引入了索引数组 indices[]
,用于制定顶点的连接方式。我们用一张图来标注一下金字塔的5个顶点:
![](https://img.haomeiwen.com/i10966263/16f4d5969af08fb3.png)
索引数组
indices[]
中代表了哪三个点连接成一个三角形。
6.6 设置读取方式
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
读取顶点数据position
,注意这里的"position"一定要跟shaderv.vsh
文件中的position
保持一致。 -
glEnableVertexAttribArray(position);
:
打开顶点数据读取通道,默认是关闭的。 -
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);
设置读取方式:
6.7 设置投影矩阵、模型视图矩阵
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
:被赋值的矩阵的数目(因为uniform
变量可以是一个数组)。
transpose
:表明在向uniform
变量赋值时该矩阵是否需要转置。
value
:传递给uniform
变量的数据的指针。 -
xDegree, yDegree, zDegree
是我们在旋转中修改的角度。
这段代码中KS
开头的使用到了三方库中的API。
6.8 加载纹理
[self setupTexture:@"scence"];
该段代码的内容在上一篇文章 OpenGL ES 纹理绘制 中有详解。
6.9 设置纹理采样器 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);
绘制方法跟上篇文章的有所不同。
- 参数一:绘制图元装配方式。
- 参数二:索引数组的个数。
- 参数三:索引数组中的类型。
- 参数四:索引数组。
6.11 从渲染缓存区显示到屏幕上
[self.currentContext presentRenderbuffer:GL_RENDERBUFFER];
最后实现转转我们还需要创建个定时器修改角度 xDegree, yDegree, zDegree
, 然后重新绘制。
网友评论