学习书籍: OpenGL 超级宝典(中文第五版) 密码:fu4w
书籍源码:OpenGL 超级宝典第五版源代码 密码:oyb4
环境搭建:OpenGL 学习 01 - Mac 搭建 OpenGL 环境
基本概念 着色器OpenGL 着色器传递渲染数据方法:
- 属性值:对顶点数据作改变的数据元素
- Uniform 值:系统定义的统一枚举值
- 纹理:图片文件等大型数据块
属性值类型:
- XYZ 顶点坐标 -> GLT_ATTRIBUTE_VERTEX
- RGBA 颜色值 -> GLT_ATTRIBUTE_COLOR
- XYZ 表面法线 -> GLT_ATTRIBUTE_NORMAL
- 第一对 ST 纹理坐标 -> GLT_ATTRIBUTE_TEXTURE0
- 第二对 ST 纹理坐标 -> GLT_ATTRIBUTE_TEXTURE1
Uniform 值:
- 单位着色器 -> GLT_SHADER_IDENTITY 参数:基本色
- 平面着色器 -> GLT_SHADER_FLAT 参数:模型矩阵 + 基本色
- 上色着色器 -> GLT_SHADER_SHADED 参数:模型矩阵
- 默认光源着色器 -> GLT_SHADER_DEFAULT_LIGHT 参数:模型矩阵 + 投影矩阵 + 基本色
- 点光源着色器 -> GLT_SHADER_POINT_LIGHT_DIFF 参数:模型矩阵 + 投影矩阵 + 光源位置 + 基本色
- 纹理替换着色器 -> GLT_SHADER_TEXTURE_REPLACE 参数:模型矩阵 + 纹理单元
- 纹理调整着色器 -> GLT_SHADER_TEXTURE_MODULATE 参数:模型矩阵 + 基本色 + 纹理单元
- 纹理光源着色器 -> GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF 参数:模型矩阵 + 投影矩阵 + 光源位置 + 基本色 + 纹理单元
// 定义着色器管理器 GLShaderManager shaderManager; // 初始化着色器管理器 shaderManager.InitializeStockShaders(); // 使用着色器 shaderManager.UseStockShader(参数...);坐标系
正投影:在正投影中,所有在这个空间范围内的所有东西都会被显示在屏幕上,看不出远近的区别
透视投影:透视投影会进行透视除法对距离观察者很远的对象进行缩短和收缩,也就是离视点越远,在视点看来物体越小,能看出远近的区别
图元OpenGL 系统定义的 7 种图元类型:
- 点 -> GL_POINTS:屏幕上单独的点
- 线段 -> GL_LINES:每对顶点定义一条线段
- 线条 -> GL_LINE_STRIP:从起始点依次经过所有后续点的线条
- 闭合线条 -> GL_LINE_LOOP:起始点和终点相连的线条
- 三角形 -> GL_TRIANGLES:每 3 个顶点定义一个三角形
- 三角形条带 -> GL_TRIANGLE_STRIP:共用一个条带上顶点的一组三角形
- 三角形扇 -> GL_TRIANGLE_FAN:以圆点为中心呈扇形的共用相邻顶点的一组三角形
// 批次初始化 GLBatch batch; batch.Begin(图元类型, 顶点数); batch.CopyVertexData3f(顶点数据); batch.End();环绕
环绕即各个点连接的顺序,在默认情况下,OpenGL 认为具有逆时针方向环绕的多边形是正面,因为我们会需要为多边形的正面和背面设置不同的物理特征。
源码解析 改变点大小// 默认情况下,点大小和其他图形不同,并不会受到透视除法影响 void glPointSize(GLfloat size);获取支持的点大小范围和步长(增量)
GLfloat sizes[2]; GLfloat step; // 获取支持的点大小范围 glGetFloatv(GL_POINT_SIZE_RANGE, sizes); // 获取支持的点步长(增量) glGetFloatv(GL_POINT_SIZE_GRANULARITY, &step);改变线宽度
void glLineWidth(GLfloat width);改变默认正面规则
glFrontFace(GL_CW);监听普通按键点击事件
// 自定义普通按键点击监听
void KeyPressFunc(unsigned char key, int x, int y) {
// 处理
}
// 注册普通按键点击回调
glutKeyboardFunc(KeyPressFunc);
综合源码分析
#include#include #include #include #include #include #include #include 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 vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f }; GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f }; GLfloat vCoast[24][3] = { {2.80, 1.20, 0.0 }, {2.0, 1.20, 0.0 }, {2.0, 1.08, 0.0 }, {2.0, 1.08, 0.0 }, {0.0, 0.80, 0.0 }, {-.32, 0.40, 0.0 }, {-.48, 0.2, 0.0 }, {-.40, 0.0, 0.0 }, {-.60, -.40, 0.0 }, {-.80, -.80, 0.0 }, {-.80, -1.4, 0.0 }, {-.40, -1.60, 0.0 }, {0.0, -1.20, 0.0 }, { .2, -.80, 0.0 }, {.48, -.40, 0.0 }, {.52, -.20, 0.0 }, {.48, .20, 0.0 }, {.80, .40, 0.0 }, {1.20, .80, 0.0 }, {1.60, .60, 0.0 }, {2.0, .60, 0.0 }, {2.2, .80, 0.0 }, {2.40, 1.0, 0.0 }, {2.80, 1.0, 0.0 } }; int nStep = 0; //初始化点批次 void SetupPointBatch() { pointBatch.Begin(GL_POINTS, 24); pointBatch.CopyVertexData3f(vCoast); pointBatch.End(); } //初始化线批次 void SetupLineBatch() { lineBatch.Begin(GL_LINES, 24); lineBatch.CopyVertexData3f(vCoast); lineBatch.End(); } //初始化线条批次 void SetupLineStripBatch() { lineStripBatch.Begin(GL_LINE_STRIP, 24); lineStripBatch.CopyVertexData3f(vCoast); lineStripBatch.End(); } //初始化闭合线条批次 void SetupLineLoopBatch() { lineLoopBatch.Begin(GL_LINE_LOOP, 24); lineLoopBatch.CopyVertexData3f(vCoast); lineLoopBatch.End(); } //初始化三角形批次 void SetupTriangleBatch() { 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(); } //初始化三角形带批次 void SetupTriangleStripBatch() { // 用代码生成三角形带的顶点位置 // x,y,z 坐标的点 GLfloat vPoints[100][3]; int iCounter = 0; GLfloat radius = 3.0f; GLfloat height = 1.0f; for(GLfloat angle = 0.0f; angle <= M3D_2PI; angle += 0.3f) { GLfloat x = radius * sin(angle); GLfloat y = radius * cos(angle); vPoints[iCounter][0] = x; vPoints[iCounter][1] = y; vPoints[iCounter][2] = 0; iCounter++; vPoints[iCounter][0] = x; vPoints[iCounter][1] = y; vPoints[iCounter][2] = height; iCounter++; } //使三角形带闭合 vPoints[iCounter][0] = vPoints[0][0]; vPoints[iCounter][1] = vPoints[0][1]; vPoints[iCounter][2] = 0; iCounter++; vPoints[iCounter][0] = vPoints[1][0]; vPoints[iCounter][1] = vPoints[1][1]; vPoints[iCounter][2] = height; iCounter++; //三角形带批次初始化 triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter); triangleStripBatch.CopyVertexData3f(vPoints); triangleStripBatch.End(); } //初始化三角形扇批次 void SetupTriangleFanBatch() { // 用代码生成三角形扇的顶点位置 // x,y,z 坐标的点 GLfloat vPoints[100][3]; int nVerts = 0; GLfloat r = 3.0f; vPoints[nVerts][0] = 0.0f; vPoints[nVerts][1] = 0.0f; vPoints[nVerts][2] = 0.0f; for (GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0f) { nVerts++; vPoints[nVerts][0] = float(cos(angle)) * r; vPoints[nVerts][1] = float(sin(angle)) * r; vPoints[nVerts][2] = -r; } //使三角形扇闭合 nVerts++; vPoints[nVerts][0] = r; vPoints[nVerts][1] = 0; vPoints[nVerts][2] = 0.0f; //三角形扇批次初始化 triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8); triangleFanBatch.CopyVertexData3f(vPoints); triangleFanBatch.End(); } //为程序作一次性的设置 void SetupRC() { //设置窗口背景颜色 glClearColor(0.7f, 0.7f, 0.7f, 1.0f ); //初始化着色器管理器 shaderManager.InitializeStockShaders(); //开启深度测试 glEnable(GL_DEPTH_TEST); //设置变换管线以使用两个矩阵堆栈 transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix); //移动摄像机的位置 cameraframe.MoveForward(-15.0f); //准备画图要用的批次 SetupPointBatch(); SetupLineBatch(); SetupLineStripBatch(); SetupLineLoopBatch(); SetupTriangleBatch(); SetupTriangleFanBatch(); SetupTriangleStripBatch(); } //画点 void DrawPointBatch(GLBatch* pBatch) { //使用着色器 shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack); //设置点大小 glPointSize(4.0f); //画点 pBatch->Draw(); //还原绘画环境 glPointSize(1.0f); } //画线 void DrawLineBatch(GLBatch* pBatch) { //使用着色器 shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack); //设置线粗 glLineWidth(2.0f); //画线 pBatch->Draw(); //还原绘画环境 glLineWidth(1.0f); } //画三角形 void DrawTriangleBatch(GLBatch* pBatch) { //画绿色面 shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen); pBatch->Draw(); //开启调节片段的深度值,使深度值产生偏移而不实际改变 3D 空间的物理位置 glPolygonOffset(-1.0f, -1.0f); glEnable(GL_POLYGON_OFFSET_LINE); //开启线条的抗锯齿 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); shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack); pBatch->Draw(); //还原绘画环境 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glDisable(GL_POLYGON_OFFSET_LINE); glLineWidth(1.0f); glDisable(GL_BLEND); glDisable(GL_LINE_SMOOTH); } //渲染画面 void RenderScene(void) { //清除一个或一组特定的缓冲区 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); //保存当前的模型视图矩阵 (单位矩阵) modelViewMatrix.PushMatrix(); //MultMatrix: 用一个矩阵乘以矩阵堆栈的顶部矩阵,相乘得到的结果随后将存储在堆栈的顶部 //处理模型相对于摄像机的位置 M3DMatrix44f mCamera; cameraframe.GetCameraMatrix(mCamera); modelViewMatrix.MultMatrix(mCamera); //处理模型自身的旋转 M3DMatrix44f mObjectframe; objectframe.GetCameraMatrix(mObjectframe); modelViewMatrix.MultMatrix(mObjectframe); //画图 switch(nStep) { case 0: DrawPointBatch(&pointBatch); break; case 1: DrawLineBatch(&lineBatch); break; case 2: DrawLineBatch(&lineStripBatch); break; case 3: DrawLineBatch(&lineLoopBatch); break; case 4: DrawTriangleBatch(&triangleBatch); break; case 5: DrawTriangleBatch(&triangleStripBatch); break; case 6: DrawTriangleBatch(&triangleFanBatch); break; } // 还原以前的模型视图矩阵 (单位矩阵) modelViewMatrix.PopMatrix(); //将在后台缓冲区进行渲染,然后在结束时交换到前台 glutSwapBuffers(); } //特殊按键(功能键或者方向键)监听 void SpecialKeys(int key, int x, int y) { //上、下、左、右按键,3D 旋转 switch (key) { case GLUT_KEY_UP: objectframe.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f); break; case GLUT_KEY_DOWN: objectframe.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f); break; case GLUT_KEY_LEFT: objectframe.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f); break; case GLUT_KEY_RIGHT: objectframe.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f); break; default: break; } //触发渲染 glutPostRedisplay(); } //普通按键监听 void KeyPressFunc(unsigned char key, int x, int y) { //空格键的 key = 32,计算空格键的按下次数,6次一循环 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 ChangeSize(int width, int height) { // 防止下面除法的除数为0导致的闪退 if(height == 0) height = 1; //设置视图窗口位置 glViewport(0, 0, width, height); // 创建投影矩阵,并将它载入到投影矩阵堆栈中 viewFrustum.SetPerspective(35.0f, float(width) / float(height), 1.0f, 500.0f); projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix()); // 在模型视图矩阵顶部载入单位矩阵 modelViewMatrix.LoadIdentity(); } //程序入口 int main(int argc, char* argv[]) { //设置当前工作目录,针对MAC OS X gltSetWorkingDirectory(argv[0]); //初始化GLUT库 glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL); //初始化窗口大小 glutInitWindowSize(800, 600); //创建窗口 glutCreateWindow("GL_POINTS"); //注册回调函数 glutReshapeFunc(ChangeSize); glutDisplayFunc(RenderScene); glutSpecialFunc(SpecialKeys); glutKeyboardFunc(KeyPressFunc); //确保驱动程序的初始化中没有出现任何问题。 GLenum err = glewInit(); if(GLEW_OK != err) { fprintf(stderr, "glew error:%sn", glewGetErrorString(err)); return 1; } //初始化设置 SetupRC(); //进入调用循环 glutMainLoop(); return 0; }
Demo 源代码在这里:github->openGLDemo->03-Primitives
有什么问题可以在下方评论区提出,写得不好可以提出你的意见,我会合理采纳的,O(∩_∩)O哈哈~,求关注求赞



