基本概念
1.光栅化:实际绘制或填充每个顶点之间的像素形成线程
2.着色:沿着顶点之间改变颜色值 能够轻松创建光照到一个立方体的效果
3.纹理贴图:将纹理图片附着到你绘图的图像上
4.混合:颜色混合效果
图元:组成图片的基本单元
OpenGL 渲染管线;一系列有序的处理阶段的序列,用于把我们应用中的数据转化到OpenGL生成一个最终的图像的一个过程
glsl:专门为图形开发设计的编程语言
着色器的渲染流程
顶点数据->顶点着色器->细分着色器->几何着色器->图元设置->剪切->光栅化->片元着色器->效果
在开发的时候 一般用到的是 顶点着色器 片元着色器
OpenGL渲染结构
使用七种基本图元
如何使用存储着色器
如何使用Uniform着色器
如何使用glbatch帮助类传递几何图形
Texture Data 纹理
Uniform
通过设置U inform变量就紧接着发送一个图元批次处理命令,Uniform变量实际上可以无限次的使用,设置一个应用于整个表面的单个颜色值,还可以是一个时间值。
atttributes 不能直接传数据到 片元着色器
属性:就是对一个顶点要做出改变的元素,实际上顶点位置本身就是一个属性,属性可以是整型布尔型车浮点型等
vertex shader 顶点着色器
Fragment shader 片元着色器
渲染过程中必备两个着色器 顶点着色器和片元着色器
有三种方式向OpenGL 着色器传递渲染数据的方法
1.属性(atttributes)
2.Uniforms
3.纹理 (Texture)
//笛卡尔坐标系
1.正投影 (2d)
2.透视投影(3d)
使用存储着色器
存储着色器分类
1.单位着色器
2.平面着色器
3.上色着色器
4.默认光源着色器
5.点光源着色器
6.纹理替换矩阵
7.纹理调整着色器
8.纹理光源着色器
常用代码
定义着色器
GLShaderManager shaderManager;
初始化着色器
shaderManager.initalizeStockShaders();
使用
shaderManager.userStockManager(参数列表)
OpenGL 组合图元
线
点
三角形
线带
线环
三角形金字塔
三角形带
三角形扇
颜色缓冲区:(GPU上内存区间),场景所需要颜色存储写入缓冲区
深度缓冲区:深度值z值 深度缓冲区-->深度测试。确定遮挡关系,保证渲染正确。
双缓冲区:生产者 消费者
模版缓冲区
GLMatrixStack 是一个用于存储和操作矩阵的类,通常在计算机图形学中使用。它的主要作用是在进行图形变换时,高效地管理和操作矩阵。
GLMatrixStack 类通常实现了以下几个基本功能:
1. 矩阵入栈(push):将当前矩阵压入栈中,作为新的栈顶矩阵。
2. 矩阵出栈(pop):弹出栈顶矩阵,并将其作为当前矩阵。
3. 替换矩阵(load):将指定的矩阵替换为当前矩阵。
4. 相乘矩阵(multiply):将当前矩阵与指定的矩阵相乘,得到新的矩阵。
5. 转置矩阵(transpose):将当前矩阵进行转置操作。
6. 逆矩阵(invert):计算当前矩阵的逆矩阵。
通过使用 GLMatrixStack,你可以在进行图形变换时,将一系列矩阵操作组合在一起,形成一个矩阵链。这样可以方便地进行复杂的图形变换,并且可以在需要时还原之前的变换状态。
GLFrame是OpenGL中用于表示物体或观察者的坐标的类。GLFrame类中存储了一个世界坐标点和两个世界坐标下的方向向量,分别用来表示当前位置点、向前方向向量和向上方向向量。
通过GLFrame类提供的旋转、移动的函数(如MoveForward、MoveUp、MoveRight)以及RotateWorld形式的旋转函数,可以快速完成平移以及旋转操作。此外,GLFrame可以用来产生模型视图矩阵,从而实现位置的移动。
GLFrame类可以通过调用GetCameraMatrix函数来产生模型视图矩阵,该函数会先产生一个旋转矩阵,然后再产生一个平移矩阵,最后将两个矩阵做叉乘得到模型视图矩阵。
你可以使用GLFrame类的相关函数来设置观察者的位置和方向,从而得到一个变换后的矩阵。这个矩阵可以用于产生位置的移动,实现物体的旋转和移动。
你可以使用GLFrame类提供的函数来设置观察者的位置和方向,其中常用的函数包括:
● MoveForward(): 默认的朝向是-z轴,所以向屏幕里面移动传正数值,向屏幕外即+z轴,需要传负数值。
● MoveUp(): 设置向上移动的距离。
● MoveRight(): 设置向右移动的距离。
● RotateWorld(): 按照世界坐标系进行旋转。
在设置观察者的位置和方向时,需要注意坐标系的选择和转换,以确保结果的准确性。此外,还可以结合其他OpenGL类和函数来实现更复杂的变换和渲染效果。
GLFrustum是OpenGL类库中的函数,用于定义一个平截头体,并计算一个用于实现透视投影的矩阵。其函数原型为glFrustum(left,right,bottom,top,near,far),各个参数含义如下:
● left和right:指明相对于垂直平面的左右坐标位置。
● bottom和top:指明相对于水平剪切面的下上位置。
● near和far:指明相对于深度剪切面的远近的距离,两个必须为正数。
通过调用glFrustum函数,可以确定视角边界的各个点,进而确定红色的可视区域,这在OpenGL ES版本中常被用于视角设定。
在OpenGL ES版本中,使用glFrustum函数来设定视角的方法如下:
public void glFrustumf(float left,float right,float bottom,float top,float near,float far)
参数用来确定视角边界的各个点。具体来说,left和right表示相对于垂直平面的左右坐标位置,bottom和top表示相对于水平剪切面的下上位置,near和far表示相对于深度剪切面的远近的距离。
调用glFrustum函数后,会根据视角边界的各个点来确定视角,从而实现透视投影的效果,使图形呈现出“近大远小”的特点。
GLBatch 是一个用于管理和批量提交 OpenGL 命令的类,它可以将一系列的 OpenGL 命令组织在一起,并一次性提交给图形渲染硬件,从而提高渲染效率。
GLBatch 类通常包含以下几个基本功能:
1. 命令存储:可以将各种OpenGL 命令(如顶点绘制、纹理绑定、矩阵变换等)存储在一个批处理中。
2. 批量提交:可以一次性将批处理中的所有命令提交给图形渲染硬件,避免了多次单独提交命令所带来的性能开销。
3. 状态管理:可以自动管理OpenGL 状态,确保在批处理中的命令能够正确执行,并且不会受到其他命令的影响。
4. 数据优化:可以对批处理中的数据进行优化,例如合并相同的顶点数据、减少数据传输等,从而提高渲染效率。
通过使用 GLBatch,你可以将多个OpenGL 命令组合在一起,形成一个批处理,并一次性提交给图形渲染硬件,从而提高渲染效率和性能。这对于处理大量的图形数据和进行复杂的渲染操作非常有用。
除了 GLBatch,还有以下几种方法可以提高 OpenGL 的渲染效率:
1. 减少状态切换:OpenGL 中的状态切换(如切换渲染目标、纹理、矩阵等)会导致性能开销。尽量减少不必要的状态切换,将相关的操作组合在一起,以减少状态切换的次数。
2. 使用顶点缓存对象(Vertex Buffer Object,VBO):将顶点数据存储在 VBO 中,可以减少 CPU 与 GPU 之间的数据传输次数,提高渲染效率。VBO 可以一次性将大量顶点数据传递给 GPU,避免了每次绘制时都传输顶点数据的开销。
3. 合理使用纹理:使用纹理可以减少三角形的绘制数量,提高渲染效率。尽量使用较小的纹理,避免使用过大的纹理,并且可以使用纹理压缩技术来减少纹理的大小。
4. 利用片段着色器(Fragment Shader)的效率:片段着色器可以进行大量的计算,但要注意避免在片段着色器中进行复杂的计算或循环。尽量使用向量操作和常量折叠等优化技巧来提高片段着色器的效率。
5. 多线程渲染:利用多线程技术可以在多个核心上并行执行渲染任务,提高渲染效率。可以使用 OpenGL 的多线程扩展(如 OpenGL 4.0 及以上版本的GL_ARB_parallel_shader_compile和GL_ARB_shader_multithread等)来实现多线程渲染。
6. 合理使用帧缓冲区对象(Framebuffer Object,FBO):FBO 可以用于渲染到离屏缓冲区,进行后期处理或多重渲染。合理使用 FBO 可以避免不必要的渲染目标切换和缓冲区拷贝,提高渲染效率。
7. 缓存和重用资源:对于经常使用的资源(如纹理、模型等),可以进行缓存并在需要时重用,避免重复加载和创建资源。
8. 优化几何数据:尽量减少三角形的数量,优化模型的拓扑结构,使用合适的索引缓冲区等,可以减少 GPU 的工作负载,提高渲染效率。
这些方法可以帮助提高 OpenGL 的渲染效率。根据具体应用场景和硬件环境,选择合适的优化策略可以显著提高图形渲染的性能。
GLGeometryTransform是OpenGL中用于管理管线的一个类,用于管理模型矩阵和投影矩阵。在使用GLGeometryTransform时,可以通过SetMatrixStacks函数将模型视图矩阵和投影矩阵分别放入对应的矩阵堆栈中。然后,在绘制图形时,可以通过GetModelViewProjectionMatrix函数获取模型视图投影矩阵,并将其传递给着色器,以便根据不同的模型视图投影矩阵选择不同的着色器进行绘制。
GLGeometryTransform可以提高OpenGL的渲染效率,因为它可以减少对模型视图投影矩阵的重复计算,从而提高渲染速度。此外,它还可以方便地管理多个矩阵,使代码更加清晰和易于维护。
使用GLGeometryTransform类可以通过以下步骤来管理模型视图投影矩阵:
1. 定义modelViewMatrix和projectionMatrix分别为模型视图矩阵堆栈和投影矩阵堆栈。
2. 在ChangeSize时初始化投影矩阵,并将其放入LoadMatrix进入投影矩阵堆栈。
3. 将模型视图矩阵堆栈和投影矩阵堆栈放入transformPipeline几何变换管线对象中进行管理。
4. 通过transformPipeline.GetModelViewProjectionMatrix()获取模型视图投影矩阵,并传递给UseStockShader使用。
5. 渲染完成后,使用PopMatrix出栈恢复模型视图矩阵堆栈。
通过使用GLGeometryTransform类,可以更方便地管理多个矩阵,从而提高OpenGL的渲染效率。
来个demo
//
// main.cpp
// 01 OpenGL 环境搭建
//
// Created by KING on 2023/12/24.
//
#include "GLTools.h"
/// 矩阵
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLGeometryTransform.h"
#include
#ifdef __APPLE__
#include
#else
#include
#endif
/*
GLMatrixStack 变化管线使用矩阵堆栈
GLMatrixStack 构造函数允许指定堆栈的最大深度、默认深度为64 这个矩阵堆在初始化时已经在堆栈中包含了单位矩阵
GLMatrixStack::GLMatrixStack(int iStackDepth = 64)
//通过调用顶部载入这个单位矩阵
void GLMatrixStack::LoadIndentiy(void)
//在堆栈顶部载入任何矩阵
void GLMatrixStack::LoadMatrix(const M3DMatrix44f m)
*/
GLShaderManager shaderManager;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrame cameraFrame;
GLFrame objectFrame;
// 投影矩阵
GLFrustum viewFrustum;
/// 容器类(7种不同的图元对应7种容器对象)
GLBatch pointBatch;
GLBatch lineBatch;
GLBatch lineStripBatch;
GLBatch lineLoopBatch;
GLBatch triangleBatch;
GLBatch triangleStringBatch;
GLBatch tringleFanBatch;
// 几何变换的管道
GLGeometryTransform transformPipline;
M3DMatrix44f shadowMatrix;
GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
// 跟踪效果步骤
int nStep = 0;
// 此函数在呈现上下文中进行任何必要的初始化。.
// 这是第一次做任何与opengl相关的任务。
void SetupRC()
{
//定义一些点,类似佛罗里达州的形状。
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 }};
//通过三角形创建金字塔
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};
//1.设置背景
glClearColor(0.7f, 0.7f,0.7f, 1.0f);
shaderManager.InitializeStockShaders();
glEnable(GL_DEPTH_TEST);
/// 设置变换管线
transformPipline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
cameraFrame.MoveForward(-15.0f);
/// 画图形 GLBatch
/// GLenum 模式
/// nVerts 点的个数
pointBatch.Begin(GL_POINTS, 24);
pointBatch.CopyVertexData3f(vCoast);
pointBatch.End();
/// 线的方式
lineBatch.Begin(GL_LINES, 24);
lineBatch.CopyVertexData3f(vCoast);
lineBatch.End();
/// 线段
lineStripBatch.Begin(GL_LINE_STRIP, 24);
lineStripBatch.CopyVertexData3f(vCoast);
lineStripBatch.End();
/// 线环
lineLoopBatch.Begin(GL_LINE_LOOP, 24);
lineLoopBatch.CopyVertexData3f(vCoast);
lineLoopBatch.End();
/// 金字塔
triangleBatch.Begin(GL_TRIANGLES, 12);
triangleBatch.CopyVertexData3f(vPyramid);
triangleBatch.End();
/// 三角形扇子
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] = -0.5f;
}
nVerts++;
vpoints[nVerts][0] = r;
vpoints[nVerts][1] = 0;
vpoints[nVerts][2] = 0;
tringleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
tringleFanBatch.CopyVertexData3f(vpoints);
tringleFanBatch.End();
//三角形条带,一个小环或圆柱段
int iCount = 0;
GLfloat radius = 3.0f;
for (GLfloat angle = 0.0f; angle <= (2 * M3D_2PI); angle += 0.3f) {
/// 圆形顶点的xy
GLfloat x = radius * sin(angle);
GLfloat y = radius * cos(angle);
vpoints[iCount][0] = x;
vpoints[iCount][1] = y;
vpoints[iCount][2] = -0.5f;
iCount++;
vpoints[iCount][0] = x;
vpoints[iCount][1] = y;
vpoints[iCount][2] = 0.5f;
iCount++;
}
// 关闭循环
//结束循环,在循环位置生成2个三角形
vpoints[iCount][0] = vpoints[0][0];;
vpoints[iCount][1] = vpoints[0][1];
vpoints[iCount][2] = 0.5f;
iCount++;
vpoints[iCount][0] = vpoints[1][0];
vpoints[iCount][1] = vpoints[1][1];
vpoints[iCount][2] = 0.5;
iCount++;
triangleStringBatch.Begin(GL_TRIANGLE_STRIP, iCount);
triangleStringBatch.CopyVertexData3f(vpoints);
triangleStringBatch.End();
}
void changeSize(int width, int height) {
glViewport(0, 0,width,height);
/// 创建投影矩阵并将它载入投影矩阵堆栈中
viewFrustum.SetPerspective(35.0f, float(width)/float(height), 1.0f, 500.f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
modelViewMatrix.LoadIdentity();
}
void keyPressFunc(unsigned char key, int x, int y) {
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) {
///移动物体方式
///1. 修改物体坐标系
///2. 修改世界坐标系
if (key == GLUT_KEY_UP) {
objectFrame.RotateWorld(m3dDegToRad(-5.f), 1.0f, 0.0f,0.0f);
}
if (key == GLUT_KEY_DOWN) {
objectFrame.RotateWorld(m3dDegToRad(5.f), 1.0f, 0.0f, 0.0f);
}
if (key == GLUT_KEY_RIGHT) {
objectFrame.RotateWorld(m3dDegToRad(5.f), 0.0f, 1.0f, 0.0f);
}
if (key == GLUT_KEY_LEFT) {
objectFrame.RotateWorld(m3dDegToRad(-5.f), 0.0f, 1.0f, 0.0f);
}
glutPostRedisplay();
}
void DrawWireFrameBatch(GLBatch* pBatch) {
/// 画颜色的部分
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipline.GetModelViewProjectionMatrix(),vGreen);
pBatch->Draw();
//边框
glPolygonOffset(-1.0f, -1.0f); //偏移深度 在同一位置要绘制填充和边线 会产生z轴冲突 所以需要偏移
glEnable(GL_POLYGON_OFFSET_LINE);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//通过调用glPolygonMode 将多边形正面或者背面设为线框模式 实现线框渲染
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glLineWidth(2.5f);
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipline.GetModelViewProjectionMatrix(),vBlack);
pBatch->Draw();
// 复原原本的设置
通过调用glPolygonMode将多边形正面或者背面设为全部填充模式
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_POLYGON_OFFSET_LINE);
glLineWidth(1.0f);
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
}
// 召唤场景
void RenderScene() {
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
/// 压栈
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
/// 矩阵乘以矩阵堆栈的顶部矩阵 相乘的结果随后存储在堆栈的顶部
modelViewMatrix.MultMatrix(mCamera);
M3DMatrix44f mObjecyFrame;
///只要使用 GetMatrix 函数就可以获取矩阵堆栈顶部的值 这个函数可以进行2次重载用来GLSHADERMANAGER的使用 或者是获取顶部矩阵的顶点副本数据
objectFrame.GetMatrix(mObjecyFrame);
/// 矩阵乘以矩阵堆栈的顶部矩阵 相乘的结果存在堆栈的 顶部
modelViewMatrix.MultMatrix(mObjecyFrame);
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipline.GetModelViewProjectionMatrix(),vBlack);
switch (nStep) {
case 0:
//设置点的大小
glPointSize(4.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:
DrawWireFrameBatch(&triangleBatch);
break;
case 5:
DrawWireFrameBatch(&tringleFanBatch);
break;
case 6:
DrawWireFrameBatch(&triangleStringBatch);
break;
default:
break;
}
//还原到以前的模型视图矩阵(单位矩阵)
modelViewMatrix.PopMatrix();
// 进行缓冲区交换
glutSwapBuffers();
}
int main(int argc, char* argv[]){
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
/// 申请一个颜色缓存区深度缓存区 双缓存区 模版缓存区
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
/// 设置window尺寸
glutInitWindowSize(800,600);
/// 创建window标题
glutCreateWindow("GL_POINTS");
/// 注册窗口改变大小的回调函数
glutReshapeFunc(changeSize);
/// 点击空格键的回调函数
glutKeyboardFunc(keyPressFunc);
/// 特殊键位函数
glutSpecialFunc(SpecialKeys);
/// 显示函数
glutDisplayFunc(RenderScene);
/// 判断下是否哦能初始化glew库 确保项目能正常使用OpenGL 框架
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW ERROR %s\n",glewGetErrorString(err));
return 1;
}
//绘制
SetupRC();
glutMainLoop();
return 0;
}