分类
tech

OpenGL 03 Data between shader stages

Description

使用 interface block 在 shader 的不同 stage 之间传递数据

Notes

vertex fetching

在 vertex shader 运行之前的一个 fixed-function stage
它会向 vertex shader 提供输入参数
这些变量就是 vertex attributes

storage qualifiers

在 OpenGL 中,我们使用 inout 关键字,在声明变量时修饰存储类型
shader 中所有用 out 声明的变量,就会被发送到下一个 stage 中用 in 声明的同样变量名的变量中

Interface Bolocks

当需要在 shader stage 间传递的变量很多的时候,我们可以用 interface block 来打组,写法和 c 的 struct 很类似

glVertexAttrib

第 1 个参数 index 用于指定 attribute 的编号
第 2 个参数 v 是属性数据本身
每一次调用该函数,它都会向 OpenGL 更新 vertex attribute 数据

layout qualifier

用于在 vertex shader 中给 in 变量指定 vertex attribute 的编号

  • Application:glVertexAttrib4fv(0, data);
  • Vertex Shader:layout (location = 0 ) in vec4 offset;

这里后者的 location 就是前者的第1个参数 index

Project structure

Codes

makefile

03_data-between-shader-stages.cpp

03_data-between-shader-stages.vert

03_data-between-shader-stages.frag

分类
tech

OpenGL 02 Shader file loading

Description

从外部文件加载 shader,绘制 1 个 triangle

Project structure

Codes

makefile

02_load-simple-shaders.cpp

02_load-simple-shaders.vert

02_load-simple-shaders.frag

分类
tech

OpenGL 01 Basic frame

Description

基本的 OpenGL 程序框架,包括1个 vertex shader 和 1 个 fragment shader,创建背景颜色动画,并绘制 1 个 point

Project structure

Codes

makefile

01_basic-frame.cpp

分类
tech

OpenGL 00 Env Setup

Description

简单搭建 & 测试下环境和 tool chainOpenGL + GLFW + GLEW

Project structure

Codes

makefile

00_env-setup.cpp

分类
tech

关于浮点数(2)half2float

闲来无事(真的吗)分析一下 half2float() 的实现(好吧其实是有人问,然后看到这个函数就觉得挺有意思的)

多翻翻 GLM 果然是一个非常涨姿势的活动。。。

Half 和 float 的规格

首先当然是看定义,half 和 float32 的不同位数的含义 IEEE-754 是这么规定的:

Difference Between Single-, Double-, Multi-, Mixed-Precision | NVIDIA Blog

区别就在于,指数 E 和尾数 M 所使用的位数不同

  • 对于指数 E,half 用 5bit 表示,float 用 8bit
  • 对于尾数 M,half 用 10bit,float 用 23bit

那么转换思路当然就是:分别对符号位 S、指数 E、尾数 M进行位移,并且注意要先给指数 E 进行合适的 offset,最后组合一下即可

0.5 as an example

首先需要用具体的例子代入,热身一下,看看 0.5 用 half 和 float 分别是怎么表示的:

首先 0.5 是十进制的表示,转换成二进制就是 0.1,再用科学计数法表示就是 1.0 * 2^(-1)

用 IEEE 754 标准来看,也就是:

符号位 s = 0,指数位 E = -1,尾数位 M = 0 (因为科学计数法第一位肯定是1,就不用表示了)

指数 E 的 offset

这里还有一点需要注意,由于指数 E 有可能为正或者为负(代表原数绝对值大于1或小于1),所以浮点数的指数表示正负数的位数各占一半,例如 half5bit 表示指数,即一共可以表示 2^5 = 32 的范围(0~31),于是它采用前半部分表示负数([0, 15],即 00000-01111),后部分表示正数([16, 31],即 10000-11111)。

因此,我们计算 half 的指数位 E 的时候,需要给它加上 15 作为 offset,同理,对于 8bit 表示指数的 float,offset 为 127。

所以,对于 0.5,其指数部分 E:

用 half 表示的时候,指数为 E = -1 = -1+15 = 14 = [0 1110]

用 float 表示的时候,指数为 E = -1 = -1+127 = 126 = [0111 1110]

用 half 表示 0.5

用 float 表示 0.5

所以,当我们谈论 half2float 的时候,对 0.5 来说,实际上发生的事情就是:

函数 half2float

half2float 只有一行:

把它拆开来分析一下就很简单了:

step1: 取符号位左移 16 位

((half & 0x8000) << 16)

0x8000 = [1000 0000 0000 0000‬]

这是一个只有最左侧(最高位)为 1 的数,任何数和它做与运算,都只会保留最高位

half & 0x8000 得到的结果,就是最高位(最左侧)的符号位,然后 << 16 就是向左移动 16 bit

(得到的就是 0 ,因为我们的目的就是只保留符号位0)

再把结果向左移动 16 位 (从 16bit 补齐为 32bit)得到

step2: 取指数部分+offset,再左移 13 位

((half & 0x7c00) + 0x1C000)

0x7c00 = [0111 1100 0000 0000]

这是一个只有第 2-6 位为 1 的数,通过和它做与运算,用 0x7c00 就可以把 half 的指数位(从左开始第 2 位到第 6 位 取出来)

取出了 half 的指数部分,再来就是给它加上 127-15 = 112 的 offset

二进制表示的 112 = [0111 0000],由于对于 half 来说,指数位是从第2-6位,也就是右侧有 10 个位数是用于表示尾数的,那么为了给 half 的指数位 + 112,也需要给它右侧补 10 个0,即

[0111 0000] => [01 1100 0000 0000 0000] => 十六进制的 0x1C000

用刚刚的结果,加上 0x1C000,就是

再把结果向左移动 13 位 (补齐为 32bit)即

step3: 取尾数部分左移 13 位

(half & 0x03FF) << 13

0x03ff = [0000 0011 1111 1111]

它标记为 1 的,就是 half 里用来表示尾数 M 的最后 10 bit

再把结果向左移动 13 位 (补齐为 32bit)即

step4: 最终把 3 次结果用或运算组合一下

实现了 half 2 float 的转换

分类
tech

关于浮点数(1)EncodeFloatRGBA & DecodeFloatRGBA

分析一下 EncodeFloatRGBA() & DecodeFloatRGBA() 的实现

翻翻 Unity Built in Shaders 是一个涨姿势的活动。。。

其中,EncodeFloatRGBA 函数将1个32bit的float数据(取值范围0~1之间)转换为float4类型。因此 EncodeFloatRGBA 相当于把一个32bit的float拆成了4份。 DecodeFloatRGBA 函数则将float4还原为float。

十进制浮点数与二进制浮点数

十进制

每位代表10的幂,因此有:

其中,每一位的范围为 0~9, 例如

二进制

每位代表2的幂,因此有:

其中,每一位的范围为 0~1, 例如

Float32 的表示规格

符号位s: 代表此Float的正负,1为负,0为正。

指数位e: 代表此Float的指数范围,8bit可表示的数值范围为0~255,由于指数有正负符号,因此国际标准IEEE 754规定,指数需要减去127,由此将0~255映射到了-127~128的范围内。

底数位m: 代表此Float的底数,也就是Float内真正存储的数值,由于科学记数法的底数形式,得出Float小数点前的第一位总是为1(在二进制中),因此省去最高位的1不存储,例如1.00101、1.11010等等会被保存为00101、11010,因此23-bit的底数实际可以表示24-bit位的数据。

1234.5678 as an example

将十进制数 1234.5678 用 float32 来表示:

将十进制浮点数1234.5678 转换为二进制浮点数为10011010010.1001000101011

用科学计数法表示,挪动小数点得到最高位的1的右侧,并记录挪动位数

10011010010.1001000101011 = 1.00110100101001000101011 * 210

由此,可以计算出符号位s,指数位e,底数m

符号位s = 0,(正数s=0)

指数位e = 10001001,(挪动了小数点10次,10 + 127 = 137,再转换为二进制)

底数位m = 00110100101001000101011,(省略最前面的1)

最终计算出的32-bit Float 为

0100 0100 1001 1010 0101 0010 0010 1011

EncodeFloatRGBA 分析

EncodeFloatRGBA函数输入参数为float v, 返回参数为float4 enc,其中的内部数据变化可以图解如下:

下面以 float v = 0.123456789 为例,追踪这个函数内部的数据变化

首先将v = 0.123456789转换为二进制,则v = 0.000111111001101011011101010

根据代码

这个点乘的意义,从二进制的角度来看是移位,256 = 28 , 65536 = 216 , 16777216 = 224

从十进制的角度来理解,12.34 * 101 = 123.4,就是将小数点右移一位,指数代表移动位数

对二进制同样,1101.1011 * 22 = 110110.11,小数点右移两位,指数为负则左移小数点

因此,对v* float4(20, 28, 216, 224),意味着将v的小数点分别右移0、8、16、24位

结果为:

下一步代码

enc = frac (enc);

frac() 函数代表舍去整数部分,只保留分数

下一步代码

与前面同理,1/256 = 2-8, 意味着将二进制数的小数点左移8位

再用 enc - tmp 使得各分量后相同位数被消除(保留前8位)

最后一位的 enc.a 因为也减去了 tmp.w 因此会产生一些误差,tmp.w非常小可以忽略不计

最后,对比v和enc的值

可以看出,得到了与最初图示相同的结果。注意enc.a 虽然有误差,但 0.00111111110 与 0.010 差值很小,可以忽略不计。

DecodeFloatRGBA 分析

DecodeFloatRGBA函数与EncodeFloatRGBA作用相反,它的输入参数为float4 enc, 返回参数为float v,其中的内部数据变化依旧可以图解如下:

根据代码,

kDecodeDot = float4(1.0, 1/256.0, 1/65536.0, 1/16777216.0)
  = float4(20,  2-8 , 2-16 , 2-24)

因此将它与 enc 点乘的结果,就是将enc的rgba分量的小数点分别左移相应的位数再求和

举例:

这样就还原了 float v 的数值

分类
tech

Articles & Studys

Float

OpenGL

分类
tech

《Real Time Rendering 3rd》笔记

分类
tech

欧拉滤波在摄影机运动控制系统轨迹规划中的应用研究

(硕士期间发表的一篇关于欧拉角、旋转矩阵的应用研究论文)

论文介绍

知网链接 PDF全文

摘要:

摄影机运动控制系统在电影特效制作中具有重要作用,摄影机运动控制系统能够精确地在拍摄现场根据需求完成轨迹,灵活运用可以实现丰富的视觉特效。

本文介绍了在为摄影机运动控制系统进行摄影机轨迹规划时,从旋转矩阵转换得到的用于表示摄影机姿态的欧拉角数值不连续的问题及其产生原因,并对如何使用欧拉滤波解决这一问题进行了详述。


关键词:

欧拉滤波; 摄影机; 运动控制系统; 轨迹规划;

分类
tech

运动控制系统 Milo 应用研究

(Maya 插件开发)

项目介绍

运动控制系统(Motion Control)

运动控制系统是电影特效拍摄中使用的一种大型设备,由于它可以实现在像素级别对摄影机的运动轨迹的精确控制,例如,使用相同的摄影机运动轨迹动画重复拍摄多条视频,最终合成得到的画面可以直接进行合成。

实拍效果测试

使用运动控制系统,可以实现相当复杂具有创意的实拍画面。例如在下面这条测试样片中,以不同的摄影机运动轨迹,对同一位男生拍摄两次,但两段拍摄画面的后半部分摄影机运动轨迹相同,合成后即可得到这位同学在与自己对话的效果。

Maya 插件开发

运动控制系统的可编程控制,使得在三维软件中设计复杂的镜头运动轨迹并交由运动控制系统实现成为了目前电影特效制作的主要手段之一。

为了更方便的在三维软件中设计并预览镜头画面效果,在 Maya 中开发了相应插件:

通过对 Milo 设备个轴向运动的绑定,实现了反向 IK 的效果,由此可以通过导入的动画轨迹反向解算为 Milo 设备 8 个自由度的运动数据,从而导出到设备的控制系统,驱动设备运动,进行拍摄。