EEVEE: Refactor curve nodes

This patches rewrites the GPU shaders of curve nodes for easier future
development. This is a non-functional change. The new code avoids code
duplication by moving common code into BKE curve mapping functions. It
also avoids ambiguous data embedding into the gradient vectors that are
passed to vectors and reduces the size of uniforms uploaded to the
shader by avoiding redundancies.

This is needed in preparation for the viewport compositor, which will
utilize and extend this implementation.

Reviewed By: fclem

Differential Revision: https://developer.blender.org/D14689
This commit is contained in:
Omar Emara 2022-05-06 13:33:23 +02:00
parent 8f6f28a0dc
commit bdfee6d831
8 changed files with 348 additions and 264 deletions

View File

@ -129,6 +129,36 @@ bool BKE_curvemapping_RGBA_does_something(const struct CurveMapping *cumap);
void BKE_curvemapping_table_F(const struct CurveMapping *cumap, float **array, int *size);
void BKE_curvemapping_table_RGBA(const struct CurveMapping *cumap, float **array, int *size);
/** Get the minimum x value of each curve map table. */
void BKE_curvemapping_get_range_minimums(const struct CurveMapping *curve_mapping,
float minimums[4]);
/** Get the reciprocal of the difference between the maximum and the minimum x value of each curve
* map table. Evaluation parameters can be multiplied by this value to be normalized. If the
* difference is zero, 1^8 is returned. */
void BKE_curvemapping_compute_range_dividers(const struct CurveMapping *curve_mapping,
float dividers[4]);
/** Compute the slopes at the start and end points of each curve map. The slopes are multiplied by
* the range of the curve map to compensate for parameter normalization. If the slope is vertical,
* 1^8 is returned. */
void BKE_curvemapping_compute_slopes(const struct CurveMapping *curve_mapping,
float start_slopes[4],
float end_slopes[4]);
/** Check if the curve map at the index is identity, that is, does nothing. A curve map is said to
* be identity if:
* - The curve mapping uses extrapolation.
* - Its range is 1.
* - The slope at its start point is 1.
* - The slope at its end point is 1.
* - The number of points is 2.
* - The start point is at (0, 0).
* - The end point is at (1, 1).
* Note that this could return false even if the curve map is identity, this happens in the case
* when more than 2 points exist in the curve map but all points are collinear. */
bool BKE_curvemapping_is_map_identity(const struct CurveMapping *curve_mapping, int index);
/**
* Call when you do images etc, needs restore too. also verifies tables.
* non-const (these modify the curve).

View File

@ -1158,6 +1158,80 @@ bool BKE_curvemapping_RGBA_does_something(const CurveMapping *cumap)
return false;
}
void BKE_curvemapping_get_range_minimums(const CurveMapping *curve_mapping, float minimums[CM_TOT])
{
for (int i = 0; i < CM_TOT; i++) {
minimums[i] = curve_mapping->cm[i].mintable;
}
}
void BKE_curvemapping_compute_range_dividers(const CurveMapping *curve_mapping,
float dividers[CM_TOT])
{
for (int i = 0; i < CM_TOT; i++) {
const CurveMap *curve_map = &curve_mapping->cm[i];
dividers[i] = 1.0f / max_ff(1e-8f, curve_map->maxtable - curve_map->mintable);
}
}
void BKE_curvemapping_compute_slopes(const CurveMapping *curve_mapping,
float start_slopes[CM_TOT],
float end_slopes[CM_TOT])
{
float range_dividers[CM_TOT];
BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
for (int i = 0; i < CM_TOT; i++) {
const CurveMap *curve_map = &curve_mapping->cm[i];
/* If extrapolation is not enabled, the slopes are horizontal. */
if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) {
start_slopes[i] = 0.0f;
end_slopes[i] = 0.0f;
continue;
}
if (curve_map->ext_in[0] != 0.0f) {
start_slopes[i] = curve_map->ext_in[1] / (curve_map->ext_in[0] * range_dividers[i]);
}
else {
start_slopes[i] = 1e8f;
}
if (curve_map->ext_out[0] != 0.0f) {
end_slopes[i] = curve_map->ext_out[1] / (curve_map->ext_out[0] * range_dividers[i]);
}
else {
end_slopes[i] = 1e8f;
}
}
}
bool BKE_curvemapping_is_map_identity(const CurveMapping *curve_mapping, int index)
{
if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) {
return false;
}
const CurveMap *curve_map = &curve_mapping->cm[index];
if (curve_map->maxtable - curve_map->mintable != 1.0f) {
return false;
}
if (curve_map->ext_in[0] != curve_map->ext_in[1]) {
return false;
}
if (curve_map->ext_out[0] != curve_map->ext_out[1]) {
return false;
}
if (curve_map->totpoint != 2) {
return false;
}
if (curve_map->curve[0].x != 0 || curve_map->curve[0].y != 0) {
return false;
}
if (curve_map->curve[1].x != 0 || curve_map->curve[1].y != 0) {
return false;
}
return true;
}
void BKE_curvemapping_init(CurveMapping *cumap)
{
int a;

View File

@ -305,6 +305,7 @@ set(GLSL_SRC
shaders/common/gpu_shader_common_color_ramp.glsl
shaders/common/gpu_shader_common_color_utils.glsl
shaders/common/gpu_shader_common_curves.glsl
shaders/common/gpu_shader_common_hash.glsl
shaders/common/gpu_shader_common_math.glsl
shaders/common/gpu_shader_common_math_utils.glsl
@ -330,7 +331,6 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_displacement.glsl
shaders/material/gpu_shader_material_eevee_specular.glsl
shaders/material/gpu_shader_material_emission.glsl
shaders/material/gpu_shader_material_float_curve.glsl
shaders/material/gpu_shader_material_fractal_noise.glsl
shaders/material/gpu_shader_material_fresnel.glsl
shaders/material/gpu_shader_material_gamma.glsl
@ -359,7 +359,6 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_point_info.glsl
shaders/material/gpu_shader_material_principled.glsl
shaders/material/gpu_shader_material_refraction.glsl
shaders/material/gpu_shader_material_rgb_curves.glsl
shaders/material/gpu_shader_material_rgb_to_bw.glsl
shaders/material/gpu_shader_material_separate_color.glsl
shaders/material/gpu_shader_material_separate_hsv.glsl
@ -388,7 +387,6 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_translucent.glsl
shaders/material/gpu_shader_material_transparent.glsl
shaders/material/gpu_shader_material_uv_map.glsl
shaders/material/gpu_shader_material_vector_curves.glsl
shaders/material/gpu_shader_material_vector_displacement.glsl
shaders/material/gpu_shader_material_vector_math.glsl
shaders/material/gpu_shader_material_vector_rotate.glsl

View File

@ -0,0 +1,162 @@
vec4 white_balance(vec4 color, vec4 black_level, vec4 white_level)
{
vec4 range = max(white_level - black_level, vec4(1e-5f));
return (color - black_level) / range;
}
float extrapolate_if_needed(float parameter, float value, float start_slope, float end_slope)
{
if (parameter < 0.0) {
return value + parameter * start_slope;
}
if (parameter > 1.0) {
return value + (parameter - 1.0) * end_slope;
}
return value;
}
/* Same as extrapolate_if_needed but vectorized. */
vec3 extrapolate_if_needed(vec3 parameters, vec3 values, vec3 start_slopes, vec3 end_slopes)
{
vec3 end_or_zero_slopes = mix(vec3(0.0), end_slopes, greaterThan(parameters, vec3(1.0)));
vec3 slopes = mix(end_or_zero_slopes, start_slopes, lessThan(parameters, vec3(0.0)));
parameters = parameters - mix(vec3(0.0), vec3(1.0), greaterThan(parameters, vec3(1.0)));
return values + parameters * slopes;
}
/* Curve maps are stored in sampler objects that are evaluated in the [0, 1] range, so normalize
* parameters accordingly. */
#define NORMALIZE_PARAMETER(parameter, minimum, range) ((parameter - minimum) * range)
void curves_combined_rgb(float factor,
vec4 color,
vec4 black_level,
vec4 white_level,
sampler1DArray curve_map,
const float layer,
vec4 range_minimums,
vec4 range_dividers,
vec4 start_slopes,
vec4 end_slopes,
out vec4 result)
{
vec4 balanced = white_balance(color, black_level, white_level);
/* First, evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the
* UI. */
vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimums.aaa, range_dividers.aaa);
result.r = texture(curve_map, vec2(parameters.x, layer)).a;
result.g = texture(curve_map, vec2(parameters.y, layer)).a;
result.b = texture(curve_map, vec2(parameters.z, layer)).a;
/* Then, extrapolate if needed. */
result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.aaa, end_slopes.aaa);
/* Then, evaluate each channel on its curve map. */
parameters = NORMALIZE_PARAMETER(result.rgb, range_minimums.rgb, range_dividers.rgb);
result.r = texture(curve_map, vec2(parameters.r, layer)).r;
result.g = texture(curve_map, vec2(parameters.g, layer)).g;
result.b = texture(curve_map, vec2(parameters.b, layer)).b;
/* Then, extrapolate again if needed. */
result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.rgb, end_slopes.rgb);
result.a = color.a;
result = mix(color, result, factor);
}
void curves_combined_only(float factor,
vec4 color,
vec4 black_level,
vec4 white_level,
sampler1DArray curve_map,
const float layer,
float range_minimum,
float range_divider,
float start_slope,
float end_slope,
out vec4 result)
{
vec4 balanced = white_balance(color, black_level, white_level);
/* Evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the
* UI. */
vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimum, range_divider);
result.r = texture(curve_map, vec2(parameters.x, layer)).a;
result.g = texture(curve_map, vec2(parameters.y, layer)).a;
result.b = texture(curve_map, vec2(parameters.z, layer)).a;
/* Then, extrapolate if needed. */
result.rgb = extrapolate_if_needed(parameters, result.rgb, vec3(start_slope), vec3(end_slope));
result.a = color.a;
result = mix(color, result, factor);
}
void curves_vector(vec3 vector,
sampler1DArray curve_map,
const float layer,
vec3 range_minimums,
vec3 range_dividers,
vec3 start_slopes,
vec3 end_slopes,
out vec3 result)
{
/* Evaluate each component on its curve map. */
vec3 parameters = NORMALIZE_PARAMETER(vector, range_minimums, range_dividers);
result.x = texture(curve_map, vec2(parameters.x, layer)).x;
result.y = texture(curve_map, vec2(parameters.y, layer)).y;
result.z = texture(curve_map, vec2(parameters.z, layer)).z;
/* Then, extrapolate if needed. */
result = extrapolate_if_needed(parameters, result, start_slopes, end_slopes);
}
void curves_vector_mixed(float factor,
vec3 vector,
sampler1DArray curve_map,
const float layer,
vec3 range_minimums,
vec3 range_dividers,
vec3 start_slopes,
vec3 end_slopes,
out vec3 result)
{
curves_vector(
vector, curve_map, layer, range_minimums, range_dividers, start_slopes, end_slopes, result);
result = mix(vector, result, factor);
}
void curves_float(float value,
sampler1DArray curve_map,
const float layer,
float range_minimum,
float range_divider,
float start_slope,
float end_slope,
out float result)
{
/* Evaluate the normalized value on the first curve map. */
float parameter = NORMALIZE_PARAMETER(value, range_minimum, range_divider);
result = texture(curve_map, vec2(parameter, layer)).x;
/* Then, extrapolate if needed. */
result = extrapolate_if_needed(parameter, result, start_slope, end_slope);
}
void curves_float_mixed(float factor,
float value,
sampler1DArray curve_map,
const float layer,
float range_minimum,
float range_divider,
float start_slope,
float end_slope,
out float result)
{
curves_float(
value, curve_map, layer, range_minimum, range_divider, start_slope, end_slope, result);
result = mix(value, result, factor);
}

View File

@ -1,33 +0,0 @@
/* ext is vec4(in_x, in_dy, out_x, out_dy). */
float curve_float_extrapolate(float x, float y, vec4 ext)
{
if (x < 0.0) {
return y + x * ext.y;
}
else if (x > 1.0) {
return y + (x - 1.0) * ext.w;
}
else {
return y;
}
}
#define RANGE_RESCALE(x, min, range) ((x - min) * range)
void curve_float(float fac,
float vec,
sampler1DArray curvemap,
float layer,
float range,
vec4 ext,
out float outvec)
{
float xyz_min = ext.x;
vec = RANGE_RESCALE(vec, xyz_min, range);
outvec = texture(curvemap, vec2(vec, layer)).x;
outvec = curve_float_extrapolate(vec, outvec, ext);
outvec = mix(vec, outvec, fac);
}

View File

@ -1,73 +0,0 @@
/* ext is vec4(in_x, in_dy, out_x, out_dy). */
float curve_extrapolate(float x, float y, vec4 ext)
{
if (x < 0.0) {
return y + x * ext.y;
}
else if (x > 1.0) {
return y + (x - 1.0) * ext.w;
}
else {
return y;
}
}
#define RANGE_RESCALE(x, min, range) ((x - min) * range)
void curves_rgb(float fac,
vec4 col,
sampler1DArray curvemap,
float layer,
vec4 range,
vec4 ext_r,
vec4 ext_g,
vec4 ext_b,
vec4 ext_a,
out vec4 outcol)
{
vec4 co = vec4(RANGE_RESCALE(col.rgb, ext_a.x, range.a), layer);
vec3 samp;
samp.r = texture(curvemap, co.xw).a;
samp.g = texture(curvemap, co.yw).a;
samp.b = texture(curvemap, co.zw).a;
samp.r = curve_extrapolate(co.x, samp.r, ext_a);
samp.g = curve_extrapolate(co.y, samp.g, ext_a);
samp.b = curve_extrapolate(co.z, samp.b, ext_a);
vec3 rgb_min = vec3(ext_r.x, ext_g.x, ext_b.x);
co.xyz = RANGE_RESCALE(samp.rgb, rgb_min, range.rgb);
samp.r = texture(curvemap, co.xw).r;
samp.g = texture(curvemap, co.yw).g;
samp.b = texture(curvemap, co.zw).b;
outcol.r = curve_extrapolate(co.x, samp.r, ext_r);
outcol.g = curve_extrapolate(co.y, samp.g, ext_g);
outcol.b = curve_extrapolate(co.z, samp.b, ext_b);
outcol.a = col.a;
outcol = mix(col, outcol, fac);
}
void curves_rgb_opti(float fac,
vec4 col,
sampler1DArray curvemap,
float layer,
vec4 range,
vec4 ext_a,
out vec4 outcol)
{
vec4 co = vec4(RANGE_RESCALE(col.rgb, ext_a.x, range.a), layer);
vec3 samp;
samp.r = texture(curvemap, co.xw).a;
samp.g = texture(curvemap, co.yw).a;
samp.b = texture(curvemap, co.zw).a;
outcol.r = curve_extrapolate(co.x, samp.r, ext_a);
outcol.g = curve_extrapolate(co.y, samp.g, ext_a);
outcol.b = curve_extrapolate(co.z, samp.b, ext_a);
outcol.a = col.a;
outcol = mix(col, outcol, fac);
}

View File

@ -1,41 +0,0 @@
/* ext is vec4(in_x, in_dy, out_x, out_dy). */
float curve_vec_extrapolate(float x, float y, vec4 ext)
{
if (x < 0.0) {
return y + x * ext.y;
}
else if (x > 1.0) {
return y + (x - 1.0) * ext.w;
}
else {
return y;
}
}
#define RANGE_RESCALE(x, min, range) ((x - min) * range)
void curves_vec(float fac,
vec3 vec,
sampler1DArray curvemap,
float layer,
vec3 range,
vec4 ext_x,
vec4 ext_y,
vec4 ext_z,
out vec3 outvec)
{
vec4 co = vec4(vec, layer);
vec3 xyz_min = vec3(ext_x.x, ext_y.x, ext_z.x);
co.xyz = RANGE_RESCALE(co.xyz, xyz_min, range);
outvec.x = texture(curvemap, co.xw).x;
outvec.y = texture(curvemap, co.yw).y;
outvec.z = texture(curvemap, co.zw).z;
outvec.x = curve_vec_extrapolate(co.x, outvec.r, ext_x);
outvec.y = curve_vec_extrapolate(co.y, outvec.g, ext_y);
outvec.z = curve_vec_extrapolate(co.z, outvec.b, ext_z);
outvec = mix(vec, outvec, fac);
}

View File

@ -28,48 +28,34 @@ static int gpu_shader_curve_vec(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
float *array, layer;
int size;
CurveMapping *curve_mapping = (CurveMapping *)node->storage;
CurveMapping *cumap = (CurveMapping *)node->storage;
BKE_curvemapping_init(curve_mapping);
float *band_values;
int band_size;
BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
float band_layer;
GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer);
BKE_curvemapping_init(cumap);
BKE_curvemapping_table_RGBA(cumap, &array, &size);
GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer);
float ext_xyz[3][4];
float range_xyz[3];
for (int a = 0; a < 3; a++) {
const CurveMap *cm = &cumap->cm[a];
ext_xyz[a][0] = cm->mintable;
ext_xyz[a][2] = cm->maxtable;
range_xyz[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable);
/* Compute extrapolation gradients. */
if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) {
ext_xyz[a][1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_xyz[a])) :
1e8f;
ext_xyz[a][3] = (cm->ext_out[0] != 0.0f) ?
(cm->ext_out[1] / (cm->ext_out[0] * range_xyz[a])) :
1e8f;
}
else {
ext_xyz[a][1] = 0.0f;
ext_xyz[a][3] = 0.0f;
}
}
float start_slopes[CM_TOT];
float end_slopes[CM_TOT];
BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
float range_minimums[CM_TOT];
BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
float range_dividers[CM_TOT];
BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
return GPU_stack_link(mat,
node,
"curves_vec",
"curves_vector_mixed",
in,
out,
tex,
GPU_constant(&layer),
GPU_uniform(range_xyz),
GPU_uniform(ext_xyz[0]),
GPU_uniform(ext_xyz[1]),
GPU_uniform(ext_xyz[2]));
band_texture,
GPU_constant(&band_layer),
GPU_uniform(range_minimums),
GPU_uniform(range_dividers),
GPU_uniform(start_slopes),
GPU_uniform(end_slopes));
}
class CurveVecFunction : public fn::MultiFunction {
@ -157,72 +143,59 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
float *array, layer;
int size;
bool use_opti = true;
CurveMapping *curve_mapping = (CurveMapping *)node->storage;
CurveMapping *cumap = (CurveMapping *)node->storage;
BKE_curvemapping_init(curve_mapping);
float *band_values;
int band_size;
BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
float band_layer;
GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer);
BKE_curvemapping_init(cumap);
BKE_curvemapping_table_RGBA(cumap, &array, &size);
GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer);
float start_slopes[CM_TOT];
float end_slopes[CM_TOT];
BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
float range_minimums[CM_TOT];
BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
float range_dividers[CM_TOT];
BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
float ext_rgba[4][4];
float range_rgba[4];
/* Shader nodes don't do white balancing. */
float black_level[4] = {0.0f, 0.0f, 0.0f, 1.0f};
float white_level[4] = {1.0f, 1.0f, 1.0f, 1.0f};
for (int a = 0; a < CM_TOT; a++) {
const CurveMap *cm = &cumap->cm[a];
ext_rgba[a][0] = cm->mintable;
ext_rgba[a][2] = cm->maxtable;
range_rgba[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable);
/* Compute extrapolation gradients. */
if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) {
ext_rgba[a][1] = (cm->ext_in[0] != 0.0f) ?
(cm->ext_in[1] / (cm->ext_in[0] * range_rgba[a])) :
1e8f;
ext_rgba[a][3] = (cm->ext_out[0] != 0.0f) ?
(cm->ext_out[1] / (cm->ext_out[0] * range_rgba[a])) :
1e8f;
}
else {
ext_rgba[a][1] = 0.0f;
ext_rgba[a][3] = 0.0f;
}
/* Check if rgb comps are just linear. */
if (a < 3) {
if (range_rgba[a] != 1.0f || ext_rgba[a][1] != 1.0f || ext_rgba[a][2] != 1.0f ||
cm->totpoint != 2 || cm->curve[0].x != 0.0f || cm->curve[0].y != 0.0f ||
cm->curve[1].x != 1.0f || cm->curve[1].y != 1.0f) {
use_opti = false;
}
}
}
if (use_opti) {
/* If the RGB curves do nothing, use a function that skips RGB computations. */
if (BKE_curvemapping_is_map_identity(curve_mapping, 0) &&
BKE_curvemapping_is_map_identity(curve_mapping, 1) &&
BKE_curvemapping_is_map_identity(curve_mapping, 2)) {
return GPU_stack_link(mat,
node,
"curves_rgb_opti",
"curves_combined_only",
in,
out,
tex,
GPU_constant(&layer),
GPU_uniform(range_rgba),
GPU_uniform(ext_rgba[3]));
GPU_constant(black_level),
GPU_constant(white_level),
band_texture,
GPU_constant(&band_layer),
GPU_uniform(&range_minimums[3]),
GPU_uniform(&range_dividers[3]),
GPU_uniform(&start_slopes[3]),
GPU_uniform(&end_slopes[3]));
}
return GPU_stack_link(mat,
node,
"curves_rgb",
"curves_combined_rgb",
in,
out,
tex,
GPU_constant(&layer),
GPU_uniform(range_rgba),
GPU_uniform(ext_rgba[0]),
GPU_uniform(ext_rgba[1]),
GPU_uniform(ext_rgba[2]),
GPU_uniform(ext_rgba[3]));
GPU_constant(black_level),
GPU_constant(white_level),
band_texture,
GPU_constant(&band_layer),
GPU_uniform(range_minimums),
GPU_uniform(range_dividers),
GPU_uniform(start_slopes),
GPU_uniform(end_slopes));
}
class CurveRGBFunction : public fn::MultiFunction {
@ -316,40 +289,34 @@ static int gpu_shader_curve_float(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
float *array, layer;
int size;
CurveMapping *curve_mapping = (CurveMapping *)node->storage;
CurveMapping *cumap = (CurveMapping *)node->storage;
BKE_curvemapping_init(curve_mapping);
float *band_values;
int band_size;
BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
float band_layer;
GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer);
BKE_curvemapping_init(cumap);
BKE_curvemapping_table_F(cumap, &array, &size);
GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer);
float start_slopes[CM_TOT];
float end_slopes[CM_TOT];
BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
float range_minimums[CM_TOT];
BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
float range_dividers[CM_TOT];
BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
float ext_xyz[4];
float range_x;
const CurveMap *cm = &cumap->cm[0];
ext_xyz[0] = cm->mintable;
ext_xyz[2] = cm->maxtable;
range_x = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable);
/* Compute extrapolation gradients. */
if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) {
ext_xyz[1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_x)) : 1e8f;
ext_xyz[3] = (cm->ext_out[0] != 0.0f) ? (cm->ext_out[1] / (cm->ext_out[0] * range_x)) : 1e8f;
}
else {
ext_xyz[1] = 0.0f;
ext_xyz[3] = 0.0f;
}
return GPU_stack_link(mat,
node,
"curve_float",
"curves_float_mixed",
in,
out,
tex,
GPU_constant(&layer),
GPU_uniform(&range_x),
GPU_uniform(ext_xyz));
band_texture,
GPU_constant(&band_layer),
GPU_uniform(range_minimums),
GPU_uniform(range_dividers),
GPU_uniform(start_slopes),
GPU_uniform(end_slopes));
}
class CurveFloatFunction : public fn::MultiFunction {