三、OpenGL初探之OpenGL初探之绘制可用键盘移动的三角形实战解析

前言:前一部分了解了OpenGL环境搭建和基本API以后,咱们先来作一个小小的练习,使用固定管线来绘制一个可移动的三角形,同时详细解释一下一些经常使用方法的含义
复制代码

1、搭建OpenGL在Mac下的环境


搭建Xcode环境,这里再也不作过多赘述了windows

传送门:juejin.im/post/5d67e1…数组

2、实战开始


(注:完整代码在文章的最后面) 一、首先来到main函数中进行环境初始化和一些函数的注册安全

int main(int argc,char* argv[])

{
    
    gltSetWorkingDirectory(argv[0]);
    
    glutInit(&argc, argv);
    
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
    
    glutInitWindowSize(800,600);
    
    glutCreateWindow("Triangle");
        
    glutReshapeFunc(ChangeSize);
    
    glutDisplayFunc(RenderScene);
    
    glutSpecialFunc(SpecialKeys);
    
    GLenum err = glewInit();
    
    if(GLEW_OK != err) {
        
        fprintf(stderr,"glew error:%s\n",glewGetErrorString(err));
        
        return 1;
        
    }
    
    SetupRC();
    
    glutMainLoop();
    
    return 0;
}

复制代码

下面来一一分析对应函数和方法的意思,bash

gltSetWorkingDirectory(argv[0]) : 是”GLTools“用来设置当前工做目录的函数,实际上在windows中是没必要要的,由于工做目录默认就是与程序的可执行程序的目录相同。可是在Mac OSX环境中,这个程序将当前工做文件夹改成应用程序捆绑包中的"/Resource"文件夹.'GLUT'的优先设定会自动设定为这个,这样写也是为了更加安全。框架

glutInit(&argc, argv): 初始化GLUT库,这个函数只是传入命令参数而且初始化glut库函数

glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL): 设置显示模式。其中GLUT_DOUBLE 指的是初始化双缓冲窗口显示模式,GLUT_RGBA RGBA颜色显示模式,GLUT_DEPTH 初始化深度测试显示模式,能够用于开启深度测试,GLUT_STENCIL 初始化模板缓冲区oop

glutInitWindowSize(800,600) 初始化一个GLUT窗口并设置窗口大小 glutCreateWindow("Triangle") 设置窗口的标题post

glutReshapeFunc(ChangeSize) 注册绑定重塑函数,当窗口第一次建立或者窗口大小发生变化,须要界面重绘时,系统会自动调用这个已经注册过的自定义函数ChangeSize glutDisplayFunc(RenderScene) 注册绑定显示函数,当屏幕发生渲染或者你使用代码强制渲染,须要界面重绘的时候,系统会自动调用这个已经注册绑定过的自定义函数RenderSize测试

glutSpecialFunc(SpecialKeys); 当你使用特殊键位的时候好比键盘的上下左右键的时候,就会走到这个方法里,在这个函数中作特殊键位区分的时候,这些特殊键位都有对应的枚举值好比上(GLUT_KEY_UP) 下(GLUT_KEY_DOWN)左(GLUT_KEY_LEFT)右(GLUT_KEY_RIGHT),我找了一些,这个简单了解一下就好了ui

#define GLUT_KEY_F1 1
#define GLUT_KEY_F2 2
#define GLUT_KEY_F3 3
#define GLUT_KEY_F4 4
#define GLUT_KEY_F5 5
#define GLUT_KEY_F6 6
#define GLUT_KEY_F7 7
#define GLUT_KEY_F8 8
#define GLUT_KEY_F9 9
#define GLUT_KEY_F10 10
#define GLUT_KEY_F11 11
#define GLUT_KEY_F12 12
/* directional keys */
#define GLUT_KEY_LEFT 100
#define GLUT_KEY_UP 101
#define GLUT_KEY_RIGHT 102
#define GLUT_KEY_DOWN 103
#define GLUT_KEY_PAGE_UP 104
#define GLUT_KEY_PAGE_DOWN 105
#define GLUT_KEY_HOME 106
#define GLUT_KEY_END 107
#define GLUT_KEY_INSERT 108

复制代码

GLenum err = glewInit(); 初始化一个GLEW库,确保OpenGL API对程序彻底可用,在试图作任何渲染以前,要检查肯定驱动程序的初始化过程当中没有任何问题

SetupRC() 自定方法,设置咱们的渲染环境

glutMainLoop() 运行循环,相似OC的Runloop,函数在调用以后,在主窗口被关闭以前都不会返回,一个应用程序只须要调用一次,这个函数负责处理咱们全部的消息,直到咱们关闭程序为止。

二、接着引入固定管线着色器和OpenGL一些基本库文件

#include "GLShaderManager.h"

#include "GLTools.h"

#include <glut/glut.h>


复制代码

GLShaderManager.h 这个移入了GLTool着色器管理器类shader manager,没有着色器,咱们就不能再OpenGL核心框架进行着色,着色器管理器不只容许咱们建立并管理着色器,还提供了一组”存储着色器“,他们可以进行一些基本的渲染操做。

GLTools.h GLTool.h头文件包含了大部分GLTool中相似C语言的独立函数

GLUT/GLUT.h 在Mac 系统下,须要#include <glut/glut.h> ; 在windows和Linux上,咱们使用freeglut的静态库版本并须要添加一个宏

三、重塑函数的实现,在窗口大小改变的时候,接受新的宽度和高度

void changeSize(int w,int h)
{
    glViewport(0, 0, w, h);
    
}

复制代码

glViewPort这个函数就是设置视口,x,y 参数表明窗口中视图的左下角坐标,而宽度、高度是像素为表示,一般x,y 都是为0,调用glViewPort会调用从新渲染RenderScene

四、接着定义一个着色器管理变量和一个批次类,GLBatch是GLTools的一个简单的容器类

GLBatch triangleBatch;

GLShaderManager shaderManager;

复制代码

五、接下来,咱们须要设置渲染环境和顶点数据

void SetupRC()

{

    glClearColor(0.0f,0.0f,1.0f,1.0f);
    
    shaderManager.InitializeStockShaders();
        
    GLfloat vVerts[] = {
        
        -0.5f,0.0f,0.0f,
        
        0.5f,0.0f,0.0f,
        
        0.0f,1.0f,0.0f,
        
    };
    
    
    triangleBatch.Begin(GL_TRIANGLES,3);
    
    triangleBatch.CopyVertexData3f(vVerts);
    
    triangleBatch.End();
}

复制代码

glClearColor 设置清屏颜色,设置到颜色缓冲区里面,是一个状态基,能够理解为窗口的背景色

shaderManager.InitializeStockShaders() 初始化一个着色器管理器

GLfloat vVerts[] = {
        
        -0.5f,0.0f,0.0f,
        
        0.5f,0.0f,0.0f,
        
        0.0f,1.0f,0.0f,
        
    };
    定义一个一维数组,里面存储三角形的三个顶点的坐标,每一个顶点有三个数(x,y,z)

复制代码

其中 triangleBatch.Begin(GL_TRIANGLES,3); 第一个参数GL_TRIANGLES指的是选择三角形的链接方式,第二个参数3表明3个顶点

triangleBatch.CopyVertexData3f(vVerts);把顶点数据copy进去,而后triangleBatch.End();表示设置完成

这里拓展一下便于理解,若是是画四边形SetUpRC中的顶点坐标须要修改为

若是是要绘制四边形,须要修改顶点数组,而后修改链接方式以下

void SetupRC()

{    
    glClearColor(0.0f,0.0f,1.0f,1.0f);
    
    shaderManager.InitializeStockShaders();
    
    GLfloat vVerts[] = {
        
        -0.5f,-0.5f,0.0f,
        
        -0.5f,0.5f,0.0f,
        
        0.5f,0.5f,0.0f,
        
        0.5f,-0.5f,0.0f,
        
    };
    
    triangleBatch.Begin(GL_TRIANGLE_FAN,4);
    
    triangleBatch.CopyVertexData3f(vVerts);
    
    triangleBatch.End();
    
}

复制代码

对比一下,顶点数组变了,而后链接方式由GL_TRIANGLES变为GL_TRIANGLE_FAN,顶点个数由3个变为4个

顶点数组的变化很明显,就不细说了
 链接方式和顶点个数变了
 //三角形的
 triangleBatch.Begin(GL_TRIANGLES,3);
 //四边形的
 triangleBatch.Begin(GL_TRIANGLE_FAN,4);

复制代码

这里再拓展一下,OpenGL图元只有点、线、三角形,如今画点,线以及多边形都没问题了,圆形就须要经过这三个作处理一下了

六、接下来咱们须要设置渲染

void RenderScene(void)

{   
    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();
        
    glutSwapBuffers();
    
}

复制代码

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT) 每次渲染前须要清除特定缓冲区好比深度缓冲区,颜色缓冲区

shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed) 不一样的着色器,参数都是不同的,可是函数名都是这个,绘制简单的三角形,用单元着色器就好了,因此选GLT_SHADER_IDENTITY,并设置绘制颜色

triangleBatch.Draw() 开始颜色渲染

glutSwapBuffers() 交换缓冲区,把渲染的内容提交上去

执行一下,就能看到蓝色背景的windows下,有一个红色的三角形

1.png

七、开始设置移动,设置移动以前须要把顶点坐标设置为全局变量,这样好处理,把下面顶点数组放到全局

GLfloat vVerts[] = {
        
        -0.5f,0.0f,0.0f,
        
        0.5f,0.0f,0.0f,
        
        0.0f,1.0f,0.0f,
        
    };

复制代码

把这段坐标换成下面这段,而后把边长减少点,便于查看效果

//blockSize 边长
GLfloat blockSize = 0.1f;

//三角形的3个点坐标
GLfloat vVerts[] = {
        -blockSize,0.0f,0.0f,
        blockSize,0.0f,0.0f,
        0.0f,blockSize,0.0f,
};

复制代码

而后开始实现SpecialKeys函数,这里拓展一下,顶点较少可使用更新顶点坐标这种移动方式,顶点较多的复杂图形,可使用矩阵进行移动

void SpecialKeys (int key , int x, int y){
    
    GLfloat stepSize = 0.1f;
    GLfloat blockX = vVerts[0];
    GLfloat blockY = vVerts[1];
    
    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;
    
    //更新点的坐标 ABC
    vVerts[0] = blockX;
    vVerts[1] = blockY;
    
    vVerts[3] = blockX + 2 * stepSize;
    vVerts[4] = blockY;
    
    vVerts[6] = blockX + stepSize;
    vVerts[7] = blockY + stepSize;
    
    triangleBatch.CopyVertexData3f(vVerts);
    
    glutPostRedisplay();
}


复制代码

其中设置blockX和blockY只是找了第一个点做为移动坐标参照物,而后相对第一个点的坐标换算出剩余两个点的坐标,好比A(-0.1,0,0) B(0.1,0,0),换算成A(blockX,0,0)那此时的B就是(block + 2 * 0.1,0, 0), vVerts[0]这里面的下标0,表明一维数组中的第一个点的x坐标,只上下左右移动,只须要改三个点对应的x,y坐标就好了, 三个点想x和y的坐标对应的下标分别是0,1和3,4和6,7,这样思路就很清楚了。

triangleBatch.CopyVertexData3f(vVerts); 这个将顶点数组经过GLBatch帮助类,将顶点数据传输到存储着色器中

**glutPostRedisplay()**这个是手动触发渲染函数。

而后直接运行,就可以获得一个能够用键盘控制上下左右移动的三角形。

完整代码以下

//
//  main.cpp
//
//  Created by battleMage on 2019/8/11.
//  Copyright © 2019 battleMage. All rights reserved.
//

#include "GLShaderManager.h"
#include "GLTools.h"
#include <glut/glut.h>

GLBatch triangleBatch;
GLShaderManager shaderManager;

//blockSize 边长
GLfloat blockSize = 0.1f;

//三角形的3个点坐标
GLfloat vVerts[] = {
    -blockSize,0.0f,0.0f,
    blockSize,0.0f,0.0f,
    0.0f, blockSize,0.0f,
};

void ChangeSize(int w,int h){
    glViewport(0,0, w, h);
}

void SpecialKeys (int key , int x, int y){
    
    GLfloat stepSize = 0.1f;
    GLfloat blockX = vVerts[0];
    GLfloat blockY = vVerts[1];
    
    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;
    
    //更新点的坐标 ABC
    vVerts[0] = blockX;
    vVerts[1] = blockY;
    
    vVerts[3] = blockX + 2 * stepSize;
    vVerts[4] = blockY;
    
    vVerts[6] = blockX + stepSize;
    vVerts[7] = blockY + stepSize;
    
    triangleBatch.CopyVertexData3f(vVerts);
    
    glutPostRedisplay();
}

void SetupRC(){
    glClearColor(0.0f,0.0f,1.0f,1.0f);
    shaderManager.InitializeStockShaders();
    triangleBatch.Begin(GL_TRIANGLES,3);
    triangleBatch.CopyVertexData3f(vVerts);
    triangleBatch.End();
}

void RenderScene(void) {
    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();
    glutSwapBuffers();
}

int main(int argc,char* argv[]){
    
    gltSetWorkingDirectory(argv[0]);
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
    glutInitWindowSize(800,600);
    glutCreateWindow("Triangle");
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpecialKeys);
    
    GLenum err = glewInit();
    if(GLEW_OK != err) {
        fprintf(stderr,"glew error:%s\n",glewGetErrorString(err));
        return 1;
    }
    
    SetupRC();
    glutMainLoop();
    return 0;
}


复制代码

效果以下:

1.png

2.png

3.png
相关文章
相关标签/搜索