00-OpenGL绘制三角形

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 
#include   //OpenGL toolkit
#include //Shader Manager Class
#ifdef __APPLE__
#include  //OS X version of GLUT
#else
#define FREEGLUT_STATIC
#include  //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去刷新屏幕,这并不是一个死循环,重绘消息实际是一条传递到一个内部消息循环中的消息,在屏幕刷新的间隔中,也会发生其他窗口事件,仍然可以检测按键动作、鼠标移动、改变窗口大小和程序结束等动作。

1
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 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

你可能感兴趣的:(00-OpenGL绘制三角形)