【OpenGL4.0】GLSL-使用Uniform Block实现着色器的数据共享

本文介绍了如何在GLSL中使用UniformBlock来简化多个着色器间的Uniform数据共享,并提供了具体的示例代码。同时,还讲解了如何使用std140布局标识符来避免查询偏移量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、在GLSL中使用Uniform Block

在GLSL渲染语言中,我们经常采用缓冲区来存储Uniform型的Block。

比如我们需要绘制下面这样的效果:


在这里我们绘制了一个圆,圆内部颜色和外部颜色不同,而且边缘部分,颜色是平滑过渡的。

我们这样来实现这一效果:

首先定义一个内径和外径。然后给这个正方形分配纹理坐标,然后通过纹理坐标离(0.5,0.5)的距离与内径和外径的关系来控制当前像素点的颜色。


我们要在Shader中提供这样的一些信息:圆的内径及外径、内部颜色和外部颜色。采用Block来将这些信息封装在一起是合适的选择。

采用Block的原因是:

如果你的程序中包含了多个着色器,而且这些着色器使用了相同的Uniform变量,你就不得不为每个着色器分别管理这些变量。Uniform变量的location是在程序链接的时候产生的,因此Uniform变量的location会随着着色器的不同而发生变化。因此,这些Uniform变量的数据必须重新产生,然后应用到新的location上。

而Uniform Block正是为了使在着色器间共享Uniform数据变得更加容易而设计的。有了Uniform Block,我们可以创建一个缓冲区对象来存储这些Uniform变量的值,然后将缓冲区对象绑定到Uniform Block。当着色器程序改变的时候,只需要将同样的缓冲区对重新绑定到在新的着色器中与之相关的Block即可。


Uniform Block的定义和C/C++中的struct很类似。在本文的例子中我们需要定义的Uniform Block为:

方法一:

uniform BlobSettings {
  vec4 InnerColor;
  vec4 OuterColor;
  float RadiusInner;
  float RadiusOuter;
};

或者是

方法二:

uniform BlobSettings{
	vec4 InnerColor;
	vec4 OuterColor;
	float RadiusInner;
	float RadiusOuter;
}Blob;
这样我们就定义了一个名为BlobSettings的Block,包含了4个uniform 变量。
在方法一中,Block中的变量依然是全局域的一部分,取用的时候不用Block名。

而在方法二中,Block中的变量则是在Blob名字域中,取用的时候需要加上Block名。

这两种方法在使用上有少许差别。


用来存储uniform数据的缓冲区对象称作UBO(uniform buffer object)。它只是一个绑定到特定location的缓冲区对象。


注意下面的代码是基于上述定义Block的方法二的基础之上的。

下面贴出完整的GLSL渲染器代码:

basic_block.vert:

#version 400

layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexTexCoord;

out vec3 TexCoord;

void main()
{
	TexCoord = VertexTexCoord;
	gl_Position = vec4(VertexPosition,1.0);
}
basic_block.frag:

#version 400

in vec3 TexCoord;
layout (location = 0) out vec4 FragColor;

uniform BlobSettings{
	vec4 InnerColor;
	vec4 OuterColor;
	float RadiusInner;
	float RadiusOuter;
}Blob;//Use an instance name with a uniform block

void main()
{
	float dx = TexCoord.x - 0.5;
	float dy = TexCoord.y - 0.5;
	float dist = sqrt(dx * dx + dy * dy);
	//smoothstep : perform Hermite interpolation between two values
	FragColor = mix(Blob.InnerColor,Blob.OuterColor,
			smoothstep(Blob.RadiusInner,Blob.RadiusOuter,dist));
}
UBO的设置:

void Scenebasic_block::setUniformBlock()
{
	GLuint blockIndex = glGetUniformBlockIndex(prog.getHandle(),
												"BlobSettings");
	GLint blockSize;
	glGetActiveUniformBlockiv(prog.getHandle(),
							blockIndex,
							GL_UNIFORM_BLOCK_DATA_SIZE,
							&blockSize);
	GLubyte *blockBuffer = (GLubyte*)malloc(blockSize);
	//Query for the offsets of the each block variable
	//layout of the data within a uniform block is implementation dependent
	const GLchar *names[] = {"BlobSettings.InnerColor","BlobSettings.OuterColor",
							"BlobSettings.RadiusInner","BlobSettings.RadiusOuter"};
	GLuint indices[4];
	glGetUniformIndices(prog.getHandle(), 4, names, indices);

	GLint offset[4];
	glGetActiveUniformsiv(prog.getHandle(), 4, indices, GL_UNIFORM_OFFSET, offset);

	GLfloat outerColor[] = {0.0f, 0.0f, 0.0f, 0.0f};
	GLfloat innerColor[] = {1.0f, 1.0f, 0.75f, 1.0f};
	GLfloat innerRadius = 0.25f, outerRadius = 0.45f;

	memcpy(blockBuffer + offset[0],innerColor,4 * sizeof(float));
	memcpy(blockBuffer + offset[1],outerColor,4 * sizeof(float));
	memcpy(blockBuffer + offset[2],&innerRadius,sizeof(float));
	memcpy(blockBuffer + offset[3],&outerRadius,sizeof(float));

	//create OpenGL buffer UBO to store the data
	GLuint uboHandle;
	glGenBuffers(1,&uboHandle);
	glBindBuffer(GL_UNIFORM_BUFFER,uboHandle);
	glBufferData(GL_UNIFORM_BUFFER,blockSize,blockBuffer,
					GL_DYNAMIC_DRAW);

	//bind the UBO th the uniform block
	glBindBufferBase(GL_UNIFORM_BUFFER,blockIndex,uboHandle);

	free(blockBuffer);
}

二、在Uniform Block中使用布局标识符std140

1、std140的使用
由于UBO中数据的布局和具体实现有关(内存对齐等等),因此我们需要在设置其值的时候查询偏移量。然后,我们可以使用标准布局std40来避免这一操作。这可以通过在定义Uniform Block的时候使用布局标识符来实现:

layout( std140 ) uniform BlobSettings {
   …
};


2、std140详解

std40布局标识符要求GLSL着色器依据一组规则来组织Uniform Block中的变量,这样使得我们能够预先计算出其中的变量的偏移量。

变量的偏移量是根据起始偏移量和在其之前的变量的大小累积计算得到的。第一个成员变量的起始偏移量总是0.


std40的布局规则


变量类型

变量大小/偏移量

标量数据类型(bool,int,uint,float)

基于基本机器类型的标量值大小

(例如,sizeof(GLfloat))

二元向量(bvec2,ivec2,uvec2,vec2)

标量类型大小的两倍

三元向量(bvec3,ivec3,uvec3,vec3)

标量类型大小的四倍

三元向量(bvec4,ivec4,uvec4,vec4)

标量类型大小的四倍

标量的数组或向量

数组中每个元素大小是基本类型的大小,偏移量是其索引值(从0开始)与元素大小的乘积。整个数组必须是vec4类型的大小的整数倍(不足将在尾部填充)。

一个或多个C列R行列主序矩阵组成的数组

以C个向量(每个有R个元素)组成的数组形式存储。会像其他数组一样填充。

如果变量是M个列主序矩阵的数组,那么它的存储形式是:M*C个向量(每个有R个元素)组成的数组。

一个或多个R行C列的行主序矩阵组成的数组

以R个向量(每个有C个元素)组成的数组。默认像其他数组一样填充。

如果变量是M个行主序矩阵组成的数组,则存储形式是M*R个向量(每个有C个元素)组成的数组。

单个结构体或多个结构体组成的数组

单个结构体成员的偏移量和大小可以由前面的规则计算出。结构大小总是vec4大小的整数倍(不足在后面补齐)。

由结构组成的数组,偏移量的计算需要考虑单个结构的对齐和补齐。结构的成员偏移量由前面的规则计算出。

 



<think>好的,用户想了解GLSL版本450着色器编程,我需要根据提供的引用资料和系统指令来组织回答。首先,我需要确认GLSL版本和对应的OpenGL版本。引用[2]提到OpenGL 4.5对应的GLSL4.50,所以版本450应该对应OpenGL 4.5。 接下来,用户提到版本指令的使用,应该解释如何在着色器开头声明版本,比如#version 450 core。然后需要介绍GLSL 4.50的新特性,比如增强的原子操作、存储块等,这些内容在引用[2]中有部分提到,但可能需要结合更多知识补充。 另外,用户可能想知道实际编程中的例子,比如如何编写顶点或片段着色器。引用[3]中的示例可能有用,但需要确保代码符合版本450的语法。比如,使用layout(location)来指定输入输出,以及使用uniform变量,如引用[4]中的time、mouse等。 还要注意引用文献的正确标注,比如提到存储块和原子操作时引用[2],提到控制结构和函数时引用[3]。需要检查是否有遗漏的引用点,并确保每个引用与内容相关。 用户可能还希望了解如何结合GLM库进行数学运算,引用[2]提到GLM与GLSL的兼容性,这部分需要简要说明。 最后生成相关问题,需要围绕版本特性、优化、应用场景等方面提问,确保问题与主题相关且能帮助用户深入学习。 检查是否符合所有系统指令:使用正确的中文,数学格式,引用标注,生成相关问题,代码块使用正确语法。确保回答结构清晰,分点明确,帮助用户逐步理解GLSL 450使用和特性。</think>### GLSL版本450着色器编程详解 GLSL版本450对应OpenGL 4.5标准,是支持现代图形渲染和计算的关键版本。以下是其核心特性及使用方法: #### 1. 版本声明 在着色器代码首行需明确声明版本: ```glsl #version 450 core ``` `core`表示使用OpenGL核心模式,强制要求现代语法规范[^2]。 #### 2. 核心特性 - **增强的存储块(SSBO)** 允许更灵活的数据交互: ```glsl layout(std430, binding=0) buffer DataBlock { vec4 positions[]; }; ``` - **原子操作扩展** 支持跨着色器调用的原子计数器: ```glsl layout(binding=0) uniform atomic_uint counter; void main() { atomicCounterIncrement(counter); } ``` - **计算着色器优化** 支持工作组划分与共享内存: ```glsl layout(local_size_x=32) in; shared vec4 tempData[32]; ``` #### 3. 典型应用示例 **顶点着色器**(结合GLM数学库[^2]): ```glsl #version 450 core layout(location=0) in vec3 aPos; uniform mat4 modelViewProjection; void main() { gl_Position = modelViewProjection * vec4(aPos, 1.0); } ``` **片段着色器**(动态效果): ```glsl #version 450 core uniform float time; // 时间参数[^4] out vec4 FragColor; void main() { vec3 color = vec3(sin(time)*0.5+0.5, 0.2, 0.8); FragColor = vec4(color, 1.0); } ``` #### 4. 兼容性注意 - 需在OpenGL驱动中启用GL_ARB_gl_spirv扩展以支持SPIR-V中间语言 - 与GLM库配合时,需包含对应版本的头文件: ```cpp #include <glm/gtc/matrix_transform.hpp> // 矩阵变换 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没有昵称阿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值