目录
- 前言
- 创建简单的应用
- 创建脚本
- 渲染底色
- 渲染底色plus
- 使用着色器(shader)
- 什么是着色器(shader)
- GLSL
- OpenGL的着色阶段
- 编写着色器
- 顶点着色器
- 片段着色器
- shader对象的创建
- 执行程序的创建
- 删除无用的shader
- 顶点数组对象VAO
- 清理VAO和Program
- 使用和展示着色器效果
- 效果
- 绘制三角形
- 推送
- 结语
前言
- 2.1 创建简单的应用
- 2.2 使用着色器
- 2.3 绘制我们的第一个三角形
- 2.4 总结
本篇文章将学习超级宝典的第二章内容,根据演示示例制作我们第一个OpenGL程序
创建简单的应用
第一小节,我们需要学习如何去编译运行我们第一个OpenGl程序
创建脚本
- 创建新类
第一步我们需要去创建一个新的C++类,该类位于我们启动项的Source Files文件下
- 继承application
#include <sb7.h>
class singlepoint_app_test : public sb7::application
{
virtual void startup()
{
}
virtual void render(double currentTime)
{
}
};
DECLARE_MAIN(singlepoint_app_test)
内容相当的简单,我们继承application类,然后通过宏DECLARE_MAIN去创建新类的实例,创建后会调用默认的run方法,实现一个窗体应用
- startup方法
初始化的时候被调用
- render方法
编辑渲染逻辑,参数currentTime是运行经过的秒数
- 注释原有的宏
因为老代码我们并没有删,所以我们把原来的宏给注释了,一个解决方案内只能存在一个主循环,保留我们新定义的实例即可
- 运行效果
创建的窗体,一个啥也没有的黑色窗口
渲染底色
现在我们要给单调的窗口添加一个纯色背景,在render里面书写代码
- glClearBufferfv
virtual void render(double currentTime)
{
const GLfloat red[] = { 1.0f, 0.0f, 0.0f, 1.0f };
glClearBufferfv(GL_COLOR, 0, red);
}
当前代码的作用就是让窗口的底色变为红色
要点1: 我们生命了一个浮点向量
red[]
,四个浮点值分别代表RGBA四个值,1.0f即255,0就是0,当前数值代表三原色的红色
要点2: glClearBufferfv,OpenGL的一个函数,作用是清除第一个参数GL_COLOR
的缓存为第三个参数red
,可能存在多个输出缓存可清除的情况,第二个参数代表清除缓存的下标,当前只有一个缓存所以是0
要点3: 所有OpenGL的函数都是gl开头的
要点4: 后缀fv表示该函数使用一组向量浮点值
- 效果
渲染底色plus
我们已经可以给窗体设置底色了,现在来个进阶版,根据render的参数currentTime设置个动态变色
virtual void render(double currentTime)
{
const GLfloat red[] = {
(float)sin(currentTime) * 0.5f,
(float)cos(currentTime) * 0.5f,
0.0f, 1.0f };
glClearBufferfv(GL_COLOR, 0, red);
}
- 效果
随着时间不短的变换窗口底色
使用着色器(shader)
什么是着色器(shader)
- 百度百科
https://baike.baidu.com/item/%E7%9D%80%E8%89%B2%E5%99%A8/411001?fr=aladdin
简而言之,就是让我们可以编辑去实现特定效果的一段程序
GLSL
GLSL就是OpenGL的着色语言,OpenGL内置了对GLSL的编译器,语法和C类似
- 百度百科
https://baike.baidu.com/item/GLSL?fromModule=lemma_search-box
OpenGL的着色阶段
OpenGL的着色分一下几个阶段
- 顶点着色器
- 细分曲面控制和评价着色器
- 评价着色器
- 几何着色器
- 片段着色器
- 计算着色器
要点: 平常需要我们手动去编写的只有
顶点着色器
和片段着色器
编写着色器
以下是一个用来获取执行程序Program的方法,我们一点点来分析
GLuint compile_shaders(void)
{
GLuint vertex_shader;
GLuint fragment_shader;
GLuint program;
//顶点着色器
static const char * vs_source[] =
{
"#version 420 core \n"
" \n"
"void main(void) \n"
"{ \n"
" gl_Position = vec4(0.0, 0.0, 0.5, 1.0); \n"
"} \n"
};
//片段着色器
static const char * fs_source[] =
{
"#version 420 core \n"
" \n"
"out vec4 color; \n"
" \n"
"void main(void) \n"
"{ \n"
" color = vec4(0.0, 0.8, 1.0, 1.0); \n"
"} \n"
};
vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, vs_source, NULL);
glCompileShader(vertex_shader);
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, fs_source, NULL);
glCompileShader(fragment_shader);
program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
return program;
}
顶点着色器
嵌入在代码中的点点着色器,本质就是一段GLSL代码的字符串,顶点着色器用来输出顶点位置
static const char * vs_source[] =
{
"#version 420 core \n"
" \n"
"void main(void) \n"
"{ \n"
" gl_Position = vec4(0.0, 0.0, 0.5, 1.0); \n"
"} \n"
};
要点1: #version 420 core,420代表的含义是使用着色语言的4.2版本,core指的是我们只想要用OpenGL核心模式所支持的特性
要点2: gl_开始的变量都是OpenGL的一部分
要点3: gl_position表示顶点输出的部分
上述顶点着色器代码意指,所有使用该顶点着色器的顶点输出位置都在屏幕正中央
片段着色器
片段着色器和顶点着色器类似,功能不同,片段着色器用来输出颜色
static const char * fs_source[] =
{
"#version 420 core \n"
" \n"
"out vec4 color; \n"
" \n"
"void main(void) \n"
"{ \n"
" color = vec4(0.0, 0.8, 1.0, 1.0); \n"
"} \n"
};
要点1: out关键字定义color作为一个输出变量
要点2: 设置给color变量的浮点值,最终会作为顶点颜色输出在屏幕上
当前片段着色器,意指所有只用该着色器的顶点颜色均是青色
shader对象的创建
我们写的着色器本质就是一段字符串,我们需要将写好的字符串编译成可被执行的shader代码
GLuint vertex_shader;
GLuint fragment_shader;
vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, vs_source, NULL);
glCompileShader(vertex_shader);
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, fs_source, NULL);
glCompileShader(fragment_shader);
要点1: glCreateShader,OpenGL内置结构,意指创建一个空的shader对象
要点2: glShaderSource,将我们写的字符串着色器代码传给shader对象,保留副本
要点3: glCompileShader,将shader对象中我们传入的字符源码进行编译
执行程序的创建
shader只是业务逻辑,我们需要创建一个程序对象来取执行逻辑
program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);
要点1: glCreateProgram,创建一个空的执行程序Program
要点2: glAttactShader,将shader对象附加给执行程序
要点3: glLinkPrigram,将各对象一起连接到可在图形处理器上运行的代码中
删除无用的shader
在通过glAttachShader附加shader到program的时候,会在program内保留shader副本,原shader无用,可删
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
要点1: glDeleteShader,删除shader对象
顶点数组对象VAO
什么是顶点数组对象?
博主刚看到这里对这个东西的理解还不是特别清楚,当前理解的大意就是,所有对顶点的操作都会缓存在这个数组内,顶点着色器的数据都来自这个数组,如果想显示渲染结果,VAO是必不可少的
virtual void startup()
{
program = compile_shaders();
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
}
要点1: glGnVertexArrays,创建VAO,生成一个顶点数组对象,存储到vao内
要点2: glBindVertexArray,将赋值过得vao对象链接到上下文,让OpenGL能获取到
要点3: 我们在startup中获取了刚才附加shader的程序对象program
我们在startup内去创建VAO,并绑定到上下文
清理VAO和Program
我们在start的时候创建了vao和program,同理的我们应该在程序结束把他们删除掉
void shutdown()
{
glDeleteVertexArrays(1, &vao);
glDeleteProgram(program);
}
要点1: shutdown方法重写自基类,在程序结束时调用
要点2: glDeleteVertexArrays,删除VAO
要点3: glDeleteProgram,删除program
使用和展示着色器效果
最终展示效果当然要写在render方法内了
virtual void render(double currentTime)
{
const GLfloat red[] = {
(float)sin(currentTime) * 0.5f,
(float)cos(currentTime) * 0.5f,
0.0f, 1.0f };
glClearBufferfv(GL_COLOR, 0, red);
glUseProgram(program);
glDrawArrays(GL_POINTS,0,1);
glPointSize(40.0f);
}
要点1: glUseProgram,告诉OpenGL用哪个program去进行渲染
要点2: glDrawArrays,执行绘图命令
- 参数1:告诉OpenGL想要渲染何种图元
- 参数2:从数组缓存中的哪一位开始绘制,一般为0
- 参数2:数组中顶点的数量
要点3: glPointSize,设置绘制顶点的大小
效果
绘制三角形
绘制三角形肯定需要三个点,结合我们之前了解到到的内容,顶点着色器设置顶点的位置,所以我们需要修改顶点着色器,设置三个顶点,就可以绘制三角形
- 顶点着色器
static const char * vs_source[] =
{
"#version 420 core \n"
" \n"
"void main(void) \n"
"{ \n"
" const vec4 vertices[] = vec4[](vec4( 0.25, -0.25, 0.5, 1.0), \n"
" vec4(-0.25, -0.25, 0.5, 1.0), \n"
" vec4( 0.25, 0.25, 0.5, 1.0)); \n"
" \n"
" gl_Position = vertices[gl_VertexID]; \n"
"} \n"
};
要点1: gl_VertexID,此时正在被处理的顶点索引
创建了一个包含三个顶点位置的浮点向量数组,然后通过gl_VertexID索引来设置顶点位置
- 绘制命令glDrawArrays
既然要绘制三角形,我们的绘制图元肯定不能是点了,需要修改
glDrawArrays(GL_TRIANGLES, 0, 3);
要点1: GL_TRIANGLES代表绘制三角形
要点2: 参数3,代表要绘制3个顶点,从下标0开始,对应输出给顶点着色器的gl_VertexID变量
- 效果
推送
- Github
https://github.com/KingSun5
结语
本篇作为OpenGL学习的第二篇,学习了如何去绘制点和三角形,认识了shader和Program和部分OpenGL的API,源码都在官方示例里面,这里就不完整贴出来了,若是觉得博主的文章写的不错,不妨关注一下博主,点赞一下博文,另博主能力有限,若文中有出现什么错误的地方,欢迎各位评论指摘。
本文属于原创文章,转载请著名作者出处并置顶!!