gl_engine: Support new radial gradient data struct and calculation

Change the shader and uniform struct to support new radial gradient.
The mathematical calculation comes from https://skia.org/docs/dev/design/conical/
This commit is contained in:
RuiwenTang 2024-11-06 12:33:49 +08:00 committed by Hermet Park
parent 159bf6949e
commit e82830147c
3 changed files with 132 additions and 31 deletions

View file

@ -89,9 +89,9 @@ struct GlLinearGradientBlock
struct GlRadialGradientBlock
{
alignas(16) float nStops[4] = {};
alignas(16) float centerPos[2] = {};
alignas(8) float radius[2] = {};
alignas(8) float stopPoints[MAX_GRADIENT_STOPS] = {};
alignas(16) float centerPos[4] = {};
alignas(16) float radius[2] = {};
alignas(16) float stopPoints[MAX_GRADIENT_STOPS] = {};
alignas(16) float stopColors[4 * MAX_GRADIENT_STOPS] = {};
};

View file

@ -21,6 +21,7 @@
*/
#include "tvgMath.h"
#include "tvgFill.h"
#include "tvgGlRenderer.h"
#include "tvgGlGpuBuffer.h"
#include "tvgGlGeometry.h"
@ -411,12 +412,20 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
}
gradientBlock.nStops[0] = nStops * 1.f;
float x, y, r;
radialFill->radial(&x, &y, &r);
gradientBlock.centerPos[0] = x;
gradientBlock.centerPos[1] = y;
gradientBlock.radius[0] = r;
float x, y, r, fx, fy, fr;
x = P(radialFill)->cx;
y = P(radialFill)->cy;
r = P(radialFill)->r;
fx = P(radialFill)->fx;
fy = P(radialFill)->fy;
fr = P(radialFill)->fr;
gradientBlock.centerPos[0] = fx;
gradientBlock.centerPos[1] = fy;
gradientBlock.centerPos[2] = x;
gradientBlock.centerPos[3] = y;
gradientBlock.radius[0] = fr;
gradientBlock.radius[1] = r;
gradientBinding = GlBindingResource{
2,

View file

@ -208,31 +208,123 @@ void main()
});
std::string STR_RADIAL_GRADIENT_VARIABLES = TVG_COMPOSE_SHADER(
layout(std140) uniform GradientInfo { \n
vec4 nStops; \n
vec2 centerPos; \n
vec2 radius; \n
vec4 stopPoints[MAX_STOP_COUNT / 4]; \n
vec4 stopColors[MAX_STOP_COUNT]; \n
} uGradientInfo ; \n
layout(std140) uniform GradientInfo { \n
vec4 nStops; \n
vec4 centerPos; \n
vec2 radius; \n
vec4 stopPoints[MAX_STOP_COUNT / 4]; \n
vec4 stopColors[MAX_STOP_COUNT]; \n
} uGradientInfo ; \n
);
std::string STR_RADIAL_GRADIENT_MAIN = TVG_COMPOSE_SHADER(
out vec4 FragColor; \n
void main() \n
{ \n
vec2 pos = vPos; \n
\n
float ba = uGradientInfo.radius.x; \n
float d = distance(uGradientInfo.centerPos, pos); \n
float t = (d / ba); \n
\n
t = gradientWrap(t); \n
\n
vec4 color = gradient(t, (d / ba), d); \n
\n
FragColor = vec4(color.rgb * color.a, color.a); \n
});
out vec4 FragColor; \n
\n
mat3 radial_matrix(vec2 p0, vec2 p1) \n
{ \n
mat3 a = mat3(0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0); \n
mat3 b = mat3(p1.y - p0.y, p0.x - p1.x, 0.0, p1.x - p0.x, p1.y - p0.y, 0.0, p0.x, p0.y, 1.0); \n
return a * inverse(b); \n
} \n
\n
vec2 compute_radial_t(vec2 c0, float r0, vec2 c1, float r1, vec2 pos) \n
{ \n
const float scalar_nearly_zero = 1.0 / float(1 << 12); \n
float d_center = distance(c0, c1); \n
float d_radius = r1 - r0; \n
bool radial = d_center < scalar_nearly_zero; \n
bool strip = abs(d_radius) < scalar_nearly_zero; \n
\n
if (radial) { \n
if (strip) return vec2(0.0, -1.0); \n
\n
float scale = 1.0 / d_radius; \n
float scale_sign = sign(d_radius); \n
float bias = r0 / d_radius; \n
vec2 pt = (pos - c0) * scale; \n
float t = length(pt) * scale_sign - bias; \n
return vec2(t, 1.0); \n
} else if (strip) { \n
mat3 transform = radial_matrix(c0, c1); \n
float r = r0 / d_center; \n
float r_2 = r * r; \n
vec2 pt = (transform * vec3(pos.xy, 1.0)).xy; \n
float t = r_2 - pt.y * pt.y; \n
\n
if (t < 0.0) return vec2(0.0, -1.0); \n
\n
t = pt.x + sqrt(t); \n
return vec2(t, 1.0); \n
} else { \n
float f = r0 / (r0 - r1); \n
bool is_swapped = abs(f - 1.0) < scalar_nearly_zero; \n
if (is_swapped) { \n
vec2 c = c0; \n
c0 = c1; \n
c1 = c; \n
f = 0.0; \n
} \n
vec2 cf = c0 * (1.0 - f) + c1 * f; \n
mat3 transform = radial_matrix(cf, c1); \n
\n
float scale_x = abs(1.0 - f); \n
float scale_y = scale_x; \n
float r1 = abs(r1 - r0) / d_center; \n
bool is_focal_on_circle = abs(r1 - 1.0) < scalar_nearly_zero; \n
if (is_focal_on_circle) { \n
scale_x *= 0.5; \n
scale_y *= 0.5; \n
} else { \n
scale_x *= r1 / (r1 * r1 - 1.0); \n
scale_y /= sqrt(abs(r1 * r1 - 1.0)); \n
} \n
transform = mat3(scale_x, 0.0, 0.0, 0.0, scale_y, 0.0, 0.0, 0.0, 1.0) * transform; \n
\n
vec2 pt = (transform * vec3(pos.xy, 1.0)).xy; \n
\n
float inv_r1 = 1.0 / r1; \n
float d_radius_sign = sign(1.0 - f); \n
bool is_well_behaved = !is_focal_on_circle && r1 > 1.0; \n
\n
float x_t = -1.0; \n
if (is_focal_on_circle) x_t = dot(pt, pt) / pt.x; \n
else if (is_well_behaved) x_t = length(pt) - pt.x * inv_r1; \n
else { \n
float temp = pt.x * pt.x - pt.y * pt.y; \n
if (temp >= 0.0) { \n
if (is_swapped || d_radius_sign < 0.0) { \n
x_t = -sqrt(temp) - pt.x * inv_r1; \n
} else { \n
x_t = sqrt(temp) - pt.x * inv_r1; \n
} \n
} \n
} \n
\n
if (!is_well_behaved && x_t < 0.0) return vec2(0.0, -1.0); \n
\n
float t = f + d_radius_sign * x_t; \n
if (is_swapped) t = 1.0 - t; \n
return vec2(t, 1.0); \n
} \n
} \n
\n
void main() \n
{ \n
vec2 pos = vPos; \n
vec2 res = compute_radial_t(uGradientInfo.centerPos.xy, \n
uGradientInfo.radius.x, \n
uGradientInfo.centerPos.zw, \n
uGradientInfo.radius.y, \n
pos); \n
if (res.y < 0.0) { \n
FragColor = vec4(0.0, 0.0, 0.0, 0.0); \n
return; \n
} \n
float t = gradientWrap(res.x); \n
vec4 color = gradient(t, res.x, length(pos - uGradientInfo.centerPos.xy)); \n
FragColor = vec4(color.rgb * color.a, color.a); \n
}
);
std::string STR_LINEAR_GRADIENT_FRAG_SHADER =
STR_GRADIENT_FRAG_COMMON_VARIABLES +