OpenGL笔记(一)
学习LearnOpenGL,心得笔记记录如下:
笔记(答案见后)
1.glEnalbleVertexAttribArray函数的作用?
2.创建VBO的一系列操作?
3.具体开启了VertexAttribArray后,应该如何设置呢?
4.GLunit是什么意思?
5.补充完成昨天的没完成的第三个问题。
6.什么是shader?什么是GPU?
7.具体画三角形的函数?
8.glEnalbeVertexAttribArray(0)和glDisableVertexAttribArray(0)里面的0是什么意思?
9.有一个问题,为什么glVertexAttribPointer的参数里面,没有指明是GL_BUFFER_ARRAY呢?
10.代码如下
GLFWwindow *window1;
window1 = glfwCreateWindow(500, 500, "First Window", NULL, NULL);
glfwWindowHint(GLFW_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_VERSION_MINOR, 3);
// glfwMakeContextCurrent(window1);
if (glewInit() != GLEW_OK)
{
cout << "Failed to initialze glew!!" << endl;
glfwTerminate();
return -1;
}
发现了一个问题,如果注释掉glfwMakeContexCurrent一行代码以后,会报错:
为什么呢?
11.在调用glVertexAttribPointer之后,有一个很重要的函数,glSwapBuffers,缺了它就画不出来
12.GLSL和C语言在编译和运行方面有什么不同?
13.今天在画很基本的三角形的时候,在提示栏出现了这样的情况:
glEnableVertexAttribArray()和glEnableVertexArrayAttrib()有什么区别?
昨天晚上在B站找到一个很好的视频课,跟着学搞清楚了很多东西,现在对于创建空白窗口的所有函数都基本搞清楚了。
在这里对有难度的地方再做一个总结。
14. glSwapBuffers(GLFWwindow *window)有什么用?
15.glColor和glClearColor函数?
16.为什么程序要设置一个堆和栈,用整个的空间存放数据不好吗,联想到线程与进程,怎么解释?
17.拓展一下昨天的问题,哪些部分是线程私有和公有的,哪些部分又是进程私有和公有的?
18.C++写一个字符串是这样的
const char * string1="Hello World";
如果要写成两行怎么弄,是这样吗?
const char *string1=" Hello
World ";
19.VAO和Vertex attribute 是什么关系?
20.有个问题,既然VertexAttrib在VAO里存着,从第Index层挖出VBO的数据放在VAO的第0层之中,为什么我就算不创建VAO也能画三角形呢?
21.OpenGL里面的三角形绘制默认是逆时针方向还是顺时针方向?怎么让OpenGL只显示背面或者只显示正面
22.小tip,怎么一键注释多行?
23.怎么把绘制出的三角形变成线框模式?
24.前面23学到了怎么把三角形变成线框模式,那怎么把四边形弄成线框模式呢?
25.如何把当前时间time转变成一个与时间有关的正弦函数?
26.Warning 1
不要在GLSL中声明一个Uniform又不使用它,这样可能会造成重大错误。
27.顶点着色器和片元着色器的输入输出有什么要求?
28.float a=0; if(a==0) cout<<"this is true“;
有问题没?
29.C++的报错的try和catch语句怎么用?
30.什么时候需要用到try和catch语句呢,为什么不用return -1呢?
31.从OpenGL读取磁盘上的文件,具体文件传输过程?
32.ifstream、fstream和ofstream 是什么意思?
33.抛出异常中有一个类exception,如下图:
笔记答案
2019.1.3
1.作用:用于开启顶点属性
首先回顾一下CPU是怎么传递顶点数据给GPU的,CPU是直接传送数据到GPU的显存中的,比如说在GPU中建立一个VBO(顶点缓冲对象)来存储顶点信息,但是问题来了,虽然数据传送到了GPU内,但是传过去的数据默认对于着色器来说是不可见的,也就是说顶点着色器本身是有属性的,这些属性,出于性能考虑,默认是关闭的,所以如果要获取我们需要的属性的话,需要用这个函数开启顶点着色器的相应属性。
所以在用顶点着色器之前,必须使用这个函数。
GLuint VBO;
glGenBuffer(1,&VBO);
//OpenGL有很多缓冲对象类型,需要进行绑定操作
glBindBuffer(GL_BUFFER_ARRAY,VBO); //将对象绑定在状态机上,这个类型就绑定了当前的VBO
glBufferData(GL_BUFFER_ARRAY,sizeof(original_data),original_data,GL_STATIC_DRAW);//从CPU传入了GPU
PS:注意的是,这个绑定的意思是,绑定了VBO之后,任何操作于GL_BUFFER_ARRAY的操作都是配置当前绑定的VBO。
glBufferData的最后一个参数是参数类型,共有三个:
GL_STATIC_DRAW:所绘制的数据基本不变;
GL_DYNAMIC_DRAW:数据改变很多;
GL_STREAM_DRAW:数据每次绘制都改变;
所以后面两种模式,一般会把数据放在GPU高速写入的内存部分。如果只是单纯绘制三角形用第一种就行。
3.具体如何设置顶点属性,这里需要用到一个函数glVertexAttribPointer();
void glVertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,const GLvoid * pointer);
这个函数有六个参数,
size:只可取1,2,3,4(初始值为4),表示每个顶点的属性数量比如,空间坐标(x,y,z)的size为3,颜色RGBα则为4.
type:即数据类型(初始为GL_FLOAT),比如(X,Y,Z)的数据类型为GL_FLOAT。
4.GLuint,就是glu int,也就是类似的GLfloat,向OpenGL申请资源,OpenGL一般只会返回一个整数句柄。
2019.1.4
5.VertexAttribPointer,意思就是顶点属性分配器,注意它处理的是BO(缓冲对象),不是指针,是指定了索引值为index的顶点属性数组的类型、格式和位置。
VertexAttribPointer(index,size,type,normalized ,stride ,offset )
index:索引值,即顶点数组的句柄?
normalized:是否需要归一化,需要则是GL_TRUE,否则GL_FALSE。
stride:步长,若为0,则选取的顶点属性是紧密排列在一起的。
offset:指的是偏移量,所以是什么意思呢?
offset单位是字节,指的是首地址偏移offset个字节开始读数据:
6.在GPU上面编译的小程序,GPU(Graphics Processing Unit)图形处理单元。
7.glDrawArrays(int mode,int first,int count);
mode就用最基本的Mode,GL_TRIANGLES,first点下标为0,count是要画的点的数量,所以在此模式下,所给的count应该是三的倍数。比如
glDrawArrays(GL_TRIANGLES,0,3);
8. 数字0是索引值Index,至于为什么是0?官方解释如下:
glVertexAttribPointer(
0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
也就是说OpenGL规定,VertexPosition的layout为0,VertexColor的layout为1,坐标的索引值为0,颜色的index为1,这是规定的属性。
9.有一个隐含的关系:不同的OPENGL类型绑定了不同的BO,但glVertexAttribPointer是之针对GL_BUFFER_ARRAY的函数,总是指向在这个类型下绑定的BO,所以不需要单独再设置渲染模式。
2019.1.5
10.记住,设置context的操作得在初始化glew之前,个人觉得是contex必须先加载进来,具体原因remain。
11.见14
12.GLSL是边编译边运行,而C是先编译再运行。
13.glEnableVertexAtrribArray()意思是启用顶点属性数组,也就是启用数组的一个属性,属性由索引值Index决定,前者只有一个参数,而glEnableVertexAtrribArray()是启用顶点数组属性,如下图所示:
这个函数需要两个参数,应该是启用某个特定VBO的某个顶点数组属性。
两个函数参数个数都不一样,这里用的是第一个启用顶点属性为Index的数组,不要搞混了。
2019.1.6
14.glSwapBuffers翻译过来是交换缓冲区的意思,既然buffer加了s,也就意味着不止一个buffer,所以这里涉及到了一个双缓冲的概念,关于双缓冲英文解释如下:
Double Buffer:
When an application draws in a single buffer image might display flickering issues. This is because the resulting output is not drawn in an instant ,but drawn pixel by pixel and usually from left to right ,top to bottom.Because this image is not drawn in an instant to the user while still being rendered to,the result may contain artifacts.To circumvent these issues,window application apply a double buffer for rendering.The front buffercontains the final output image that is shown at the screen,while all the rendering commands draw the back buffer.As soon as all the rendering commands are finished we swap the back buffer to the front buffer,so the image is instantly displayed to the user,removing all the aforementioned artifact.
总结中文解释如下:
因为电脑绘图是一个个像素逐一画的,需要时间,如果单一缓冲,我们可能会看到具体绘画过程,会造成屏幕闪烁等问题,而我们用户不需要具体看到你绘制的过程,所以为了解决这个问题,这里用了双缓冲技术,用两个内存区域来保存数据,分为前缓冲区和后缓冲区,前缓冲区用于展示屏幕上的内容,而后缓冲区就用来绘制,然后每一帧开始的时候,将两个缓冲区交换,这样后缓冲区又可以画新的内容。
15.之前提到了,由于是双缓冲机制,每一帧的画面都要重新交换缓存(当然如果是static_draw具体的每帧要不要重新画我还不清楚),如果说每帧都要重新加载,那么之前绘制在屏幕上的内容需要先清除掉,也就是利用glClear()函数
glClear()函数有三种参数:
GL_COLOR_BUFFER_BIT
GL_DEPTH_BUFFER_BIT
GL_STENCIL_BUFFER_BIT
第一个就是清除颜色缓冲。
而glClearColor函数是glClear函数的具体设置参数的一个子函数(我是这么理解的),用来设置清屏颜色,应放在glClear函数前面设置。
2019.1.7
没写汇总,明天写
2019.1.8
16.先解决一下昨天的问题:
栈是线程独有的,堆是大家公有的空间,堆分为全局堆和局部堆,前者是未被分配的空间,后者是已经分配的
17.线程私有:寄存器,线程栈,程序计数器。
线程共享:堆,全局变量,静态变量,地址空间
进程私有:地址空间,堆和栈,寄存器、全局变量
进程共享:进程目录、代码段、公共数据、进程ID
18.这样写不对,应该这么写:
const char* string1 = "Hello"
" World";
19.VAO是用于选取VBO的数据进行整理存放的地方,VAO可以有很多栏(手动测试发现是16栏(0到15)):
每一栏存放的就是顶点属性,即vertex attributes,所以
glVertexAttribPointer()函数的作用就是挖出VBO中的特定数据,将其放在自己的第index栏中,保存起来。
除了上述十六栏属性,还有最后一栏是放EBO的
#version 330 core
layout(location=0) in vec3 aPos;
void main(){
gl_Position=vec4(aPos.x ,aPos.y, aPos.z,1.0f); }
;
这里的layout(location=0)指的就是从VAO的第index层调用。
20.应该是搞错了,没有VAO,glVertexAttribPointer,就用不了
2019.1.9
21.三角形是逆时针绘制的,OpenGL默认同时画正面和背面值,如果要、
剔除背面的话 只需要两行代码
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
22.选中后,ctrl+k+c。
23.在GLEW初始化之后加这个即可:
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
注意是GL_LINE不是GL_LINES.
2019.1.10 学了 傅老师的shader(1)课程
24.remain
float time=glfwGetTime();
float res=sin(time)/2+0.5f;`
2019.1.11
27.The vertex shader should receive some form of input otherwise it would be pretty ineffective.(必须输入) The vertex shader differs in its input, in that it receives its input straight from the vertex data. To define how the vertex data is organized we specify the input variables with location metadata so we can configure the vertex attributes on the CPU. We’ve seen this in the previous tutorial as layout (location = 0). The vertex shader thus requires an extra layout specification for its inputs so we can link it with the vertex data.
The other exception is that the fragment shader requires a vec4 color output variable(必须输出), since the fragment shaders needs to generate a final output color. If you’d fail to specify an output color in your fragment shader OpenGL will render your object black (or white)(否则默认为黑色或者白色).
28.有问题,浮点数是不能与0做比较的,double型也不可以跟0作比较,如果非要比较应该这么写:
float a=0;
if(a>-0.000001&&a<0.000001)
2019.1.12
29.C++抛出异常用到三个关键字:try catch和throw
所以try是检查是否存在异常,catch是捕获异常的位置并处理它,throw是抛出异常
try{
//包含可能异常的语句
}
catch (类型名[形参名]) //捕获特定类型的异常
{
//处理异常的语句
}
举个例子:
#include <iostream>
void f(int b) {
int a = 5;
if (b == 0)
throw "Failed to divide zero!";
std:: cout << a / b << std::endl;
}
int main()
{
try
{
f(2);
f(0);
}
catch (const char* s) //如果捕捉到了此类型的throw
{
std::cout << s << std::endl;
}
}
感觉有点像switch case语句,catch捕获的是不同类型的异常。
额外注意的,可以输入…三个点表示任何类型,如下:
30.大多数情况下都是直接
if(特殊情况) return ;
用的频率可能很低,所以什么时候要用呢?
当自己封装一些函数的时候,要给别人用,不需要让别人看代码内部实现的时候,
不用return 直接结束函数,而是用抛出异常的方式。
31.看图
先从磁盘读取文件到FileBuffer内,但是由于磁盘的文件格式各不相同,所以要把他全部转换为文件流StringBuffer的方式,但是CPU只认char类型,不认识string,所以要把string通过灰色路线抓换成char。
32.ifstream: input file stream
fstream: file stream
ofstream: output file stream
33.exception(异常)类的定义如下:
class exception{
public:
exception () throw(); //构造函数
exception (const exception&) throw(); //拷贝构造函数
exception& operator= (const exception&) throw(); //运算符重载
virtual ~exception() throw(); //虚析构函数
virtual const char* what() const throw(); //虚函数
}
上述的what函数是一个虚函数,里面放的是字符串常量,如果不存在的时候输出
Unknown Exception
用法如下
#include <iostream>
using std::exception;
void f(int b) {
int a = 5;
if (b == 0)
throw exception();
std:: cout << a / b << std::endl;
}
int main()
{
try{
f(2);
f(0);
}
catch (exception &a)
{
std::cout << a.what() << std::endl;
std::cout << "cuowu" << std::endl;
}
}
我没有定义exception里面的内容,所以输出如下:
但是如果改成
void f(int b) {
int a = 5;
if (b == 0)
throw exception("False!");
std:: cout << a / b << std::endl;
}
则输出: