Cycles: Implement extrapolation for RGB curves

Previously RGB Curves node will clamp input to 0..1 which is rather useless
when one wants to use HDR image textures and do bit of correction on them.

Now kernel code supports extrapolation of baked LUT based on first/last two
table points and performs linear extrapolation.

The only tricky part is to guess the range to bake the LUT for. Currently
it's using simple approach -- minmax of the input curves. While this behaves
ok for the simple cases it's easy to trick the system up causing incorrect
results.

Not sure we can solve those issues in a general case and since the new code
is giving more expected results it's not that bad actually. In the worst
case artist migh always create explicit point to make sure LUT is created
for the needed HDR range.

Reviewers: brecht, juicyfruit

Subscribers: sebastian_k

Differential Revision: https://developer.blender.org/D1658
This commit is contained in:
Sergey Sharybin
2015-12-04 20:17:25 +05:00
parent 258564a7b4
commit ed5dbb0a7b
6 changed files with 125 additions and 19 deletions

View File

@@ -19,6 +19,21 @@
float ramp_lookup(color ramp[RAMP_TABLE_SIZE], float at, int component)
{
if (at < 0.0 || at > 1.0) {
float t0, dy;
if(at < 0.0) {
t0 = ramp[0][component];
dy = t0 - ramp[1][component];
at = -at;
}
else {
t0 = ramp[RAMP_TABLE_SIZE - 1][component];
dy = t0 - ramp[RAMP_TABLE_SIZE - 2][component];
at = at - 1.0;
}
return t0 + dy * at * (RAMP_TABLE_SIZE - 1);
}
float f = clamp(at, 0.0, 1.0) * (RAMP_TABLE_SIZE - 1);
/* clamp int as well in case of NaN */
@@ -37,14 +52,18 @@ float ramp_lookup(color ramp[RAMP_TABLE_SIZE], float at, int component)
shader node_rgb_curves(
color ramp[RAMP_TABLE_SIZE] = {0.0},
float min_x = 0.0,
float max_x = 1.0,
color ColorIn = 0.0,
float Fac = 0.0,
output color ColorOut = 0.0)
{
ColorOut[0] = ramp_lookup(ramp, ColorIn[0], 0);
ColorOut[1] = ramp_lookup(ramp, ColorIn[1], 1);
ColorOut[2] = ramp_lookup(ramp, ColorIn[2], 2);
color c = (ColorIn - color(min_x, min_x, min_x)) / (max_x - min_x);
ColorOut[0] = ramp_lookup(ramp, c[0], 0);
ColorOut[1] = ramp_lookup(ramp, c[1], 1);
ColorOut[2] = ramp_lookup(ramp, c[2], 2);
ColorOut = mix(ColorIn, ColorOut, Fac);
}

View File

@@ -19,8 +19,27 @@
CCL_NAMESPACE_BEGIN
ccl_device float4 rgb_ramp_lookup(KernelGlobals *kg, int offset, float f, bool interpolate)
ccl_device float4 rgb_ramp_lookup(KernelGlobals *kg,
int offset,
float f,
bool interpolate,
bool extrapolate)
{
if((f < 0.0f || f > 1.0f) && extrapolate) {
float4 t0, dy;
if(f < 0.0f) {
t0 = fetch_node_float(kg, offset);
dy = t0 - fetch_node_float(kg, offset + 1);
f = -f;
}
else {
t0 = fetch_node_float(kg, offset + RAMP_TABLE_SIZE - 1);
dy = t0 - fetch_node_float(kg, offset + RAMP_TABLE_SIZE - 2);
f = f - 1.0f;
}
return t0 + dy * f * (RAMP_TABLE_SIZE-1);
}
f = saturate(f)*(RAMP_TABLE_SIZE-1);
/* clamp int as well in case of NaN */
@@ -43,7 +62,7 @@ ccl_device void svm_node_rgb_ramp(KernelGlobals *kg, ShaderData *sd, float *stac
decode_node_uchar4(node.y, &fac_offset, &color_offset, &alpha_offset, NULL);
float fac = stack_load_float(stack, fac_offset);
float4 color = rgb_ramp_lookup(kg, *offset, fac, interpolate);
float4 color = rgb_ramp_lookup(kg, *offset, fac, interpolate, false);
if(stack_valid(color_offset))
stack_store_float3(stack, color_offset, float4_to_float3(color));
@@ -55,16 +74,24 @@ ccl_device void svm_node_rgb_ramp(KernelGlobals *kg, ShaderData *sd, float *stac
ccl_device void svm_node_rgb_curves(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset)
{
uint fac_offset = node.y;
uint color_offset = node.z;
uint out_offset = node.w;
uint fac_offset, color_offset, out_offset;
decode_node_uchar4(node.y,
&fac_offset,
&color_offset,
&out_offset,
NULL);
float fac = stack_load_float(stack, fac_offset);
float3 color = stack_load_float3(stack, color_offset);
float r = rgb_ramp_lookup(kg, *offset, color.x, true).x;
float g = rgb_ramp_lookup(kg, *offset, color.y, true).y;
float b = rgb_ramp_lookup(kg, *offset, color.z, true).z;
const float min_x = __int_as_float(node.z),
max_x = __int_as_float(node.w);
const float range_x = max_x - min_x;
color = (color - make_float3(min_x, min_x, min_x)) / range_x;
float r = rgb_ramp_lookup(kg, *offset, color.x, true, true).x;
float g = rgb_ramp_lookup(kg, *offset, color.y, true, true).y;
float b = rgb_ramp_lookup(kg, *offset, color.z, true, true).z;
color = (1.0f - fac)*color + fac*make_float3(r, g, b);
stack_store_float3(stack, out_offset, color);
@@ -81,9 +108,9 @@ ccl_device void svm_node_vector_curves(KernelGlobals *kg, ShaderData *sd, float
float fac = stack_load_float(stack, fac_offset);
float3 color = stack_load_float3(stack, color_offset);
float r = rgb_ramp_lookup(kg, *offset, (color.x + 1.0f)*0.5f, true).x;
float g = rgb_ramp_lookup(kg, *offset, (color.y + 1.0f)*0.5f, true).y;
float b = rgb_ramp_lookup(kg, *offset, (color.z + 1.0f)*0.5f, true).z;
float r = rgb_ramp_lookup(kg, *offset, (color.x + 1.0f)*0.5f, true, false).x;
float g = rgb_ramp_lookup(kg, *offset, (color.y + 1.0f)*0.5f, true, false).y;
float b = rgb_ramp_lookup(kg, *offset, (color.z + 1.0f)*0.5f, true, false).z;
color = (1.0f - fac)*color + fac*make_float3(r*2.0f - 1.0f, g*2.0f - 1.0f, b*2.0f - 1.0f);
stack_store_float3(stack, out_offset, color);