一、Buffers
1. OpenGL在4.5为所有的glGen*()函数增加了glCreate*()版本。二者区别如下:
glGen*()只有在bind之后才会生成真正的object,而glCreate*()在create时立刻生成真正object。
所以,想调用glNamedBufferStorage()之类带Named的函数,要么先bind一次buffer,要么直接在创建buffer时用glCreate*()的版本。
1.1 数据初始化
可用glNamedBufferStorage()/glBufferStorage()或者glNamedBufferData()/glBufferdata().
Storage()不能对同一个buffer做两次。Data()可以。
Storage()最后一个参数flag有GL_DYNAMIC_STORAGE_BIT则允许后续subdata()更新数据。
1.2 数据更新
可用glNamedBufferSubData()/glBufferSubData()。
若初始化数据时是Storage(),则Storage的flag需有GL_DYNAMIC_STORAGE_BIT。
2. OpenGL4.5同样为VAO引入了诸多操作,这里介绍放置数据的规则。
第一种:separate,SoA,structure of arrays。部分函数和注释见example1。
example1:
static const float positions[] =
{
0.25, -0.25, 0.5, 1.0,
- 0.25, -0.25, 0.5, 1.0,
0.25, 0.25, 0.5, 1.0
};
static const float colors[] =
{
1.0f, 0.0f, 0.0f,
0, 1, 0,
0, 0, 1
};
glCreateVertexArrays(1, &vao);
GLuint buffers[2];
glCreateBuffers(2, buffers);
glNamedBufferStorage(buffers[0], sizeof(positions), positions, NULL);
glVertexArrayVertexBuffer(vao, 13, buffers[0], 0, sizeof(vmath::vec4)); // 该vao的13号buffer binding point
glVertexArrayAttribFormat(vao, 11, 4, GL_FLOAT, GL_FALSE, 0); // 该vao的11号attribute index
glVertexArrayAttribBinding(vao, 11, 13); // 当该vao bind的时候,11号attribute从13号buffer binding拿数据。
glEnableVertexArrayAttrib(vao, 11); // 启用该vao的11号attribute。vs的location从这里拿。
glNamedBufferStorage(buffers[1], sizeof(colors), colors, NULL);
glVertexArrayVertexBuffer(vao, 1, buffers[1], 0, sizeof(vmath::vec3));
glVertexArrayAttribFormat(vao, 1, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(vao, 1, 1);
glEnableVertexArrayAttrib(vao, 1);
glBindVertexArray(vao);
3. 也可以将所有data放到一个struct里,只用1个buffer。(interleave,交错。AoS,array of structures)
example2:
static const float vertices[] =
{
//positions //colors
0.25, -0.25, 0.5, 1.0, 1.0f, 0.0f, 0.0f,
-0.25, -0.25, 0.5, 1.0, 0, 1, 0,
0.25, 0.25, 0.5, 1.0, 0, 0, 1
};
glCreateVertexArrays(1, &vao);
GLuint buffer;
glCreateBuffers(1, &buffer);
glNamedBufferStorage(buffer, sizeof(vertices), vertices, NULL);
glVertexArrayVertexBuffer(vao, 9, buffer, 0, sizeof(vmath::vec4) + sizeof(vmath::vec3)); // 将buffer bind到vao的9号 binding point
glVertexArrayAttribFormat(vao, 5, 4, GL_FLOAT, GL_FALSE, 0); // 5号attribute从头开始取
glVertexArrayAttribBinding(vao, 5, 9); //5号attribute去9号binding point取
glEnableVertexArrayAttrib(vao, 5);
glVertexArrayAttribFormat(vao, 1, 3, GL_FLOAT, GL_FALSE, sizeof(vmath::vec4)); // 1号attribute从sizeof(vmath::vec4)的地方开始取
glVertexArrayAttribBinding(vao, 1, 9); //1号attribute去9号binding point取
glEnableVertexArrayAttrib(vao, 1);
glBindVertexArray(vao);
二、uniform block
在shader中,有些数据对所有顶点是通用的(如MVP矩阵)。我们可以分别通过glUniform*()将他们分别传入shader,也可以通过uniform block将他们一次性传入。
shader example:
#version 420 core
layout (std140, binding = 2) uniform UB
{
vec3 translate;
int scale;
}ub;
其中std140声明了UB的内存布局为标准布局,binding = 2声明了这个shader将来要去uniform binding point = 2 的地方取buffer。
然后在opengl中,创建buffer并给数据,与之前差不多:
typedef struct UBSTRUCT
{
vmath::vec3 translate;
int scale;
}ubstruct;
...
static const ubstruct translates = { vmath::vec3(0.1, 0.1, 0), 5 };
...
GLuint ubo;
glCreateBuffers(1, &ubo);
//glNamedBufferStorage(ubo, sizeof(translates), &translates, NULL);
glNamedBufferData(ubo, sizeof(translates), &translates, GL_STATIC_DRAW);
最后,需要将这个buffer bind到GL_UNIFORM_BUFFER的某个binding point。这里将ubo bind到2号 uniform binding point,这样所有layout(binding = 2)的uniform block都将来这个buffer取data。
将该buffer绑定至GL_UNIFORM_BUFFER的2号binding point:
glBindBufferBase(GL_UNIFORM_BUFFER, 2, ubo);
//glBindBufferRange(GL_UNIFORM_BUFFER, 2, ubo, 0, sizeof(translates)); // 可以将一个ubo内不同数据绑定至不同binding point
三、atomic counter
说白了它就是一个buffer。GL_ATOMIC_COUNTER_BUFFER。有自己的index。(就像GL_UNIFORM_BUFFER有自己的index一样)
生成、绑定完全类似。
在shader中,声明为 layout (binding = 3, offset = 0) uniform atomic_uint counter;表示GL_ATOMIC_COUNTER_BUFFER的3号binding point, offset为0。
在shader中,可调用三种方法:
uint atomicCounter(atomic_uint c);
// 减一并返回新值
uint atomicCounterDecrement(atomic_uint c);
//加一并返回旧值
uint atomicCounterIncrement(atomic_uint c);
atomic counter若在fragmentshader中使用,可以观测pixel的绘制顺序(将counter处理为颜色)。此法需要在渲染循环中每次清零atomic counter buffer。
四、Texture
1. 创建texture:同样有两个版本。glCreateTextures()/glGenTextures()。
glCreateTextures()需要指明target,glGenTextures()不用,但是需要bind到某个target之后才会生成真正的texture object。
glCreateTexture()之后,使用glTextureStorage2D()初始化内存空间,或者
glGenTexture()之后,bind到某个target,使用glTexStorage2D()初始化空间。
1.1 初始化texture
glTextureStorage2D()/glTexStorage2D()中有个参数,指明有几个mipmap。该函数对所有mipmap初始化。
1.2 往texture灌数据
glTextureSubImage2D()直接往texture object灌,或者glTexSubImage2D()往当前target灌。
2. 创建sampler
若不创建sampler,则OpenGL会自动为每个texture生成sampler。
使用非默认sampler的好处在于,不用对每张贴图进行环绕方式等设置,只用对sampler进行设置即可。
(一个是.cpp这里的sampler,一个是shader那里的sampler,他们是一一对应的。shader端的sampler以uniform的形式存在)
glCreateSamplers(1, &sampler);
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glBindSampler(5, sampler); // 绑定至5号uniform binding point
其中对GL_TEXTURE_MIN_FILTER的设置才会启用mipmap,否则texture只会从level0的mipmap采样。
GL_TEXTURE_MAG_FILTER不需要设置mipmap之间的采样。
2.1 绑定texture
首先:每个texture unit都有自己的GL_TEXTURE_2D等,glActiveTexture(GL_TEXTUREi)之后,所有对target的设置都将是对GL_TEXTUREi的target上的texture设置。默认激活的是GL_TEXTURE0。
不使用sampler:直接设置texture并使用glActiveTexture(GL_TEXTUREi), glBindTexture(GL_TEXTURE_2D)将之bind至 i 号texture binding point 的 GL_TEXTURE_2D。
使用了sampler:texture就直接用数字指定绑定的texture unit,即:
glBindTextureUnit(5, texture);
注意:在shader里,sampler声明成uniform,但是它的 layout (binding = x) 与 uniform block 的 layout (binding = x)不冲突,前者是texture binding point,后者是uniform buffer 的binding point.
2.2 生成mipmap
两种方法。glGenerateMipmap(GL_TEXTURE_2D);或者glGenerateTextureMipmap(texture);前者对target上的texture,后者直接对texture