CSharpGL(33)使用uniform块来优化对uniform变量的读写

+BIT祝威+悄悄在此留下版了个权的信息说:

Uniform块

如果shader程序变得比较复杂,那么其中用到的uniform变量数量也会上升。通常会在多个shader程序中用到同一个uniform变量。而uniform buffer object就是一种优化uniform变量访问,以及在不同的shader程序间共享uniform数据的方法。

写法

首先了解一下uniform块的写法。

 uniform b { // ‘b’ 对应于外部访问时的名称
vec4 v1;// 块中的变量列表
bool v2;// …
}; // 访问成员时使用v1、v2

或者

 uniform b { // ‘b’ 对应于外部访问时的名称
vec4 v1;// 块中的变量列表
bool v2;// …
} name; // 访问成员时使用name.v1、name.v2

注意,shader程序中的数据类型有两种:不透明的和透明的;其中不透明的包括sampler、image和atomic counter。一个uniform块中只能包含透明类型的变量。

另外,在同一个shader程序里的两个uniform块,里面的变量名都不能相同。

下面我们以具体例子的编写过程来说明在如何使用uniform块,顺便了解一下CSharpGL是如何简化对uniform块的使用的。

+BIT祝威+悄悄在此留下版了个权的信息说:

Shader

我认为用Modern OpenGL渲染,首先要写shader。我们先看一个简单的vertex shader。

 #version  core

 uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix; in vec3 vPos;
in vec3 vColor;
out vec3 fColor; void main(void) { gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(vPos, 1.0); fColor = vColor;
}

我们就把这里面的uniform变量换作块,如下,只是把原来的uniform变量包了起来,并命名为“Uniforms”。

 #version  core

 uniform Uniforms {
mat4 projectionMatrix;
mat4 viewMatrix;
mat4 modelMatrix;
}; in vec3 vPos;
in vec3 vColor;
out vec3 fColor; void main(void) { gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(vPos, 1.0); fColor = vColor;
}

而fragment shader则更简单:

 #version  core

 in vec3 fColor;

 out vec4 out_Color;

 void main(void) {
out_Color = vec4(fColor, 1.0f);
}
+BIT祝威+悄悄在此留下版了个权的信息说:

准备工作

传送一个自定义的struct

Uniform块,实际上对应一个在应用程序客户端的struct类型。对于示例中的‘Uniforms’块,我们可以定义如下的结构体。为了方便对照,我们也用‘Uniforms’作为 struct 名,其实你可以用任意你喜欢的名字。

     struct Uniforms : IEquatable<Uniforms>
{
public mat4 projection;
public mat4 view;
public mat4 model; public Uniforms(mat4 projection, mat4 view, mat4 model)
{
this.projection = projection;
this.view = view;
this.model = model;
} public bool Equals(Uniforms other)
{
return this.projection == other.projection
&& this.view == other.view
&& this.model == other.model;
}
}

今后我们就将数据准备好后保存到一个 Uniforms 对象,最终传送到shader。

uniform块结构

这里是重点了。传送float类型的uniform变量,我们有 UniformFloat ;传送vec3类型的uniform变量,我们有 UniformVec3 。但是uniform块传送的是一个个可以任意自定义的不同的结构体(例如上面的struct Uniforms),因此最好用一个泛型的 UniformBlock<T> 。

     public class UniformBlock<T> : UniformSingleVariableBase where T : struct, IEquatable<T>
{
protected T value; public T Value
{
get { return this.value; }
set
{
if (!value.Equals(this.value))
{
this.value = value;
this.Updated = true;
}
}
} public UniformBlock(string blockName) : base(blockName) { } public UniformBlock(string blockName, T value) : base(blockName) { this.Value = value; } protected override void DoSetUniform(ShaderProgram program)
{
// ...
}
}

UniformBlock<T> 更新uniform块的操作比较复杂:它要创建一个uniform buffer object,并与之绑定;以后它只需更新这个buffer里的数据,就可以实现对uniform块的更新。

        protected override void DoSetUniform(ShaderProgram program)
{
if (uniformBufferPtr == null)
{
uniformBufferPtr = Initialize(program);
}
else
{
IntPtr pointer = uniformBufferPtr.MapBuffer(MapBufferAccess.WriteOnly, bind: true);
unsafe
{
var array = (byte*)pointer.ToPointer();
byte[] bytes = this.value.ToBytes();
for (int i = ; i < bytes.Length; i++)
{
array[i] = bytes[i];
}
}
uniformBufferPtr.UnmapBuffer(unbind: true);
} this.Updated = false;
} /// <summary>
/// Initialize and setup uniform block's value.
/// </summary>
/// <param name="program"></param>
/// <returns></returns>
private UniformBufferPtr Initialize(ShaderProgram program)
{
uint uboIndex = glGetUniformBlockIndex(program.ProgramId, this.VarName);
var uboSize = new uint[];
glGetActiveUniformBlockiv(program.ProgramId, uboIndex, OpenGL.GL_UNIFORM_BLOCK_DATA_SIZE, uboSize);
UniformBufferPtr result = null;
using (var buffer = new UniformBuffer<byte>(BufferUsage.StaticDraw, noDataCopyed: false))
{
byte[] bytes = this.value.ToBytes();
buffer.Create(bytes.Length);
unsafe
{
var array = (byte*)buffer.Header.ToPointer();
for (int i = ; i < bytes.Length; i++)
{
array[i] = bytes[i];
}
} result = buffer.GetBufferPtr() as UniformBufferPtr;
} // 将此uniform块与此uniform buffer object绑定。
glBindBufferBase(OpenGL.GL_UNIFORM_BUFFER, uboIndex, result.BufferId); return result;
} private UniformBufferPtr uniformBufferPtr = null;

DoSetUniform

SetUniform()

对于普通的uniform变量,CSharpGL用 Renderer.SetUniform(string varName, T value) where T : struct 即可(无论是什么类型的uniform都可以处理)。对于Uniform块,也可以用这个方法!

+BIT祝威+悄悄在此留下版了个权的信息说:

UniformBlockRenderer

有了上述准备,我们就可以使用uniform块了。

创建渲染器

按照CSharpGL的传统,下面来创建一个UniformBlockRenderer,负责加载shader、模型数据和渲染工作。

     class UniformBlockRenderer : Renderer
{
public static UniformBlockRenderer Create()
{
var model = new Teapot();// model
var shaderCodes = new ShaderCode[];// shaders
shaderCodes[] = new ShaderCode(File.ReadAllText(@"shaders \UniformBlock.vert"), ShaderType.VertexShader);
shaderCodes[] = new ShaderCode(File.ReadAllText(@"shaders \UniformBlock.frag"), ShaderType.FragmentShader);
var map = new AttributeNameMap();// mapping relation between model and shaders
map.Add("vPos", Teapot.strPosition);
map.Add("vColor", Teapot.strColor);
var renderer = new UniformBlockRenderer(model, shaderCodes, map);// renderer return renderer;
}
}

设置uniform块的值

就像普通的uniform变量一样,我们也在 Renderer.DoRender() 方法里更新uniform块。

         protected override void DoRender(RenderEventArgs arg)
{
mat4 projection = arg.Camera.GetProjectionMatrix();
mat4 view = arg.Camera.GetViewMatrix();
mat4 model = this.GetModelMatrix();
// 设置uniform块,只需这一行。
this.SetUniform("Uniforms", new Uniforms(projection, view, model)); base.DoRender(arg);
}

完成的效果如图,能够正常渲染,说明我们成功地更新了uniform块里的数据。

+BIT祝威+悄悄在此留下版了个权的信息说:

下载

CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL

+BIT祝威+悄悄在此留下版了个权的信息说:

总结

借助C#的struct与byte[]的相互转换,加上CSharpGL对Modern Rendering的封装,实际上我们不需要调用 glGetUniformIndices 、 glGetActiveUniformsiv (用于获取shader中uniform块里的各个变量的偏移量)这些接口,就可以使用uniform块了。

当shader中写了一个uniform块时,你只需在应用程序客户端也写一个对应的 struct ,然后用 Renderer.SetUniform(blockName, structObj); 一行即可实现对uniform块数据的更新。

PS:测试过程中发现对于vec3结果正常,但是vec4却有诡异的情况,后来想到应用程序客户端里的vec4与shader里的vec4的xyzw布局不一样,可能因此导致原本属于w的数据最终传送到了shader里的y上。于是在调整了CSharpGL里的vec4的字段顺序后就一切正常了。为了避免以后我或者其他人在忘记\不知情的情况下擅自改动了vec4的字段顺序,我用 [FieldOffset(...)] 特性标注了vec4的各个字段。这真是一个很深的bug。多亏我对C#的struct布局也是有所了解。

CSharpGL(33)使用uniform块来优化对uniform变量的读写的更多相关文章

  1. OpenGL ES 中Uniform块

    1.采用uniform Block的原因如果你的程序中包含了多个着色器,而且这些着色器使用了相同的Uniform变量,你就不得不为每个着色器分别管理这些变量.Uniform变量的location是在程 ...

  2. (转载)一张表搞清楚西门子S7系列标准DB块与优化DB块

    在TIA Portal中为S7-1200/S7-1500 CPU 添加一个 DB 块时,其缺省属性为优化的 DB ,优化的 DB 块与标准的 DB 块整体对比如下表所示: 项 标准 DB 优化 DB ...

  3. 高级OPENGL, 利用uniform块接口

    1.找到需要的uniform块的索引, 将程序对象的该uniform块索引绑定uniform 缓冲对象的绑定点 2.建立uniform缓冲对象,对象绑定GL_UNIFORM_BUFFER缓冲目标,为缓 ...

  4. java静态代码块、静态方法、静态变量、构造代码块、普通代码块

    一.静态代码块 1.在java类中(方法中不能存在静态代码块)使用static关键字和{}声明的代码块: public class CodeBlock{ static{ System.out.prin ...

  5. 用dfs遍历联通块(优化)

    一.题目(CF 598D) 输入一个n x m的字符矩阵,求从某个空点出发,能碰到多少面墙壁,总共询问k次.(3 ≤m,n ≤1000,1 ≤ k ≤ min(nm,100 000)) 二.解题思路 ...

  6. SQL优化 | Oracle 绑定变量

    之前整理过一篇有关绑定变量的文章,不太详细,重新补充一下. Oracle 绑定变量 http://www.cndba.cn/Dave/article/1275 一.绑定变量 bind variable ...

  7. JavaScript函数表达式、闭包、模仿块级作用域、私有变量

    函数表达式是一种非常有用的技术,使用函数表达式可以无需对函数命名,从而实现动态编程.匿名函数,是一种强大的方式,一下总结了函数表达式的特点: 1.函数表达式不同于函数声明,函数声明要求有名字,但函数表 ...

  8. ES6块级作用域及新变量声明(let)

    很多语言中都有块级作用域,但JS没有,它使用var声明变量,以function来划分作用域,大括号“{}” 却限定不了var的作用域.用var声明的变量具有变量提升(declaration hoist ...

  9. Spark性能优化(2)——广播变量、本地缓存目录、RDD操作、数据倾斜

    广播变量 背景 一般Task大小超过10K时(Spark官方建议是20K),需要考虑使用广播变量进行优化.大表小表Join,小表使用广播的方式,减少Join操作. 参考:Spark广播变量与累加器 L ...

随机推荐

  1. .NET Core全新路线图

    .NET Core / ASP.NET Core 1 RTM发布两周后,社区也很积极,收到了非常多的反馈,上周五微软的scott Hunter 在dotnet团队官方博客上发布了.NET Core全新 ...

  2. [PHP内核探索]PHP中的哈希表

    在PHP内核中,其中一个很重要的数据结构就是HashTable.我们常用的数组,在内核中就是用HashTable来实现.那么,PHP的HashTable是怎么实现的呢?最近在看HashTable的数据 ...

  3. JavaScript Array对象

    介绍Js的Array 数组对象. 目录 1. 介绍:介绍 Array 数组对象的说明.定义方式以及属性. 2. 实例方法:介绍 Array 对象的实例方法:concat.every.filter.fo ...

  4. ASP.NET MVC with Entity Framework and CSS一书翻译系列文章之目录导航

    ASP.NET MVC with Entity Framework and CSS是2016年出版的一本比较新的.关于ASP.NET MVC.EF以及CSS技术的图书,我将尝试着翻译本书以供日后查阅. ...

  5. Android 7.1 - App Shortcuts

    Android 7.1 - App Shortcuts 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Shortcuts 文中如有纰漏,欢迎大家留言 ...

  6. 简约之美Jodd-http--深入源码理解http协议

    Jodd 是一个开源的 Java 工具集, 包含一些实用的工具类和小型框架.简单,却很强大! jodd-http是一个轻巧的HTTP客户端.现在我们以一个简单的示例从源码层看看是如何实现的? Http ...

  7. ASP.NET Core 中文文档 第四章 MVC(4.6)Areas(区域)

    原文:Areas 作者:Dhananjay Kumar 和 Rick Anderson 翻译:耿晓亮(Blue) 校对:许登洋(Seay) Areas 是 ASP.NET MVC 用来将相关功能组织成 ...

  8. python 数据类型 -- 元组

    元组其实是一种只读列表, 不能增,改, 只可以查询 对于不可变的信息将使用元组:例如数据连接配置 元组的两个方法: index, count >>> r = (1,1,2,3) &g ...

  9. BPM配置故事之案例4-子表

    公司渐渐对采购管理重视起来了,新招聘了采购主管老李,老李对现有的申请表很不满意,要求将申请物资和申请原因改成物资明细表 物资明细表 小明只好继续致电大毛-- 大毛:把申请物资和申请原因删掉,新增一个数 ...

  10. maven 快照

    大型应用软件一般由多个模块组成,一般它是多个团队开发同一个应用程序的不同模块,这是比较常见的场景.例如,一个团队正在对应用程序的应用程序,用户界面项目(app-ui.jar:1.0) 的前端进行开发, ...