二十三 OpenGL索引绘图(GLSL实现金字塔纯色填充和纹理+
2020-09-04 本文已影响0人
王俏
GLSL实现
效果图
纯色填充
image纹理+颜色混合填充
image流程
image0. 准备工作
- 头文件引入
#import "GLESMath.h"
#import "GLESUtils.h"
#import <OpenGLES/ES2/gl.h>
- 扩展属性
@interface HTView()
//CAEAGLLayer
@property(nonatomic,strong)CAEAGLLayer *htEagLayer;
//上下文
@property(nonatomic,strong)EAGLContext *htContext;
//渲染缓冲区id
@property(nonatomic,assign)GLuint htColorRenderBuffer;
//帧缓冲区id
@property(nonatomic,assign)GLuint htColorFrameBuffer;
//着色器程序id
@property(nonatomic,assign)GLuint htProgram;
//顶点缓存区id
@property (nonatomic , assign) GLuint htVertices;
- 内部属性
@implementation HTView
{
//x轴方向的旋转弧度
float xDegree;
//y轴方向的旋转弧度
float yDegree;
//z轴方向的旋转弧度
float zDegree;
//是否围绕x轴旋转
BOOL bX;
//是否围绕y轴旋转
BOOL bY;
//是否围绕z轴旋转
BOOL bZ;
//定时器
NSTimer* htTimer;
}
流程1-5参考: 编译链接自定义的着色器(shader)流程
6.开始绘制 [self renderLayer]
- 6.1.设置清屏颜色,清除缓冲区
//1.设置清屏颜色
glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
//清除缓冲区
glClear(GL_COLOR_BUFFER_BIT);
- 6.2.设置视口大小
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);
- 6.3.读顶点着色器程序和片元着色器程序
NSString *vertFile = [[NSBundle mainBundle]pathForResource:@"shaderv" ofType:@"vsh"];
NSString *fragFile = [[NSBundle mainBundle]pathForResource:@"shaderf" ofType:@"fsh"];
- 6.4.判断self.htProgram是否存在,存在则清空其文件
//4.判断self.htProgram是否存在,存在则清空其文件
if(self.htProgram){
glDeleteProgram(self.htProgram);
self.htProgram = 0;
}
- 6.5.加载程序到htProgram中来
self.htProgram = [self loadShader:vertFile withFrag:fragFile];
- 6.6.链接
glLinkProgram(self.htProgram);
- 6.7.获取链接状态
GLint linkStatus;
glGetProgramiv(self.htProgram, GL_LINK_STATUS, &linkStatus);
if(linkStatus == GL_FALSE){
GLchar message[512];
glGetProgramInfoLog(self.htProgram, sizeof(message), 0, &message[0]);
NSString *messageString = [NSString stringWithUTF8String:message];
NSLog(@"Program Link Error:%@",messageString);
return;
}
NSLog(@"Programe Link Success!");
- 6.8.使用program
glUseProgram(self.htProgram);
- 6.9.创建顶点数组 和 索引数组
//(1)顶点数组 前3顶点值(x,y,z),后3位颜色值(RGB)
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
};
// 索引数组
GLuint indices[] =
{
0, 1, 2,
0, 2, 3,
0, 4, 1,
1, 4, 2,
2, 4, 3,
3, 4, 0,
};
- 6.10.处理顶点数据
//.判断顶点缓存区是否为空,如果为空则申请一个缓存区标识符
if (self.htVertices == 0) {
glGenBuffers(1, &_htVertices);
}
glBindBuffer(GL_ARRAY_BUFFER, _htVertices);
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
- 6.11.将顶点数据通过htPrograme传递到顶点着色程序的position
//1).glGetAttribLocation,用来获取vertex attribute的入口的.
GLuint position = glGetAttribLocation(self.htProgram, "position");
//2).设置合适的格式从buffer里面读取数据
glEnableVertexAttribArray(position);
//3).最后数据是通过glVertexAttribPointer传递过去的。
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, NULL);
- 6.12.处理顶点颜色值
//(1).glGetAttribLocation,用来获取vertex attribute的入口的.
//注意:第二参数字符串必须和shaderv.glsl中的输入变量positionColor保持一致
GLuint positionColor = glGetAttribLocation(self.htProgram, "positionColor");
//(2).设置合适的格式从buffer里面读取数据
glEnableVertexAttribArray(positionColor);
//(3).设置读取方式
glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, NULL);
- 6.13.找到htProgram中的projectionMatrix和
modelViewMatrix
GLuint projectionMatrixID = glGetUniformLocation(self.htProgram, "projectionMatrix");
GLuint modelViewMatrixID = glGetUniformLocation(self.htProgram, "modelViewMatrix");
- 6.14.设置投影矩阵
//创建4 * 4投影矩阵
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(projectionMatrixID, 1, GL_FALSE, (GLfloat*)&_projectionMatrix.m[0][0]);
- 6.15. 设置模型视图矩阵
//(1)创建一个4 * 4 矩阵,模型视图矩阵,获取单元矩阵
KSMatrix4 _modelViewMatrix;
//获取单元矩阵
ksMatrixLoadIdentity(&_modelViewMatrix);
//(2)平移,z轴负方向平移10
ksTranslate(&_modelViewMatrix, 0.0, 0.0, -10.0);
//(3)创建一个4 * 4 矩阵,旋转矩阵,初始化为单元矩阵
KSMatrix4 _rotationMatrix;
//初始化为单元矩阵
ksMatrixLoadIdentity(&_rotationMatrix);
//(4)旋转
ksRotate(&_rotationMatrix, xDegree, 1.0, 0.0, 0.0); //绕X轴
ksRotate(&_rotationMatrix, yDegree, 0.0, 1.0, 0.0); //绕Y轴
ksRotate(&_rotationMatrix, zDegree, 0.0, 0.0, 1.0);
//(5)把变换矩阵相乘
ksMatrixMultiply(&_modelViewMatrix, &_rotationMatrix, &_modelViewMatrix);
//(6)将模型视图矩阵传递到顶点着色器
glUniformMatrix4fv(modelViewMatrixID, 1, GL_FALSE, (GLfloat*)&_modelViewMatrix.m[0][0]);
- 6.16.开启正背面剔除
glEnable(GL_CULL_FACE);
- 6.17.使用索引绘图
glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(indices[0]), GL_UNSIGNED_INT, indices);
- 6.18.提交渲染到本地窗口系统显示
[self.htContext presentRenderbuffer:GL_RENDERBUFFER];
- 6.19 shaderv.vsh
attribute vec4 position;
attribute vec4 positionColor;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
varying lowp vec4 varyColor;
void main()
{
varyColor = positionColor;
vec4 vPos;
//4*4 * 4*4 * 4*1
vPos = projectionMatrix * modelViewMatrix * position;
//ERROR
//vPos = position * modelViewMatrix * projectionMatrix ;
gl_Position = vPos;
}
- 6.20 shaderf.fsh
varying lowp vec4 varyColor;
void main()
{
gl_FragColor = varyColor;
}
点击事件和定时器控制旋转
#pragma mark - XYClick
- (IBAction)XClick:(id)sender {
//开启定时器
if (!htTimer) {
htTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(reDegree) userInfo:nil repeats:YES];
}
//更新的是X还是Y
bX = !bX;
}
- (IBAction)YClick:(id)sender {
//开启定时器
if (!htTimer) {
htTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(reDegree) userInfo:nil repeats:YES];
}
//更新的是X还是Y
bY = !bY;
}
- (IBAction)ZClick:(id)sender {
//开启定时器
if (!htTimer) {
htTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(reDegree) userInfo:nil repeats:YES];
}
//更新的是X还是Y
bZ = !bZ;
}
-(void)reDegree
{
//如果停止X轴旋转,X = 0则度数就停留在暂停前的度数.
//更新度数
xDegree += bX * 5;
yDegree += bY * 5;
zDegree += bZ * 5;
//重新渲染
[self renderLayer];
}
纹理+颜色混合填充逻辑
在纯色填充上修改shaderf.fsh和shaderv.vsh
->绘制函数中需要增加 设置纹理坐标映射+ 载入纹理
image
shaderv.vsh
attribute vec4 position;
attribute vec4 positionColor;
attribute vec2 textCoord;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
varying lowp vec4 varyColor;
varying lowp vec2 varyTextCoord;
void main()
{
varyColor = positionColor;
varyTextCoord = textCoord;
vec4 vPos;
//4*4 * 4*4 * 4*1
vPos = projectionMatrix * modelViewMatrix * position;
//ERROR
//vPos = position * modelViewMatrix * projectionMatrix ;
gl_Position = vPos;
}
shaderf.fsh
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.8;
vec4 tempColor = mask * (1.0 - alpha) + weakMask * alpha;
gl_FragColor = tempColor;
}
绘制流程修改
- 9.创建顶点数组 和 索引数组 (顶点数组中加入了纹理坐标映射,注意映射的纹理映射的正反面)
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, 1.0f, 0.0f, //右下2
-0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, //左下3
0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, //顶点4
};
// 索引数组
GLuint indices[] =
{
0, 1, 2,
0, 2, 3,
0, 4, 1,
1, 4, 2,
2, 4, 3,
3, 4, 0,
};
11.将顶点数据通过htPrograme传递到顶点着色程序的position(坐标读取时两次连续读取之间的间隔改为8)
//1).glGetAttribLocation,用来获取vertex attribute的入口的.
GLuint position = glGetAttribLocation(self.htProgram, "position");
//2).设置合适的格式从buffer里面读取数据
glEnableVertexAttribArray(position);
//3).最后数据是通过glVertexAttribPointer传递过去的。
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*8, NULL);
- 11.2 处理纹理数据
//1).glGetAttribLocation,用来获取vertex attribute的入口的.
//注意:第二参数字符串必须和shaderv.vsh中的输入变量:textCoord保持一致
GLuint textCoor = glGetAttribLocation(self.htProgram, "textCoord");
//2).设置合适的格式从buffer里面读取数据
glEnableVertexAttribArray(position);
//3).最后数据是通过glVertexAttribPointer传递过去的。
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*8, NULL);
- 12.处理顶点颜色值(坐标读取时两次连续读取之间的间隔改为8)
//(1).glGetAttribLocation,用来获取vertex attribute的入口的.
//注意:第二参数字符串必须和shaderv.glsl中的输入变量:positionColor保持一致
GLuint positionColor = glGetAttribLocation(self.htProgram, "positionColor");
//(2).设置合适的格式从buffer里面读取数据
glEnableVertexAttribArray(textCoor);
//(3).设置读取方式
glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*8, (float *)NULL + 6);
- 12.2 加载纹理和设置 纹理采样器 sampler2D
- 加载纹理 [self setupTexture:@"stone"];
//加载纹理
-(GLuint )setupTexture:(NSString *)file
{
//1.将UIImage 转换为CGImageRef
CGImageRef spriteImage = [UIImage imageNamed:file].CGImage;
//判断图片是否获取成功
if(!spriteImage){
NSLog(@"Failed to load image: %@",file);
exit(1);
}
//2、读取图片的大小,宽和高
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
//3.获取图片字节数 宽*高*4(RGBA) 开辟存储空间
GLubyte *spriteData = (GLubyte *)calloc(width*height*4,sizeof(GLubyte));
//4.创建上下文
/*
参数1:data,指向要渲染的绘制图像的内存地址
参数2:width,bitmap的宽度,单位为像素
参数3:height,bitmap的高度,单位为像素
参数4:bitPerComponent,内存中像素的每个组件的位数,比如32位RGBA,就设置为8
参数5:bytesPerRow,bitmap的没一行的内存所占的比特数
参数6:colorSpace,bitmap上使用的颜色空间 kCGImageAlphaPremultipliedLast:RGBA
*/
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
//5、在CGContextRef上--> 将图片绘制出来
CGRect rect = CGRectMake(0, 0, width, height);
CGContextDrawImage(spriteContext, rect, spriteImage);
//6. 画图完毕就释放上下文
CGContextRelease(spriteContext);
//7.绑定纹理到默认的纹理ID
glBindTexture(GL_TEXTURE_2D, 0);
//8.设置纹理属性
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);
//9.载入纹理2D数据
/*
参数1:纹理模式,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
参数2:加载的层次,一般设置为0
参数3:纹理的颜色值GL_RGBA
参数4:宽
参数5:高
参数6:border,边界宽度
参数7:format
参数8:type
参数9:纹理数据
*/
float fw = width, fh = height;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
//10. 释放spriteData
free(spriteData);
return 0;
}
* 设置纹理采样器 sampler2D
glUniform1f(glGetAttribLocation(self.htProgram, "colorMap"), 0);
- 开启混合
glEnable(GL_BLEND);