123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- Rendering Graphics
- ==================
- 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.
- *(Author's note: In retrospect, I probably should have used something
- like ANGLE, but I would have to modify it to accommodate my specific
- use-cases.)*
- Most rendering is dependent upon effects. Effects are used by all video
- objects in libobs; they're used to easily bundle related vertex/pixel
- shaders in to one file.
- An effect file has a nearly identical syntax to Direct3D 11 HLSL effect
- files. The only differences are as follows:
- - Sampler states are named "sampler_state"
- - Position semantic is called "POSITION" rather than "SV_Position"
- - Target semantic is called "TARGET" rather than "SV_Target"
- *(Author's note: I'm probably missing a few exceptions here, if I am
- please let me know)*
- The Graphics Context
- --------------------
- Using graphics functions isn't possible unless the current thread has
- entered a graphics context, and the graphics context can only be used by
- one thread at a time. To enter the graphics context, use
- :c:func:`obs_enter_graphics()`, and to leave the graphics context, use
- :c:func:`obs_leave_graphics()`.
- Certain callback will automatically be within the graphics context:
- :c:member:`obs_source_info.video_render`, and the draw callback
- parameter of :c:func:`obs_display_add_draw_callback()`, and
- :c:func:`obs_add_main_render_callback()`.
- Creating Effects
- ----------------
- Effect Parameters
- ^^^^^^^^^^^^^^^^^
- To create an effect, it's recommended to start with the uniforms
- (parameters) of the effect.
- There are a number of different types of uniforms:
- +------------------+---------------+------------------+------------+------------+
- | Floating points: | **float** | **float2** | **float3** | **float4** |
- +------------------+---------------+------------------+------------+------------+
- | Matrices: | **float3x3** | **float4x4** | | |
- +------------------+---------------+------------------+------------+------------+
- | Integers: | **int** | **int2** | **int3** | **int4** |
- +------------------+---------------+------------------+------------+------------+
- | Booleans: | **bool** | | | |
- +------------------+---------------+------------------+------------+------------+
- | Textures: | **texture2d** | **texture_cube** | | |
- +------------------+---------------+------------------+------------+------------+
- To get the effect uniform parameters, you use
- :c:func:`gs_effect_get_param_by_name()` or
- :c:func:`gs_effect_get_param_by_idx()`.
- Then the uniforms are set through the following functions:
- - :c:func:`gs_effect_set_bool()`
- - :c:func:`gs_effect_set_float()`
- - :c:func:`gs_effect_set_int()`
- - :c:func:`gs_effect_set_matrix4()`
- - :c:func:`gs_effect_set_vec2()`
- - :c:func:`gs_effect_set_vec3()`
- - :c:func:`gs_effect_set_vec4()`
- - :c:func:`gs_effect_set_texture()`
- - :c:func:`gs_effect_set_texture_srgb()`
- There are two "universal" effect parameters that may be expected of
- effects: **ViewProj**, and **image**. The **ViewProj** parameter
- (which is a float4x4) is used for the primary view/projection matrix
- combination. The **image** parameter (which is a texture2d) is a
- commonly used parameter for the main texture; this parameter will be
- used with the functions :c:func:`obs_source_draw()`,
- :c:func:`gs_draw_sprite()`, and
- :c:func:`obs_source_process_filter_end()`.
- Here is an example of effect parameters:
- .. code:: cpp
- uniform float4x4 ViewProj;
- uniform texture2d image;
- uniform float4 my_color_param;
- uniform float my_float_param;
- Effect parameters can also have default values. Default parameters of
- elements that have multiple elements should be treated as an array.
- Here are some examples of default parameters:
- .. code:: cpp
- 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;
- Effect Sampler States
- ^^^^^^^^^^^^^^^^^^^^^
- Then, if textures are used, sampler states should be defined. Sampler
- states have certain sub-parameters:
- - **Filter** - The type of filtering to use. Can be one of the
- following values:
- - **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** - Specifies how to handle the sampling
- when the coordinate goes beyond 0.0..1.0. Can be one of the following
- values:
- - **Wrap** or **Repeat**
- - **Clamp** or **None**
- - **Mirror**
- - **Border** (uses *BorderColor* to fill the color)
- - **MirrorOnce**
- - **BorderColor** - Specifies the border color if using the "Border"
- address mode. This value should be a hexadecimal value representing
- the color, in the format of: AARRGGBB. For example, 7FFF0000 would
- have its alpha value at 127, its red value at 255, and blue and green
- at 0. If *Border* is not used as an addressing type, this value is
- ignored.
- Here is an example of writing a sampler state in an effect file:
- .. code:: cpp
- sampler_state defaultSampler {
- Filter = Linear;
- AddressU = Border;
- AddressV = Border;
- BorderColor = 7FFF0000;
- };
- This sampler state would use linear filtering, would use border
- addressing for texture coordinate values beyond 0.0..1.0, and the border
- color would be the color specified above.
- When a sampler state is used, it's used identically to the HLSL form:
- .. code:: cpp
- [...]
- 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);
- }
- Effect Vertex/Pixel Semantics
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Then structures should be defined for inputs and outputs vertex
- semantics.
- Vertex components can have the following semantics:
- - **COLOR** - Color value (*float4*).
- - **POSITION** - Position value (*float4*).
- - **NORMAL** - Normal value (*float4*).
- - **TANGENT** - Tangent value (*float4*).
- - **TEXCOORD[0..7]** - Texture cooordinate value (*float2*, *float3*, or
- *float4*).
- Here is an example of a vertex semantic structure:
- .. code:: cpp
- struct VertexIn {
- float4 my_position : POSITION;
- float2 my_texcoord : TEXCOORD0;
- };
- These semantic structures are then passed in as a parameter to the
- primary shader entry point, and used as a return value for the vertex
- shader. Note that the vertex shader is allowed to return different
- semantics than it takes in; but the return type of the vertex shader and
- the parameter of the pixel shader must match.
- The semantic structure used for the parameter to the vertex shader
- function will require that the vertex buffer have those values, so if
- you have POSITION and TEXCOORD0, the vertex buffer will have to have at
- least a position buffer and a texture coordinate buffer in it.
- For pixel shaders, they need to return with a TARGET semantic (which is
- a float4 RGBA value). Here is an example of how it's usually used with
- a pixel shader function:
- .. code:: cpp
- float4 MyPixelShaderFunc(VertInOut vert_in) : TARGET
- {
- return image.Sample(def_sampler, vert_in.uv);
- }
- Effect Techniques
- ^^^^^^^^^^^^^^^^^
- Techniques are used to define the primary vertex/pixel shader entry
- functions per pass. One technique can have multiple passes or custom
- pass setup.
- *(Author's note: These days, multiple passes aren't really needed; GPUs
- are powerful enough to where you can perform all actions in the same
- shader. Named passes can be useful for custom draw setups, but even
- then you can just make it a separate technique. For that reason, it's
- best to just ignore the extra pass functionality.)*
- If you're making an effect filter for video sources, typically you'd
- name the pass **Draw**, and then
- :c:func:`obs_source_process_filter_end()` will automatically call that
- specific effect name. However, you can also use
- :c:func:`obs_source_process_filter_tech_end()` to make the filter use a
- specific technique by its name.
- The first parameter of the vertex/pixel shader functions in passes
- should always be the name of its vertex semantic structure parameter.
- For techniques, it's better to show some examples of how techniques
- would be used:
- .. code:: cpp
- uniform float4x4 ViewProj;
- uniform texture2d image;
- struct VertInOut {
- float4 my_position : POSITION;
- float2 my_texcoord : 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);
- }
- };
- Using Effects
- -------------
- The recommended way to use effects is like so:
- .. code:: cpp
- for (gs_effect_loop(effect, "technique")) {
- [draw calls go here]
- }
- This will automatically handle loading/unloading of the effect and its
- shaders for a given technique name.
- Rendering Video Sources
- -----------------------
- A synchronous video source renders in its
- :c:member:`obs_source_info.video_render` callback.
- Sources can render with custom drawing (via the OBS_SOURCE_CUSTOM_DRAW
- output capability flag), or without. When sources render without custom
- rendering, it's recommended to render a single texture with
- :c:func:`obs_source_draw()`. Otherwise the source is expected to
- perform rendering on its own and manage its own effects.
- Libobs comes with a set of default/standard effects that can be accessed
- via the :c:func:`obs_get_base_effect()` function. You can use these
- effects to render, or you can create custom effects with
- :c:func:`gs_effect_create_from_file()` and render with a custom effect.
- Rendering Video Effect Filters
- ------------------------------
- For most video effect filters, it comprises of adding a layer of
- processing shaders to an existing image in its
- :c:member:`obs_source_info.video_render` callback. When this is the
- case, it's expected that the filter has its own effect created, and to
- draw the effect, one would simply use the
- :c:func:`obs_source_process_filter_begin()` function, set the parameters
- on your custom effect, then call either
- :c:func:`obs_source_process_filter_end()` or
- :c:func:`obs_source_process_filter_tech_end()` to finish rendering the
- filter.
- Here's an example of rendering a filter from the color key filter:
- .. code:: cpp
- 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);
- }
|