OpenGL绘制三角形
《OpenGL文章汇总》
视频通过FFmpeg软解码为YUV原始数据或VideoToolBox硬解码为CVImageBuffer后,通过OpenGL进行渲染,具体如何渲染的,先看OpenGL渲染一个三角形的过程。
一、绘制一个三角形需要什么?怎么才能渲染出一个三角形?
1.设置当前工作目录gltSetWorkingDirectory(argv[0]);
2.初始化GLUT库
3.初始化GLEW库,初始化驱动程序中所有丢失的入口点
4.开启主消息循环并结束main函数
5.设置窗口显示模式,双缓冲区窗口(绘图在离屏缓冲区执行),RGB颜色模式,模板缓冲区
6.创建窗口大小和标题
7.设置窗口大小改变回调函数视口(把绘图坐标映射到窗口坐标),设置坐标系,将2D三角形显示到3D的屏幕上,设置视口glViewport(0,0,w,h)左下角为从窗口的左下角开始为原点,视口是以实际屏幕坐标定义了窗口中的区域,OpenGL可以在这个区域中进行绘图的裁剪区域被映射到新的视口,如果指定了一个比窗口坐标更小的视口,渲染区域就会缩小
OpenGL负责笛卡尔坐标系到窗口像素间的映射,光栅化后显示到屏幕上,改变视口并不会改变基础坐标系
8.注册函数回调包含OpenGL渲染代码,设置一组浮点数来表示红色,并传到GLShaderManager存储着色器即GLT_SHADER_IDENTITY着色器,此着色器使用指定颜色以默认笛卡尔坐标系在屏幕上渲染几何图形,批次处理器GLBatch调用draw将几何图像交给着色器开始绘制后,进入开始复制数据(三角形三个分量x、y、z顶点组成的数组,可复制表面法线,颜色,纹理坐标),
9.指定初始化双缓冲区(iOS渲染View也是双缓冲区)渲染环境,在后台缓冲区进行渲染,运行OpenGL状态机中的句柄,三角形绘制完成
#include <stdio.h>
#include <GLTools.h> //OpenGL toolkit
#include <GLShaderManager.h>//Shader Manager Class
#ifdef __APPLE__
#include <glut/glut.h> //OS X version of GLUT
#else
#define FREEGLUT_STATIC
#include <GL/glut.h> //Windows FreeGlut equivalent
#endif
GLBatch triangleBatch;
GLShaderManager shaderManager;
//Window has changed size, or has just been created. In either case, we need to use the window dimensions to set the viewport and projection matrix.
void ChangeSize(int w,int h){
glViewport(0, 0, w, h);
}
//This function does any needed initialization on the rendering context.
//This is the first opportunity to do any OpenGL related tasks.
void SetupRC(){
//Blue background
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
shaderManager.InitializeStockShaders();
//Load up a triangle
GLfloat vVerts[] = {
-0.5f,0.0f,0.0f,
0.5f,0.0f,0.0f,
0.0f,0.5f,0.0f,
};
triangleBatch.Begin(GL_TRIANGLES, 3);
triangleBatch.CopyVertexData3f(vVerts);
triangleBatch.End();
}
//Called to draw scene
void RenderScene(void){
//Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
GLfloat vRed[] = {1.0f,0.0f,0.0f,1.0f};
shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed);
triangleBatch.Draw();
//Perform the buffer swap to display the back buffer
glutSwapBuffers();
}
//Main entry point for GLUT based programs
int main(int argc,char* argv[]){
//设置当前工作目录
gltSetWorkingDirectory(argv[0]);
//传输命令行参数并初始化GLUT库
glutInit(&argc, argv);
//创建窗口使用哪种类型显示模式,双缓冲串口和RGBA颜色模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
//窗口大小
glutInitWindowSize(800, 600);
//窗口标题
glutCreateWindow("Triangle");
//窗口改变大小回调函数,以便能够设置视点
glutReshapeFunc(ChangeSize);
//注册函数包含OpenGL渲染代码
glutDisplayFunc(RenderScene);
//初始化GLEW库,初始化驱动程序中所有丢失的入口点,确保OpenGL API可用
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n",glewGetErrorString(err));
return 1;
}
//渲染环境,运行中的OpenGL状态机的句柄
SetupRC();
//开启主消息循环并结束main函数
glutMainLoop();
return 0;
}
image
如果出现上图,要删除info.plist中的NSApplication
图片.png
删除后运行正常,视口的中心和窗口的中心重叠
image
如果将视口设置为窗口的一半,则大小往左下角移动,视口的原点在左下角
image
二、特殊按键响应,移动正方形,位置跟随变化,坐标变化
绘制渲染正方形,并监听方向键,对正方形坐标进行修改,glut提供了一个回调函数glutSpecialFunc,注册了在按一个特殊按键时被调用的函数。
SpecialKeys()函数最后一行glutPostRedisplay();告诉GLUT需要更新窗口内容,激活屏幕刷新会调用RenderScene函数
GLBatch squareBatch;
GLShaderManager shaderManager;
GLfloat blockSize = 0.1f;
GLfloat vVerts[] = {
-blockSize,-blockSize,0.0f,
blockSize,-blockSize,0.0f,
blockSize,blockSize,0.0f,
-blockSize,blockSize,0.0f};
void SpecialKeys(int key,int x,int y){
GLfloat stepSize = 0.025f;
GLfloat blockX = vVerts[0];//Upper left X
GLfloat blockY = vVerts[7];//Upper left Y
if (key == GLUT_KEY_UP) {
blockY += stepSize;
}
if (key == GLUT_KEY_DOWN) {
blockY -= stepSize;
}
if (key == GLUT_KEY_LEFT) {
blockX -= stepSize;
}
if (key == GLUT_KEY_RIGHT) {
blockX += stepSize;
}
//Collision detection
if (blockX < -1.0f) blockX = -1.0f;
if (blockX > (1.0f - blockSize * 2)) blockX = 1.0f -blockSize * 2;
if (blockY < -1.0f + blockSize * 2) blockY = -1.0f + blockSize * 2;
if (blockY > 1.0f) blockY = 1.0f;
//Recalculate vertex positions
vVerts[0] = blockX;
vVerts[1] = blockY - blockSize * 2;
vVerts[3] = blockX + blockSize * 2;
vVerts[4] = blockY - blockSize * 2;
vVerts[6] = blockX + blockSize*2;
vVerts[7] = blockY;
vVerts[9] = blockX;
vVerts[10] = blockY;
squareBatch.CopyVertexData3f(vVerts);
glutPostRedisplay();
}
//Window has changed size, or has just been created. In either case, we need to use the window dimensions to set the viewport and projection matrix.
void ChangeSize(int w,int h){
printf("视口宽:%d,视口高:%d", w,h);
glViewport(0, 0, w, h);
}
//This function does any needed initialization on the rendering context.
//This is the first opportunity to do any OpenGL related tasks.
void SetupRC(){
//Blue background
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
shaderManager.InitializeStockShaders();
//Load up a square
squareBatch.Begin(GL_TRIANGLE_FAN, 4);
squareBatch.CopyVertexData3f(vVerts);
squareBatch.End();
}
//Called to draw scene
void RenderScene(void){
//Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
GLfloat vRed[] = {1.0f,0.0f,0.0f,1.0f};
shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed);
squareBatch.Draw();
//Perform the buffer swap to display the back buffer
glutSwapBuffers();
}
//Main entry point for GLUT based programs
int main(int argc,char* argv[]){
//设置当前工作目录
gltSetWorkingDirectory(argv[0]);
//传输命令行参数并初始化GLUT库
glutInit(&argc, argv);
//创建窗口使用哪种类型显示模式,双缓冲串口和RGBA颜色模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
//窗口大小
glutInitWindowSize(800, 600);
//窗口标题
glutCreateWindow("Move Block with Arrow Keys");
//窗口改变大小回调函数,以便能够设置视点
glutReshapeFunc(ChangeSize);
//注册函数包含OpenGL渲染代码
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);
//初始化GLEW库,初始化驱动程序中所有丢失的入口点,确保OpenGL API可用
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n",glewGetErrorString(err));
return 1;
}
//渲染环境,运行中的OpenGL状态机的句柄
SetupRC();
//开启主消息循环并结束main函数
glutMainLoop();
return 0;
}
0
三、实现简单动画,将glutPostRedisplay函数的调用放在RenderScene函数末尾处,则如果有坐标变化,会一直循环执行RenderScene去刷新屏幕,这并不是一个死循环,重绘消息实际是一条传递到一个内部消息循环中的消息,在屏幕刷新的间隔中,也会发生其他窗口事件,仍然可以检测按键动作、鼠标移动、改变窗口大小和程序结束等动作。
1GLBatch squareBatch;
GLShaderManager shaderManager;
GLfloat blockSize = 0.1f;
GLfloat vVerts[] = {
-blockSize,-blockSize,0.0f,
blockSize,-blockSize,0.0f,
blockSize,blockSize,0.0f,
-blockSize,blockSize,0.0f};
void BounceFunc(void){
static GLfloat xDir = 1.0f;
static GLfloat yDir = 1.0f;
GLfloat stepSize = 0.005f;
GLfloat blockX = vVerts[0]; // Upper left X
GLfloat blockY = vVerts[7]; // Upper left Y
blockY += stepSize * yDir;
blockX += stepSize * xDir;
// Collision detection
if(blockX < -1.0f) { blockX = -1.0f; xDir *= -1.0f; }
if(blockX > (1.0f - blockSize * 2)) { blockX = 1.0f - blockSize * 2; xDir *= -1.0f; }
if(blockY < -1.0f + blockSize * 2) { blockY = -1.0f + blockSize * 2; yDir *= -1.0f; }
if(blockY > 1.0f) { blockY = 1.0f; yDir *= -1.0f; }
// Recalculate vertex positions
vVerts[0] = blockX;
vVerts[1] = blockY - blockSize*2;
vVerts[3] = blockX + blockSize*2;
vVerts[4] = blockY - blockSize*2;
vVerts[6] = blockX + blockSize*2;
vVerts[7] = blockY;
vVerts[9] = blockX;
vVerts[10] = blockY;
squareBatch.CopyVertexData3f(vVerts);
// glutPostRedisplay();
}
//Window has changed size, or has just been created. In either case, we need to use the window dimensions to set the viewport and projection matrix.
void ChangeSize(int w,int h){
printf("视口宽:%d,视口高:%d", w,h);
glViewport(0, 0, w, h);
}
//This function does any needed initialization on the rendering context.
//This is the first opportunity to do any OpenGL related tasks.
void SetupRC(){
//Blue background
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
shaderManager.InitializeStockShaders();
//Load up a square
squareBatch.Begin(GL_TRIANGLE_FAN, 4);
squareBatch.CopyVertexData3f(vVerts);
squareBatch.End();
}
//Called to draw scene
void RenderScene(void){
//Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
GLfloat vRed[] = {1.0f,0.0f,0.0f,1.0f};
shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed);
squareBatch.Draw();
//Perform the buffer swap to display the back buffer
glutSwapBuffers();
BounceFunc();
glutPostRedisplay();
}
//Main entry point for GLUT based programs
int main(int argc,char* argv[]){
//设置当前工作目录
gltSetWorkingDirectory(argv[0]);
//传输命令行参数并初始化GLUT库
glutInit(&argc, argv);
//创建窗口使用哪种类型显示模式,双缓冲窗口和RGBA颜色模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
//窗口大小
glutInitWindowSize(800, 600);
//窗口标题
glutCreateWindow("Move Block with Arrow Keys");
//窗口改变大小回调函数,以便能够设置视点
glutReshapeFunc(ChangeSize);
//注册函数包含OpenGL渲染代码
glutDisplayFunc(RenderScene);
//初始化GLEW库,初始化驱动程序中所有丢失的入口点,确保OpenGL API可用
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n",glewGetErrorString(err));
return 1;
}
//渲染环境,运行中的OpenGL状态机的句柄
SetupRC();
//开启主消息循环并结束main函数
glutMainLoop();
return 0;
}
四、iOS环境下基于OpenGL ES
1、创建OpenGL ES上下文创建一个GLKViewController,在ViewDidLoad声明周期中:
- GLKViewController:UIViewController的子类,接收当视图需要重绘时的消息
- GLKView:UIView的子类,简化了通过用CoreAnimation层来自动创建并管理帧缓存和渲染缓存共享内存所需要做的工作。
//1.创建OpenGL ES上下文创建一个GLKViewController
GLKView *view = (GLKView *)self.view;
NSAssert([view isKindOfClass:[GLKView class]], @"ViewController's View is Not A GLKView");
//创建OpenGL ES2.0上下文
view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
//设置当前上下文
[EAGLContext setCurrentContext:view.context];
2.声明一个GLKBaseEffect属性,并在ViewDidLoad实例化
//声明一个GLKBaseEffect属性
@property (nonatomic,strong) GLKBaseEffect *baseEffect;
self.baseEffect = [[GLKBaseEffect alloc] init];
//使用静态颜色绘制
self.baseEffect.useConstantColor = GL_TRUE;
//设置默认绘制颜色,参数分别是RGBA
self.baseEffect.constantColor = GLKVector4Make(1.0f, 1.0f, 1.0f, 1.0f);
//设置背景颜色为黑色
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
3.顶点数据 我们需要绘制一个三角形自然需要三个顶点,OpenGL ES采用的笛卡尔坐标系
image//顶点结构体
typedef struct {
GLKVector3 positionCoords;
} sceneVertex;
//三角形的三个顶点
static const sceneVertex vertices[] = {
{{-0.5f,-0.5f,0.0}},
{{0.5f,-0.5f,0.0}},
{{-0.5f,0.5f,0.0}},
};
4.生成缓存并且为缓存提供数据,这是最重要的一步
//声明缓存ID属性
@property (nonatomic,assign)GLuint *vertextBufferID;
//viewdidload中生成并绑定缓存数据
glGenBuffers(1, &vertextBufferID);
glBindBuffer(GL_ARRAY_BUFFER, vertextBufferID); //绑定指定标识符的缓存为当前缓存
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
- glGenBuffers申请一个标识符
- glBindBuffer 将标识符绑定到GL_ARRAY_BUFFER
- glBufferData复制顶点数据从CPU到GPU
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
[self.baseEffect prepareToDraw];
//Clear Frame Buffer
glClear(GL_COLOR_BUFFER_BIT);
//开启缓存
glEnableVertexAttribArray(GLKVertexAttribPosition);
//设置缓存数据指针
glVertexAttribPointer(GLKVertexAttribPosition,
3,
GL_FLOAT,
GL_FALSE,//小数点固定数据是否被改变
sizeof(sceneVertex),
NULL);//从开始位置
//绘图
glDrawArrays(GL_TRIANGLES, 0, 3);
}
5.释放缓存数据 在dealloc方法中释放掉缓存数据
- (void)dealloc{
GLKView *view = (GLKView *)self.view;
[EAGLContext setCurrentContext:view.context];
if ( 0 != vertextBufferID) {
glDeleteBuffers(1,
&vertextBufferID);
vertextBufferID = 0;
}
}
最后的运行结果图如下
image
网友评论