好的,又是因为要面试,这个面试需要复习opengl、图像算法、机器学习、算法题。所以会写相应的系列文章。。。
基础架构介绍
首先opengl是一个做显示的的东西。首先对他的一个基本流程有一个初步的概念。
常见的工作流程:
- 创建一个对象,用一个id保存他的应用
- 将对象绑定至上下文的目标位置(比如窗口对象)
- 设置绑定的一些选项
- 进行一系列的操作
- 将目标位置对象id设置回0,解绑这个对象
opengl的搭建可以借助vs2017,在nuget安装nupengl
安装之后可以试一下跑一个最简单的函数
//初始化 glut
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(SCR_WIDTH, 70);
glutInitWindowSize(SCR_WIDTH, SCR_HEIGHT);
glutCreateWindow("test");
// 初始化 glew(功能与glad类似)
glewInit();
// 程序中需要的初始化,比如加载shader、加载文字、加载素材等
init();
glutTimerFunc(20, timer, 0);
// 设置主函数
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutMouseFunc(MouseEvent);
glutMotionFunc(MotionMove);
// 主函数循环
glutMainLoop();
顶点着色器、片段着色器
我们能看到的三维效果其实也是一系列的变化后的二维图片,这个过程就是opengl图像渲染管线要干的事。其实随着技术的迭代跟新,已经由传统的顶点着色器和片段着色器,衍生了很多别的部分,如下图所示
针对顶点着色器:
- 顶点做色器主要作用是指定形状
- 以数组(顶点数据)的形式传递3个3D坐标作为这个过程的输入
- 顶点数据含有顶点属性,最简单的顶点属性是颜色,初此之外还有法向量等
- 为了让opengl知道你的这个顶点需要构成什么,需要制定一个模式,常见的有GL_POINTS、GL_TRIANGLES、GL_LINE_STRIP。如果是point,那么opengl会一个个读数组数据,如果是triangle则三个三个的读数组数据。即使三角形,也分为下面几种的绘制方式。这主要影响数组中数据排列方式。
针对片段着色器:
1.片段着色器主要指定颜色、光照、阴影等光影效果。
顶点输入
顶点包括x、y和z方向,需要注意,要将数据归一化到 [-1,1] 这个区间。
比如这是一组顶点数据
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
绘制出来的的三角形如下
在高级一点的版本里有顶点缓冲对象(VBO),它主要做缓存处理,如果不使用,顶点会一个个发送到显卡内存区,比较占CPU。
顶点着色器
下面是一个三角形的顶点着色器
#version 330 core
// 输入数据 aPos输入是一个(x,y)的向量
// aColor是一个4维向量
attribute vec2 aPos;
attribute vec4 aColor;
// 输出至片段着色器的数据
out vec4 Color;
void main()
{
// color在顶点着色器里不用,直接输出
Color=aColor;
// gl_Position是内置函数,这里是4个参数,x,y,z还有alpha值,暂时alpha值为1
gl_Position = vec4(aPos, 0.0, 1.0);
}
片段着色器
下面是一个三角形的顶点着色器
#version 330 core
// 从顶点着色器的输入
in vec4 Color;
void main()
{
// gl_FragColor 是内置函数,这里是4个参数
gl_FragColor = Color;
}
把上面过程串起来
最简单的串接步骤如下所示:
- 分别读Shader内容(readShaderSource函数)
- 分别编译为顶点着色器、片段着色器(genShader函数)
- 把同一组的顶点着色器、片段着色器连接起来(linkProgram函数)
因为这个步骤比较程式化,所以写好一个shader读取函数基本就是通用变了。
我这里贴出我自己的一个框架:
static char* readShaderSource(const char *fileName)
{
FILE *fp;
char *content = NULL;
int count = 0;
if (fileName != NULL)
{
fp = fopen(fileName, "rt");
if (fp != NULL