The Wayback Machine - https://web.archive.org/web/20211213021449/https://github.com/obsproject/obs-studio/pull/5448
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement non-default blending modes #5448

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

@jw0z96
Copy link

@jw0z96 jw0z96 commented Oct 24, 2021

Description

Implements non-default blending modes for sources, allowing for compositing functionality similar to Photoshop/Nuke etc.
image
The implemented blending modes are:

  • Normal
  • Additive
  • Subtractive
  • Screen
  • Multiply
  • Lighten
  • Darken

Screenshots of each mode's behaviour are available here.

Blending modes have been implemented to leverage the existing blend state functionality, so changes to allow the blending equation's operation type to be modified have been added. Each blending mode is a preset combination of the blending equation's source/destination factors and operation, so the number of modes implemented is somewhat limited. Non-default blending modes require scene items to be rendered to a texture (using the same render pipeline for non-default scale filtering modes), but are otherwise 'free' from a performance point of view.

Motivation and Context

  • Allows for compositing techniques where chroma/luma keying does not provide the desired effect.
  • This has been requested here and here

How Has This Been Tested?

I've only tested these changes on macOS, cherry-picked onto this branch to allow me to build on my device. As such, the d3d11 changes are untested.

Blending mode settings have been tested in scene loading/saving.
Tested with image sources, media sources, window capture sources, and display capture sources.

Types of changes

  • New feature (non-breaking change which adds functionality) -->
  • Documentation (a change to documentation pages)

Checklist:

  • My code has been run through clang-format.
  • I have read the contributing document.
  • My code is not on the master branch.
  • The code has been tested.
  • All commit messages are properly formatted and commits squashed where appropriate.
  • I have included updates to all appropriate documentation.
@jw0z96 jw0z96 force-pushed the source-blending branch from ba439d4 to 234de51 Oct 24, 2021
@WizardCM
Copy link
Collaborator

@WizardCM WizardCM commented Oct 24, 2021

Thank you for putting in the effort to create this PR!

I expect this will require extensive testing on Windows and Linux as they have sources that behave differently from macOS.

Downloads:

@WizardCM
Copy link
Collaborator

@WizardCM WizardCM commented Oct 24, 2021

After some basic testing on Windows, this works mostly as expected. There are some visual differences.

Left: OBS, Right: Photoshop CS6. Windows 10.

Layers: Subtract (Window Capture), Normal (Browser Source), Normal (Color Source, white)
image

Layers: Lighten (Browser Source), Normal (Window Capture), Normal (Color Source, white)
image

I've also verified that the Screenshot (source) function does not include any strange blending artifacts, which is awesome to see.

@jw0z96
Copy link
Author

@jw0z96 jw0z96 commented Oct 25, 2021

Thanks for testing @WizardCM ! I'm glad to see it's working on windows/d3d11.

And yes, I had noticed some visual differences when comparing the results of the blending modes I've added, to those in Krita (Photoshop alternative) and Natron (Nuke alternative). I think the visual differences were down to either:

  1. An additional viewer colour-space transform happening in the other apps, that isn't happening in OBS (most likely a result of my colour management settings being incorrect in either app).
  2. The named blending modes not being equivalent between apps. For example, I think "Linear Dodge" in photoshop is implemented as a component-wise addition of colours, so would be equivalent to "Additive" in the blending modes I've added. For Nuke/Natron this is just named "Plus" I believe. The "Subtractive" mode should just be a component-wise subtraction of colours (The source's output colour subtracted from the backgound's current colour), so I'm not sure why it looks different from Photoshop in your testing...

Instead of matching the blending mode names/behaviour to other apps, I aimed to match the results numerically to those described here, according to which ones I was able to implement using the programmable blending pipeline for both OpenGL and d3d11. I think the best way to ensure that the modes I've implemented are numerically correct, is to devise some kind of "test image" (similar to the rainbow + rubber duck image from the above docs, and my original forum post), and to compare the results of blending on the test image in OBS to results calculated mathematically (I'll try to think of something).

@rehatkathuria
Copy link

@rehatkathuria rehatkathuria commented Oct 29, 2021

Chiming in to say thank you for this work!

@jw0z96
Copy link
Author

@jw0z96 jw0z96 commented Oct 30, 2021

OK, I've devised two very non-scientific images to use for testing these blending modes across different platforms.
Foreground test image:
test_fg
Background test image:
test_bg
Test scene in OBS:
image

...and here are the visual results of each blending mode on macOS:
Additive:
OBS_additive
Subtractive:
OBS_subtractive
Screen:
OBS_screen
Multiply:
OBS_multiply
Darken:
OBS_darken
Lighten:
OBS_lighten

If any users on Windows/Linux can post the results of the same scene setup, that'd be much appreciated!

Comparing these to blending modes in Krita, I get the same visual result for each blending mode in areas of solid colour. In areas of gradient, there is a slight difference in the mathematical modes (add, subtract, screen, multiply). From some experimentation in Natron, I've concluded that this is because Krita (like most image editors) performs all of it's colour/blending operations in linear colour space, converting to sRGB only for it's render view and file export. For OBS, it looks like the test images are loaded in as sRGB, so we'll perform the blending operations in sRGB space. I'm no colour scientist, so this may be incorrect - it's just my conclusion from experimenting with various combinations of Natron's viewer and file input colour space options.

If it's critical that these blending modes replicate the behaviour of other software exactly, I'll consider re-writing this PR to use a shader based approach. That would allow for an 'un-sRGB' step prior to blending, and potentially more blending modes that couldn't otherwise be expressed using programmable blend states. I think this approach may be slightly less performant, as it would require an extra texture copy & draw call for each source using non-default blending, but I'm not sure how much of a concern that is in practise. I'd imagine that the current approach in this PR is sufficient for most use cases, considering that leveraging the programmable blending pipeline makes this effectively free, from a performance standpoint.

@awwbees
Copy link

@awwbees awwbees commented Nov 12, 2021

thank you for this work! I was just looking for this feature, and it's cool that you've started work on this so recently.

on Windows, the Additive, Subtractive, Darken, and Lighten modes work, but when I use Screen or Multiplicative, the image becomes invisible. here are the Windows screenshots you requested for your test:

Additive:
Screenshot 2021-11-12 15-41-07

Subtractive:
Screenshot 2021-11-12 15-41-24

Screen:
Screenshot 2021-11-12 15-41-33

Multiply:
Screenshot 2021-11-12 15-41-37

Lighten:
Screenshot 2021-11-12 15-41-41

Darken:
Screenshot 2021-11-12 15-41-45

As you can see, "Screen" and "Multiply" show only the background image. if I drag the foreground image around, it just appears as a fully transparent box.

poking around in the code, it seems like the issue seems to be with any blend modes that use COLOR (GS_BLEND_DSTCOLOR, GS_BLEND_INVSRCCOLOR, etc). I will poke at this a bit more, but it's a bit outside my expertise, so I don't promise that I will make progress. but perhaps this information is enough for you to identify the problem!

@awwbees
Copy link

@awwbees awwbees commented Nov 12, 2021

oh! once the issues are resolved, I also might suggest adding "Soft Additive" and "2x Multiply". I believe these would be defined as:

//Soft Additive
{
   GS_BLEND_INVDSTCOLOR,
   GS_BLEND_ONE,
   GS_BLEND_OP_ADD,
}

and

//2x Multiply
{
   GS_BLEND_DSTCOLOR,
   GS_BLEND_SRCCOLOR,
   GS_BLEND_OP_ADD,
}

(these are adapted from the "Common blend types" section on this page: https://docs.unity3d.com/Manual/SL-Blend.html)

@jw0z96
Copy link
Author

@jw0z96 jw0z96 commented Nov 13, 2021

Hi @awwbees, thanks for testing this! I'm not too sure why the Screen and Multiply blending modes are not working on Windows - all of the blending modes use the same functions for setting up the blending state, so there's very little that should go wrong between the different blending modes, but I'll try and reproduce it next time I'm able to access a Windows machine.

Would you be able to post a log file from your OBS session? I presume the problem is limited to the d3d11 implementation, but I believe OBS can run on either d3d11 or OpenGL under Windows(?), so it'd be good to check. It may also be worth testing these modes with different images, or different source types.

Here's some screenshots of the two additional blending modes you suggested, Soft Additive:
OBS_soft_additive
and Multiply 2x:
OBS_multiply_2x

Soft additive seems to produce the same result as my implementation of Screen, though I believe they should be different (Screen is implemented as src + (dst/src), Soft Additive is implemented as (src/dst) + dst). Multiply 2x looks good, but results in black in areas of transparency on image sources, because presumably OBS is using premultiplied alpha:
OBS_multiply_2x_alpha

I was able to workaround this in the regular Multiply mode by setting the destination blend factor to 1-srcAlpha, though it's worth noting that this issue is also present in the Darken blending mode (as I've noted here, but have forgotten to mention in this thread 😄). I don't believe this is a huge issue as it only effects image sources with transparency, which could be recomposited onto a white background as a workaround.

I'll leave it up to someone else to decide whether we should add these two additional modes - I've intentionally left out a few modes that could be implemented (divide, inverse-divide, reverse-subtract) as I didn't want to introduce too many new options, and some modes have limited practical use.

@awwbees
Copy link

@awwbees awwbees commented Nov 13, 2021

Would you be able to post a log file from your OBS session?

sure thing! I'm new to the world of OBS dev... is there a doc on how to do this? or maybe you just want me to post stdout?

@jw0z96
Copy link
Author

@jw0z96 jw0z96 commented Nov 13, 2021

@awwbees I'm not sure how to access it on windows, but it should be available from the toolbar/menubar:

image

@awwbees
Copy link

@awwbees awwbees commented Nov 14, 2021

aha! yes, the log certainly looks relevant:
2021-11-13 22-43-57.txt
(note these lines at the end: 22:44:42.591: device_draw (D3D11): Failed to create blend state (80070057))

and I switched over to opengl and thing appear to function correctly, save for some weirdness at the edges in Multiply:
Screenshot 2021-11-13 22-59-37

I tried to build in Debug mode to see if I could learn more about the "Failed to create blend state" errors, but I hit some linker errors, looks like I'd need to futz with my config to get Debug working. but, in my case, switching over to opengl appears to be a sufficient solution to get what I want.

@awwbees
Copy link

@awwbees awwbees commented Nov 14, 2021

although... "Screen" and "Additive" appear to look identical in all scenarios I've seen, so maybe something is still not right.

@jw0z96
Copy link
Author

@jw0z96 jw0z96 commented Nov 14, 2021

aha! yes, the log certainly looks relevant: 2021-11-13 22-43-57.txt (note these lines at the end: 22:44:42.591: device_draw (D3D11): Failed to create blend state (80070057))

Ah, that's useful - If it's hitting the codepath I expect it to, then error code 0x80070057 suggests invalid arguments are being passed to CreateBlendState. Fortunately there aren't too many parameters to this function, so it'll be a case of checking whether what's being set in libobs-d3d11/d3d11-subsystem.cpp:gs_device::AddBlendState makes sense - I'll try and get OBS building on a windows machine and take a look.

Thanks again for your help!

@jw0z96
Copy link
Author

@jw0z96 jw0z96 commented Nov 14, 2021

Ok, I think I've got a lead on what the issue is (which thankfully won't require me to set up an OBS build on windows to test):

This github issue hits a suspiciously similar problem, and thankfully the solution is explained in the comments - we're setting an invalid blend factor for the srcAlpha and dstAlpha as according to section 17.2 of the D3D11 spec. The reason why this issue isn't hit on OpenGL, is because we're setting the colour and alpha blend factors simultaneously using glBlendFunc rather than glBlendFuncSeparate, which seems to set a sensible alpha factor for blend factors using SRC_COLOR or DST_COLOR.

I should be able to solve this by specifying the colour and alpha blend factors seperately in libobs/obs-scene.c:obs_blend_mode_params, which thankfully I'll be able to test on OpenGL 😄 .

@jw0z96 jw0z96 force-pushed the source-blending branch from 234de51 to 289e659 Nov 14, 2021
@awwbees
Copy link

@awwbees awwbees commented Nov 15, 2021

awesome, yeah, that appears to work! here is D3D11 on Windows. OpenGL looked the same.

Additive:
Screenshot 2021-11-14 23-24-01

Subtract:
Screenshot 2021-11-14 23-24-05

Screen:
Screenshot 2021-11-14 23-24-09

Multiply:
Screenshot 2021-11-14 23-24-13

Lighten:
Screenshot 2021-11-14 23-24-16

Darken:
Screenshot 2021-11-14 23-24-20

@jw0z96
Copy link
Author

@jw0z96 jw0z96 commented Nov 15, 2021

Thanks again for testing @awwbees - I'll mark the testing task as complete now that we know that this feature functions identically between OpenGL and d3d11 backends.

Latest binaries:
linux64
macOS
win32
win64

@awwbees
Copy link

@awwbees awwbees commented Nov 15, 2021

one very minor bug I noticed: duplicating a layer doesn't duplicate its blend mode settings, it just defaults to "Normal". I'm not sure if this is indicative of other serialization issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment