美文网首页OpenGL初探
第十三节—GLTools自带图形绘制和视变换

第十三节—GLTools自带图形绘制和视变换

作者: L_Ares | 来源:发表于2020-08-29 22:33 被阅读0次

本文为L_Ares个人写作,包括图片皆为个人亲自操作,以任何形式转载请表明原文出处。

OpenGL的一些库中是有一些自带图形的,用于一些简单的图形绘制是很好用的,另外本节代码也会为下一节做一下铺垫。

直接上代码,代码中都有非常详细的注释,而且都是经过测试可以跑起来的。


//
//  main.cpp
//  07矩阵堆栈
//
//  Created by EasonLi on 2020/8/29.
//  Copyright © 2020 EasonLi. All rights reserved.
//

#include <stdio.h>

#pragma mark - 引用类

//着色器管理器。负责管理和创建着色器管理器。里面有一些固定的着色器,可用于初级操作
#include "GLShaderManager.h"
//GLTool工具类,里面包含了GLTool大多数类似C语言的函数
#include "GLTools.h"
//矩阵堆栈类。包含了矩阵堆栈的操作
#include "GLMatrixStack.h"
//管道类,用于更加方便的管理矩阵
#include "GLGeometryTransform.h"
//用于设置观察者或者物体的位置
#include "GLFrame.h"
//投影矩阵获取
#include "GLFrustum.h"
//三角形批次类
#include "GLBatch.h"
//3d数学类
#include "math3d.h"

//宏定义判断系统,引入GLUT
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

#pragma mark - 公共变量

//着色器管理器
GLShaderManager shaderManager;
//几何变换管道
GLGeometryTransform transformPipeline;

//模型视图矩阵
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;

//观察者(相机)位置
GLFrame cameraFrame;
//物体世界坐标位置
GLFrame objectFrame;

//视景体,用来构造投影矩阵
GLFrustum viewFrustum;

//三角形批次类
//球
GLTriangleBatch sphereBatch;
//环
GLTriangleBatch torusBatch;
//圆柱
GLTriangleBatch cylinderBatch;
//锥
GLTriangleBatch coneBatch;
//磁盘
GLTriangleBatch diskBatch;

//颜色
GLfloat vRed[] = {1.f,0.f,0.f,1.f};
GLfloat vBlack[] = {0.f,0.f,0.f,1.f};

//记录点击空格的次数
int step = 0;

#pragma mark - 函数

/**
 设置窗口
 */
void ChangeSize(int w,int h)
{
    
    if (h == 0) {
        h = 1;
    }
    
    //设置视口
    glViewport(0, 0, w, h);
    
    //设置透视投影
    //1.观察者垂直视角度数。2.宽高比。3.视点与近剪裁面距离。4.视点与远剪裁面距离
    viewFrustum.SetPerspective(36.f, float(w)/float(h), 1.f, 500.f);
    
    //projectionMatrix矩阵堆栈加载透视投影矩阵
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    //modelViewMatrix堆栈矩阵加载一个单元矩阵。其实不加载也没事,源码中可以看到,已经帮忙加载了一个了。
    modelViewMatrix.LoadIdentity();
    
    //通过GLGeometryTransform管理矩阵堆栈(模型视图矩阵堆栈、投影矩阵堆栈)
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    
    
}

/**
 设置渲染环境
 */
void SetUpRC ()
{
    
    //设置清屏颜色
    glClearColor(0.3f, 0.3f, 0.3f, 1.f);
    //着色器管理器初始化
    shaderManager.InitializeStockShaders();
    
    //开启需要用到的环境
    //开启深度测试
    glEnable(GL_DEPTH_TEST);
    
    //设置观察者和物体的距离,保证物体都能看得到
    //这里有两种方式(1)(2)
    //(1)将观察者(摄像机)位置沿其Z轴向内部移动15个单位。负数往里面移动,正数向外面移动。
    //cameraFrame.MoveForward(-15.f);
    
    //(2)将物体移动15个单位,观察者(摄像机)不动
    objectFrame.MoveForward(15.f);
    
    //这里直接使用一下GLTools里面给过的图形
    /**
     球:
     gltMakeSphere(GLTriangleBatch& sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks);
     参数:
     (1)sphereBatch:三角形批次类对象
     (2)fRadius:球体半径
     (3)iSlices:从球体底部堆叠到球体顶部的三角形带的数量。可以看出球体是一圈圈的三角形带组成的。
     (4)iStacks:围绕球体一圈排列的三角形片段对数
     建议:一个对称性较好的球体一般满足片段数量是三角形带数量的2倍。iSlices * 2 = iStacks
     绘制球体都是围绕Z轴的,因为这样Z轴的正方向(+)就是球体顶部,Z轴负方向(-)就是球体底部。
     */
    gltMakeSphere(sphereBatch, 3.f, 10, 20);
    
    /**
     环:
     gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
     参数:
     (1)torusBatch:三角形批次类对象
     (2)majorRadius:圆环中心到外边缘的半径(大圆半径)
     (3)minorRadius:圆环中心到内边缘的半径(小圆半径)
     (4)numMajor:大圆的三角形数量
     (5)numMinor:小圆的三角形数量
     */
    gltMakeTorus(torusBatch, 3.f, 1, 16, 16);
    
    /**
     圆柱:
     gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks);
     参数:
     (1)cylinderBatch:三角形批次类对象
     (2)baseRadius:底部圆半径
     (3)topRadius:顶部圆半径
     (4)fLength:圆柱体的长度(高度)
     (5)numSlices:围绕Z轴的三角形带的对数
     (6)numStacks:从圆柱底到圆柱顶的三角形带的数量
     */
    gltMakeCylinder(cylinderBatch, 3.f, 3.f, 3.f, 16, 3);
    
    /**
     锥:特殊的圆柱
     gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks);
     参数:
     (1)cylinderBatch:三角形批次类对象
     (2)baseRadius:底部圆的半径
     (3)topRadius:顶部圆半径,锥体直接给0就行了
     (4)fLength:锥体长度(高度)
     (5)numSlices:围绕Z轴的三角形带的对数
     (6)numStacks:从圆柱底到圆柱顶的三角形带的数量
     */
    gltMakeCylinder(coneBatch, 3.f, 0.f, 3.f, 16, 3);
    
    /**
     磁盘:
     gltMakeDisk(GLTriangleBatch& diskBatch, GLfloat innerRadius, GLfloat outerRadius, GLint nSlices, GLint nStacks);
     参数:
     (1)diskBatch:三角形批次类对象
     (2)innerRadius:内部圆半径
     (3)outerRadius:外部圆半径
     (4)nSlices:围绕Z轴的三角形对的数量
     (5)nStacks:从内边缘到外边缘三角形带的对数
     */
    gltMakeDisk(diskBatch, 1.f, 3.f, 16, 4);
    
}

/**
 利用三角形批次类进行绘制
 */
void DrawBatch(GLTriangleBatch *dBatch)
{
    
    //绘制图形
    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vRed);
    
    //这里用的是指针,看清楚上面,不能用点方法
    dBatch->Draw();
    
    //绘制黑色线条
    //这里就需要开启颜色混合、多边形偏移、抗锯齿等功能
    
    //开启多边形偏移
    glEnable(GL_POLYGON_OFFSET_LINE);
    //设置偏移的面和模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    //指定偏移量
    glPolygonOffset(-1.f, -1.f);
    
    //开启混合
    glEnable(GL_BLEND);
    //设置混合因子
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    //开启线段模式抗锯齿(简单的抗锯齿)
    glEnable(GL_LINE_SMOOTH);
    
    //可以设置一下线段的宽度
    glLineWidth(3.f);
    
    //利用平面着色器画黑线
    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
    dBatch->Draw();
    
    //绘制完成记得关闭之前开启的功能,回复图形填充方式,并恢复线段宽度
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
    glLineWidth(1.f);
    
}

//观察者移动(摄像机),物体不动
void CameraMoveObjNo()
{
    //复制栈顶的值,并且压入栈顶
    modelViewMatrix.PushMatrix();
    
    //获取观察者(摄像头)矩阵
    //先定义一个矩阵,方便一会存储获取到的观察者(摄像头)矩阵
    M3DMatrix44f mCamera;
    //从cameraFrame中获取它栈顶的矩阵,然后赋值给mCamera,好用来做矩阵变换,这样做能保证多图形的情况下,不改变原
    //cameraFrame的栈顶矩阵,而用一份复制出来的操作,不会影响到其他的图形绘制
    cameraFrame.GetCameraMatrix(mCamera);
    //让modelViewMatrix模型视图矩阵和观察者(摄像头)矩阵相乘,发生第一步变化,相乘的结果会存储到
    //modelViewMatrix栈顶里面
    modelViewMatrix.MultMatrix(mCamera);
    
    //获取物体的矩阵
    //先定义一个矩阵,也是方便存储获取到的物体仿射变换后的矩阵
    M3DMatrix44f mObject;
    //从objectFrame中获取其栈顶的矩阵,并且赋值给mObject,原理和上面的一样
    objectFrame.GetMatrix(mObject);
    //让modelViewMatrix模型视图矩阵和mObject相乘,结果矩阵依然存储在modelViewMatrix栈顶
    modelViewMatrix.MultMatrix(mObject);
    
}

//物体移动,观察者(摄像机)不动
void ObjMoveCameraNo()
{
    
    modelViewMatrix.PushMatrix(objectFrame);
    
}

/**
 渲染
 */
void RenderScene()
{
    
    //清空缓冲区
    //虽然模版缓冲区都没用到,但是养成全部清空的好习惯肯定不会错
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    //当观察者移动(摄像机),而物体不移动的时候,对应了SetUpRC中的(1)
    //CameraMoveObjNo();
    
    //当观察者不动,物体移动的时候,对应了SetUpRC中的(2)
    ObjMoveCameraNo();
    
    
    //到上面这一步,矩阵的变换已经都做完了,下面要绘制了
    //判断空格点击次数,更换不同的图形
    switch (step) {
            
        case 0:
            DrawBatch(&sphereBatch);
            break;
            
        case 1:
            DrawBatch(&torusBatch);
            break;
            
        case 2:
            DrawBatch(&cylinderBatch);
            break;
        
        case 3:
            DrawBatch(&coneBatch);
            break;
            
        case 4:
            DrawBatch(&diskBatch);
            break;
            
    }
    
    //绘制完成一个图形,就把栈顶的矩阵可以Pop出去了,不要影响下一个图形的绘制
    modelViewMatrix.PopMatrix();
    
    //交换缓冲区
    glutSwapBuffers();
    
}

/**
 设置特殊键位(上下左右)
 */
void SpecialKeys(int key,int x,int y)
{
    
    //这里还是移动世界坐标系,因为画的东西都有太多的顶点了,移动物体的成本太高,效果是一样的。
    switch (key) {
            
        case GLUT_KEY_UP:
            objectFrame.RotateWorld(m3dDegToRad(-5.f), 1.f, 0.f, 0.f);
            break;
            
        case GLUT_KEY_DOWN:
            objectFrame.RotateWorld(m3dDegToRad(5.f), 1.f, 0.f, 0.f);
            break;
            
        case GLUT_KEY_LEFT:
            objectFrame.RotateWorld(m3dDegToRad(-5.f), 0.f, 1.f, 0.f);
            break;
            
        case GLUT_KEY_RIGHT:
            objectFrame.RotateWorld(m3dDegToRad(5.f), 0.f, 1.f, 0.f);
            break;
            
    }
    
    //移动完了要发送渲染信号
    glutPostRedisplay();
    
}

/**
 设置键盘键位(空格)
 */
void SpaceKey(unsigned char key,int x,int y)
{
    
    //拿空格键的ASCII码来判定点击的是键盘上的空格键
    if (key == 32) {
        step++;
        if(step > 4) step = 0;
    }
    
    switch (step) {
        case 0:
            glutSetWindowTitle("Sphere球");
            break;
        case 1:
            glutSetWindowTitle("Torus圆环");
            break;
        case 2:
            glutSetWindowTitle("Cylinder圆柱");
            break;
        case 3:
            glutSetWindowTitle("Cone锥");
            break;
        case 4:
            glutSetWindowTitle("Disk磁盘");
            break;
    }
    //发送重新渲染的信号
    glutPostRedisplay();
    
}

#pragma mark - main

int main(int argc,char *argv[])
{
    
    //设置工作目录和项目目录都在/Resouce下面,GLUT优先级做过,但是手动确保一定
    gltSetWorkingDirectory(argv[0]);
    
    //GLUT初始化
    glutInit(&argc, argv);
    
    //设置显示模式
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA | GLUT_STENCIL);
    
    //设置窗口大小
    glutInitWindowSize(600, 600);
    
    //创建窗口并命名
    glutCreateWindow("Sphere球");
    
    //注册函数
    //注册窗口
    glutReshapeFunc(ChangeSize);
    //注册渲染
    glutDisplayFunc(RenderScene);
    //注册特殊键位
    glutSpecialFunc(SpecialKeys);
    //注册空格键位
    glutKeyboardFunc(SpaceKey);
    
    //初始化Glew库
    GLenum status = glewInit();
    if (status != GLEW_OK) {
        printf("glew init error : %s \n",glewGetErrorString(status));
    }
    
    //设置渲染环境
    SetUpRC();
    
    //构建本地循环
    glutMainLoop();
    
    return 0;
    
}

显示效果我就不贴图了,有问题的话可以留言。

这上面void SetUpRC()void RenderScene()中的(1)(2)的变换,就是视变换的结果,说的通俗点,就是山不向你走来,你就向山走去。

相关文章

  • 第十三节—GLTools自带图形绘制和视变换

    本文为L_Ares个人写作,包括图片皆为个人亲自操作,以任何形式转载请表明原文出处。 OpenGL的一些库中是有一...

  • OpenGLES入门(三)

    上一章中还是对平面图形的绘制和简单的图形变换,在本章中将会开始绘制简单的3D图形,并对图形进行组合变换。 开始学习...

  • CGAffineTransform

    CGAffineTransform用于绘制2D图形的仿射变换矩阵。 仿射变换矩阵用于旋转,缩放,平移或倾斜在图形上...

  • Paint/Canvas/Path高级绘制【2】

    Canvas 1. 概念 画布,通过画笔绘制几何图形、文本、路径和位图等 2. 常用API类型 分为 绘制,变换,...

  • OpenGL: 使用变换管线管理矩阵堆栈,绘制各种图形

    penGL: 使用变换管线管理矩阵堆栈,绘制各种图形

  • HTML5 Canvas坐标变换

    我们在使用 Canvas 绘制图形时,可能会想对绘制的图形进行变换,例如让图形旋转90度,或者让图像缩小放大等,这...

  • HTML5 Canvas坐标变换

    我们在使用 Canvas 绘制图形时,可能会想对绘制的图形进行变换,例如让图形旋转90度,或者让图像缩小放大等,这...

  • Canvas详解

    1.概念 画布,通过画笔绘制几何图形,文本,路径和位图等。 2.常用api 常用Api分为绘制,变换,状态保存和恢...

  • Core Graphics框架 :仿射变换与齐次坐标

    Core Graphics框架中的图形变换 在Core Graphics框架图形绘制的时候,经常会有对图形进行平移...

  • 图形的变换

    在了解矩阵的变换之后,我们就可以开始对图形进行变换。这里使用的变换图形是之前绘制出来的笑脸箱子。欢迎光临我的个人网...

网友评论

    本文标题:第十三节—GLTools自带图形绘制和视变换

    本文链接:https://www.haomeiwen.com/subject/drxssktx.html