首页
/ OBS Studio 渲染图形子系统:Effect 文件语法与视频渲染管线实战指南

OBS Studio 渲染图形子系统:Effect 文件语法与视频渲染管线实战指南

2026-09-04 09:46:10作者:明树来

本文基于 OBS Studio 官方文档 Rendering Graphics 展开,系统讲解 libobs 自研图形子系统的设计理念、graphics context 线程模型、effect 文件(uniform/sampler/语义/technique)的完整语法,以及视频源与滤镜的渲染流程。读完本文,你将能够独立编写 libobs effect 文件、在 video_render 回调中正确使用 effect 参数并实现自定义视频滤镜,并可对照仓库内 default.effectcolor-key-filter.c 等真实源码验证每个知识点。

图形子系统的设计初衷:为什么要"自研"

libobs 拥有一个自研的可编程图形子系统,统一封装了 Direct3D 11 与 OpenGL 两种后端。文档原文明确给出了设计动机:

Libobs has a custom-made programmable graphics subsystem that wraps both Direct3D 11 and OpenGL. The reason why it was designed with a custom graphics subsystem was to accommodate custom capture features only available on specific operating systems. (libobs 拥有一个自研的可编程图形子系统,封装了 Direct3D 11 和 OpenGL。之所以设计自研图形子系统,是为了适配仅在特定操作系统上可用的自定义采集功能。)

作者还附了一条坦诚的备注:回顾来看本可以基于 ANGLE 之类的方案,但为了满足特定用例仍选择了自研。

从当前仓库的源码结构看,这套子系统已经演化出三个后端实现:

文档主体只提到 D3D 11 与 OpenGL,仓库中 Metal 后端属于文档之外的后续扩展,读者按文档语义理解 D3D/OpenGL 双后端即可。而文档所说的"effect"正是这个子系统的核心概念:libobs 中所有视频对象(source)的渲染都依赖 effect。effect 的作用是把相关联的 vertex/pixel shader 打包进同一个文件,便于共享函数与参数、快速切换 shader。effect 的核心数据结构定义在 libobs/graphics/effect.h 中,包括 gs_effect(持有 params 参数数组与 techniques 数组)、gs_effect_technique(持有若干 pass)与 gs_effect_pass(持有顶点/像素 shader 及其参数映射):

// libobs/graphics/effect.h
struct gs_effect_technique {
    char *name;
    enum effect_section section;
    struct gs_effect *effect;
    DARRAY(struct gs_effect_pass) passes;
};

struct gs_effect {
    bool processing;
    bool cached;
    char *effect_path, *effect_dir;
    gs_effect_param_array_t params;                 // 所有 uniform 参数
    DARRAY(struct gs_effect_technique) techniques; // 所有 technique
    struct gs_effect_technique *cur_technique;
    struct gs_effect_pass *cur_pass;
    gs_eparam_t *view_proj, *world, *scale;
    graphics_t *graphics;
    ...
};

从源码注释可以印证文档的说法:"Effects are built via the effect parser, and shaders are automatically generated for each technique's pass"(effect 由 effect parser 构建,每个 technique 的 pass 会自动生成对应的 shader),对应的解析器位于 libobs/graphics/effect-parser.c

Graphics Context:进入图形上下文的规则

使用任何图形函数之前,当前线程必须已进入 graphics context;而 graphics context 同一时刻只能被一个线程使用。文档给出了明确的进入/离开方式:

  • 进入:obs_enter_graphics()
  • 离开:obs_leave_graphics()

这两个函数的实现在 libobs/obs.c 中非常简短,本质是对 graphics_t 上下文的进入/离开转发:

void obs_enter_graphics(void)
{
        if (obs->video.graphics)
                gs_enter_context(obs->video.graphics);
}

void obs_leave_graphics(void)
{
        if (obs->video.graphics)
                gs_leave_context();
}

函数声明位于 libobs/obs.h。值得注意的是源码中的判空保护:视频尚未初始化(obs->video.graphics 为空)时调用是安全的空操作。

文档同时列出了自动处于 graphics context 中的回调,在这些回调里无需手动 enter/leave:

  1. obs_source_info.video_render —— 每个 source 的渲染回调(同步视频源即在此绘制);
  2. obs_display_add_draw_callback() 的绘制回调参数;
  3. obs_add_main_render_callback() 注册的主渲染回调。

这条规则的实际意义是:绝大多数插件开发者的绘图代码写在 video_render 里,天然满足上下文约束;只有当你需要在非渲染线程(例如自己的 worker 线程)中操作纹理时才需要显式 obs_enter_graphics() / obs_leave_graphics(),并注意同一时间只能有一个线程持有该上下文。

Effect 文件语法:与 HLSL Effect 的三个差异

effect 文件的语法与 Direct3D 11 HLSL effect 文件几乎完全一致,只有以下三处差异:

差异点 libobs effect 写法 D3D 11 HLSL 写法
采样器状态关键字 sampler_state sampler
位置语义 POSITION SV_Position
目标语义 TARGET SV_Target

(文档附注:作者自述可能遗漏了个别的差异项。)

创建 Effect:参数(Uniform)

文档建议编写 effect 时先确定 uniform(参数)。支持的 uniform 类型如下表:

类别 类型
浮点数 floatfloat2float3float4
矩阵 float3x3float4x4
整数 intint2int3int4
布尔 bool
纹理 texture2dtexture_cube

获取参数的两个 API:

  • gs_effect_get_param_by_name() —— 按名字获取;
  • gs_effect_get_param_by_idx() —— 按索引获取。

设置参数的函数族:

  • gs_effect_set_bool()
  • gs_effect_set_float()
  • gs_effect_set_int()
  • gs_effect_set_matrix4()
  • gs_effect_set_vec2()
  • gs_effect_set_vec3()
  • gs_effect_set_vec4()
  • gs_effect_set_texture()
  • gs_effect_set_texture_srgb()

两个"通用"参数:ViewProj 与 image

文档特别指出,effect 可能会被期望具备两个通用参数

  • ViewProjfloat4x4):主视图/投影矩阵的乘积,顶点着色器用它把模型空间坐标变换到裁剪空间;
  • imagetexture2d):主纹理。obs_source_draw()gs_draw_sprite()gs_draw_quadf() 以及 obs_source_process_filter_end() 等函数都约定使用该参数名传入"当前要绘制/处理的纹理"。

effect 文件中声明参数的示例:

uniform float4x4 ViewProj;
uniform texture2d image;

uniform float4 my_color_param;
uniform float my_float_param;

Uniform 默认值

参数可以带默认值;多元素类型(向量、矩阵)的默认值按数组方式书写。文档给出的示例:

uniform float4x4 my_matrix = {1.0, 0.0, 0.0, 0.0,
                              0.0, 1.0, 0.0, 0.0,
                              0.0, 0.0, 1.0, 0.0,
                              0.0, 0.0, 0.0, 1.0};

uniform float4 my_float4 = {1.0, 0.5, 0.25, 0.0};
uniform float my_float = 4.0;
uniform int my_int = 5;

在源码层面,每个 uniform 由 struct gs_effect_param 承载(见 libobs/graphics/effect.h),其中的 cur_val(当前值)与 default_val(默认值)正是"可覆盖的默认参数"机制的落地位置,changed 标志位用于标记该参数是否被运行时修改过。

Effect 采样器状态(sampler_state)

如果 effect 使用了纹理,就应该定义采样器状态。采样器状态包含以下子参数:

Filter —— 过滤方式,可选值:

  • Anisotropy
  • Point
  • Linear
  • MIN_MAG_POINT_MIP_LINEAR
  • MIN_POINT_MAG_LINEAR_MIP_POINT
  • MIN_POINT_MAG_MIP_LINEAR
  • MIN_LINEAR_MAG_MIP_POINT
  • MIN_LINEAR_MAG_POINT_MIP_LINEAR
  • MIN_MAG_LINEAR_MIP_POINT

AddressU / AddressV —— 纹理坐标越出 0.0..1.0 区间时的采样行为,可选值:

  • WrapRepeat
  • ClampNone
  • Mirror
  • Border(使用 BorderColor 填充颜色)
  • MirrorOnce

BorderColor —— 使用 Border 寻址模式时的边界颜色,写成 AARRGGBB 格式的十六进制数。例如 7FFF0000 表示 alpha 为 127、红色 255、绿色与蓝色为 0。若未使用 Border 寻址,此值被忽略。

文档给出的采样器状态写法示例:

sampler_state defaultSampler {
        Filter      = Linear;
        AddressU    = Border;
        AddressV    = Border;
        BorderColor = 7FFF0000;
};

该采样器使用线性过滤,对越界的纹理坐标采用边界寻址,边界颜色为上述半透明红色。

仓库中最标准的样例是 libobs/data/default.effect,所有基础渲染都基于它:

sampler_state def_sampler {
	Filter   = Linear;
	AddressU = Clamp;
	AddressV = Clamp;
};

在像素着色器中使用采样器

采样器状态的使用方式与 HLSL 形式一致:声明 texture2dsampler_state 后,在像素着色器中调用 Sample。文档示例:

uniform texture2d image;

sampler_state defaultSampler {
        Filter      = Linear;
        AddressU    = Clamp;
        AddressV    = Clamp;
};

float4 MyPixelShaderFunc(VertInOut vert_in) : TARGET
{
        return image.Sample(def_sampler, vert_in.uv);
}

顶点/像素语义(Semantics)

需要为输入/输出定义顶点语义结构。顶点分量可用的语义:

  • COLOR —— 颜色值(float4);
  • POSITION —— 位置值(float4);
  • NORMAL —— 法线值(float4);
  • TANGENT —— 切线值(float4);
  • TEXCOORD[0..7] —— 纹理坐标值(float2float3float4)。

顶点语义结构示例:

struct VertexIn {
        float4 my_position : POSITION;
        float2 my_texcoord : TEXCOORD0;
};

规则要点(文档原文归纳):

  1. 该语义结构作为顶点 shader 入口函数的参数,并作为顶点 shader 的返回值类型
  2. 顶点 shader 允许返回与输入不同的语义集合,但顶点 shader 的返回类型必须与像素 shader 的参数类型匹配
  3. 顶点 shader 函数参数中出现的语义,决定了顶点缓冲区必须提供对应内容——例如声明了 POSITION 与 TEXCOORD0,则顶点缓冲至少要包含位置缓冲和纹理坐标缓冲;
  4. 像素 shader 必须以 TARGET 语义返回一个 float4(RGBA)。

Technique 与 Pass

Technique 用于按 pass 定义每对顶点/像素 shader 入口函数;一个 technique 可以有多个 pass 或自定义 pass 设置。文档中作者给出了非常实用的建议:

如今多 pass 已经没什么必要,GPU 足够强大,可以在同一个 shader 中完成全部操作。命名 pass 对自定义绘制布局或许有些用处,但你也可以直接把它写成独立的 technique。因此建议直接忽略多余 pass 功能

一个重要的命名约定:如果你在为视频源编写 effect 滤镜,通常把 pass 命名为 Draw——因为 obs_source_process_filter_end() 会自动调用名为 Draw 的 technique;如需指定其他 technique,可使用 obs_source_process_filter_tech_end() 按名字显式选择。

此外,pass 中顶点/像素 shader 函数的第一个参数必须始终是其顶点语义结构的名称。文档给出的完整 technique 示例(可直接照抄作为骨架):

uniform float4x4 ViewProj;
uniform texture2d image;

struct VertInOut {
        float4 pos : POSITION;
        float2 uv : TEXCOORD0;
};

VertInOut MyVertexShaderFunc(VertInOut vert_in)
{
        VertInOut vert_out;
        vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);
        vert_out.uv  = vert_in.uv;
        return vert_out;
}

float4 MyPixelShaderFunc(VertInOut vert_in) : TARGET
{
        return image.Sample(def_sampler, vert_in.uv);
}

technique Draw
{
        pass
        {
                vertex_shader = MyVertexShaderFunc(vert_in);
                pixel_shader  = MyPixelShaderFunc(vert_in);
        }
};

对照仓库中的 default.effect,可以看到真实项目里一个 effect 文件可以同时定义多个像素着色器(PSDrawBarePSDrawAlphaDividePSDrawAlphaDivideTonemapPSDrawAlphaDivideR10L 等),再分别挂到不同 technique 上,分别服务"裸绘制""alpha 分离""HDR tone map""10bit PQ 编码"等不同渲染路径。

使用 Effect:gs_effect_loop

文档推荐的 effect 使用方式:

for (gs_effect_loop(effect, "technique")) {
        [draw calls go here]
}

该循环会自动处理给定 technique 名下 effect 及其 shader 的加载/卸载。从 libobs/graphics/effect.hgs_effect 结构可以看到支撑这一机制的字段:cur_techniquecur_passloop_passlooping 正是 gs_effect_loop 迭代 pass 状态机的内部状态。

渲染视频源(Video Sources)

文档对同步视频源的渲染路径给出明确描述:

  • 同步视频源在自己的 obs_source_info.video_render 回调中渲染;
  • source 可以选择自定义绘制OBS_SOURCE_CUSTOM_DRAW 输出能力标志),也可以不用;
  • 不使用自定义绘制时,推荐用 obs_source_draw() 绘制单张纹理
  • 使用自定义绘制时,source 需自行完成渲染并自行管理 effect。

obs_source_draw() 的原型见 libobs/obs.h

EXPORT void obs_source_draw(gs_texture_t *image, int x, int y,
                            uint32_t cx, uint32_t cy, bool flip);

此外,libobs 自带一组默认/标准 effect,可通过 obs_get_base_effect() 获取;既可以直接用这些 effect 渲染,也可以用 gs_effect_create_from_file() 创建自定义 effect 再渲染。

当前仓库中 obs_get_base_effect() 支持的枚举完整列于 libobs/obs.h

enum obs_base_effect {
        OBS_EFFECT_DEFAULT,             /**< RGB/YUV */
        OBS_EFFECT_DEFAULT_RECT,        /**< RGB/YUV (using texture_rect) */
        OBS_EFFECT_OPAQUE,              /**< RGB/YUV (alpha set to 1.0) */
        OBS_EFFECT_SOLID,               /**< RGB/YUV (solid color only) */
        OBS_EFFECT_BICUBIC,             /**< Bicubic downscale */
        OBS_EFFECT_LANCZOS,             /**< Lanczos downscale */
        OBS_EFFECT_BILINEAR_LOWRES,     /**< Bilinear low resolution downscale */
        OBS_EFFECT_PREMULTIPLIED_ALPHA, /**< Premultiplied alpha */
        OBS_EFFECT_REPEAT,              /**< RGB/YUV (repeating) */
        OBS_EFFECT_AREA,                /**< Area rescale */
};

EXPORT gs_effect_t *obs_get_base_effect(enum obs_base_effect effect);

实现位于 libobs/obs.cobs_get_base_effect()。这些基础 effect 对应的源文件就放在 libobs/data/ 目录下,共 21 个 .effect 文件(如 default.effectdefault_rect.effectbicubic_scale.effectlanczos_scale.effectsolid.effectdeinterlace_*.effect 等),与上述枚举一一对应,读者可逐一打开对照效果实现。

渲染视频效果滤镜(Video Effect Filters)

文档指出:大多数视频效果滤镜,就是在 video_render 回调中为已有图像叠加一层处理 shader。此时的标准流程是:

  1. 滤镜拥有自己的 effect(用 gs_effect_create_from_file() 创建);
  2. 调用 obs_source_process_filter_begin() 开始处理;
  3. 在自己的 effect 上设置参数(gs_effect_set_* 系列);
  4. 调用 obs_source_process_filter_end()(或 obs_source_process_filter_tech_end() 指定 technique)完成渲染。

文档以色度键(color key)滤镜为例给出了完整渲染函数:

static void color_key_render(void *data, gs_effect_t *effect)
{
        struct color_key_filter_data *filter = data;

        if (!obs_source_process_filter_begin(filter->context, GS_RGBA,
                                OBS_ALLOW_DIRECT_RENDERING))
                return;

        gs_effect_set_vec4(filter->color_param, &filter->color);
        gs_effect_set_float(filter->contrast_param, filter->contrast);
        gs_effect_set_float(filter->brightness_param, filter->brightness);
        gs_effect_set_float(filter->gamma_param, filter->gamma);
        gs_effect_set_vec4(filter->key_color_param, &filter->key_color);
        gs_effect_set_float(filter->similarity_param, filter->similarity);
        gs_effect_set_float(filter->smoothness_param, filter->smoothness);

        obs_source_process_filter_end(filter->context, filter->effect, 0, 0);

        UNUSED_PARAMETER(effect);
}

这段示例并非纸上谈兵——它几乎逐行对应仓库中的真实实现。plugins/obs-filters/color-key-filter.c 中的 color_key_render_v1()

static void color_key_render_v1(void *data, gs_effect_t *effect)
{
        struct color_key_filter_data *filter = data;

        if (!obs_source_process_filter_begin(filter->context, GS_RGBA, OBS_ALLOW_DIRECT_RENDERING))
                return;

        gs_effect_set_vec4(filter->color_param, &filter->color);
        gs_effect_set_float(filter->contrast_param, filter->contrast);
        gs_effect_set_float(filter->brightness_param, filter->brightness);
        gs_effect_set_float(filter->gamma_param, filter->gamma);
        gs_effect_set_vec4(filter->key_color_param, &filter->key_color);
        gs_effect_set_float(filter->similarity_param, filter->similarity);
        gs_effect_set_float(filter->smoothness_param, filter->smoothness);

        obs_source_process_filter_end(filter->context, filter->effect, 0, 0);

        UNUSED_PARAMETER(effect);
}

从该文件的源码结构看,v1 版本通过 obs_module_file("color_key_filter.effect") 定位 effect 文件创建滤镜;仓库中还实现了 color_key_render_v2(),它会先用 obs_source_get_color_space()GS_CS_SRGB / GS_CS_SRGB_16F / GS_CS_709_EXTENDED 之间按优先级选取源色彩空间,再据此选择不同的处理路径——这是在"begin → set params → end"基础流程之上的色彩空间增强用法,供需要处理宽色域素材的滤镜参考。

obs_source_process_filter_begin() / obs_source_process_filter_end() 的实现位于 libobs/obs-source.c,其中 obs_source_process_filter_begin 默认以 GS_CS_SRGB 色彩空间委托给 obs_source_process_filter_begin_with_color_space();而 obs_source_process_filter_end() 在滤镜没有自定义 effect 时,会回退到 obs_get_base_effect(OBS_EFFECT_DEFAULT) 完成透传绘制(见 libobs/obs-source.c),这也解释了为什么"滤镜不处理时图像依然能正常走管线"。

小结:一张对照表串起整条管线

阶段 关键 API / 概念 仓库参考位置
线程进入图形上下文 obs_enter_graphics() / obs_leave_graphics() libobs/obs.c
自动处于上下文的回调 video_render、display 绘制回调、主渲染回调 docs/sphinx/graphics.rst
编写 effect uniform / sampler_state / 语义 / technique(D3D11 语法三处差异) libobs/data/default.effect
参数读写 gs_effect_get_param_by_name/idx()gs_effect_set_*() libobs/graphics/effect.h
使用 effect gs_effect_loop(effect, "technique") libobs/graphics/effect.h
源绘制 obs_source_draw()OBS_SOURCE_CUSTOM_DRAWobs_get_base_effect() libobs/obs.h
滤镜流程 obs_source_process_filter_begin/end()(或 _tech_end() 指定 technique) plugins/obs-filters/color-key-filter.clibobs/obs-source.c

掌握以上内容后,编写 OBS 视频滤镜的完整路径就是:创建 effect 文件(参数 → 采样器 → 语义结构 → 命名 Draw 的 technique)→ 在 create 回调中用 gs_effect_create_from_file() 载入并用 gs_effect_get_param_by_name() 取参 → 在 video_render 回调中按 begin → set params → end 三步完成每帧处理。文中所有示例均可在对应源码路径下直接验证,适用前提是基于当前仓库版本的 libobs API 开发插件。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
docsdocs
暂无描述
Markdown
889
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341