根据之前介绍的7种图元连接方式:点、线、线段、线环、三角形、三角形带、三角形扇 进行绘制图形。还有OpenGL中现成的几何图形API调用。
一、效果图
001.gif二、流程图
image.png三、源码
//======导入头文件======
#include "GLShaderManager.h"
#include "GLTools.h"
#include <GLUT/GLUT.h>
//矩阵工具类。加载单元矩阵、矩阵、矩阵相乘、压栈、出栈、缩放、平移、旋转
#include "GLMatrixStack.h"
//矩阵工具类。表示位置。通过设置origin、forward、up
#include "GLFrame.h"
//矩阵工具类。快速设置正投影、透视投影。完成3D->2D映射过程
#include "GLFrustum.h"
//三角形批次类。利用它将顶点、光照、纹理、颜色等数据传入存储着色器
#include "GLBatch.h"
//变换管道类。用来快速在代码中传输视图矩阵、投影矩阵、视图投影变换矩阵等。
#include "GLGeometryTransform.h"
//数学库
#include <math.h>
//======导入头文件======
//======声明全局变量======
//存储着色器
GLShaderManager shaderManager;
//视图模型矩阵
GLMatrixStack modelViewMatrix;
//透视矩阵
GLMatrixStack projectionMatrix;
//观察者视图坐标
GLFrame cameraFrame;
//图形环绕时用到的视图坐标
GLFrame objectFrame;
//图元绘制时的投影方式
GLFrustum viewFrustum;
//容器类
//点、线、线段、线环、三角形、三角形带、三角形扇
GLBatch pointBatch;
GLBatch lineBatch;
GLBatch lineStripBatch;
GLBatch lineLoopBatch;
GLBatch triangleBatch;
GLBatch triangleStripBatch;
GLBatch triangleFanBatch;
//几何变换管道。存储 模型矩阵、投影矩阵、模型视图矩阵
GLGeometryTransform transformPipeline;
//定义个黑色
GLfloat vBlack[] = {0.0f,0.0f,0.0f,1.0f};
//绿色
GLfloat vGreen[] = {0.0f,1.0f,0.0f,1.0f};
//记录当前按空格的次数,用来切换图案
int nStep = 0;
//======声明全局变量======
//填充颜色+画黑边
void DrawWireFramedBatch(GLBatch* pBatch)
{
/*------------1、填充颜色-画绿色部分----------------*/
/* GLShaderManager 中的Uniform 值——平面着色器
参数1:平面着色器
参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
--transformPipeline 变换管线(指定了2个矩阵堆栈)
参数3:颜色值
*/
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
pBatch->Draw();
/*-----------2、画边框-黑色边框-------------------*/
/*
glEnable(GLenum mode); 用于启用各种指定功能。功能由参数决定
glDisable(GLenum mode); 用于关闭指定功能 功能由参数决定
参数列表:http://blog.csdn.net/augusdi/article/details/23747081
注意:glEnable() 不能写在glBegin() 和 glEnd()中间
*/
//----开启----
//开启多边形偏移
glEnable(GL_POLYGON_OFFSET_LINE);
//设置偏移量
glPolygonOffset(-1.0f, -1.0f);
//开启反锯齿,让线变得更光滑好看
glEnable(GL_LINE_SMOOTH);
//开启颜色混合
glEnable(GL_BLEND);
//设置混合因子
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//设置正面背面的填充模式
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//设置线条宽度
glLineWidth(2.5f);
/* GLShaderManager 中的Uniform 值——平面着色器
参数1:平面着色器
参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
--transformPipeline.GetModelViewProjectionMatrix() 获取的
GetMatrix函数就可以获得矩阵堆栈顶部的值
参数3:颜色值(黑色)
*/
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
pBatch->Draw();
//----关闭+复原----
//复原填充方式和线段宽度
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glLineWidth(1.0f);
//关闭偏移
glDisable(GL_POLYGON_OFFSET_LINE);
//关闭颜色混合
glDisable(GL_BLEND);
//关闭反锯齿
glDisable(GL_LINE_SMOOTH);
}
//窗口变化时候的回调函数
void changeSize(int w,int h)
{
//防止宽高比中除数为0的bug
if(h==0) h=1;
//1、创建窗口
glViewport(0, 0, w, h);
//2、创建投影矩阵->透视投影(角度、宽高比、最小距离、最大距离)
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
//3、拿到这个投影矩阵,把它加载到我们声明的透视矩阵
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//4、在我们的视图模型矩阵 顶部载入单元矩阵,相当于初始化
modelViewMatrix.LoadIdentity();
//5、设置变换管道
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
//所有必要的初始化操作在这里进行
void setupRC()
{
//1、背景色
glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
//2、初始化着色器
shaderManager.InitializeStockShaders();
//3、开启深度测试
glEnable(GL_DEPTH_TEST);
//4、观察者的初始化位置
cameraFrame.MoveForward(-15.0f);
//5、设置顶点数据
//6、提交批次类 ==注意没一个批次类对于一种图形
//=====点====
GLfloat vCoast[9] = {
3,3,0,
0,3,0,
3,0,0
};
//点//==========================================================================================
pointBatch.Begin(GL_POINTS, 3);
pointBatch.CopyVertexData3f(vCoast);
pointBatch.End();
//线//==========================================================================================
lineBatch.Begin(GL_LINES, 3);
lineBatch.CopyVertexData3f(vCoast);
lineBatch.End();
//线段//==========================================================================================
lineStripBatch.Begin(GL_LINE_STRIP, 3);
lineStripBatch.CopyVertexData3f(vCoast);
lineStripBatch.End();
//线环//==========================================================================================
lineLoopBatch.Begin(GL_LINE_LOOP, 3);
lineLoopBatch.CopyVertexData3f(vCoast);
lineLoopBatch.End();
//三角形-金字塔//==========================================================================================
GLfloat vPyramid[12][3] = {
-2.0f, 0.0f, -2.0f,
2.0f, 0.0f, -2.0f,
0.0f, 4.0f, 0.0f,
2.0f, 0.0f, -2.0f,
2.0f, 0.0f, 2.0f,
0.0f, 4.0f, 0.0f,
2.0f, 0.0f, 2.0f,
-2.0f, 0.0f, 2.0f,
0.0f, 4.0f, 0.0f,
-2.0f, 0.0f, 2.0f,
-2.0f, 0.0f, -2.0f,
0.0f, 4.0f, 0.0f
};
triangleBatch.Begin(GL_TRIANGLES, 12);
triangleBatch.CopyVertexData3f(vPyramid);
triangleBatch.End();
//三角形扇-六边形//==========================================================================================
GLfloat vPoints[100][3];
int nVerts = 0;
//半径
GLfloat r = 3.0f;
//原点(x,y,z) = (0,0,0);
vPoints[nVerts][0] = 0.0f;
vPoints[nVerts][1] = 0.0f;
vPoints[nVerts][2] = 0.0f;
//M3D_2PI 就是2Pi 的意思,就一个圆的意思。 绘制圆形
for(GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0f) {
//数组下标自增(每自增1次就表示一个顶点)
nVerts++;
/*
弧长=半径*角度,这里的角度是弧度制,不是平时的角度制
既然知道了cos值,那么角度=arccos,求一个反三角函数就行了
*/
//**利用数学公式计算
//x点坐标 cos(angle) * 半径
vPoints[nVerts][0] = float(cos(angle)) * r;
//y点坐标 sin(angle) * 半径
vPoints[nVerts][1] = float(sin(angle)) * r;
//z点的坐标
vPoints[nVerts][2] = -0.5f;
}
// 结束扇形 前面一共绘制7个顶点(包括圆心)
//添加闭合的终点
//课程添加演示:屏蔽177-180行代码,并把绘制节点改为7.则三角形扇形是无法闭合的。
nVerts++;
vPoints[nVerts][0] = r;
vPoints[nVerts][1] = 0;
vPoints[nVerts][2] = 0.0f;
// 加载!
//GL_TRIANGLE_FAN 以一个圆心为中心呈扇形排列,共用相邻顶点的一组三角形
triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
triangleFanBatch.CopyVertexData3f(vPoints);
triangleFanBatch.End();
//三角形带- 圆柱段//==========================================================================================
//顶点下标
int iCounter = 0;
//半径
GLfloat radius = 3.0f;
//从0度~360度,以0.3弧度为步长
for(GLfloat angle = 0.0f; angle <= (2.0f*M3D_PI); angle += 0.3f)
{
//或许圆形的顶点的X,Y
GLfloat x = radius * sin(angle);
GLfloat y = radius * cos(angle);
//绘制2个三角形(他们的x,y顶点一样,只是z点不一样)
vPoints[iCounter][0] = x;
vPoints[iCounter][1] = y;
vPoints[iCounter][2] = -0.5;
iCounter++;
vPoints[iCounter][0] = x;
vPoints[iCounter][1] = y;
vPoints[iCounter][2] = 0.5;
iCounter++;
}
// 关闭循环
printf("三角形带的顶点数:%d\n",iCounter);
//结束循环,在循环位置生成2个三角形
vPoints[iCounter][0] = vPoints[0][0];
vPoints[iCounter][1] = vPoints[0][1];
vPoints[iCounter][2] = -0.5;
iCounter++;
vPoints[iCounter][0] = vPoints[1][0];
vPoints[iCounter][1] = vPoints[1][1];
vPoints[iCounter][2] = 0.5;
iCounter++;
// GL_TRIANGLE_STRIP 共用一个条带(strip)上的顶点的一组三角形
triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
triangleStripBatch.CopyVertexData3f(vPoints);
triangleStripBatch.End();
}
//进行渲染
void RenderScene(void)
{
//1、清楚缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |GL_STENCIL_BUFFER_BIT);
//2、压栈 -> 为了记录最初状态,用来撤回 顶部是单元矩阵
modelViewMatrix.PushMatrix();
//3、初始化一个观察者矩阵
M3DMatrix44f mCamreaMatrix;
//把观察者矩阵放进去
cameraFrame.GetCameraMatrix(mCamreaMatrix);
//矩阵相乘放入顶部 : 单元矩阵(1) * mCamreaMatrix = New_mCamreaMatrix
modelViewMatrix.MultMatrix(mCamreaMatrix);
//4、初始化一个物体本身矩阵
M3DMatrix44f mObjectMatrix;
objectFrame.GetMatrix(mObjectMatrix);
//New_mCamreaMatrix * mObjectMatrix = New_mObjectMatrix
modelViewMatrix.MultMatrix(mObjectMatrix);
//5、使用平面着色器存储栈顶的矩阵 (平面着色器,几何图形的变换矩阵,颜色)
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
//6、设置属性,draw,还原属性
switch (nStep) {
case 0:
//设置点大小
glPointSize(5.0f);
pointBatch.Draw();
//用完还原
glPointSize(1.0f);
break;
case 1:
//设置线的宽度
glLineWidth(2.0f);
lineBatch.Draw();
glLineWidth(1.0f);
break;
case 2:
glLineWidth(2.0f);
lineStripBatch.Draw();
glLineWidth(1.0f);
break;
case 3:
glLineWidth(2.0f);
lineLoopBatch.Draw();
glLineWidth(1.0f);
break;
case 4:
DrawWireFramedBatch(&triangleBatch);
break;
case 5:
DrawWireFramedBatch(&triangleStripBatch);
break;
case 6:
DrawWireFramedBatch(&triangleFanBatch);
break;
default:
break;
}
//7、出栈、进行撤回操作。
modelViewMatrix.PopMatrix();
//8、进行交换缓冲区
glutSwapBuffers();
}
//空格键回调
void KeyPressFunc(unsigned char key, int x, int y)
{
//这里32刚好对应空格键
if(key == 32)
{
nStep++;
if(nStep > 6)
nStep = 0;
}
switch(nStep)
{
case 0:
glutSetWindowTitle("GL_POINTS");
break;
case 1:
glutSetWindowTitle("GL_LINES");
break;
case 2:
glutSetWindowTitle("GL_LINE_STRIP");
break;
case 3:
glutSetWindowTitle("GL_LINE_LOOP");
break;
case 4:
glutSetWindowTitle("GL_TRIANGLES");
break;
case 5:
glutSetWindowTitle("GL_TRIANGLE_STRIP");
break;
case 6:
glutSetWindowTitle("GL_TRIANGLE_FAN");
break;
}
glutPostRedisplay();
}
//上下左右键回调
void SpecialKeys(int key, int x, int y)
{
//上下=沿着X轴旋转 左右=沿着Y轴旋转
if(key == GLUT_KEY_UP)
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_DOWN)
objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_LEFT)
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT)
objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
glutPostRedisplay();
}
int main(int argc,char *argv[])
{
//1、设置工作目录、初始化
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
//2、初始化缓冲区 双缓冲、颜色、深度、模板缓冲区
glutInitDisplayMode(GLUT_DOUBLE |GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
//3、设置窗口大小、标题
glutInitWindowSize(800, 600);
glutCreateWindow("jpy");
//========================
//4、注册所需回调函数
//窗口重塑回调
glutReshapeFunc(changeSize);
//渲染回调
glutDisplayFunc(RenderScene);
//空格回调
glutKeyboardFunc(KeyPressFunc);
//特殊键位回调
glutSpecialFunc(SpecialKeys);
//========================
//5、固定操作。初始化GLEW库,做检测
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n",glewGetErrorString(err));
return 1;
}
//进行初始化操作
setupRC();
//开始loop监听各种回调
glutMainLoop();
return 0;
}
四、拓展
我们上面的代码使用的是自己计算各个顶点,传入三角形批次类中,然后进行绘制。那么如果我们进行一些复杂图形绘制,又要计算多少个顶点呢?那得多麻烦啊!!!
GLTriangleBatch 这个类里面有一些已经封装好的API,我们可以直接调用!!!
我们来看一下都有哪些:
//首先我们要声明一下
//球
GLTriangleBatch sphereBatch;
//环
GLTriangleBatch torusBatch;
//圆柱
GLTriangleBatch cylinderBatch;
//锥
GLTriangleBatch coneBatch;
//磁盘
GLTriangleBatch diskBatch;
在SetupRC()中进行初始化
// 球
/*
gltMakeSphere(GLTriangleBatch& sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks);
参数1:sphereBatch,三角形批次类对象
参数2:fRadius,球体半径
参数3:iSlices,从球体底部堆叠到顶部的三角形带的数量;其实球体是一圈一圈三角形带组成
参数4:iStacks,围绕球体一圈排列的三角形对数
建议:一个对称性较好的球体的片段数量是堆叠数量的2倍,就是iStacks = 2 * iSlices;
绘制球体都是围绕Z轴,这样+z就是球体的顶点,-z就是球体的底部。
*/
gltMakeSphere(sphereBatch, 3.0, 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.0f, 0.75f, 15, 15);
// 圆柱
/*
void 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, 2.0f, 2.0f, 3.0f, 15, 2);
//锥
/*
void 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,圆柱底部堆叠到顶部圆环的三角形数量
*/
//圆柱体,从0开始向Z轴正方向延伸。
//圆锥体,是一端的半径为0,另一端半径可指定。
gltMakeCylinder(coneBatch, 2.0f, 0.0f, 3.0f, 13, 2);
// 磁盘
/*
void 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.5f, 3.0f, 13, 3);
在 RenderScene 中进行绘制,直接调用上面代码中的 DrawWireFramedBatch 方法
switch(nStep) {
case 0:
DrawWireFramedBatch(&sphereBatch);
break;
case 1:
DrawWireFramedBatch(&torusBatch);
break;
case 2:
DrawWireFramedBatch(&cylinderBatch);
break;
case 3:
DrawWireFramedBatch(&coneBatch);
break;
case 4:
DrawWireFramedBatch(&diskBatch);
break;
}
网友评论