diff options
Diffstat (limited to 'intern/opencolorio/gpu_shader_display_transform_frag.glsl')
-rw-r--r-- | intern/opencolorio/gpu_shader_display_transform_frag.glsl | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/intern/opencolorio/gpu_shader_display_transform_frag.glsl b/intern/opencolorio/gpu_shader_display_transform_frag.glsl new file mode 100644 index 00000000000..3c2352c13ba --- /dev/null +++ b/intern/opencolorio/gpu_shader_display_transform_frag.glsl @@ -0,0 +1,195 @@ +/* Blender OpenColorIO implementation */ + +/* -------------------------------------------------------------------- */ +/** \name Curve Mapping Implementation + * \{ */ + +#ifdef USE_CURVE_MAPPING + +float read_curve_mapping(int table, int index) +{ + return texelFetch(curve_mapping_texture, index, 0)[table]; +} + +float curvemap_calc_extend(int table, float x, vec2 first, vec2 last) +{ + if (x <= first[0]) { + if (curve_mapping.use_extend_extrapolate == 0) { + /* horizontal extrapolation */ + return first[1]; + } + else { + float fac = (curve_mapping.ext_in_x[table] != 0.0) ? + ((x - first[0]) / curve_mapping.ext_in_x[table]) : + 10000.0; + return first[1] + curve_mapping.ext_in_y[table] * fac; + } + } + else if (x >= last[0]) { + if (curve_mapping.use_extend_extrapolate == 0) { + /* horizontal extrapolation */ + return last[1]; + } + else { + float fac = (curve_mapping.ext_out_x[table] != 0.0) ? + ((x - last[0]) / curve_mapping.ext_out_x[table]) : + -10000.0; + return last[1] + curve_mapping.ext_out_y[table] * fac; + } + } + return 0.0; +} + +float curvemap_evaluateF(int table, float value) +{ + float mintable_ = curve_mapping.mintable[table]; + float range = curve_mapping.range[table]; + float mintable = 0.0; + int CM_TABLE = curve_mapping.lut_size - 1; + + float fi; + int i; + + /* index in table */ + fi = (value - mintable) * range; + i = int(fi); + + /* fi is table float index and should check against table range i.e. [0.0 CM_TABLE] */ + if (fi < 0.0 || fi > float(CM_TABLE)) { + return curvemap_calc_extend(table, + value, + vec2(curve_mapping.first_x[table], curve_mapping.first_y[table]), + vec2(curve_mapping.last_x[table], curve_mapping.last_y[table])); + } + else { + if (i < 0) { + return read_curve_mapping(table, 0); + } + if (i >= CM_TABLE) { + return read_curve_mapping(table, CM_TABLE); + } + fi = fi - float(i); + float cm1 = read_curve_mapping(table, i); + float cm2 = read_curve_mapping(table, i + 1); + return mix(cm1, cm2, fi); + } +} + +vec4 curvemapping_evaluate_premulRGBF(vec4 col) +{ + col.rgb = (col.rgb - curve_mapping.black.rgb) * curve_mapping.bwmul.rgb; + + vec4 result; + result.r = curvemap_evaluateF(0, col.r); + result.g = curvemap_evaluateF(1, col.g); + result.b = curvemap_evaluateF(2, col.b); + result.a = col.a; + return result; +} + +#endif /* USE_CURVE_MAPPING */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Dithering + * \{ */ + +/* Using a triangle distribution which gives a more final uniform noise. + * See Banding in Games:A Noisy Rant(revision 5) Mikkel Gjøl, Playdead (slide 27) */ +/* GPUs are rounding before writing to framebuffer so we center the distribution around 0.0. */ +/* Return triangle noise in [-1..1[ range */ +float dither_random_value(vec2 co) +{ + /* Original code from https://www.shadertoy.com/view/4t2SDh */ + /* Uniform noise in [0..1[ range */ + float nrnd0 = fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); + /* Convert uniform distribution into triangle-shaped distribution. */ + float orig = nrnd0 * 2.0 - 1.0; + nrnd0 = orig * inversesqrt(abs(orig)); + nrnd0 = max(-1.0, nrnd0); /* Removes nan's */ + return nrnd0 - sign(orig); +} + +vec2 round_to_pixel(sampler2D tex, vec2 uv) +{ + vec2 size = vec2(textureSize(tex, 0)); + return floor(uv * size) / size; +} + +vec4 apply_dither(vec4 col, vec2 uv) +{ + col.rgb += dither_random_value(uv) * 0.0033 * parameters.dither; + return col; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main Processing + * \{ */ + +/* Prototypes: Implementation is generaterd and defined after. */ +vec4 OCIO_to_scene_linear(vec4 pixel); +vec4 OCIO_to_display(vec4 pixel); + +vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay) +{ +#ifdef USE_CURVE_MAPPING + col = curvemapping_evaluate_premulRGBF(col); +#endif + + if (parameters.use_predivide) { + if (col.a > 0.0 && col.a < 1.0) { + col.rgb *= 1.0 / col.a; + } + } + + /* NOTE: This is true we only do de-premul here and NO premul + * and the reason is simple -- opengl is always configured + * for straight alpha at this moment + */ + + /* Convert to scene linear (usually a no-op). */ + col = OCIO_to_scene_linear(col); + + /* Apply exposure in scene linear. */ + col.rgb *= parameters.scale; + + /* Convert to display space. */ + col = OCIO_to_display(col); + + /* Blend with overlay in UI colorspace. + * + * UI colorspace here refers to the display linear color space, + * i.e: The linear color space w.r.t. display chromaticity and radiometry. + * We separate the colormanagement process into two steps to be able to + * merge UI using alpha blending in the correct color space. */ + if (parameters.use_overlay) { + col.rgb = pow(col.rgb, vec3(parameters.exponent * 2.2)); + col = clamp(col, 0.0, 1.0); + col *= 1.0 - col_overlay.a; + col += col_overlay; /* Assumed unassociated alpha. */ + col.rgb = pow(col.rgb, vec3(1.0 / 2.2)); + } + else { + col.rgb = pow(col.rgb, vec3(parameters.exponent)); + } + + if (parameters.dither > 0.0) { + vec2 noise_uv = round_to_pixel(image_texture, texCoord_interp.st); + col = apply_dither(col, noise_uv); + } + + return col; +} + +/** \} */ + +void main() +{ + vec4 col = texture(image_texture, texCoord_interp.st); + vec4 col_overlay = texture(overlay_texture, texCoord_interp.st); + + fragColor = OCIO_ProcessColor(col, col_overlay); +} |