[toc]
前言
OpenGL
提供了一些绘图函数。到目前为止我们使用的glDrawArrays
绘图函数属于”顺序绘制
”。这意味着顶点缓冲区从指定的偏移量
开始被扫描,每X
(点为1
,直线为2等)个顶点
构成一个图元
。这样使用起来非常方便,缺点是当多个图元共用一个顶点时,这个顶点必须在顶点缓冲区中出现多次。也就是说,这些顶点没有共享的概念。属于”索引绘制
”的函数则提供这种共享机制。我们除了一个顶点缓存区
外,还有一个索引缓存区用来存放顶点的索引值
。索引缓存区的扫描和顶点缓存区类似,以每X
个索引对应的顶点构成一个基本图元
。共享机制在提高内存使用效率上非常重要,因为计算机中的绝大多数图形对象都是三角形网格构成的,这些三角形有很多都是共用顶点。
- 顺序绘制,一个金字塔,需要传入
18
个顶点的信息
- 索引绘制
1.只需要传入5
个顶点的位置和绘制不同面的连接方式.
GLSL绘制金字塔
- 0.vsh,fsh自定义着色器的代码
1..vsh
...
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
...
void main(){
...
vec4 vPos;
vPos = projectionMatrix * modelViewMatrix * position;
gl_Position = vPos;
}
2.fsh
varying lowp vec4 varyColor;
void main(){
gl_FragColor = varyColor;
}
增加两个
uniform
修饰的4x4矩阵,模型矩阵和投影矩阵.
将它们的相乘之后的结果返回给gl_Position
;
- 1.设置图层
- (void)setUpLayer{
self.myEagLayer = (CAEAGLLayer *)self.layer;
[self setContentScaleFactor:[[UIScreen mainScreen] scale]];
self.myEagLayer.opaque = YES;
NSDictionary *dict = @{
kEAGLDrawablePropertyRetainedBacking:@(NO),
kEAGLDrawablePropertyColorFormat:kEAGLColorFormatRGBA8
};
self.myEagLayer.drawableProperties = dict;
}
+ (Class)layerClass{
return [CAEAGLLayer class];
}
- 2.设置上下文
- (void)setContext{
EAGLContext *context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!context){
NSLog(@"context init error");
return;
}
if (![EAGLContext setCurrentContext:context]){
NSLog(@"setCurrentContext error");
return;
}
self.myContext = context;
}
- 3.清空缓冲区
- (void)deleBuffer{
glDeleteBuffers(1, &_myColorRenderBuffer);
self.myColorRenderBuffer = 0;
glDeleteBuffers(1, &_myColorFrameBuffer);
self.myColorFrameBuffer = 0;
}
- 设置renderBuffer
- (void)setUpRenderBuffer{
GLuint buffer;
glGenRenderbuffers(1, &buffer);
self.myColorRenderBuffer = buffer;
glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffer);
[self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myEagLayer];
}
- 设置renderBuffer
- (void)setUpFrameBuffer{
GLuint buffer;
glGenFramebuffers(1, &buffer);
self.myColorFrameBuffer = buffer;
glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);
}
- 开启绘制
- 加载链接自定义着色器
//清屏设置颜色
glClearColor(0.0, 0.0, 0.3, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
//调整视口
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);
//获取顶点着色器程序和片段着色器程序的文件路径
NSString *shaderVfile = [[NSBundle mainBundle] pathForResource:@"shaderV" ofType:@"vsh"];
NSString *shaderFfile = [[NSBundle mainBundle] pathForResource:@"shaderF" ofType:@"fsh"];
//存在清空
if (self.myProgram){
glDeleteProgram(self.myProgram);
self.myProgram = 0;
}
//加载 绑定 链接 使用
// self.myProgram = [self loadShader:shaderVfile frag:shaderFfile];
self.myProgram = [GLESUtils loadProgram:shaderVfile withFragmentShaderFilepath:shaderFfile];
//链接
glLinkProgram(self.myProgram);
GLint linkSuccess;
//获取链接状态
glGetProgramiv(self.myProgram, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE){
GLchar messages[1024];
glGetProgramInfoLog(self.myProgram, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(@"error%@", messageString);
return ;
}
NSLog(@"link success");
glUseProgram(self.myProgram);
- 清屏设置颜色
- 调整视口
- 获取顶点着色器程序和片段着色器程序的文件路径
- 加载 绑定 链接 使用
- 传入顶点数据和颜色值
// 创建顶点数组和索引数组
GLfloat attArr[] = {
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, //左上0
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, //右上1
-0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, //左下2
0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, //右下3
0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, //顶点4
};
//索引
GLuint indices[] = {
0, 3, 2,
0, 1, 3,
0, 2, 4,
0, 4, 1,
2, 3, 4,
1, 4, 3,
};
// 判断顶点缓冲区是否为空,如果为空则申请一个缓冲区标识
if (self.myVertices == 0){
glGenBuffers(1, &_myVertices);
}
//处理顶点数据
glBindBuffer(GL_ARRAY_BUFFER, _myVertices);
glBufferData(GL_ARRAY_BUFFER, sizeof(attArr), attArr, GL_DYNAMIC_DRAW);
// 获取文件中position
GLuint position = glGetAttribLocation(self.myProgram, "position");
//打开position
glEnableVertexAttribArray(position);
//读取方式
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, NULL);
// 获取文件中positionColor
GLuint positionColor = glGetAttribLocation(self.myProgram, "positionColor");
//打开positionColor
glEnableVertexAttribArray(positionColor);
//读取方式
glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6,(float *)NULL + 3);
- 模型矩阵和投影矩阵的值设置
//获取myProgram中的projectionMatrix、modelViewMatrix
GLuint projectionMatrixSlot = glGetUniformLocation(self.myProgram, "projectionMatrix");
GLuint modelViewMatrixSlot = glGetUniformLocation(self.myProgram, "modelViewMatrix");
float width = self.frame.size.width;
float height = self.frame.size.height;
KSMatrix4 _projectionMatrix;
ksMatrixLoadIdentity(&_projectionMatrix);
float aspect = width/height;
ksPerspective(&_projectionMatrix, 30, aspect, 5.0f, 20.f);
glUniformMatrix4fv(projectionMatrixSlot, 1, GL_FALSE, (GLfloat *)&_projectionMatrix.m[0][0]);
KSMatrix4 _modelViewMatrix;
ksMatrixLoadIdentity(&_modelViewMatrix);
ksTranslate(&_modelViewMatrix, 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);
glUniformMatrix4fv(modelViewMatrixSlot, 1, GL_FALSE, (GLfloat *)&_modelViewMatrix.m[0][0]);
void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
;
参数列表:
location
:指要更改的uniform变量的位置
count
:更改矩阵的个数
transpose
:是否要转置矩阵,并将它作为uniform
变量的值。必须为GL_FALSE
value
:执行count
个元素的指针,用来更新指定uniform变量
- 开始正背面剔除 颜色混合 开始绘制
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
//2.开启组合函数 计算混合颜色因子
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(indices[0]), GL_UNSIGNED_INT, indices);
[self.myContext presentRenderbuffer:GL_RENDERBUFFER];
使用索引绘图
void glDrawElements(GLenum mode,GLsizei count,GLenum type,const GLvoid * indices)
;
参数列表:
mode
:要呈现的画图的模型
GL_POINTS
GL_LINES
GL_LINE_LOOP
GL_LINE_STRIP
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
count
:绘图个数
type
:类型
GL_BYTE
GL_UNSIGNED_BYTE
GL_SHORT
GL_UNSIGNED_SHORT
GL_INT
GL_UNSIGNED_INT
indices
:绘制索引数组
5.旋转动画
- (IBAction)roteXAction:(UIButton *)sender {
if (!myTimer){
myTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(roteAction) userInfo:nil repeats:YES];
}
bX = !bX;
}
- (IBAction)roteYAction:(UIButton *)sender {
if (!myTimer){
myTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(roteAction) userInfo:nil repeats:YES];
}
bY = !bY;
}
- (IBAction)roteZAction:(UIButton *)sender {
if (!myTimer){
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(roteAction) userInfo:nil repeats:YES];
}
bZ = !bZ;
}
- (void)roteAction{
xDegree += bX*5;
yDegree += bY*5;
zDegree += bZ*5;
[self render];
}
点击绕对应轴旋转,再次点击停止.
如果停止X
轴旋转,X = 0
则度数就停留在暂停前的度数.
效果图:
GLKit绘制金字塔
- 1.初始化
-
OPVertex
保存顶点坐标和颜色值
typedef struct OPVertex{
GLKVector3 positionCoord;
GLKVector3 colorCoord;
}OPVertex;
- 2.设置上下文,开启深度测试
- (void)setUpcontext{
self.myContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
GLKView *view = (GLKView *)self.view;
view.context = self.myContext;
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
BOOL isSuc = [EAGLContext setCurrentContext:self.myContext];
if (!isSuc){
NSLog(@"setCurrentContext error");
return;
}
glEnable(GL_DEPTH_TEST);
}
- 开始绘制
- 设置顶点数据和索引绘制数组
self.vertices = (OPVertex *)malloc(sizeof(OPVertex)*kCount);
self.vertices[0] = (OPVertex){{-0.5f, 0.5f, 0.0f},{1.0f, 0.0f, 1.0f}};
self.vertices[1] = (OPVertex){{0.5f, 0.5f, 0.0f},{1.0f, 0.0f, 1.0f}};
self.vertices[2] = (OPVertex){{-0.5f, -0.5f, 0.0f},{1.0f, 1.0f, 1.0f}};
self.vertices[3] = (OPVertex){{0.5f, -0.5f, 0.0f},{1.0f, 1.0f, 1.0f}};
self.vertices[4] = (OPVertex){{0.0f, 0.0f, 1.0f},{0.0f, 1.0f, 0.0f}};
//绘图索引
GLuint indices[] =
{
0, 3, 2,
0, 1, 3,
0, 2, 4,
0, 4, 1,
2, 3, 4,
1, 4, 3,
};
- 计算顶点个数
self.count = sizeof(indices) /sizeof(GLuint);
- 将顶点数组放入数组缓冲区中
GL_ARRAY_BUFFER
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(OPVertex)*kCount, self.vertices, GL_STATIC_DRAW);
-
将索引数组存储到索引缓冲区
GL_ELEMENT_ARRAY_BUFFER
GLuint bufferIndex;
glGenBuffers(1, &bufferIndex);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferIndex);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
- 使用顶点数据
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, NULL);
- 使用颜色数据
glEnableVertexAttribArray(GLKVertexAttribColor);
glVertexAttribPointer(GLKVertexAttribColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, (GLfloat *)NULL + 3);
- 初始化着色器
self.mEffect = [[GLKBaseEffect alloc]init];
- 创建投影视图矩阵
CGSize size = self.view.bounds.size;
float aspect = fabs(size.width / size.height);
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90.0), aspect, 0.1f, 100.0);
projectionMatrix = GLKMatrix4Scale(projectionMatrix, 1.0f, 1.0f, 1.0f);
self.mEffect.transform.projectionMatrix = projectionMatrix;
- 模型视图矩阵
GLKMatrix4 modelViewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0.0f, 0.0f, -2.0f);
self.mEffect.transform.modelviewMatrix = modelViewMatrix;
- 创建一个定时器用来旋转金字塔
[NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer * _Nonnull timer) {
self.XDegree += 0.1f * self.XB;
self.YDegree += 0.1f * self.YB;
self.ZDegree += 0.1f * self.ZB ;
}];
- 开始绘制
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
glClearColor(0.1f, 0.3f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
[self.effect prepareToDraw];
glDrawElements(GL_TRIANGLES, self.count, GL_UNSIGNED_INT, 0);
}
效果图:
总结
image- API的工作
- 设置图层
- 设置上下文
- 清空缓冲区
- 设置renderBuffer
- 设置FrameBuffer
- 绘制
- 把顶点数据,颜色数据等从
CPU
内存复制到GPU
上 - 在可编程顶点着色器中对顶点坐标纹理坐标,颜色数据,模型矩阵,投影矩阵等输入.
- 输出 gl_position等
- 然后信息图元装配,视口变换等,生成一组二维数据传递给片段着色器.
- 片段着色器输出gl_Fragcolor
- 最后通过片段操作
- 数据放在帧缓存区
在开发中,基本上是按照这种模式进行代码编写.
网友评论