三、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
相關文章
相關標籤/搜索