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

TSL: Add CDLNode #29510

Merged
merged 7 commits into from
Oct 3, 2024
Merged

TSL: Add CDLNode #29510

merged 7 commits into from
Oct 3, 2024

Conversation

donmccurdy
Copy link
Collaborator

@donmccurdy donmccurdy commented Sep 27, 2024

Implements ASC Color Decision List (CDL) v1.2.

I'm not ready to do an integration with the TSL tone mapping nodes quite yet, but here's an early comparison, configuring the CDL to emulate the AgX “Punchy” look, with higher saturation and contrast:

ACES Filmic Reinhard AgX Base AgX Punchy
coffeemat_aces coffeemat_reinhard coffeemat_base coffeemat_punchy

The goal is to provide configurability for our current hard-coded tone mappers.

Related:

/cc @WestLangley @sunag

Copy link

github-actions bot commented Sep 27, 2024

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 688.13
170.5
688.13
170.5
+0 B
+0 B
WebGPU 807.25
217.46
807.63
217.59
+380 B
+135 B
WebGPU Nodes 806.76
217.32
807.14
217.46
+380 B
+136 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 462.85
111.78
462.85
111.78
+0 B
+0 B
WebGPU 536.06
144.73
536.06
144.73
+0 B
+0 B
WebGPU Nodes 492.14
134.46
492.14
134.46
+0 B
+0 B

const luma = inputNode.rgb.dot( vec3( luminanceCoefficients ) );

// clamp( ( in * slope ) + offset ) ^ power
const output = max( inputNode.rgb.mul( slopeNode ).add( offsetNode ), 0.0 ).pow( powerNode ).toVar();
Copy link
Collaborator

@WestLangley WestLangley Sep 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed offline, this should be implemented in log space, and then converted back to linear.

See https://www.mdpi.com/2313-433X/3/4/40 where is says:

This conversion (to log space) is necessary because the CDL Equations (1) and (2) encode meaningful color-correction metadata only if operating on CVs (code values) of a “log” color-space.

This can be corrected later if you want.

EDITED for clarity.

Copy link
Collaborator Author

@donmccurdy donmccurdy Sep 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some doubts about this rule (see introduction to https://blender.stackexchange.com/a/55239/43930), but, all usage I have in mind for future PRs would indeed be within a log space. That said — I don't think we can put the conversion inside of CDLNode, as the particular choice of log space may vary, and further color correction operations may follow in the same log space. If we're implementing looks for AgX we'd likely use AgX Log (Kraken), and for ACES Filmic we'd perhaps use ACEScc. #29450 may eventually need to be extended to handle the common log spaces.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was basing my comment on Filament's implementation and the assumption that log space was the correct space. I am not sure if the particular choice of log space is important for our purposes.

Since we have a node-based system -- and not a fixed pipeline -- I would convert to log and back inside the CDLNode so the calculation is correct.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CDL spec and the ACES document above disagree about this; the former is very clear that application in linear spaces is allowed, so long as you're consistent in communicating the color space expected for any particular set of CDL coefficients.

The ACES pipeline can of course be more opinionated about what to allow, as can we... But I don't think we can embed a particular color space transform into the operation and still call it a CDL, it will be impossible to compare results with other software, and that's the real point of using a CDL.

If we are offering an artist-friendly color grading API like Filament’s ColorGrading.cpp (perhaps we should!), then embedding the log conversion would be entirely appropriate. The CDL would be a single step, in log space, within that process. But the CDL is a mathematical operation, low-level and not particularly artist-friendly, and it must be possible to compose different pipelines around it... which I feel that an internal log conversion will block.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

linear spaces is allowed, so long as you're consistent in communicating the color space expected for any particular set of CDL coefficients.

In that case, I would argue the color space should be added as a parameter, and the algorithm modified to honor it.

That parameter would include the current working color space, in which case no color space transform is applied.

Copy link
Collaborator Author

@donmccurdy donmccurdy Oct 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm afraid I don't see how this can work. Other common color operators (like contrast) may receive comparable benefit from using a log space, and I think it will be impractical (for performance, numerical precision, and creative intent) to enforce a color space conversion within low-level operators.

Note that the CDL is not itself a "Look" (or LMT as in the ACES literature), a CDL might be just a component of a look. Or one CDL might be applied before tone mapping, and another after. For that reason, we also cannot require that the input to a CDL must be in the global working color space.

To your points — if we wanted to say that a Look should always be done in a log space, I'm aware of no issues with that statement, and I have no plans to do otherwise!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CDL spec ... is very clear that application in linear spaces is allowed

You are correct. Both linear and log space are allowed.

Just so I understand your point, are you planning to add a log-conversion Node of some sort, so users who prefer to apply the CDL in log space can do so?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you planning to add a log-conversion Node of some sort ...

Yes, certainly! Or maybe log conversion could be supported by THREE.ColorManagement and the existing ColorSpaceNode directly... but I'm nervous about getting that right, and inclined to start with 1-2 dedicated log space nodes instead.

I am also hoping to find a simpler higher-level abstraction so that most users do not need to interact with the CDL node directly, but I am not sure, yet, what that will mean.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I see we are not quite sure where this is headed... So I think this is OK, and we'll see what happens! :-)

An example would be awesome. 🙏

src/nodes/display/CDLNode.js Outdated Show resolved Hide resolved
@sunag sunag added this to the r170 milestone Sep 27, 2024
@donmccurdy
Copy link
Collaborator Author

donmccurdy commented Sep 27, 2024

@sunag I notice the nodes defined in https://github.com/mrdoob/three.js/blob/dev/src/nodes/display/ColorAdjustment.js are simpler, just a Fn declaration and not a class. Are there particular pros/cons to defining a node one way or another? I'm tempted to switch this over to a Fn declaration and to move it to ColorAdjustment.js.

@sunag
Copy link
Collaborator

sunag commented Sep 27, 2024

Sounds like a good idea, if you use Fn this will simplify it, it will automatically apply nodeObject() to the parameters and reduce the initial structure.

@sunag
Copy link
Collaborator

sunag commented Sep 28, 2024

I think we can move this node for addons too? #29511

@donmccurdy
Copy link
Collaborator Author

I think my preference would be to move it into ColorAdjustment.js — especially once it's simplified, it's not so different from the other functions there. But moving the color adjustment functions all into addons could make sense, perhaps excluding luminance, which I think we depend on elsewhere.

@donmccurdy
Copy link
Collaborator Author

Reimplemented as a simpler function and moved to ColorAdjustment.js. I've also changed the clamp closer to the approach taken by Filament, though I'm unsure whether we need the final max(output, 0.0) step.

@sunag
Copy link
Collaborator

sunag commented Oct 3, 2024

I'm unsure whether we need the final max(output, 0.0) step.

I think it would be better to use max externally if necessary since the limits can be applied easily.

It would be better remove the names from .toVar(), this may conflict with other variables depending on the flow there may be other names that are the same, it is certainly something we will improve soon.

@donmccurdy
Copy link
Collaborator Author

@sunag done and done! I did find .toVar( 'name' ) very useful while debugging an error in my shaders, so if that's something we can make easier in the future that would be awesome. :)

@sunag sunag merged commit 84a80fe into mrdoob:dev Oct 3, 2024
12 checks passed
@donmccurdy donmccurdy deleted the feat/cdl-node branch October 3, 2024 21:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants