这个金字塔的外部效果,由顶点颜色和纹理颜色混合成,我们先实现用顶点颜色实现这个金字塔的样子,最终效果如下图所示:
绘制这个金字塔,我们分6部去实现这个效果:
第一步:绘制图层:
self.mEagLayer = (CAEAGLLayer *)self.layer;
[self setContentScaleFactor:[[UIScreen mainScreen]scale]];
self.mEagLayer.opaque = YES;
self.mEagLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],kEAGLDrawablePropertyRetainedBacking,kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat, nil];
第二步设置上下文:
EAGLContext *context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
if(!context){
NSLog(@"创建失败");
return;
}
if(![EAGLContext setCurrentContext:context]){
NSLog(@"设置失败");
return;
}
self.mContext = context;
第三步:删除缓冲区
glDeleteBuffers(1, &_myColorRenderBuffer);
_myColorRenderBuffer = 0;
glDeleteBuffers(1, &_myColorFrameBuffer);
_myColorFrameBuffer = 0;
第四步、第五步:设置RenderBuffer和FrameBuffer
-(void)setupFrameBuffer{
//1、定义一个缓存区
GLuint buffer;
//2、申请缓冲区标志
glGenFramebuffers(1, &buffer);
//3、赋值
self.myColorFrameBuffer = buffer;
//4、设置当前的frameBuffer
glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);
//5、将myColorFrameBuffer配置到GL_COLOR_ATTACHMENT0附着点上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);
}
//4、设置renderbuffer
-(void)setupRenderBuffer{
//1、定义一个缓冲区
GLuint buffer;
//2、申请一个缓冲区标志
glGenRenderbuffers(1, &buffer);
//3、赋值
self.myColorRenderBuffer = buffer;
//4、将标示符绑定
glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffer);
[self.mContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.mEagLayer];
}
在第六步之前,我们还需要编写GLSL着色器:
新建两个空文件,shaderf.glsl和shaderv.glsl
在shaderf.glsl中的代码:
precision highp float;
varying lowp vec4 varyColor;
void main(){
gl_FragColor = varyColor;
}
在shaderv.glsl中的代码:
attribute vec4 position;
attribute vec4 positionColor;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
varying lowp vec4 varyColor;
void main(){
varyColor = positionColor;
vec4 vPos;
vPos = projectionMatrix * modelViewMatrix * position;
gl_Position = vPos;
}
然后我们将加载GLSL文件的代码写成两个函数来实现:
-(GLuint)loadShader:(NSString *)vert frag:(NSString *)frag{
//创建两个临时变量
GLuint verShader,fragShader;
//创建一个program
GLuint program = glCreateProgram();
//编译文件
[self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
[self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
//创建最终的程序
glAttachShader(program, verShader);
glAttachShader(program, fragShader);
//释放不需要的shader
glDeleteShader(verShader);
glDeleteShader(fragShader);
return program;
}
//链接shader
-(void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file{
//读取文件路径字符串
NSString *content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
//获取文件路径字符串
const GLchar *source = (GLchar *)[content UTF8String];
//创建一个shader
*shader = glCreateShader(type);
//将顶点着色器源码附加到着色器对象上
glShaderSource(*shader, 1, &source, NULL);
//将着色器源代码编译成目标代码
glCompileShader(*shader);
}
第六步:在写完着色器之后,开始绘制金字塔,这一步是最重要的。
//1、清屏颜色
glClearColor(0.5, 0.7, 0.9, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
CGFloat scale = [[UIScreen mainScreen]scale];
//2、设置视口
glViewport(self.frame.origin.x*scale, self.frame.origin.y, self.frame.size.width*scale, self.frame.size.height*scale);
//3、获取顶点着色器、片源着色器
NSString *vertFile = [[NSBundle mainBundle]pathForResource:@"shaderv" ofType:@"glsl"];
NSString *fragFile = [[NSBundle mainBundle]pathForResource:@"shaderf" ofType:@"glsl"];
//4、判断self.myProgram是否存在,
if(self.myProgram){
glDeleteProgram(self.myProgram);
self.myProgram = 0;
}
//5、加载程序到myProgram中来
self.myProgram = [self loadShader:vertFile frag:fragFile];
//6、链接
glLinkProgram(self.myProgram);
GLint linkSuccess;
//7、获取链接状态
glGetProgramiv(self.myProgram, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE) {
GLchar messages[256];
glGetProgramInfoLog(self.myProgram, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(@"error:%@", messageString);
return ;
}else {
glUseProgram(self.myProgram);
}
//8、创建顶点数组&索引数组
GLfloat attrArr[] =
{
-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
};
//(2).索引数组
GLuint indices[] =
{
0, 3, 2,
0, 1, 3,
0, 2, 4,
0, 4, 1,
2, 3, 4,
1, 4, 3,
};
//(3)判断顶点缓冲区是否为空,如果为空则申请一个缓冲区标识符
if(self.myVertices == 0){
glGenBuffers(1, &_myVertices);
}
//9、处理顶点数据
glBindBuffer(GL_ARRAY_BUFFER, _myVertices);
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
GLuint position = glGetAttribLocation(self.myProgram, "position");
//打开position
glEnableVertexAttribArray(position);
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, NULL);
//10、处理顶点颜色值
GLuint positionColor = glGetAttribLocation(self.myProgram, "positionColor");
glEnableVertexAttribArray(positionColor);
glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, (float *)NULL +3);
//11、找到myProgram中的projectionMatrix、modelViewMatrix 2个矩阵的地址。如果找到则返回地址,否则返回-1,表示没有找到2个对象。
GLuint projectionMatrixSlot = glGetUniformLocation(self.myProgram, "projectionMatrix");
GLuint modelViewMatrixSlot = glGetUniformLocation(self.myProgram, "modelViewMatrix");
float width = self.frame.size.width;
float height = self.frame.size.height;
//12、创建4*4投影矩阵
KSMatrix4 _projectionMatrix;
ksMatrixLoadIdentity(&_projectionMatrix);
float aspect = width/height;
ksPerspective(&_projectionMatrix, 30, aspect, 5.0, 20.0);
glUniformMatrix4fv(projectionMatrixSlot, 1, GL_FALSE, (GLfloat *)&_projectionMatrix.m[0][0]);
//13、创建一个4*4矩阵
KSMatrix4 _modelViewMatrix;
//获取单元矩阵
ksMatrixLoadIdentity(&_modelViewMatrix);
//平移z轴平移-10
ksTranslate(&_modelViewMatrix, 0.0, 0.0, -10.0);
//创建4*4矩阵,旋转矩阵
KSMatrix4 _rotationMatrix;
//设置为单元矩阵
ksMatrixLoadIdentity(&_rotationMatrix);
//旋转
ksRotate(&_rotationMatrix, xAngle, 1.0, 0.0, 0.0);
ksRotate(&_rotationMatrix, yAngle, 0.0, 1.0, 0.0);
ksRotate(&_rotationMatrix, zAngle, 0.0, 0.0, 1.0);
//矩阵相乘
ksMatrixMultiply(&_modelViewMatrix, &_rotationMatrix, &_modelViewMatrix);
//将模型视图矩阵传递到顶点着色器
glUniformMatrix4fv(modelViewMatrixSlot, 1, GL_FALSE, (GLfloat *)&_modelViewMatrix.m[0][0]);
//开启正背面剔除
glEnable(GL_CULL_FACE);
glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(indices[0]), GL_UNSIGNED_INT, indices);
//要求本地窗口系统显示OpenGL ES渲染<目标>
[self.mContext presentRenderbuffer:GL_RENDERBUFFER];
以上的代码就是绘制顶点颜色金字塔的代码。
下面我们开始用纹理颜色和顶点颜色混合的方法去实现金字塔
首先,将两个GLSL文件代码进行修改:
precision highp float;
varying lowp vec4 varyColor;
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main(){
lowp vec4 temp = texture2D(colorMap,varyTextCoord);
vec4 mask = varyColor;
float alpha = 0.3;
vec4 tempColor = mask * (1.0-alpha) + temp * alpha;
gl_FragColor = tempColor;
}
attribute vec4 position;
attribute vec4 positionColor;
attribute vec2 textCoordinate;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
varying lowp vec4 varyColor;
varying lowp vec2 varyTextCoord;
void main(){
varyColor = positionColor;
varyTextCoord = textCoordinate;
vec4 vPos;
vPos = projectionMatrix * modelViewMatrix * position;
gl_Position = vPos;
}
}
接下来在绘制的方法中的金字塔坐标去添加纹理坐标:
GLfloat attrArr[] =
{
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,1.0f,//左上0
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.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,1.0f,//右下3
0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,1.0f//顶点4
};
在坐标设置完之后,我们需要处理纹理数据
//处理纹理数据
GLuint textCoor = glGetAttribLocation(self.myProgram, "textCoordinate");
glEnableVertexAttribArray(textCoor);
glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*8, (float *)NULL+6);
[self setupTexture:@"fengjing.jpg"];
//设置纹理采样器
glUniform1i(glGetUniformLocation(self.myProgram, "textCoordinate"), 0);
//从图片中加载纹理的函数
-(GLuint)setupTexture:(NSString *)fileName{
//1、将UIImage转换成CGImageRef
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
//判断图片是否获取成功
if(!spriteImage){
NSLog(@"图片加载失败");
exit(1);
}
//2、读取图片大小,宽和高
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
//3、获取图片字节数 宽*高*4(RGBA)
GLubyte *spriteDate = (GLubyte *)calloc(width*height*4, sizeof(GLubyte));
//4、创建上下文
CGContextRef spriteContext = CGBitmapContextCreate(spriteDate, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
//5、在CGContextRef上--> 将图片绘制出来
CGRect rect = CGRectMake(0, 0, width, height);
//6、使用默认方式绘制
CGContextDrawImage(spriteContext, rect, spriteImage);
//翻转
CGContextTranslateCTM(spriteContext, rect.origin.x, rect.origin.y);
CGContextTranslateCTM(spriteContext, 0, rect.size.height);
CGContextScaleCTM(spriteContext, 1.0, -1.0);
CGContextTranslateCTM(spriteContext, -rect.origin.x, -rect.origin.y);
CGContextDrawImage(spriteContext, rect, spriteImage);
//7、画完图就释放上下文
CGContextRelease(spriteContext);
//8、绑定纹理到默认的纹理ID
glBindTexture(GL_TEXTURE_2D, 0);
//9.设置纹理属性
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
float fw = width,fh = height;
//10.载入纹理2D数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteDate);
//11、释放spriteDate
free(spriteDate);
return 0;
}
其中在处理顶点数据和顶点颜色时,我们需要修改数组长度为8
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*8, NULL);
glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*8, (float *)NULL +3);
当修改完之后,最终效果如下图所示:
完整demo