OBS Studio libobs 图形数学扩展:角度换算、平滑扭矩与随机浮点的 math-extra 模块实战解析
本篇围绕 OBS Studio 仓库中的图形数学扩展接口 math-extra.h 展开,对应 Sphinx 参考文档 reference-libobs-graphics-math.rst。你将学到 RAD/DEG 角度换算宏与各级 epsilon 常量的精确定义、rand_float 随机浮点生成函数,以及源码中比文档更完整的极坐标/笛卡尔换算、扭矩平滑(torque)函数族——并了解它们在场景切换过渡动画(transition)等真实场景中的调用方式。
模块定位:为什么需要 math-extra
OBS 的渲染核心 libobs 内置了一套自研的图形数学库,向量、矩阵、四元数等分别位于 vec2.c、vec3.c、matrix4.c、quat.c 等文件中。而一些“无法归入某一具体类型”的通用数学函数被集中放在了 math-extra 模块中。math-extra.h 头部注释明确说明了这一设计意图:
/*
* A few general math functions that I couldn't really decide where to put.
*
* Polar/Cart conversion, torque functions (for smooth movement), percentage,
* random floats.
*/
即:极坐标/笛卡尔换算、用于平滑运动的扭矩函数、百分比计算、随机浮点。使用方式按 Sphinx 文档 reference-libobs-graphics-math.rst 所述,直接包含头文件即可:
#include <graphics/math-extra.h>
需要区分的是两个相关文件:
- math-defs.h:定义角度换算宏、epsilon 常量族与浮点比较辅助函数,是纯头文件;
- math-extra.h / math-extra.c:声明并实现可执行函数,
math-extra.c内部#include "math-defs.h"依赖前者。
角度换算宏 RAD 与 DEG
参考文档列出的两个核心宏定义在 math-defs.h:
| 宏 | 定义 | 语义 |
|---|---|---|
RAD(val) |
(val) * 0.0174532925199432957692369076848f |
将浮点数角度值转换为弧度 |
DEG(val) |
(val) * 57.295779513082320876798154814105f |
将浮点数弧度值转换为角度 |
两个换算系数分别是 π/180 和 180/π 的高精度十进制展开。注意宏参数带括号包裹,可以安全地传入复杂表达式,例如 RAD(-90.0f + angle_offset)。此外,math-defs.h 还为缺少 M_PI 定义的 C 标准库环境补充了 M_PI 常量:
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795f
#endif
配合 RAD/DEG,编写 OBS 图形代码(例如设置旋转矩阵、极坐标转换)时可以直接以角度为直观单位参与计算,再统一换算为弧度喂给 sinf/cosf 等三角函数。
epsilon 常量族与 M_INFINITE
文档列出的四个常量同样来自 math-defs.h,完整取值如下:
| 常量 | 取值 | 含义 |
|---|---|---|
LARGE_EPSILON |
1e-2f |
较大的误差容限,用于距离接近性判断 |
EPSILON |
1e-4f |
常规浮点相等比较容限 |
TINY_EPSILON |
1e-5f |
更严格的微小容限 |
M_INFINITE |
3.4e38f |
近似 float 最大值的“无穷大”哨兵值 |
这些容限并不是摆设——同一头文件里还定义了浮点近似比较的内联函数,它依赖上述精度参数工作:
static inline bool close_float(float f1, float f2, float precision)
{
return fabsf(f1 - f2) <= precision;
}
后续 calc_torquef、cart_to_polar 等函数内部都通过 close_float + EPSILON 判断两个值是否“足够接近”,避免直接对 float 做 == 比较带来的经典浮点陷阱。
极坐标与笛卡尔坐标换算
math-extra.h 声明了四组坐标/法线转换函数,实现在 math-extra.c:
struct vec2;
struct vec3;
EXPORT void polar_to_cart(struct vec3 *dst, const struct vec3 *v);
EXPORT void cart_to_polar(struct vec3 *dst, const struct vec3 *v);
EXPORT void norm_to_polar(struct vec2 *dst, const struct vec3 *norm);
EXPORT void polar_to_norm(struct vec3 *dst, const struct vec2 *polar);
polar_to_cart / cart_to_polar
以 struct vec3(见 vec3.h)承载:极坐标输入约定 x 为俯仰角、y 为方位角、z 为长度;输出为标准笛卡尔坐标。核心实现:
void polar_to_cart(struct vec3 *dst, const struct vec3 *v)
{
struct vec3 cart;
float sinx = cosf(v->x);
float sinx_z = v->z * sinx;
cart.x = sinx_z * sinf(v->y);
cart.z = sinx_z * cosf(v->y);
cart.y = v->z * sinf(v->x);
vec3_copy(dst, &cart);
}
逆过程 cart_to_polar 先求长度 vec3_len 作为 z,再用 asinf、atan2f 反解两个角度;当长度接近 0 时通过 close_float(polar.z, 0.0f, EPSILON) 判定并直接归零,避免 asin(y/r) 在 r 趋于 0 时产生数值噪声:
void cart_to_polar(struct vec3 *dst, const struct vec3 *v)
{
struct vec3 polar;
polar.z = vec3_len(v);
if (close_float(polar.z, 0.0f, EPSILON)) {
vec3_zero(&polar);
} else {
polar.x = asinf(v->y / polar.z);
polar.y = atan2f(v->x, v->z);
}
vec3_copy(dst, &polar);
}
norm_to_polar / polar_to_norm
这一组处理单位法线向量与二维极坐标(方位角 + 俯仰角)之间的转换,实现更简洁,因为法线长度已被假设为 1:
void norm_to_polar(struct vec2 *dst, const struct vec3 *norm)
{
dst->x = atan2f(norm->x, norm->z);
dst->y = asinf(norm->y);
}
void polar_to_norm(struct vec3 *dst, const struct vec2 *polar)
{
float sinx = sinf(polar->x);
dst->x = sinx * cosf(polar->y);
dst->y = sinx * sinf(polar->y);
dst->z = cosf(polar->x);
}
从源码结构看,这类接口服务于光源方向、观察方向等需要以角度参数驱动的场景,是 OBS 效果着色器(effect)背后 C 层参数准备的常用工具。
扭矩函数族:实现平滑运动的 calc_torque
“torque functions (for smooth movement)” 是该模块的特色内容。它实现的是基于目标距离的缓动逼近:当前值每帧朝目标值移动一段“扭矩距离”,且速度随剩余距离缩放,同时用最小调整量防止末尾过慢、用钳制防止过冲。
标量版 calc_torquef
EXPORT float calc_torquef(float val1, float val2, float torque, float min_adjust, float t);
参数含义:val1 当前值、val2 目标值、torque 扭矩系数、min_adjust 最小调整量(防止过慢)、t 本帧时间步长。math-extra.c 中的实现逻辑清晰:
float calc_torquef(float val1, float val2, float torque, float min_adjust, float t)
{
float out = val1;
float dist;
bool over;
if (close_float(val1, val2, EPSILON))
return val2;
dist = (val2 - val1) * torque;
over = dist > 0.0f;
if (over) {
if (dist < min_adjust) /* prevents from going too slow */
dist = min_adjust;
out += dist * t; /* add torque */
if (out > val2) /* clamp if overshoot */
out = val2;
} else {
if (dist > -min_adjust)
dist = -min_adjust;
out += dist * t;
if (out < val2)
out = val2;
}
return out;
}
可以看到它正是文档中 EPSILON 容限的典型应用:起点即终点时直接返回目标值,避免无意义的逐帧抖动。
向量版 calc_torque
EXPORT void calc_torque(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2,
float torque, float min_adjust, float t);
math-extra.c 中先计算两点的方向向量 dir 与原始距离 orig_dist,把 orig_dist * torque 作为本帧应移动的距离(距离越远移动越快),再用 LARGE_EPSILON 判断本帧位移是否已覆盖全部剩余距离,若是则直接钳制到目标点 v2。这就是文档所列 LARGE_EPSILON 的典型用途场景。
真实调用点:场景切换的 MANUAL 过渡
calc_torquef 在 OBS 中有一个直接的生产级用例——场景切换(transition)的手动控制模式。obs-source-transition.c 中每帧 tick 的逻辑为:
if (transition->transition_mode == OBS_TRANSITION_MODE_MANUAL) {
if (transition->transition_manual_torque == 0.0f) {
transition->transition_manual_val = transition->transition_manual_target;
} else {
transition->transition_manual_val = calc_torquef(transition->transition_manual_val,
transition->transition_manual_target,
transition->transition_manual_torque,
transition->transition_manual_clamp, t);
}
}
即当切换源处于手动模式(OBS_TRANSITION_MODE_MANUAL)时,切换进度值 transition_manual_val 不会瞬间跳变到目标值,而是按 torque(扭矩系数)与 clamp(最小调整量)平滑逼近——这正是“torque functions (for smooth movement)” 注释所指的功能。
百分比计算:get_percentage 内联函数
头文件内还内联提供了两个百分比计算函数,用于求 mid 在区间 [start, end] 中的相对位置(归一化到 0..1):
static inline float get_percentage(float start, float end, float mid)
{
return (mid - start) / (end - start);
}
static inline float get_percentagei(int start, int end, int mid)
{
return (float)(mid - start) / (float)(end - start);
}
整数版先做整数减法再统一转 float,保证区间计算精度。这两个函数被 vec2.c 与 vec3.c 等向量实现内部使用,属于向量/边界插值计算的基础工具。
随机浮点:rand_float
参考文档明确收录的函数是 rand_float:
.. function:: float rand_float(int positive_only)
Generates a random floating point value (from -1.0f..1.0f, or
0.0f..1.0f if *positive_only* is set).
即生成随机浮点数:positive_only 为真时返回 0.0f..1.0f,否则返回 -1.0f..1.0f。math-extra.c 的实现基于 rand()/RAND_MAX 并做了 double 中间换算,以避免整数除法截断:
float rand_float(int positive_only)
{
if (positive_only)
return (float)((double)rand() / (double)RAND_MAX);
else
return (float)(((double)rand() / (double)RAND_MAX * 2.0) - 1.0);
}
仓库中可查到的一个使用示例是自动配置向导的测试场景 TestMode.hpp:
vec4_set(&r, rand_float(true) * 100.0f, rand_float(true) * 100.0f,
rand_float(true) * 50000.0f + 10000.0f, 0.0f);
它利用 rand_float(true) 的 0..1 输出构造测试用随机向量。
构建与引用路径
从 libobs/CMakeLists.txt 可以看到 graphics/math-extra.h 被同时列入 libobs 的源文件与公共头文件安装列表,因此下游模块(包括 OBS 前端 Qt 工程)可以按 #include <graphics/math-extra.h> 的方式引用——这也是参考文档中示例代码的写法。仓库内实际引用方包括:
- libobs/obs-source-transition.c(transition 平滑进度,
calc_torquef) - libobs/obs-output.c
- frontend/wizards/TestMode.hpp(
rand_float) - frontend/wizards/AutoConfigTestPage.cpp
- 向量库自身:libobs/graphics/vec2.c、libobs/graphics/vec3.c(
get_percentage系列)
小结
math-extra 模块虽小,却是 libobs 图形数学体系中“胶水层”的角色:
- math-defs.h 提供
RAD/DEG角度换算宏、LARGE_EPSILON/EPSILON/TINY_EPSILON/M_INFINITE常量族及close_float比较工具; - math-extra.h 提供极坐标/笛卡尔/法线三组坐标转换、标量与向量两级
calc_torque平滑函数、百分比内联函数与rand_float随机浮点生成; - 这些工具在场景切换过渡(
obs_transition_tick)、自动配置测试等真实代码路径中被直接调用,是理解 OBS 底层动画与图形参数处理的入手点之一。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00