OpenGL编程逐步深入(五)Uniform 变量
准备知识
在这个教程中我们会遇到一种新的Shader变量类型,即uniform变量。attribute(属性)变量和uniform变量的不同之处在于attribute 变量中包含顶点的具体数据,当每次执行shader调用时会从顶点缓存中重新加载一个新的值。而uniform类型的变量在整个绘制调用中始终使用同一个变量。这意味着你在绘制调用前加载的值在每个vertex shader调用时能访问到相同的值。uniform变量在存储光照参数(光照位置、方向等)、变换矩阵、纹理对象句柄等这一类型的数据时非常有用。
在这个教程中我们最终会看到一些东西在屏幕上移动。我们將它和一个uniform变量进行绑定,GLUT为我们提供了空闲回调函数,在绘制每一帧的时候改变变量的值。GLUT并不会重复的调用渲染回调函数,除非在需要的时候,例如窗口最大化、最小化或被其他窗口覆盖。如果我们在启动应用程序后不对窗口进行任何操作,渲染回调函数仅仅会被调用一次。可以通过在这个回调函数中使用printf向控制台打印信息来验证这个结论。在GLUT中只注册渲染回调函数在前几节教程中没什么问题,但本节我们需要反复的改变变量的值,因此我们通过注册空闲回调函数实现。空闲处理函数即使没有接收到来自Windows系统事件时也会被GLUT实时的调用。你可以为这个回调专门写一个函数,或者把渲染回调函数也指定为空闲回调函数。在本教程我们我们采用后者,在渲染回调函数中更新变量的值。
项目配置
参考上节。
程序代码
我们在上一节代码基础上进行调整。
清单1.主程序 tutorial05.cpp代码
/*
Copyright 2010 Etay Meiri
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Tutorial 05 - uniform variables
*/
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <GL/glew.h>
#include <GL/freeglut.h>
#include "ogldev_math_3d.h"
GLuint VBO;
GLuint gScaleLocation;
const char* pVSFileName = "shader.vs";
const char* pFSFileName = "shader.fs";
static void RenderSceneCB()
{
glClear(GL_COLOR_BUFFER_BIT);
static float Scale = 0.0f;
Scale += 0.001f;
glUniform1f(gScaleLocation, sinf(Scale));
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(0);
glutSwapBuffers();
}
static void InitializeGlutCallbacks()
{
glutDisplayFunc(RenderSceneCB);
glutIdleFunc(RenderSceneCB);
}
static void CreateVertexBuffer()
{
Vector3f Vertices[3];
Vertices[0] = Vector3f(-1.0f, -1.0f, 0.0f);
Vertices[1] = Vector3f(1.0f, -1.0f, 0.0f);
Vertices[2] = Vector3f(0.0f, 1.0f, 0.0f);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
}
static void AddShader(GLuint ShaderProgram, const char* pShaderText, GLenum ShaderType)
{
GLuint ShaderObj = glCreateShader(ShaderType);
if (ShaderObj == 0) {
fprintf(stderr, "Error creating shader type %d\n", ShaderType);
exit(1);
}
const GLchar* p[1];
p[0] = pShaderText;
GLint Lengths[1];
Lengths[0]= strlen(pShaderText);
glShaderSource(ShaderObj, 1, p, Lengths);
glCompileShader(ShaderObj);
GLint success;
glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success);
if (!success) {
GLchar InfoLog[1024];
glGetShaderInfoLog(ShaderObj, 1024, NULL, InfoLog);
fprintf(stderr, "Error compiling shader type %d: '%s'\n", ShaderType, InfoLog);
exit(1);
}
glAttachShader(ShaderProgram, ShaderObj);
}
static void CompileShaders()
{
GLuint ShaderProgram = glCreateProgram();
if (ShaderProgram == 0) {
fprintf(stderr, "Error creating shader program\n");
exit(1);
}
string vs, fs;
if (!ReadFile(pVSFileName, vs)) {
exit(1);
};
if (!ReadFile(pFSFileName, fs)) {
exit(1);
};
AddShader(ShaderProgram, vs.c_str(), GL_VERTEX_SHADER);
AddShader(ShaderProgram, fs.c_str(), GL_FRAGMENT_SHADER);
GLint Success = 0;
GLchar ErrorLog[1024] = { 0 };
glLinkProgram(ShaderProgram);
glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &Success);
if (Success == 0) {
glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
fprintf(stderr, "Error linking shader program: '%s'\n", ErrorLog);
exit(1);
}
glValidateProgram(ShaderProgram);
glGetProgramiv(ShaderProgram, GL_VALIDATE_STATUS, &Success);
if (!Success) {
glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
fprintf(stderr, "Invalid shader program: '%s'\n", ErrorLog);
exit(1);
}
glUseProgram(ShaderProgram);
gScaleLocation = glGetUniformLocation(ShaderProgram, "gScale");
assert(gScaleLocation != 0xFFFFFFFF);
}
int _tmain(int argc, _TCHAR* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);
glutInitWindowSize(1024, 768);
glutInitWindowPosition(100, 100);
glutCreateWindow("Tutorial 05");
InitializeGlutCallbacks();
// Must be done after glut is initialized!
GLenum res = glewInit();
if (res != GLEW_OK) {
fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
return 1;
}
printf("GL version: %s\n", glGetString(GL_VERSION));
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
CreateVertexBuffer();
CompileShaders();
glutMainLoop();
return 0;
}
代码解读
glutIdleFunc(RenderSceneCB);
这里我们將渲染回调函数RenderSceneCB也注册为空闲回调函数,要注意的是如果你你打算定义一个专门的函数处理空闲回调,需要在后面增加glutPostRedisplay() 函数的调用,这样的话空闲回调函数就会反复的调用而渲染回调函数则不会。
gScaleLocation = glGetUniformLocation(ShaderProgram, "gScale");
assert(gScaleLocation != 0xFFFFFFFF);
在链接后,我们查询Program对象获取uniform变量的位置,这是另外一个c/c++执行环境需要映射到shader执行环境的案例。你不能直接访问shader的内容,也不能直接更新shader的变量。当你编译shader后,GLSL编译器会为每一个uniform变量分配一个索引。shader在编译器内部表示中都通过索引来访问它的变量。这个索引在程序中通过glGetUniformLocation也能够获取,调用函数时需要传入program对象的句柄和变量名称,该函数会返回变量的索引(出错时返回-1)。这里检测错误是非常必要的,否则将来更新变量时不会传递到shader中。这个函数调用失败主要两个原因,你的变量名拼写错误或它被编译器优化掉了,如果GLSL编译器发现该变量在Shader中没有用到就会去除它,这种情况下会导致glGetUniformLocation 调用失败。
static float Scale = 0.0f;
Scale += 0.001f;
glUniform1f(gScaleLocation, sinf(Scale));
这里我们定义一个静态float类型变量Scale ,在每次调用渲染回调函数时把它的值增加0.001,实际上传递给shader的值是Scale 变量的正弦值,这样做很好的创建了一个在-1.0到1.0之间的循环。需要注意的是sinf的参数是要传入一个弧度还是角度,这里我们无需关注,我们只需要产生一个在-1到1的正弦波。sinf函数的返回值通过glUniform1f函数传递到shader中,OpenGl提供的这个函数有多种形式glUniform{1234}{if}。你可以使用它將值加载到1维、2维、3维或4维向量中。第一个参数为之前调用glGetUniformLocation获取的位置索引。
清单2. shader.vs代码
#version 330
layout (location = 0) in vec3 Position;
uniform float gScale;
void main()
{
gl_Position = vec4(gScale * Position.x, gScale * Position.y, Position.z, 1.0);
}
uniform float gScale;
这里我们定义了一个uniform类型变量。
gl_Position = vec4(gScale * Position.x, gScale * Position.y, Position.z, 1.0);
我们用gScale乘上Positon向量X/Y的值,确保vec4函数参数值在每一帧渲染时会改变,然后你就能够解释屏幕中的三角形为什么一会变大一会变小。
编译运行
运行程序你会看到大小不断变化的三角形。
OpenGL编程逐步深入(五)Uniform 变量的更多相关文章
- (Python OpenGL)【4】Uniform变量 PyOpenGL
(Python OpenGL) 原文:http://ogldev.atspace.co.uk/www/tutorial05/tutorial05.html(英文) __author__ = " ...
- OpenGL红宝书例子2.2 uniform变量的使用
1. 简单介绍一下OpenGL可编程渲染管线的流程 顶点着色 --> 细分着色 --> 几何着色 --> 片元着色 --> 计算着色 一般我们主要参与的阶段是顶点着色和片元着色 ...
- OpenGL ES着色器语言之变量和数据类型(一)(官方文档第四章)和varying,uniform,attribute修饰范围
OpenGL ES着色器语言之变量和数据类型(一)(官方文档第四章) 所有变量和函数在使用前必须声明.变量和函数名是标识符. 没有默认类型,所有变量和函数声明必须包含一个声明类型以及可选的修饰符. ...
- 用MFC实现OpenGL编程
一.OpenGL简介 众所周知,OpenGL原先是Silicon Graphics Incorporated(SGI公司)在他们的图形工作站上开发高质量图像的接口.但最近几年它成为一个非常优秀的开放式 ...
- OpenGL学习笔记(五)变换
目录 变换 向量 向量的运算 向量与标量运算 向量取反 向量加减 求向量长度 向量的单位化 向量相乘 点乘(Dot Product) 叉乘 矩阵 矩阵的加减 矩阵的数乘 矩阵相乘 矩阵与向量相乘 与单 ...
- CSharpGL(33)使用uniform块来优化对uniform变量的读写
CSharpGL(33)使用uniform块来优化对uniform变量的读写 +BIT祝威+悄悄在此留下版了个权的信息说: Uniform块 如果shader程序变得比较复杂,那么其中用到的unifo ...
- OpenGL编程指南(第七版)
OpenGL编程指南(第七版) 转自:http://blog.csdn.net/w540982016044/article/details/21287645 在接触OpenGL中,配置显得相当麻烦,特 ...
- C#编程总结(五)多线程带给我们的一些思考
C#编程总结(五)多线程带给我们的一些思考 如有不妥之处,欢迎批评指正. 1.什么时候使用多线程? 这个问题,对于系统架构师.设计者.程序员,都是首先要面对的一个问题. 在什么时候使用多线程技术? 在 ...
- OpenGL ES着色器语言之变量和数据类型(二)(官方文档第四章)
OpenGL ES着色器语言之变量和数据类型(二)(官方文档第四章) 4.5精度和精度修饰符 4.5.1范围和精度 用于存储和展示浮点数.整数变量的范围和精度依赖于数值的源(varying,unifo ...
随机推荐
- 开心的小明(南阳oj49)(01背包)
开心的小明 时间限制:1000 ms | 内存限制:65535 KB 难度:4 描写叙述 小明今天非常开心.家里购置的新房就要领钥匙了,新房里有一间他自己专用的非常宽敞的房间.更让他高兴的是,妈妈 ...
- Activity管理笔记
文章仅记录自己学习该模块时的一点理解,看到哪写到哪.所以特别散. AMS管理四大组件外加进程管理,当中最庞大的算是Activity了吧. 1.AMS中对ActivityStack划分为两类.当中一类是 ...
- js中callback执行
<!DOCTYPE HTML> <html> <head> <meta charset="GBK" /> <title> ...
- nyoj--55--懒省事的小明(STL优先队列)
懒省事的小明 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 小明很想吃果子,正好果园果子熟了.在果园里,小明已经将所有的果子打了下来,而且按果子的不同种类分 ...
- [jzoj 6086] [GDOI2019模拟2019.3.26] 动态半平面交 解题报告 (set+线段树)
题目链接: https://jzoj.net/senior/#main/show/6086 题目: 题解: 一群数字的最小公倍数就是对它们质因数集合中的每个质因数的指数取$max$然后相乘 这样的子树 ...
- [Codeforces 911F] Tree Destruction 解题报告(贪心)
题目链接: http://codeforces.com/contest/911/problem/F 题目大意: 给你一棵树,每次挑选这棵树的两个度数为1的点,加上他们之间的边数(距离),然后将其中一个 ...
- dialog.setCancelable与setCanceledOnTouchOutside的区别
dialog.setCancelable(false); dialog弹出后会点击屏幕或物理返回键,dialog不消失 dialog.setCanceledOnTouchOutside(false); ...
- HttpWebRequest WebExcepton: The remote server returned an error: (407) Proxy Authentication Required.
1. Supply the credentials of the Currently Logged on User to the Proxy object similar to this: // Be ...
- ActiveMQ学习笔记(7)----ActiveMQ支持的传输协议
1. 连接到ActiveMQ Connector: Active提供的,用来实现连接通讯的功能,包括:client-to-broker,broker-to-broker.ActiveMQ允许客户端使用 ...
- python中*号用法总结
python 中有很多地方用到星号,有时候会想知道这个*是干嘛用的,总结如下,有不当之处,还望不吝指出,谢谢.1.乘法: 在很多时候是用作乘法的,例如: In [90]: 2*7 Out[90]: 1 ...