diff --git a/examples/example/entities/particlesTest.js b/examples/example/entities/particlesTest.js index e7f88ce468..2ebbe6489e 100644 --- a/examples/example/entities/particlesTest.js +++ b/examples/example/entities/particlesTest.js @@ -71,7 +71,7 @@ Entities.editEntity(particles, { radiusSpread: 0.0, radiusStart: 0.0, - particleRadius: 2 * PARTICLE_RADIUS, // Bezier interpolation used means that middle value isn't intersected + particleRadius: PARTICLE_RADIUS, radiusFinish: 0.0 }); break; diff --git a/libraries/entities-renderer/src/textured_particle.slv b/libraries/entities-renderer/src/textured_particle.slv index e1f18c2b5f..d1552f7ba7 100644 --- a/libraries/entities-renderer/src/textured_particle.slv +++ b/libraries/entities-renderer/src/textured_particle.slv @@ -56,13 +56,51 @@ float bezierInterpolate(float y1, float y2, float y3, float u) { return (1.0 - u) * (1.0 - u) * y1 + 2.0 * (1.0 - u) * u * y2 + u * u * y3; } -vec4 interpolate3Vec4(vec4 y1, vec4 y2, vec4 y3, float u) { - return vec4(bezierInterpolate(y1.x, y2.x, y3.x, u), - bezierInterpolate(y1.y, y2.y, y3.y, u), - bezierInterpolate(y1.z, y2.z, y3.z, u), - bezierInterpolate(y1.w, y2.w, y3.w, u)); +float interpolate3Points(float y1, float y2, float y3, float u) { + // Makes the interpolated values intersect the middle value. + + if ((u <= 0.5f && y1 == y2) || (u >= 0.5f && y2 == y3)) { + // Flat line. + return y2; + } + + if ((y2 >= y1 && y2 >= y3) || (y2 <= y1 && y2 <= y3)) { + // U or inverted-U shape. + // Make the slope at y2 = 0, which means that the control points half way between the value points have the value y2. + if (u <= 0.5f) { + return bezierInterpolate(y1, y2, y2, 2.0f * u); + } else { + return bezierInterpolate(y2, y2, y3, 2.0f * u - 1.0f); + } + + } else { + // L or inverted and/or mirrored L shape. + // Make the slope at y2 be the slope between y1 and y3, up to a maximum of double the minimum of the slopes between y1 + // and y2, and y2 and y3. Use this slope to calculate the control points half way between the value points. + // Note: The maximum ensures that the control points and therefore the interpolated values stay between y1 and y3. + float slope = y3 - y1; + float slope12 = y2 - y1; + float slope23 = y3 - y2; + if (abs(slope) > abs(2.0f * slope12)) { + slope = 2.0f * slope12; + } else if (abs(slope) > abs(2.0f * slope23)) { + slope = 2.0f * slope23; + } + + if (u <= 0.5f) { + return bezierInterpolate(y1, y2 - slope / 2.0f, y2, 2.0f * u); + } else { + return bezierInterpolate(y2, y2 + slope / 2.0f, y3, 2.0f * u - 1.0f); + } + } } +vec4 interpolate3Vec4(vec4 y1, vec4 y2, vec4 y3, float u) { + return vec4(interpolate3Points(y1.x, y2.x, y3.x, u), + interpolate3Points(y1.y, y2.y, y3.y, u), + interpolate3Points(y1.z, y2.z, y3.z, u), + interpolate3Points(y1.w, y2.w, y3.w, u)); +} void main(void) { TransformCamera cam = getTransformCamera(); @@ -82,7 +120,7 @@ void main(void) { varColor = interpolate3Vec4(particle.color.start, particle.color.middle, particle.color.finish, age); // anchor point in eye space - float radius = bezierInterpolate(particle.radius.start, particle.radius.middle, particle.radius.finish, age); + float radius = interpolate3Points(particle.radius.start, particle.radius.middle, particle.radius.finish, age); vec4 quadPos = radius * UNIT_QUAD[twoTriID]; vec4 anchorPoint;