向Vertex Shader传递vertex attribute
在VBO、VAO和EBO那一节,介绍了如何向Vertex Shader传递vertex attribute的基本方法。现在我准备把这个话题再次扩展开。
传递整型数据
之前我们的顶点属性数据都是float类型的,现在我使用int(unsigned int)类型或者double类型的数据怎么办?
比如我现在用GLubyte
来定义三角形的颜色:
GLfloat trianglePosition[] =
{
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
GLubyte triangleColor[] =
{
255, 0, 0,
0, 255, 0,
0, 0, 255
};
GLuint vbo[2] = { 0 };
glCreateBuffers(2, vbo);
glNamedBufferStorage(vbo[0], sizeof(trianglePosition), trianglePosition, 0);
glNamedBufferStorage(vbo[1], sizeof(triangleColor), triangleColor, 0);
GLuint vao = 0;
glCreateVertexArrays(1, &vao);
glEnableVertexArrayAttrib(vao, 0);
glEnableVertexArrayAttrib(vao, 1);
glVertexArrayVertexBuffer(vao, 3, vbo[0], 0, sizeof(GLfloat) * 3);
glVertexArrayVertexBuffer(vao, 5, vbo[1], 0, sizeof(GLubyte) * 3); //设置vao与binding point关联的buffer的stride是sizeof(GLubyte)*3
glVertexArrayAttribBinding(vao, 0, 3);
glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribFormat(vao, 1, 3, GL_UNSIGNED_BYTE, GL_TRUE, 0); //设置data type为GL_UNSIGNED_BYTE,并且normalized设置为GL_TRUE
glVertexArrayAttribBinding(vao, 1, 5);
以上代码你应该很熟悉,有两处我加了注释,标记出与传递GLfloat
类型数据的不同之处。第一处是设置binding point对应的buffer的stride,这个很容易理解,没什么值得讨论的东西。关键看第二处:
glVertexArrayAttribFormat(vao, 1, 3, GL_UNSIGNED_BYTE, GL_TRUE, 0); //设置data type为GL_UNSIGNED_BYTE,并且normalized设置为GL_TRUE
之前我就一直纳闷这个命令的GLboolean normalized
到底是干啥用的,现在终于搞清楚了:这个参数只对整型的顶点属性数据起作用,来决定是否对整数类型的数据进行归一化,怎么个归一化法呢?我演示给你看:
//vertex shader
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color; //是vec3而不是uvec3
...
void main(void)
{
...
}
注意虽然数据类型是GLubyte类型的,但是传递到vertex shader却是以float为基础的vec3。这时候vertex shader接受到的color数据其实是:(1.0, 0.0, 0.0)
,(0.0, 1.0, 0.0)
和(0.0, 0.0, 1.0)
。对于无符号的整数类型譬如GLuint,GLushort和GLubyte,会从[0, MAX]线性映射到[0.0, 1.0];而对于有符号整型譬如GLuint,GLushort和GLubyte,则会从[Min, Max]线性映射到[-1.0, 1.0]。公式分别如下:
- 无符号类型的归一化:\(f = \frac{c}{2^{b}-1}\)
- 有符号类型的归一化:\(f = \frac{2c-1}{2^{b}-1}\)
c表示整数数值的大小,b表示这个整数有多少位,比如GLubyte和GLbyte是8位,GLuint和GLint是32位。
如果我们设置nomalized属性为GL_FALSE,那么整数会被直接强制转换为浮点数类型,也就是说vertex shader接受的color数据就会变成:(255.0, 0.0, 0.0)
,
(0.0, 255.0, 0.0)
和(0.0, 0.0, 255.0)
。
如果数值非常大的整数归一化到浮点数,是会丢失精度的,因此范围比较大的整数不适合归一化成浮点数,这时候我们需要直接引用整数类型的顶点属性(或者更多的时候是你需要的就是整数类型的顶点属性):
glVertexAttribIFormat(vao, 1, 3, GL_UNSIGNED_BYTE, 0);
此命令中的I
字符表示的是整数类型的意思,因为是直接引用的整数类型的数据,所以此命令不需要GLboolean normalized
参数。shader也变为:
//vertex shader
layout(location = 0) in vec3 position;
layout(location = 1) in uvec3 color; //是vec3而不是uvec3
...
void main(void)
{
...
}
如代码所示:我们可以在shader中直接引用无符号整数类型的数据了。
传递双精度浮点型数据
有了前面的铺垫,传递double类型的数据很自然就能想到存在类似这样的命令:
void glVertexArrayAttribLFormat(GLuint vaobj,
GLuint attribindex,
GLint size,
GLenum type,
GLuint relativeoffset);
使用起来也和你想的一样:
GLfloat trianglePosition[] =
{
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
GLdouble triangleColor[] =
{
1.0, 1.0, 1.0,
0.5, 0.5, 0.5,
0.0, 0.0, 0.0
};
GLuint vbo[2] = { 0 };
glCreateBuffers(2, vbo);
glNamedBufferStorage(vbo[0], sizeof(trianglePosition), trianglePosition, 0);
glNamedBufferStorage(vbo[1], sizeof(triangleColor), triangleColor, 0);
GLuint vao = 0;
glCreateVertexArrays(1, &vao);
glEnableVertexArrayAttrib(vao, 0);
glEnableVertexArrayAttrib(vao, 1);
glVertexArrayVertexBuffer(vao, 3, vbo[0], 0, sizeof(GLfloat) * 3);
glVertexArrayVertexBuffer(vao, 5, vbo[1], 0, sizeof(GLdouble) * 3); //设置vao与binding point关联的buffer的stride是sizeof(GLdouble)*3
glVertexArrayAttribBinding(vao, 0, 3);
glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribLFormat(vao, 1, 3, GL_DOUBLE, 0); //设置data type为GL_DOUBLE
glVertexArrayAttribBinding(vao, 1, 5);
//vertex shader
layout(location = 0) in vec3 position;
layout(location = 1) in dvec3 color; //dvec3表示double类型的向量
...
void main(void)
{
...
}
interleaved attributes
之前我们使用vertex attribute的方式称为separate attributes,意思是每个vertex attribute分别单独存在两个vbo中。或者像这样,也是separate attribute的变种:
//空间位置和颜色连续存放到一个buffer中
GLfloat triangle[] =
{
-1.0f, -1.0f, //空间位置
1.0f, -1.0f,
0.0f, 1.0f,
1.0f, 0.0f, 0.0f, //颜色
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f
};
glNamedBufferStorage(vbo, sizeof(triangle), triangle, 0);
glEnableVertexArrayAttrib(vao, 0);
glEnableVertexArrayAttrib(vao, 1);
glVertexArrayVertexBuffer(vao, 3, vbo, 0, 2 * sizeof(GLfloat));
glVertexArrayAttribFormat(vao, 0, 2, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(vao, 0, 3);
glVertexArrayVertexBuffer(vao, 5, vbo, 6 * sizeof(GLfloat), 3 * sizeof(GLfloat)); //颜色的offset是跨过空间位置空间
glVertexArrayAttribFormat(vao, 1, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(vao, 1, 5);
现在我再举例说明如何使用interleaved attributes,以加深对glVertexArrayVertexBuffer
和glVertexArrayAttribLFormat
的理解。
仍然是绘制一个三角形:
//空间位置和颜色交叉存放
GLfloat triangle[] =
{
-1.0f, -1.0f, //空间位置
1.0f, 0.0f, 0.0f, //颜色
1.0f, -1.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f,
0.0f, 0.0f, 1.0f
};
glNamedBufferStorage(vbo, sizeof(triangle), triangle, 0);
glEnableVertexArrayAttrib(vao, 0);
glEnableVertexArrayAttrib(vao, 1);
glVertexArrayVertexBuffer(vao, 3, vbo, 0, 5 * sizeof(GLfloat));
glVertexArrayAttribFormat(vao, 0, 2, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(vao, 0, 3);
glVertexArrayVertexBuffer(vao, 5, vbo, 0, 5 * sizeof(GLfloat));
glVertexArrayAttribFormat(vao, 1, 3, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat)); //颜色的relativeoffset是跳过顶点位置,即两个GLfloat
glVertexArrayAttribBinding(vao, 1, 5);
其实颜色的offset也可以指定给glVertexArrayVertexBuffer
的第三个参数offset
,而不是glVertexArrayAttribFormat
的最后一个参数relativeoffset
。
glVertexArrayVertexBuffer(vao, 5, vbo, 2 * sizeof(GLfloat), 5 * sizeof(GLfloat));//指定颜色数据的offset
glVertexArrayAttribFormat(vao, 1, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(vao, 1, 5);
那这offset
和relativeoffset
到底有什么区别呢?
下面以如下方式绘制一个三角形和一个矩形。
GLfloat triangle_rect[] =
{
//三角形的空间位置和颜色交叉存放
-1.0f, -1.0f, //三角形的空间位置
1.0f, 0.0f, 0.0f, //三角形的颜色
1.0f, -1.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
//矩形的空间位置和颜色也是交叉存放
-0.5f, 0.0f, //矩形的空间位置
0.0f, 0.0f, 0.0f, //矩形的颜色
-0.5f, -1.0f,
0.3f, 0.3f, 0.3f,
0.5f, 0.0f,
0.7f, 0.7f, 0.7f,
0.5f, -1.0f,
1.0f, 1.0f, 1.0f
};
glNamedBufferStorage(vbo, sizeof(triangle_rect), triangle_rect, 0);
glEnableVertexArrayAttrib(triangle_vao, 0);
glEnableVertexArrayAttrib(triangle_vao, 1);
glVertexArrayVertexBuffer(triangle_vao, 3, vbo, 0, 5 * sizeof(GLfloat));
glVertexArrayAttribFormat(triangle_vao, 0, 2, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(triangle_vao, 0, 3);
glVertexArrayVertexBuffer(triangle_vao, 5, vbo, 2 * sizeof(GLfloat), 5 * sizeof(GLfloat));
glVertexArrayAttribFormat(triangle_vao, 1, 3, GL_FLOAT, GL_FALSE, 2 * 0);
glVertexArrayAttribBinding(triangle_vao, 1, 5);
glEnableVertexArrayAttrib(rect_vao, 0);
glEnableVertexArrayAttrib(rect_vao, 1);
glVertexArrayVertexBuffer(rect_vao, 3, vbo, 3 * 5 * sizeof(GLfloat), 5 * sizeof(GLfloat));
glVertexArrayAttribBinding(rect_vao, 0, 3);
glVertexArrayAttribFormat(rect_vao, 0, 2, GL_FLOAT, GL_FALSE, 0);
glVertexArrayVertexBuffer(rect_vao, 5, vbo, 3 * 5 * sizeof(GLfloat), 5 * sizeof(GLfloat)); //矩形颜色的offset为跨过所有的三角形的数据
glVertexArrayAttribBinding(rect_vao, 1, 5);
glVertexArrayAttribFormat(rect_vao, 1, 3, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat)); //矩形颜色的relative offset为跨过自身的空间位置数据
看到上述代码,我相信你可能明白一些了:offset往往描述的是跨越到属于自己的数据区(跨过三角形的所有顶点数据),而在自己的数据区跨越是用relative offset来描述的(跨过矩形本身的空间位置数据)。其实也是有公式的:
//索引某个顶点的attribute公式
location = binding[attrib.binding].memory + // Start of data store in memory
binding[attrib.binding].offset + // Offset of vertex attribute in buffer
binding[attrib.binding].stride * vertex.index + // Start of *this* vertex
vertex.relative_offset; // Start of attribute relative to vertex
顶点的自动补全和截断
自动补全:
...
glVertexArrayAttribFormat(vao, 1, 1, GL_FLOAT, GL_FALSE, 0); //指定attribute index 1只有一个float分量
//vertex shader
layout(location = 1) in vec4 color; //color的y和z分量被补全为0,w分量为1
...
截断:
...
glVertexArrayAttribFormat(vao, 1, 4, GL_FLOAT, GL_FALSE, 0); //指定attribute index 1只有一个4个float分量
//vertex shader
layout(location = 1) in vec2 color; //只拿到了x和y分量,z和w直接被丢弃掉了
...
小结
通过这一节,我们掌握了如下内容:
- 能够给Vertex Shader传递整数类型的vertex attribute,以及归一化和不归一化的区别
- 能够给Vertex Shader传递double类型的vertex attribute
- 学会separate attribute和interleaved attribute的顶点数据组织以及传递方式,两种类型分别有两种,一共四种
- 了解顶点属性的自动补全和截断
向Vertex Shader传递vertex attribute的更多相关文章
- Stage3d 由浅到深理解AGAL的管线vertex shader和fragment shader || 简易教程 学习心得 AGAL 非常非常好的入门文章
Everyday Stage3D (一) Everyday Stage3D (二) Triangle Everyday Stage3D (三) AGAL的基本概念 Everyday Stage3D ( ...
- PlayCanvas PBR材质shader代码分析(vertex shader)
顶点shader主要对顶点坐标变换,将顶点坐标从local->world->view->clip 空间变换 local空间:模型物体坐标系 world空间:世界空间坐标系 view空 ...
- linearizing the depth in vertex shader
please refer to http://www.mvps.org/directx/articles/linear_z/linearz.htm When using a programmable ...
- 学习笔记:GLSL Core Tutorial – Vertex Shader(内置变量说明)
1.每个Vertex Shader都有用户定义的输入属性,例如:位置,法线向量和纹理坐标等.Vertex Shaders也接收一致变量(uniform variables). uniform vari ...
- GLSL写vertex shader和fragment shader
0.一般来说vertex shader处理顶点坐标,然后向后传输,经过光栅化之后,传给fragment shader,其负责颜色.纹理.光照等等. 前者处理之后变成裁剪坐标系(三维),光栅化之后一般认 ...
- vertex shader(4)
Swizzling and Masking 如果你使用输入.常量.临时寄存器作为源寄存器,你可以彼此独立地swizzle .x,.y,.z,.w值.如果你使用输出.临时寄存器作为目标寄存器,你可以把. ...
- vertex shader(3)
之前我们学习了如何声明顶点着色器.如何设置常量寄存器中的常量.接下来我们学习如何写和编译一个顶点着色器程序. 在我们编译一个顶点着色器之前,首先需要写一个. 有17种不同的指令(instruction ...
- vertex shader(2)
一次只有一个vertex shader是活跃的.你可以有多个vertex shader,如果一个物体特殊的变换或者灯光,你可以选择合适的vertex shader来完成这个任务. 你可能想使用vert ...
- vertex shader(1)
Vertex shader Architecture: 所有在vertex shader中的数据都用128-bit的quad-floats表示(4x32-bit). vertex shader线性地执 ...
随机推荐
- iNeuOS工业互联平台,生产过程业务联动控制
1.概述 工业物联网也好.工业互联网也好或是其他生产系统,反向控制始终无法回避.搞工业最直接.最体现效果的两个方面是采集各种数据和生产过程业务控制,所谓大数据预测和分析,那是仁者见仁.智者见智,下一篇 ...
- OpenCV图像处理中“找圆技术”的使用
一.为什么"找圆" 圆是基本图形的一种,更为重要的是,自然情况下采集的图像,很少大量存在"圆":但凡存在的,大都是人工的,那么就必然代表特定的意义,从而 ...
- Hibernate的Dao层通用设计
hibernate作为一款优秀的数据库持久化框架,在现实的运用中是非常广泛的.它的出现让不熟悉sql语法的程序员能开发数据库连接层成为一种可能,但是理想与现实永远是有差距的.开发过程中如果只使用hql ...
- Android 之 手动创建活动
•活动是什么 活动(Activity)是最容易吸引用户的地方,它是一种可以包含用户界面的组件: 主要用于和用户进行交互: 一个应用程序可以包含零个或多个活动. 接下来,我们来学习一下活动的基本用法. ...
- 移动端调试vConsole
当我们在进行移动端开发的时候,经常会出现在pc显示正常,在移动端却各种异常的情况.这时候我们在手机上又看不到error log. 所以我们就需要vConsole这样一个移动端开发神器. 那具体要怎么使 ...
- Python—关于Pandas缺失值问题(国内唯一)
获取文中的CSV文件用于代码编程以及文章首发地址,请点击下方超链接 获取CSV,用于编程调试请点这 在本文中,我们将使用Python的Pandas库逐步完成许多不同的数据清理任务.具体而言,我们将重点 ...
- Redis系列-存储篇string主要操作命令
Redis系列-存储篇string主要操作命令 通过上两篇的介绍,我们的redis服务器基本跑起来.db都具有最基本的CRUD功能,我们沿着这个脉络,开始学习redis丰富的数据结构之旅,当然先从最简 ...
- 为什么要放弃 JSP?他们终于给出了答案
前言 以前的项目大多数都是Java程序猿又当爹又当妈,既搞前,又搞后端. 随着时代的发展,渐渐的许多大中小公司开始把前后端的界限分的越来越明确,前端工程师只负责前端的事情,后端工程师只管后端的事情.正 ...
- Day09_46_Set集合_SortedSet03
SortedSet03 让SortedSet集合完成比较,还有另外一种方法,那就是单独编写一个比较器. java.util.comparator 在TreeSet集合创建的时候可以在集合中传入一个比较 ...
- 【golang】golang 相关问题?
1.golang 官网下载很慢: 中科大镜像:http://mirrors.ustc.edu.cn/golang/ 2.go get 下载依赖很慢的问题: https://goproxy.io/zh/ ...