diff options
author | Brecht Van Lommel <brechtvanlommel@gmail.com> | 2019-06-07 18:49:58 +0300 |
---|---|---|
committer | Brecht Van Lommel <brechtvanlommel@gmail.com> | 2019-06-07 21:51:40 +0300 |
commit | fb03f50e069d66c99391e4796e1b9eaa2b4cc133 (patch) | |
tree | 10c761cf548c6d4aa9160eed7c2798a96b798ccb /source/blender/draw | |
parent | d05f27c7b95371db9e7e4e34a919a3623682a114 (diff) |
Fix T64625: Eevee image textures with alpha have dark edges
Now texture storage of images is defined by the alpha mode of the image. The
downside of this is that there can be artifacts near alpha edges where pixels
with zero alpha bleed in. It also adds more code complexity since image textures
are no longer all stored the same way.
This changes allows us to keep using sRGB texture formats, which have edge
darkening when stored with premultiplied alpha. Game engines seems to generally
do the same thing, and we want to be compatible with them.
Diffstat (limited to 'source/blender/draw')
12 files changed, 74 insertions, 25 deletions
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c index cd35395064b..9babd8cd058 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c @@ -467,6 +467,8 @@ static DRWShadingGroup *DRW_gpencil_shgroup_fill_create(GPENCIL_e_data *e_data, else { GPUTexture *texture = GPU_texture_from_blender(gp_style->ima, &iuser, GL_TEXTURE_2D); DRW_shgroup_uniform_texture(grp, "myTexture", texture); + DRW_shgroup_uniform_bool_copy( + grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL)); stl->shgroups[id].texture_clamp = gp_style->flag & GP_STYLE_COLOR_TEX_CLAMP ? 1 : 0; DRW_shgroup_uniform_int(grp, "texture_clamp", &stl->shgroups[id].texture_clamp, 1); @@ -633,6 +635,8 @@ DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(GPENCIL_e_data *e_data, else { GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D); DRW_shgroup_uniform_texture(grp, "myTexture", texture); + DRW_shgroup_uniform_bool_copy( + grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL)); BKE_image_release_ibuf(image, ibuf, NULL); } @@ -789,6 +793,8 @@ static DRWShadingGroup *DRW_gpencil_shgroup_point_create(GPENCIL_e_data *e_data, else { GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D); DRW_shgroup_uniform_texture(grp, "myTexture", texture); + DRW_shgroup_uniform_bool_copy( + grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL)); BKE_image_release_ibuf(image, ibuf, NULL); } diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl index 192720a4e98..1fdfd05332e 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl @@ -19,6 +19,7 @@ uniform int drawmode; uniform float layer_opacity; uniform sampler2D myTexture; +uniform bool myTexturePremultiplied; uniform int texture_clamp; uniform int viewport_xray; @@ -99,11 +100,15 @@ float linearrgb_to_srgb(float c) } } -vec4 texture_read_as_srgb(sampler2D tex, vec2 co) +vec4 texture_read_as_srgb(sampler2D tex, bool premultiplied, vec2 co) { /* By convention image textures return scene linear colors, but * grease pencil still works in srgb. */ vec4 color = texture(tex, co); + /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */ + if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) { + color.rgb = color.rgb / color.a; + } color.r = linearrgb_to_srgb(color.r); color.g = linearrgb_to_srgb(color.g); color.b = linearrgb_to_srgb(color.b); @@ -118,8 +123,10 @@ void main() vec2 rot_tex = (matrot_tex * (texCoord_interp - t_center)) + t_center + texture_offset; vec4 tmp_color; tmp_color = (texture_clamp == 0) ? - texture_read_as_srgb(myTexture, rot_tex * texture_scale) : - texture_read_as_srgb(myTexture, clamp(rot_tex * texture_scale, 0.0, 1.0)); + texture_read_as_srgb( + myTexture, myTexturePremultiplied, rot_tex * texture_scale) : + texture_read_as_srgb( + myTexture, myTexturePremultiplied, clamp(rot_tex * texture_scale, 0.0, 1.0)); vec4 text_color = vec4(tmp_color[0], tmp_color[1], tmp_color[2], tmp_color[3] * texture_opacity); vec4 chesscolor; diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl index 569a68679b4..7fed42aca0d 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl @@ -1,6 +1,7 @@ uniform int color_type; uniform int mode; uniform sampler2D myTexture; +uniform bool myTexturePremultiplied; uniform float gradient_f; uniform vec2 gradient_s; @@ -57,11 +58,15 @@ float linearrgb_to_srgb(float c) } } -vec4 texture_read_as_srgb(sampler2D tex, vec2 co) +vec4 texture_read_as_srgb(sampler2D tex, bool premultiplied, vec2 co) { /* By convention image textures return scene linear colors, but * grease pencil still works in srgb. */ vec4 color = texture(tex, co); + /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */ + if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) { + color.rgb = color.rgb / color.a; + } color.r = linearrgb_to_srgb(color.r); color.g = linearrgb_to_srgb(color.g); color.b = linearrgb_to_srgb(color.b); @@ -86,15 +91,13 @@ void main() } } - vec4 tmp_color = texture_read_as_srgb(myTexture, mTexCoord); - /* Solid */ if ((color_type == GPENCIL_COLOR_SOLID) || (no_texture)) { fragColor = mColor; } /* texture */ if ((color_type == GPENCIL_COLOR_TEXTURE) && (!no_texture)) { - vec4 text_color = texture_read_as_srgb(myTexture, mTexCoord); + vec4 text_color = texture_read_as_srgb(myTexture, myTexturePremultiplied, mTexCoord); if (mix_stroke_factor > 0.0) { fragColor.rgb = mix(text_color.rgb, colormix.rgb, mix_stroke_factor); fragColor.a = text_color.a; @@ -108,7 +111,7 @@ void main() } /* pattern */ if ((color_type == GPENCIL_COLOR_PATTERN) && (!no_texture)) { - vec4 text_color = texture_read_as_srgb(myTexture, mTexCoord); + vec4 text_color = texture_read_as_srgb(myTexture, myTexturePremultiplied, mTexCoord); fragColor = mColor; /* mult both alpha factor to use strength factor with color alpha limit */ fragColor.a = min(text_color.a * mColor.a, mColor.a); diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl index 7d0ebe88aa4..bc703d2a078 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl @@ -1,5 +1,6 @@ uniform int color_type; uniform sampler2D myTexture; +uniform bool myTexturePremultiplied; uniform float gradient_f; @@ -37,11 +38,15 @@ float linearrgb_to_srgb(float c) } } -vec4 texture_read_as_srgb(sampler2D tex, vec2 co) +vec4 texture_read_as_srgb(sampler2D tex, bool premultiplied, vec2 co) { /* By convention image textures return scene linear colors, but * grease pencil still works in srgb. */ vec4 color = texture(tex, co); + /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */ + if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) { + color.rgb = color.rgb / color.a; + } color.r = linearrgb_to_srgb(color.r); color.g = linearrgb_to_srgb(color.g); color.b = linearrgb_to_srgb(color.b); @@ -68,10 +73,11 @@ void main() /* texture for endcaps */ vec4 text_color; if (uvfac[1] == ENDCAP) { - text_color = texture_read_as_srgb(myTexture, vec2(mTexCoord.x, mTexCoord.y)); + text_color = texture_read_as_srgb( + myTexture, myTexturePremultiplied, vec2(mTexCoord.x, mTexCoord.y)); } else { - text_color = texture_read_as_srgb(myTexture, mTexCoord); + text_color = texture_read_as_srgb(myTexture, myTexturePremultiplied, mTexCoord); } /* texture */ diff --git a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl index 96f8f6e4c7a..59a463f49c3 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl @@ -140,7 +140,10 @@ vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped) return matcap_uv * 0.496 + 0.5; } -vec4 workbench_sample_texture(sampler2D image, vec2 coord, bool nearest_sampling) +vec4 workbench_sample_texture(sampler2D image, + vec2 coord, + bool nearest_sampling, + bool premultiplied) { vec2 tex_size = vec2(textureSize(image, 0).xy); /* TODO(fclem) We could do the same with sampler objects. @@ -148,8 +151,8 @@ vec4 workbench_sample_texture(sampler2D image, vec2 coord, bool nearest_sampling vec2 uv = nearest_sampling ? (floor(coord * tex_size) + 0.5) / tex_size : coord; vec4 color = texture(image, uv); - /* Unpremultiply, ideally shaders would be added so this is not needed. */ - if (!(color.a == 0.0 || color.a == 1.0)) { + /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */ + if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) { color.rgb = color.rgb / color.a; } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl index 51bce639b63..c78b2182d04 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl @@ -2,6 +2,7 @@ uniform float ImageTransparencyCutoff = 0.1; uniform sampler2D image; uniform bool imageNearest; +uniform bool imagePremultiplied; uniform float alpha = 0.5; uniform vec2 invertedViewportSize; @@ -43,7 +44,7 @@ void main() vec4 diffuse_color; #if defined(V3D_SHADING_TEXTURE_COLOR) - diffuse_color = workbench_sample_texture(image, uv_interp, imageNearest); + diffuse_color = workbench_sample_texture(image, uv_interp, imageNearest, imagePremultiplied); if (diffuse_color.a < ImageTransparencyCutoff) { discard; } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl index af9f1d14f4a..c673b2484de 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl @@ -7,6 +7,7 @@ uniform float materialRoughness; uniform sampler2D image; uniform float ImageTransparencyCutoff = 0.1; uniform bool imageNearest; +uniform bool imagePremultiplied; #ifdef NORMAL_VIEWPORT_PASS_ENABLED in vec3 normal_viewport; @@ -40,7 +41,7 @@ void main() vec4 color; # if defined(V3D_SHADING_TEXTURE_COLOR) - color = workbench_sample_texture(image, uv_interp, imageNearest); + color = workbench_sample_texture(image, uv_interp, imageNearest, imagePremultiplied); if (color.a < ImageTransparencyCutoff) { discard; } diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c index 6ca55d48681..d570fda9dac 100644 --- a/source/blender/draw/engines/workbench/workbench_materials.c +++ b/source/blender/draw/engines/workbench/workbench_materials.c @@ -296,6 +296,8 @@ void workbench_material_shgroup_uniform(WORKBENCH_PrivateData *wpd, V3D_SHADING_TEXTURE_COLOR) { GPUTexture *tex = GPU_texture_from_blender(material->ima, material->iuser, GL_TEXTURE_2D); DRW_shgroup_uniform_texture(grp, "image", tex); + DRW_shgroup_uniform_bool_copy( + grp, "imagePremultiplied", (material->ima->alpha_mode == IMA_ALPHA_PREMUL)); DRW_shgroup_uniform_bool_copy(grp, "imageNearest", (interp == SHD_INTERP_CLOSEST)); } else { diff --git a/source/blender/draw/modes/object_mode.c b/source/blender/draw/modes/object_mode.c index 8dae160682a..bbf364960c9 100644 --- a/source/blender/draw/modes/object_mode.c +++ b/source/blender/draw/modes/object_mode.c @@ -974,9 +974,10 @@ static void DRW_shgroup_empty_image(OBJECT_Shaders *sh_data, const bool use_alpha_blend = (ob->empty_image_flag & OB_EMPTY_IMAGE_USE_ALPHA_BLEND) != 0; GPUTexture *tex = NULL; + Image *ima = ob->data; - if (ob->data != NULL) { - tex = GPU_texture_from_blender(ob->data, ob->iuser, GL_TEXTURE_2D); + if (ima != NULL) { + tex = GPU_texture_from_blender(ima, ob->iuser, GL_TEXTURE_2D); if (tex) { size[0] = GPU_texture_width(tex); size[1] = GPU_texture_height(tex); @@ -1023,6 +1024,8 @@ static void DRW_shgroup_empty_image(OBJECT_Shaders *sh_data, DRW_shgroup_uniform_float(grp, "size", &ob->empty_drawsize, 1); DRW_shgroup_uniform_vec2(grp, "offset", ob->ima_ofs, 1); DRW_shgroup_uniform_texture(grp, "image", tex); + DRW_shgroup_uniform_bool_copy( + grp, "imagePremultiplied", (ima->alpha_mode == IMA_ALPHA_PREMUL)); DRW_shgroup_uniform_vec4(grp, "objectColor", ob->color, 1); DRW_shgroup_uniform_bool_copy(grp, "useAlphaTest", !use_alpha_blend); if (sh_cfg == GPU_SHADER_CFG_CLIPPED) { diff --git a/source/blender/draw/modes/paint_texture_mode.c b/source/blender/draw/modes/paint_texture_mode.c index a31efccda88..5f833e4c6a1 100644 --- a/source/blender/draw/modes/paint_texture_mode.c +++ b/source/blender/draw/modes/paint_texture_mode.c @@ -206,6 +206,7 @@ static void PAINT_TEXTURE_engine_init(void *vedata) } static DRWShadingGroup *create_texture_paint_shading_group(PAINT_TEXTURE_PassList *psl, + const Image *image, const struct GPUTexture *texture, const DRWContextState *draw_ctx, const bool nearest_interp) @@ -219,6 +220,8 @@ static DRWShadingGroup *create_texture_paint_shading_group(PAINT_TEXTURE_PassLis DRWShadingGroup *grp = DRW_shgroup_create(masking_enabled ? sh_data->image_mask : sh_data->image, psl->image_faces); DRW_shgroup_uniform_texture(grp, "image", texture); + DRW_shgroup_uniform_bool_copy( + grp, "imagePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL)); DRW_shgroup_uniform_float(grp, "alpha", &draw_ctx->v3d->overlay.texture_paint_mode_opacity, 1); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_bool_copy(grp, "nearestInterp", nearest_interp); @@ -227,6 +230,8 @@ static DRWShadingGroup *create_texture_paint_shading_group(PAINT_TEXTURE_PassLis const bool masking_inverted = (imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) > 0; GPUTexture *stencil = GPU_texture_from_blender(imapaint->stencil, NULL, GL_TEXTURE_2D); DRW_shgroup_uniform_texture(grp, "maskingImage", stencil); + DRW_shgroup_uniform_bool_copy( + grp, "maskingImagePremultiplied", (imapaint->stencil->alpha_mode == IMA_ALPHA_PREMUL)); DRW_shgroup_uniform_vec3(grp, "maskingColor", imapaint->stencil_col, 1); DRW_shgroup_uniform_bool_copy(grp, "maskingInvertStencil", masking_inverted); } @@ -283,7 +288,7 @@ static void PAINT_TEXTURE_cache_init(void *vedata) if (tex) { DRWShadingGroup *grp = create_texture_paint_shading_group( - psl, tex, draw_ctx, interp == SHD_INTERP_CLOSEST); + psl, ima, tex, draw_ctx, interp == SHD_INTERP_CLOSEST); stl->g_data->shgroup_image_array[i] = grp; } else { @@ -297,7 +302,7 @@ static void PAINT_TEXTURE_cache_init(void *vedata) if (tex) { DRWShadingGroup *grp = create_texture_paint_shading_group( - psl, tex, draw_ctx, imapaint->interp == IMAGEPAINT_INTERP_CLOSEST); + psl, ima, tex, draw_ctx, imapaint->interp == IMAGEPAINT_INTERP_CLOSEST); stl->g_data->shgroup_image_array[0] = grp; } else { diff --git a/source/blender/draw/modes/shaders/object_empty_image_frag.glsl b/source/blender/draw/modes/shaders/object_empty_image_frag.glsl index deb82a8904e..88220140aec 100644 --- a/source/blender/draw/modes/shaders/object_empty_image_frag.glsl +++ b/source/blender/draw/modes/shaders/object_empty_image_frag.glsl @@ -9,6 +9,7 @@ out vec4 fragColor; #ifndef USE_WIRE uniform sampler2D image; +uniform bool imagePremultiplied; #endif uniform int depthMode; @@ -24,11 +25,15 @@ float linearrgb_to_srgb(float c) } } -vec4 texture_read_as_srgb(sampler2D tex, vec2 co) +vec4 texture_read_as_srgb(sampler2D tex, bool premultiplied, vec2 co) { /* By convention image textures return scene linear colors, but * overlays still assume srgb. */ vec4 color = texture(tex, co); + /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */ + if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) { + color.rgb = color.rgb / color.a; + } color.r = linearrgb_to_srgb(color.r); color.g = linearrgb_to_srgb(color.g); color.b = linearrgb_to_srgb(color.b); @@ -40,7 +45,7 @@ void main() #ifdef USE_WIRE fragColor = finalColor; #else - vec4 tex_col = texture_read_as_srgb(image, texCoord_interp); + vec4 tex_col = texture_read_as_srgb(image, imagePremultiplied, texCoord_interp); fragColor = finalColor * tex_col; if (useAlphaTest) { diff --git a/source/blender/draw/modes/shaders/paint_texture_frag.glsl b/source/blender/draw/modes/shaders/paint_texture_frag.glsl index 4a3c5cb430c..af7ea99e6a1 100644 --- a/source/blender/draw/modes/shaders/paint_texture_frag.glsl +++ b/source/blender/draw/modes/shaders/paint_texture_frag.glsl @@ -7,11 +7,13 @@ in vec2 masking_uv_interp; out vec4 fragColor; uniform sampler2D image; +uniform bool imagePremultiplied; uniform float alpha = 1.0; uniform bool nearestInterp; #ifdef TEXTURE_PAINT_MASK uniform sampler2D maskingImage; +uniform bool maskingImagePremultiplied; uniform vec3 maskingColor; uniform bool maskingInvertStencil; #endif @@ -26,11 +28,15 @@ float linearrgb_to_srgb(float c) } } -vec4 texture_read_as_srgb(sampler2D tex, vec2 co) +vec4 texture_read_as_srgb(sampler2D tex, bool premultiplied, vec2 co) { /* By convention image textures return scene linear colors, but * overlays still assume srgb. */ vec4 color = texture(tex, co); + /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */ + if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) { + color.rgb = color.rgb / color.a; + } color.r = linearrgb_to_srgb(color.r); color.g = linearrgb_to_srgb(color.g); color.b = linearrgb_to_srgb(color.b); @@ -45,11 +51,12 @@ void main() uv = (floor(uv_interp * tex_size) + 0.5) / tex_size; } - vec4 color = texture_read_as_srgb(image, uv); + vec4 color = texture_read_as_srgb(image, imagePremultiplied, uv); color.a *= alpha; #ifdef TEXTURE_PAINT_MASK - vec4 mask = vec4(texture(maskingImage, masking_uv_interp).rgb, 1.0); + vec4 mask = vec4( + texture_read_as_srgb(maskingImage, maskingImagePremultiplied, masking_uv_interp).rgb, 1.0); if (maskingInvertStencil) { mask.rgb = 1.0 - mask.rgb; } |