这两天用Visual Studio2012完成了一些OpenGL入门程序(三角形和矩形、着色器类等),对OpenGL有了一个大概的了解与运用。
笔记总结:
一、OpenGL在其核心是一个C库,不支持类型重载,在函数参数不同的时候就要为其定义新的函数。
在OpenGL中,任何事物都在3D空间中,而屏幕和窗口却是2D像素数组,这导致OpenGL的大部分工作都是关于把3D坐标转变为适应屏幕的2D像素。
处理过程是由OpenGL的图形渲染管线(实际上指的是一堆原始图形数据途经一个输送管道,期间经过各种变化处理最终出现在屏幕的过程)管理的。
图形渲染管线可以被划分为两个主要部分:第一部分把你的3D坐标转换为2D坐标,第二部分是把2D坐标转变为实际的有颜色的像素。
二、图形渲染管线可以被划分为几个阶段,每个阶段需要把前一个阶段的输出作为输入。过程:顶点数据转变为最后渲染出来的像素。
由于具有并行执行特性,当今大多数显卡都有成千上万的小处理核心,在GPU上为每一个(渲染管线)阶段运行各自的小程序,从而在图形渲染管线中快速处理你的数据。
三、顶点数据->顶点着色器->图元装配->几何着色器->光栅化->片段着色器->测试与混合(可以自定义的着色器:顶点着色器、几何着色器(通常使用默认)、片段着色器)。坐标和颜色值构成的到底是什么?需要指定数据所表示的渲染类型:图元(Primitive),任何一个绘制指令的调用都将把图元传递给OpenGL。
(GL_POINTS、GL_TRIANGLES、GL_LINE_STRIP)
必须定义至少一个顶点着色器和一个片段着色器(因为GPU中没有默认的顶点/片段着色器)。
图元装配:将顶点着色器输出的所有顶点作为输入,并所有的点装配成指定图元的形状。
顶点着色器:单独的顶点作为输入,把3D坐标转为另一种3D坐标,同时允许对顶点属性进行一些基本处理。
几何着色器:把图元形式的一系列顶点的集合作为输入,可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状。
光栅化:把图元映射为最终屏幕上相应的像素,生成片段。在片段着色器运行之前会执行裁切。裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。
片段着色器:计算一个像素的最终颜色,这也是所有OpenGL高级效果产生的地方。
Alpha测试和混合:检测片段的对应的深度值,判断这个像素是其它物体的前面还是后面,决定是否应该丢弃。也会检查alpha值(定义一个物体的透明度)并对物体进行混合。
四、OpenGL仅当3D坐标在3个轴(x、y和z)上都为-1.0到1.0的范围内时才处理它。
标准化设备坐标:x、y和z值在-1.0到1.0的一小段空间。任何落在范围外的坐标都会被丢弃/裁剪,不会显示在屏幕上。y轴正方向为向上,(0, 0)坐标是这个图像的中心。
标准化设备坐标接着会变换为屏幕空间坐标,通过glViewport函数提供的数据进行视口变换。所得的屏幕空间坐标又会被变换为片段输入到片段着色器中。
五、顶点着色器在GPU上创建内存用于储存顶点数据,还要配置OpenGL如何解释这些内存,并且指定其如何发送给显卡。接着会处理我们在内存中指定数量的顶点。
顶点缓冲对象VBO管理这个内存,它会在GPU内存(显存)中储存大量顶点。使用这些缓冲对象的好处是我们可以一次性的发送一大批数据到显卡上。
从CPU把数据发送到显卡相对较慢,所以只要可能我们都要尝试尽量一次性发送尽可能多的数据。当数据发送至显卡的内存中后,顶点着色器几乎能立即访问顶点,这是个非常快的过程。
VBO有一个独一无二的ID,使用glGenBuffers函数和一个缓冲ID生成一个VBO对象:GLuint VBO;glGenBuffers(1, &VBO);
使用glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上:glBindBuffer(GL_ARRAY_BUFFER, VBO);
从这一刻起,我们使用的任何(在GL_ARRAY_BUFFER目标上的)缓冲调用都会用来配置当前绑定的缓冲(VBO)。
调用glBufferData函数,把之前定义的顶点数据复制到缓冲的内存中:glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,GL_STATIC_DRAW);
(第四个参数指定了希望显卡如何管理给定的数据。GL_STATIC_DRAW :数据不会或几乎不会改变。GL_DYNAMIC_DRAW:数据会被改变很多。GL_STREAM_DRAW :数据每次绘制时都会改变。)
每个着色器都起始于一个版本声明。#version 330 core
layout (location = 0)设定输入变量的位置值。
六、在计算机图形中颜色被表示为有4个元素的数组:红色、绿色、蓝色和alpha(透明度)分量,通常缩写为RGBA。
当在OpenGL或GLSL中定义一个颜色的时候,我们把颜色每个分量的强度设置在0.0到1.0之间。
七、要在渲染前指定OpenGL该如何解释顶点数据。(VBO)
核心模式要求使用VAO,如果绑定VAO失败,OpenGL会拒绝绘制任何东西。
任何随后的顶点属性调用都会储存在VAO中,配置顶点属性指针时只要绑定相应的VAO就行了。在不同顶点数据和属性配置之间切换只需要绑定不同的VAO就行了。设置的所有状态都将存储在VAO中。
绘制多个物体时,先要生成/配置所有的VAO(和必须的VBO及属性指针),然后储存它们供后面使用。
当我们打算绘制物体的时候就拿出相应的VAO,绑定它,绘制完物体后,再解绑VAO。VAO会储存解绑调用,所以确保没有在解绑VAO之前解绑索引数组缓冲,否则它就没有这个EBO配置了。
八、用线框模式绘制:glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);直到使用glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)将其设置回默认模式。
九、着色器是一种非常独立的程序,之间不能相互通信,唯一的沟通只有通过输入和输出。当链接着色器至一个程序的时候,它会把每个着色器的输出链接到下个着色器的输入。
着色器的开头总是要声明版本,接着是输入和输出变量、uniform和main函数。每个着色器的入口点都是main函数,在这个函数中处理所有的输入变量,并将结果输出到输出变量中。
输入变量也叫顶点属性。能声明的顶点属性是有上限的,一般由硬件来决定。(OpenGL确保至少有16个包含4分量的顶点属性可用)
默认基础数据类型:int、float、double、uint和bool 容器类型:向量(Vector)和矩阵(Matrix)
类型及含义:
vecn :包含n个float分量的默认向量一个向量的分量可以通过vec.x这种方式获取:分别使用.x、.y、.z和.w来获取它们的第1、2、3、4个分量
bvecn :包含n个bool分量的向量GLSL也允许对颜色使用rgba,或是对纹理坐标使用stpq访问相同的分量
ivecn :包含n个int分量的向量重组:可以使用上面4个字母任意组合来创建一个和原来向量一样长的(同类型)新向量,只要原来向量有那些分量即可
uvecn :包含n个unsigned int分量的向量(eg. vec2 someVec;vec4 differentVec = someVec.xyxx;vec2 vect = vec2(0.5f, 0.7f);vec4 result = vec4(vect, 0.0f, 0.0f);)
dvecn :包含n个double分量的向量
十、顶点着色器接收的是一种特殊形式的输入,从顶点数据中直接接收输入。
为了定义顶点数据该如何管理,使用location这一元数据指定输入变量,这样才可以在CPU上配置顶点属性。layout (location = 0)。
顶点着色器需要为它的输入提供一个额外的layout标识,这样我们才能把它链接到顶点数据。
片段着色器需要一个vec4颜色输出变量,因为需要生成一个最终输出的颜色。如果在片段着色器没有定义输出颜色,OpenGL会把物体渲染为黑色(或白色)。
从一个着色器向另一个着色器发送数据必须在发送方中声明一个输出,在接收方着色器中声明一个类型和名字都一样的输入,OpenGL就会把两个变量链接到一起,它们之间就能发送数据了。
十一、Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但和顶点属性有些不同,uniform是全局的。
查询Uniform地址不要求之前使用过着色器程序,但更新一个Uniform之前必须先使用程序(glUseProgram),因为它是在当前激活的着色器程序中设置的。
片段插值:渲染时光栅化阶段通常会造成比原指定顶点更多的片段,光栅会根据每个片段在三角形形状上所处相对位置决定这些片段的位置。基于这些位置,它会插值所有片段着色器的输入变量。
eg.有一个线段,上面端点是绿色的,下面端点是蓝色的。如果一个片段着色器在线段的70%的位置运行,它的颜色输入属性就是30%蓝 + 70%绿。
编程总结:
一、着色器类编程中我所使用的语句是 Shader *ourShader; ourShader = new Shader("shader.vert", "shader.frag"); ourShader->Use();这和LearnOpenGL中的不太一样,但个人觉得这个更好用一些。但需要注意的是,.vert和.frag都要放在工程的下面,否则无法正确读取文件(一开始不知道,断点调试了很久只知道字符读不进去)。
一开始还没学到着色器类时用的是这个方式:
const char *fragmentShaderSource =
"#version 330 core\n"
"uniform vec4 vertexColor;\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vertexColor;\n"
"}\n\0";
然后再编译,链接到着色器程序。
二、OpenGL的函数多,有点函数的参数也多,无法完全记下它们。目前我只能通过注释来记录,防止以后忘记。
三、有些地方还是半知半解,但已经能运用其画出纯色三角形、矩形、变色三角形(使用Uniform)等,暂时不求甚解,日后再加强理解。
四、错误日志的必要性:一开始程序成功运行,但本来应该是彩色的三角形却显示的是白色。通过错误日志得知着色器链接失败,因此可以直接找到对应地方进行调试修改。
实现效果:
一、矩形
二、变色三角形
三、彩色三角形