mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 10:48:53 +01:00
internal/atlas: optimization: send premultiplied alpha from vertex to fragment shader. (#1996)
Note that this applies only to the builtin shaders - interface for Kage stays unchanged for compatibility. Minor compatibility delta: when interpolating alpha values, previous code has created nonsense values, such as, when interpolating from fully-transparent-black (0,0,0,0) to opaque-white (1,1,1,1), something like half-transparent-grey (0.25,0.25,0.25,0.5) where half-transparent-white (0.5,0.5,0.5,0.5) is used by the new code. I assume this is a strict improvement, however this may warrant some testing. Possible later improvement could be moving the premultiplication from fragment shader to CPU. Did not do this as it makes the code rather inconsistent of Kage vs built-in shader usage. Updates #1772
This commit is contained in:
parent
476f4e3f9a
commit
f2209a0b51
13
image.go
13
image.go
@ -223,8 +223,13 @@ type Vertex struct {
|
||||
SrcY float32
|
||||
|
||||
// ColorR/ColorG/ColorB/ColorA represents color scaling values.
|
||||
// 1 means the original source image color is used.
|
||||
// 0 means a transparent color is used.
|
||||
// Their interpretation depends on the concrete draw call used:
|
||||
// - DrawTriangles: straight-alpha encoded color multiplier.
|
||||
// If ColorA is 0, the vertex is fully transparent and color is ignored.
|
||||
// If ColorA is 1, the vertex has the color (ColorR, ColorG, ColorB).
|
||||
// Vertex colors are interpolated linearly respecting alpha.
|
||||
// - DrawTrianglesShader: arbitrary floating point values sent to the shader.
|
||||
// These are interpolated linearly and independently from each other.
|
||||
ColorR float32
|
||||
ColorG float32
|
||||
ColorB float32
|
||||
@ -293,6 +298,8 @@ const MaxIndicesNum = graphics.IndicesNum
|
||||
|
||||
// DrawTriangles draws triangles with the specified vertices and their indices.
|
||||
//
|
||||
// Vertex contains color values, which are interpreted as straight-alpha colors.
|
||||
//
|
||||
// If len(indices) is not multiple of 3, DrawTriangles panics.
|
||||
//
|
||||
// If len(indices) is more than MaxIndicesNum, DrawTriangles panics.
|
||||
@ -406,6 +413,8 @@ func init() {
|
||||
|
||||
// DrawTrianglesShader draws triangles with the specified vertices and their indices with the specified shader.
|
||||
//
|
||||
// Vertex contains color values, which can be interpreted for any purpose by the shader.
|
||||
//
|
||||
// For the details about the shader, see https://ebiten.org/documents/shader.html.
|
||||
//
|
||||
// If len(indices) is not multiple of 3, DrawTrianglesShader panics.
|
||||
|
@ -420,16 +420,38 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
|
||||
i.processSrc(src)
|
||||
}
|
||||
|
||||
// If a color matrix is used, but the matrix is merely a scaling matrix,
|
||||
// and the scaling cannot cause out-of-range colors, do not use a color matrix
|
||||
// when rendering but instead multiply all vertex colors by the scale.
|
||||
// This speeds up rendering.
|
||||
//
|
||||
// NOTE: this is only safe when not using a custom Kage shader,
|
||||
// as custom shaders may be using vertex colors for different purposes
|
||||
// than colorization. However, currently there are no Ebiten APIs that
|
||||
// support both shaders and color matrices.
|
||||
cr := float32(1)
|
||||
cg := float32(1)
|
||||
cb := float32(1)
|
||||
ca := float32(1)
|
||||
if !colorm.IsIdentity() && colorm.ScaleOnly() {
|
||||
cr = colorm.At(0, 0)
|
||||
cg = colorm.At(1, 1)
|
||||
cb = colorm.At(2, 2)
|
||||
ca = colorm.At(3, 3)
|
||||
colorm = affine.ColorMIdentity{}
|
||||
r := colorm.At(0, 0)
|
||||
g := colorm.At(1, 1)
|
||||
b := colorm.At(2, 2)
|
||||
a := colorm.At(3, 3)
|
||||
if r >= 0 && g >= 0 && b >= 0 && a >= 0 && r <= 1 && g <= 1 && b <= 1 {
|
||||
// Color matrices work on non-premultiplied colors.
|
||||
// This color matrix can only make colors darker or equal,
|
||||
// and thus can never invoke color clamping.
|
||||
// Thus the simpler vertex color scale based shader can be used.
|
||||
//
|
||||
// Negative color values can become positive and out-of-range
|
||||
// after applying to vertex colors below, which can make the min() in the shader kick in.
|
||||
//
|
||||
// Alpha values smaller than 0, combined with negative vertex colors,
|
||||
// can also make the min() kick in, so that shall be ruled out too.
|
||||
cr, cg, cb, ca = r, g, b, a
|
||||
colorm = affine.ColorMIdentity{}
|
||||
}
|
||||
}
|
||||
|
||||
var dx, dy float32
|
||||
|
@ -83,7 +83,8 @@ vertex VertexOut VertexShader(
|
||||
VertexOut out = {
|
||||
.position = projectionMatrix * float4(in.position, 0, 1),
|
||||
.tex = in.tex,
|
||||
.color = in.color,
|
||||
// Fragment shader wants premultiplied alpha.
|
||||
.color = float4(in.color.rgb, 1) * in.color.a,
|
||||
};
|
||||
|
||||
return out;
|
||||
@ -230,13 +231,12 @@ struct FragmentShaderImpl {
|
||||
if (useColorM) {
|
||||
c.rgb /= c.a + (1.0 - sign(c.a));
|
||||
c = (color_matrix_body * c) + color_matrix_translation;
|
||||
c *= v.color;
|
||||
c.rgb *= c.a;
|
||||
c *= v.color;
|
||||
c.rgb = min(c.rgb, c.a);
|
||||
} else {
|
||||
float4 s = v.color;
|
||||
c *= float4(s.r, s.g, s.b, 1.0) * s.a;
|
||||
c *= v.color;
|
||||
}
|
||||
c = min(c, c.a);
|
||||
return c;
|
||||
}
|
||||
};
|
||||
|
@ -117,7 +117,9 @@ varying vec4 varying_color_scale;
|
||||
|
||||
void main(void) {
|
||||
varying_tex = A1;
|
||||
varying_color_scale = A2;
|
||||
|
||||
// Fragment shader wants premultiplied alpha.
|
||||
varying_color_scale = vec4(A2.rgb, 1) * A2.a;
|
||||
|
||||
mat4 projection_matrix = mat4(
|
||||
vec4(2.0 / viewport_size.x, 0, 0, 0),
|
||||
@ -267,16 +269,18 @@ void main(void) {
|
||||
color.rgb /= color.a + (1.0 - sign(color.a));
|
||||
// Apply the color matrix or scale.
|
||||
color = (color_matrix_body * color) + color_matrix_translation;
|
||||
color *= varying_color_scale;
|
||||
// Premultiply alpha
|
||||
color.rgb *= color.a;
|
||||
// Apply color scale.
|
||||
color *= varying_color_scale;
|
||||
// Clamp the output.
|
||||
color.rgb = min(color.rgb, color.a);
|
||||
# else
|
||||
vec4 s = varying_color_scale;
|
||||
color *= vec4(s.r, s.g, s.b, 1.0) * s.a;
|
||||
// Apply color scale.
|
||||
color *= varying_color_scale;
|
||||
// No clamping needed as the color matrix shader is used then.
|
||||
# endif // defined(USE_COLOR_MATRIX)
|
||||
|
||||
color = min(color, color.a);
|
||||
|
||||
gl_FragColor = color;
|
||||
|
||||
#endif // defined(FILTER_SCREEN)
|
||||
|
Loading…
Reference in New Issue
Block a user