GLSL 教程【04】-- 统一变量Uniform

关键词:

Uniform Variables

文章目录

  • 一、说明
  • 二、关于uniform
    • [2.1 变量介绍](#2.1 变量介绍)
    • [2.2 变量定义](#2.2 变量定义)
    • [2.3 如何查询Uniform变量访问位置](#2.3 如何查询Uniform变量访问位置)
    • [2.4 给Uniform变量赋值](#2.4 给Uniform变量赋值)
  • 三、传递数组
  • 四、传递结构
  • 五、范例
    • [5.1 Uniform的使用过程](#5.1 Uniform的使用过程)
    • [5.2 定义Uniform变量](#5.2 定义Uniform变量)
    • [5.3 获取Uniform变量指针](#5.3 获取Uniform变量指针)
    • [5.4 glGetUniformLocation细节](#5.4 glGetUniformLocation细节)
    • [5.5 给Uniform变量赋值](#5.5 给Uniform变量赋值)

上一篇:属性变量 下一篇:统一块

一、说明

统一变量充当常量,至少在绘制调用期间如此。应用程序将这些变量提供给图形管道,并且它们可以在管道的所有阶段中访问,即任何着色器都可以访问每个统一变量,只要它声明了该变量。就着色器而言,这些变量是只读的。

二、关于uniform

2.1 变量介绍

在本教程中,我们遇到一种新型着色器变量 - 统一变量。属性变量和统一变量之间的区别在于,属性变量包含特定于顶点的数据,因此每次着色器调用都会从顶点缓冲区重新加载新值,而统一变量的值在整个绘制调用中保持不变。这意味着您在进行绘制调用之前加载该值,然后您可以在每次调用顶点着色器时访问相同的值。统一变量对于存储诸如光照参数(光照位置和方向等)、变换矩阵、纹理对象句柄等数据很有用。

在本教程中,我们终于在屏幕上看到了一些移动的东西。我们使用统一变量(其值每帧都会更改)和 GLUT 提供的空闲回调函数的组合来完成此操作。关键是 GLUT 不会重复调用我们的渲染回调函数 - 除非必须这样做。 GLUT 必须在事件发生后调用渲染回调,例如最小化和最大化窗口或被另一个窗口发现它。如果我们在启动应用程序后不更改窗口布局中的任何内容,则渲染回调只会被调用一次。您可以通过在渲染函数中添加 printf 调用来亲自查看。您只会看到一次输出,如果最小化然后最大化窗口,您将再次看到它。对于之前的教程来说,在 GLUT 中仅注册渲染回调就可以了,但在这里我们想要重复更改变量的值。我们通过注册一个空闲函数回调来做到这一点。当没有从窗口系统接收到任何事件时,空闲函数由 GLUT 调用。您可以为此回调指定一个专用函数,您可以在其中执行任何簿记操作,例如时间更新,或者简单地将渲染回调函数注册为空闲回调。在本教程中,我们将执行后面的操作并更新渲染函数内的变量。要点是:

  • 如何定义
  • 和CSGL程序关联
  • 在着色器定义接收
  • 如何刷新

2.2 变量定义

统一变量可以分组为块,也可以单独定义。在这里,我们将开始查看各个定义,下一节将查看统一块。

在着色器内部,使用关键字定义统一uniform。例如,要定义一个vec4命名,myVar我们可以在着色器中编写

c 复制代码
uniform vec4 myVar;

Uniform 也可以在着色器内部初始化,例如要初始化 vec4,我们可以按如下方式进行:

c 复制代码
uniform vec4 myVar = {0.5, 0.2, 0.7, 1.0};

2.3 如何查询Uniform变量访问位置

着色器内初始化很棒,因为它使我们不必设置制服。我们可以在着色器初始化中设置默认值,并且仅当我们需要统一变量的不同值时才需要从应用程序设置一个值。

在应用程序内部,要设置统一变量,我们必须首先获取其位置,可以使用以下函数检索该位置:

c 复制代码
GLint glGetUniformLocation(GLuint program, const char *name);

参数:

program:链接程序的句柄

name:统一变量的名称

返回:变量的位置,如果名称不对应于活动的统一变量,则返回 -1。

活动统一变量是在着色器内部实际使用的变量,而不仅仅是声明的变量。编译器可以随意丢弃代码中未使用的变量。因此,即使在着色器中声明了uniform,只要不使用它,其报告的位置就可以为-1。

2.4 给Uniform变量赋值

假设程序已链接并且变量在代码中有效使用,则返回值是变量的位置,稍后可用于设置其值。为了设置值,OpenGL 提供了一个大的函数系列,涵盖所有数据类型,以及多种设置值的方法。例如,考虑myVar上面定义的变量。要设置vec4, 并假设p为链接程序的句柄,我们可以编写:

1)以散列数方式对vec4f变量赋值:

c 复制代码
GLint myLoc = glGetUniformLocation(p, "myVar");
glUniform4f(  myLoc, 1.0f, 2.0f, 2.5f, 2.7f);

上面对一个vec4f的变量赋值。观点是将vec4f看成4个独立数值。

如果用数组序列的观点,我们可以写...

2)以数组方式对vec4f变量赋值:

c 复制代码
float myFloats[4] = {1.0f, 2.0f, 2.5f, 2.7f};
GLint myLoc = glGetUniformLocation(p, "myVar");
glUniform4fv(  myLoc, 1, myFloats);

3)如果GLSL程序也很多,对于多个程序中某个进行Uniform操作,用

glProgramUniform4f函数 取代上文的glUniform4f函数。

这两个函数的签名如下:

c 复制代码
void glProgramUniform4f(GLuint program, GLint location, GLfloat f1, ..., GLfloat f4);
上文参数对应:
void glProgramUniform4fv(GLuint 程序、GLint 位置、GLsizei 计数、const GLfloat *值);

参数:

program:链接程序的句柄

location:统一变量在程序 p 中的位置 f1...f4,values:设置统一的值

count:要设置的项目数。对于基本数据类型,这始终是一种。当考虑数组时,该值表示要设置的数组中的元素序号。

GLSL 中的所有基本数据类型和预定义矩阵都有类似的函数。

三、传递数组

现在考虑一下,在我们的着色器中,我们有一个数组类型的变量,声明如下:

c 复制代码
uniform vec4 color[2];

从应用程序的角度来看,我们可以将变量视为数组并一次性设置所有组件。对于大多数基本类型(而不是结构)来说都是如此,请参见下文。

c 复制代码
float myColors[8] = {1.0f, 0.0f, 0.0f, 0.0f,     0.0f, 1.0f, 0.0f, 0.0f};
GLint myLoc = glGetUniformLocation(p, "color");
glProgramUniform4fv(p, myLoc, 2, myColors);

请注意,我们将 的第三个参数设置glProgramUniform4fv为 2,因为我们要设置数组的两个元素,即两个vec4。应用程序数组myColors根据需要有八个浮点数。

另一种方法是单独设置每个元素。例如,假设我们只想设置 GLSL 数组的第二个元素,即color[1]。然后我们可以写:

c 复制代码
GLfloat aColor[4] = {0.0f, 1.0f, 1.0f, 0.0f};
myLoc = glGetUniformLocation(p, "color[1]");
glProgramUniform4fv(p, myLoc, 1, aColor);

每个基本数据类型都有一个 glProgramUniform 函数,它们有两种形式,如 vec4 情况所示:

c 复制代码
[stextbox]
void glProgramUniform4f(GLuint program, GLint location, GLfloat v1, ..., GLfloat v4);
void glProgramUniform4fv(GLuint 程序、GLint 位置、GLsizei 计数、GLfloat *v);

参数:

程序:链接的程序句柄

location:变量的位置 v1...v4:

vec4 的每个分量的值

count:要设置的vec4的数量

v:指向可以找到浮点数据的位置的指针

对于矩阵,此函数有一个特殊版本,请参阅下面的 mat4 示例:

cpp 复制代码
void glProgramUniformMatrix4fv(GLuint program, GLint location, GLsizei count, GLboolean transpose, GLfloat *v);

参数:

program:链接的程序句柄

location:变量的位置

count:要设置的vec4的数量

transpose:是否转置,则矩阵将在加载到统一之前转置

v:指向可以找到浮点数据的位置的指针

四、传递结构

在 GLSL 中,我们可以以类似于 C 的方式定义结构体。例如,以下代码片段声明一个具有两个 vec4 的结构体,并定义该类型的统一变量。

c 复制代码
struct Colors{
    vec4 Color1;
    vec4 Color2;
};

uniform Colors myColors;

为了在着色器中使用结构体的字段,我们使用与 C 中相同的表示法。例如,以下 main 函数假定上面的声明:

c 复制代码
void main()
{
    vec 4 aColor = myColors.Color1 + myColors.Color2;
    ...
}

为了在应用程序中设置这些变量,我们还使用与 C 中相同的表示法。我们无法将结构作为一个整体进行设置,因为它无法获取它的位置。我们必须逐个字段地设置它。以下示例显示了如何操作:

c 复制代码
float myFloats[8] = {0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0};
 
GLint myLoc = glGetUniformLocation(p, "myColors.Color1");
glProgramUniform4fv(p, myLoc, 1, myFloats);
myLoc = glGetUniformLocation(p, "myColors.Color2");
glProgramUniform4fv(p, myLoc, 1, &(myFloats[4]));

使用结构数组很简单。考虑以下统一声明:

c 复制代码
uniform Colors myColors[2];

在着色器中我们可以编写以下内容:

c 复制代码
void main()
{
    vec 4 aColor = myColors[0].Color1 + myColors[1].Color2;
    ...
}

在我们的应用程序中,我们以相同的方式进行:

c 复制代码
GLint myLoc;
float myFloats[8] = {0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0};
float myOtherFloats[8[ = {1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0};
 
myLoc = glGetUniformLocation(p, "myColors[0].Color1");
glProgramUniform4fv(p, myLoc, 1, myFloats);
myLoc = glGetUniformLocation(p, "myColors[0].Color2");
glProgramUniform4fv(p, myLoc, 1, &(myFloats[4]));
myLoc = glGetUniformLocation(p, "myColors[1].Color1");
glProgramUniform4fv(p, myLoc, 1, myOtherFloats);
myLoc = glGetUniformLocation(p, "myColors[1].Color2");
glProgramUniform4fv(p, myLoc, 1, &(myOtherFloats[4]));

五、范例

给出两个点,A和B,在着色器中画出这两个点的线段。

5.1 Uniform的使用过程

分三个步骤

  • 定义
  • 寻址
  • 赋值

5.2 定义Uniform变量

着色器程序中,如下定义是合法的:

cpp 复制代码
struct TheStruct{
  vec3 first;
  vec4 second;
  mat4x3 third;
};

uniform vec3 oneUniform;
uniform TheStruct aUniformOfArrayType;
uniform mat4 matrixArrayUniform[25];
uniform TheStruct uniformArrayOfStructs[10];

禁忌:

  • 定义后变量不能在SLGL中赋值
  • 不能作为输出变量输出。

5.3 获取Uniform变量指针

在OpenGL客户端获得Uniform的地址,通过glGetUniformLocation函数获取uniform的指针,输入变量:

cpp 复制代码
GLint glGetUniformLocation(	GLuint program, 	const GLchar *name);

参数:

  • program :指定要查询的程序对象。
  • name:指向一个以 null 结尾的字符串,其中包含要查询其位置的统一变量的名称。
    例如:
cpp 复制代码
local = glGetUniformLocation(shader, 'myVar'.encode('ascii'))

5.4 glGetUniformLocation细节

glGetUniformLocation 返回一个整数,表示程序对象中特定统一变量的位置。 name 必须是一个以 null 结尾的字符串,不包含空格。 name 必须是程序中的活动统一变量名称,它不是结构体、结构体数组或者向量或矩阵的子组件。如果名称不对应于程序中的活动统一变量,如果名称以保留前缀"gl_"开头,或者如果名称与原子计数器或命名统一块关联,则此函数返回-1。

作为结构体或结构体数组的统一变量可以通过为结构体中的每个字段调用 glGetUniformLocation 来查询。数组元素运算符"[]"和结构体字段运算符"."可以在名称中使用,以便选择数组中的元素或结构中的字段。使用这些运算符的结果不允许是另一个结构、结构数组或向量或矩阵的子组件。除非名称的最后部分表示统一变量数组,否则可以使用数组的名称或使用附加"[0]"的名称来检索数组的第一个元素的位置。

在程序对象成功链接之前,分配给统一变量的实际位置是未知的。链接发生后,可以使用命令 glGetUniformLocation 来获取统一变量的位置。然后可以将该位置值传递给 glUniform 以设置统一变量的值,或传递给 glGetUniform 以查询统一变量的当前值。程序对象成功链接后,统一变量的索引值保持固定,直到下一个链接命令发生。如果链接成功,则只能在链接后查询统一变量位置和值。

5.5 给Uniform变量赋值

使用glUniformXX函数改变Uniform变量值。"XX"随变量类型不同而不同。

cpp 复制代码
void glUniform4i(	GLint location, 	GLint v0, 	GLint v1, 	GLint v2, 	GLint v3);

更改Uniform变量值:
glUniform4f

例如:物体在屏幕上平移。可以得到随时间动态的效果。

python 复制代码
while not glfw.window_should_close(window):
	 ct = glfw.get_time()
	 glUniform4f(local, sin(ct) ,  cos(ct) ,0, 1.0)
相关推荐
汪子熙10 分钟前
Angular 服务器端应用 ng-state tag 的作用介绍
前端·javascript·angular.js
Envyᥫᩣ19 分钟前
《ASP.NET Web Forms 实现视频点赞功能的完整示例》
前端·asp.net·音视频·视频点赞
大二转专业2 小时前
408算法题leetcode--第24天
考研·算法·leetcode
凭栏落花侧2 小时前
决策树:简单易懂的预测模型
人工智能·算法·决策树·机器学习·信息可视化·数据挖掘·数据分析
hong_zc3 小时前
算法【Java】—— 二叉树的深搜
java·算法
吱吱鼠叔4 小时前
MATLAB计算与建模常见函数:5.曲线拟合
算法·机器学习·matlab
Мартин.4 小时前
[Meachines] [Easy] Sea WonderCMS-XSS-RCE+System Monitor 命令注入
前端·xss
嵌入式AI的盲5 小时前
数组指针和指针数组
数据结构·算法
昨天;明天。今天。6 小时前
案例-表白墙简单实现
前端·javascript·css
数云界6 小时前
如何在 DAX 中计算多个周期的移动平均线
java·服务器·前端