继上一篇文章我们加载了一张图片并且进行了纹理翻转五种方案的介绍。
这篇文章我们将进一步了解OpenGL ES创建3D图形并且加载纹理再加上与颜色的混合。
这次要做的内容可在上一篇文章的基础上做修改,有许多东西都是一样的。
效果图
可怜的表情包 - -
日常来个思维导图
思维导图
继续按照思维导图一步一步来,但是这次就不展示多余的截图了。
有需要的来这里自取。
创建顶点着色器
tips:着色器中的注释在使用时尽量去掉,免得出现不必要的错误
//顶点坐标
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;
//内建变量赋值 投影矩阵 * 模型视图矩阵 * 顶点坐标
gl_Position = projectionMatrix * modelViewMatrix * position;
}
创建片元着色器
tips:着色器中的注释在使用时尽量去掉,免得出现不必要的错误
//接收来自顶点着色器的坐标颜色
varying lowp vec4 varyColor;
//接收来自顶点着色器的纹理坐标
varying lowp vec2 varyTextCoord;
//纹理取样器
uniform sampler2D colorMap;
void main()
{
// texture2D 读取纹素
lowp vec4 tex = texture2D(colorMap, vec2(varyTextCoord.x,1.0-varyTextCoord.y));
//纹素 * 颜色 赋值给内建变量gl_FragColor
gl_FragColor = tex * varyColor ;
}
初始化
1. 创建view
2. import
#import
#import "GLESUtils.h"
#import "GLESMath.h"
这次我引入了一些不一样的东西
GLESMath
:主要是一些矩阵创建、载入矩阵、矩阵旋转、平移等操作。
GLESUtils
:主要是对于创建着色器以及program的一些方法的封装,减少我们view中的代码。
3. 声明变量
较上一篇文章新增了三个成员变量,用来控制旋转角度
{
float xDegree;//X轴旋转角度
float yDegree;//Y轴旋转角度
float zDegree;//Z轴旋转角度
}
@property(strong,nonatomic)CAEAGLLayer *eaglLayer;
@property(strong,nonatomic)EAGLContext *context;
@property(assign,nonatomic)GLuint program;
@property(assign,nonatomic)GLuint frameBuffer;
@property(assign,nonatomic)GLuint renderBuffer;
创建图层layer
-(void)setupLayer{
self.eaglLayer = (CAEAGLLayer *)self.layer;
[self setContentScaleFactor:[UIScreen mainScreen].scale];
NSDictionary *options = @{kEAGLDrawablePropertyRetainedBacking:@(false),
kEAGLDrawablePropertyColorFormat:kEAGLColorFormatRGBA8};
self.eaglLayer.drawableProperties = options;
}
//重写layerClass方法
+(Class)layerClass{
return [CAEAGLLayer class];
}
设置上下文context
-(void)setupContext{
self.context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
if (!_context) {
NSLog(@"context创建失败");
return;
}
if ([EAGLContext setCurrentContext:self.context]==false) {
NSLog(@"设置当前context失败!");
return;
}
}
清除缓冲区
可有可无,惯性操作
-(void)deleteBuffers{
glDeleteBuffers(1, &_frameBuffer);
_frameBuffer = 0;
glDeleteBuffers(1, &_renderBuffer);
_renderBuffer = 0 ;
}
创建渲染缓冲区
-(void)setupRenderBuffer{
GLuint bufferID;
glGenRenderbuffers(1, &bufferID);
self.renderBuffer = bufferID;
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
[self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.eaglLayer];
}
创建帧缓冲区
-(void)setupFrameBuffer{
GLuint bufferID;
glGenFramebuffers(1, &bufferID);
self.frameBuffer = bufferID;
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.renderBuffer);
}
绘制
-(void)draw{
//设置背景色
glClearColor(0.75, 0.85, 0.85, 1);
//清理颜色缓冲、深度缓冲
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//缩放
GLfloat 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 *vertexShaderPath = [[NSBundle mainBundle]pathForResource:@"PyramidVertexShader" ofType:@"vsh"];
NSString *fragmentShaderPath = [[NSBundle mainBundle]pathForResource:@"PyramidFragmentShader" ofType:@"fsh"];
//调用封装方法创建program
GLuint program = [GLESUtils loadProgram:vertexShaderPath withFragmentShaderFilepath:fragmentShaderPath];
//使用program
glUseProgram(program);
self.program = program;
//创建顶点数组 & 索引数组
//顶点数组 前3顶点值(x,y,z),后3位颜色值(RGB) ,最后2位纹理坐标(s,t)
GLfloat attrArr [] = {
-0.5, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0, 1.0,
0.5, 0.5, 0.0, 0.0, 0.5, 0.0, 1.0, 1.0,
-0.5, -0.5, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0,
0.5, -0.5, 0.0, 0.0, 0.0, 0.5, 1.0, 0.0,
0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5
};
//索引数组
GLuint indices[] =
{
0, 3, 2,
0, 1, 3,
0, 2, 4,
0, 4, 1,
2, 3, 4,
1, 4, 3,
};
//定义标识符
GLuint bufferID;
//申请标识符
glGenBuffers(1, &bufferID);
//绑定缓冲区
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
//将顶点数组的数据copy到顶点缓冲区中
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
//从program中获取position 顶点属性
GLuint position = glGetAttribLocation(self.program, "position");
//开启顶点属性通道
glEnableVertexAttribArray(position);
//设置顶点读取方式
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, (GLfloat *)NULL + 0);
//从program中获取positionColor 颜色属性
GLuint positionColor = glGetAttribLocation(self.program, "positionColor");
//开启颜色属性通道
glEnableVertexAttribArray(positionColor);
//设置颜色读取方式
glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, (GLfloat *)NULL + 3);
//从program中获取textCoordinate 纹理属性
GLuint textCoordinate = glGetAttribLocation(self.program, "textCoordinate");
//开启纹理属性通道
glEnableVertexAttribArray(textCoordinate);
//设置纹理读取方式
glVertexAttribPointer(textCoordinate, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, (GLfloat *)NULL + 6);
//从program中获取投影矩阵 & 模型视图矩阵
GLuint projectionMatrix_S = glGetUniformLocation(self.program, "projectionMatrix");
GLuint modelViewMartix_S = glGetUniformLocation(self.program, "modelViewMatrix");
//获取view宽高 计算宽高比
float width = self.frame.size.width;
float height = self.frame.size.height;
float aspect = width / height; //长宽比
//创建 4*4 投影矩阵
KSMatrix4 _projectionMatrix;
//加载投影矩阵
ksMatrixLoadIdentity(&_projectionMatrix);
//获取透视矩阵
/*
参数1:矩阵
参数2:视角,度数为单位
参数3:纵横比
参数4:近平面距离
参数5:远平面距离
*/
ksPerspective(&_projectionMatrix, 30.0, aspect, 5.0f, 20.0f); //透视变换,视角30°
//将投影矩阵传递到顶点着色器
/*
void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
参数列表:
location:指要更改的uniform变量的位置
count:更改矩阵的个数
transpose:是否要转置矩阵,并将它作为uniform变量的值。必须为GL_FALSE
value:执行count个元素的指针,用来更新指定uniform变量
*/
glUniformMatrix4fv(projectionMatrix_S, 1, GL_FALSE, (GLfloat*)&_projectionMatrix.m[0][0]);
//模型视图矩阵
KSMatrix4 _modelViewMatrix;
//加载矩阵
ksMatrixLoadIdentity(&_modelViewMatrix);
//沿着z轴平移
ksTranslate(&_modelViewMatrix, 0.0, 0.0, -10.0);
//旋转矩阵
KSMatrix4 _rotateMartix;
//加载旋转矩阵
ksMatrixLoadIdentity(&_rotateMartix);
//旋转
ksRotate(&_rotateMartix, xDegree, 1.0, 0, 0);
ksRotate(&_rotateMartix, yDegree, 0, 1.0, 0);
ksRotate(&_rotateMartix, zDegree, 0, 0, 1.0);
//把变换矩阵相乘.将_modelViewMatrix矩阵与_rotationMatrix矩阵相乘,结合到模型视图
ksMatrixMultiply(&_modelViewMatrix, &_rotateMartix, &_modelViewMatrix);
//将模型视图矩阵传递到顶点着色器
glUniformMatrix4fv(modelViewMartix_S, 1, GL_FALSE, (GLfloat *)&_modelViewMatrix.m[0][0]);
//开启正背面剔除
glEnable(GL_CULL_FACE);
//开启颜色混合
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//加载纹理
[self loadTexture:@"xhj"];
//设置纹理采样器,这里的 0 对应 glBindTexture的 0
glUniform1i(glGetUniformLocation(self.program, "colorMap"), 0);
//使用索引绘图
/*
void glDrawElements(GLenum mode,GLsizei count,GLenum type,const GLvoid * indices);
参数列表:
mode:要呈现的画图的模型GL_POINTS、GL_LINES、GL_TRIANGLES等等
count:绘图个数
type:类型GL_BYTE、GL_UNSIGNED_BYTE、GL_SHORT、GL_UNSIGNED_SHORT、GL_INT、GL_UNSIGNED_INT
indices:绘制索引数组
*/
glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(indices[0]), GL_UNSIGNED_INT, indices);
//将渲染缓冲区 呈现到 屏幕上
[self.context presentRenderbuffer:GL_RENDERBUFFER];
}
//纹理加载方法
-(void)loadTexture:(NSString *)name{
CGImageRef cgImg = [UIImage imageNamed:name].CGImage;
if (!cgImg) {
NSLog(@"获取图片失败!");
return;
}
size_t width = CGImageGetWidth(cgImg);
size_t height = CGImageGetHeight(cgImg);
// *4 因为RGBA
GLubyte * byte = (GLubyte *)calloc(width * height * 4, sizeof(GLubyte));
CGContextRef contextRef = CGBitmapContextCreate(byte, width, height, 8, width * 4, CGImageGetColorSpace(cgImg), kCGImageAlphaPremultipliedLast);
CGRect rect = CGRectMake(0, 0, width, height);
CGContextDrawImage(contextRef, rect, cgImg);
CGContextRelease(contextRef);
glBindTexture(GL_TEXTURE_2D, 0);
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 w = width;
float h = height;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, byte);
free(byte);
}
统一调用并创建手势控制图形旋转
-(void)layoutSubviews{
[super layoutSubviews];
[self setupLayer];
[self setupContext];
[self deleteBuffers];
[self setupRenderBuffer];
[self setupFrameBuffer];
[self draw];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
[self addGestureRecognizer:pan];
}
-(void)pan:(UIPanGestureRecognizer *)pan{
//获取偏移量
// 返回的是相对于最原始的手指的偏移量
CGPoint transP = [pan translationInView:self];
xDegree = -transP.y;
yDegree = -transP.x;
[self draw];
}
彩蛋
在上面代码的基础上。小修小改之后可以弄出这种效果
两张纹理相互叠加渲染,效果如下
思路:开启两个纹理通道,加载两个纹理,片元着色器中两个纹理相乘赋值给内建变量。
那么这篇文章就到此为止了,有需要源码的同学,可以留言私信 ^ _ ^