From e82830147c7e30686f98624170c995963db5ae31 Mon Sep 17 00:00:00 2001 From: RuiwenTang Date: Wed, 6 Nov 2024 12:33:49 +0800 Subject: [PATCH] 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/ --- src/renderer/gl_engine/tvgGlCommon.h | 6 +- src/renderer/gl_engine/tvgGlRenderer.cpp | 21 +++- src/renderer/gl_engine/tvgGlShaderSrc.cpp | 136 ++++++++++++++++++---- 3 files changed, 132 insertions(+), 31 deletions(-) diff --git a/src/renderer/gl_engine/tvgGlCommon.h b/src/renderer/gl_engine/tvgGlCommon.h index 077acadd..0ae4267e 100644 --- a/src/renderer/gl_engine/tvgGlCommon.h +++ b/src/renderer/gl_engine/tvgGlCommon.h @@ -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] = {}; }; diff --git a/src/renderer/gl_engine/tvgGlRenderer.cpp b/src/renderer/gl_engine/tvgGlRenderer.cpp index 2b2ba81b..85b5ea6c 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -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, diff --git a/src/renderer/gl_engine/tvgGlShaderSrc.cpp b/src/renderer/gl_engine/tvgGlShaderSrc.cpp index a0b684b6..659c7e58 100644 --- a/src/renderer/gl_engine/tvgGlShaderSrc.cpp +++ b/src/renderer/gl_engine/tvgGlShaderSrc.cpp @@ -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 +