/** * @see https://github.com/ShenCiao/Ciallo/blob/main/docs/shaders/articulatedLine.vert */ export declare enum Location { BARYCENTRIC = 0, POINTA = 1, POINTB = 2, VERTEX_NUM = 3 } export declare const vert = "\n layout(std140) uniform SceneUniforms {\n mat3 u_ProjectionMatrix;\n mat3 u_ViewMatrix;\n mat3 u_ViewProjectionInvMatrix;\n vec4 u_BackgroundColor;\n vec4 u_GridColor;\n float u_ZoomScale;\n float u_CheckboardStyle;\n };\n \n \n#ifdef USE_WIREFRAME\n layout(location = 0) in vec3 a_Barycentric;\n out vec3 v_Barycentric;\n#endif\n\n layout(location = 1) in vec4 a_PointA;\n layout(location = 2) in vec4 a_PointB;\n layout(location = 3) in float a_VertexNum;\n \n layout(std140) uniform ShapeUniforms {\n mat3 u_ModelMatrix;\n vec4 u_StrokeColor;\n vec4 u_ZIndexStrokeWidth;\n vec4 u_Opacity;\n vec4 u_Stamp;\n };\n\n out vec2 p0;\n out float r0;\n out float l0;\n out vec2 p1;\n out float r1;\n out float l1;\n out vec2 p;\n \n void main() {\n \n#ifdef USE_WIREFRAME\n v_Barycentric = a_Barycentric;\n#endif\n\n \n mat3 model;\n vec4 fillColor;\n vec4 strokeColor;\n float zIndex;\n float strokeWidth;\n\n model = u_ModelMatrix;\n strokeColor = u_StrokeColor;\n zIndex = u_ZIndexStrokeWidth.x;\n strokeWidth = u_ZIndexStrokeWidth.y;\n\n p0 = a_PointA.xy;\n r0 = a_PointA.z;\n l0 = a_PointA.w;\n p1 = a_PointB.xy;\n r1 = a_PointB.z;\n l1 = a_PointB.w;\n\n vec2 tangentDirection = normalize(p1 - p0);\n vec2 normalDirection = vec2(-tangentDirection.y, tangentDirection.x);\n float cosTheta = (r0 - r1)/distance(p0, p1);\n if(abs(cosTheta) >= 1.0) return; \n\n vec2 tangent = normalize(p1 - p0);\n vec2 normal = vec2(-tangent.y, tangent.x);\n\n float vertexNum = a_VertexNum;\n\n vec2 position;\n vec2 offsetSign;\n float r;\n if (vertexNum < 0.5) {\n position = p0;\n r = r0;\n offsetSign = vec2(-1.0, -1.0);\n } else if (vertexNum < 1.5) {\n position = p0;\n r = r0;\n offsetSign = vec2(-1.0, 1.0);\n } else if (vertexNum < 2.5) {\n position = p1;\n r = r1;\n offsetSign = vec2(1.0, 1.0);\n } else if (vertexNum < 3.5) {\n position = p1;\n r = r1;\n offsetSign = vec2(1.0, -1.0);\n }\n\n // Apply the half angle formula from cos(theta) to tan(theta/2)\n float tanHalfTheta = sqrt((1.0+cosTheta) / (1.0-cosTheta));\n float cotHalfTheta = 1.0 / tanHalfTheta;\n float normalTanValue = vec4(tanHalfTheta, tanHalfTheta, cotHalfTheta, cotHalfTheta)[gl_VertexID];\n // Corner case: The small circle is very close to the big one, casuing large offset in the normal direction, discard the edge\n if(normalTanValue > 10.0 || normalTanValue < 0.1) return;\n \n vec2 trapzoidVertexPosition = position + \n offsetSign.x * r * tangentDirection + \n offsetSign.y * r * normalDirection * normalTanValue;\n p = trapzoidVertexPosition;\n \n gl_Position = vec4((u_ProjectionMatrix \n * u_ViewMatrix\n * model \n * vec3(trapzoidVertexPosition, 1)).xy, zIndex, 1);\n }\n "; export declare const frag = "\n layout(std140) uniform SceneUniforms {\n mat3 u_ProjectionMatrix;\n mat3 u_ViewMatrix;\n mat3 u_ViewProjectionInvMatrix;\n vec4 u_BackgroundColor;\n vec4 u_GridColor;\n float u_ZoomScale;\n float u_CheckboardStyle;\n };\n \n layout(std140) uniform ShapeUniforms {\n mat3 u_ModelMatrix;\n vec4 u_StrokeColor;\n vec4 u_ZIndexStrokeWidth;\n vec4 u_Opacity;\n vec4 u_Stamp;\n };\n \n \n#ifdef USE_WIREFRAME\n in vec3 v_Barycentric;\n\n float edgeFactor() {\n float u_WireframeLineWidth = 1.0;\n vec3 d = fwidth(v_Barycentric);\n vec3 a3 = smoothstep(vec3(0.0), d * u_WireframeLineWidth, v_Barycentric);\n return min(min(a3.x, a3.y), a3.z);\n }\n#endif\n\n \n in vec2 p0;\n in float r0;\n in float l0;\n in vec2 p1;\n in float r1;\n in float l1;\n in vec2 p;\n\n #ifdef USE_FILLIMAGE\n uniform sampler2D u_Texture;\n #endif\n\n out vec4 outputColor;\n \n float epsilon = 0.000001;\n\n const int EquiDistance = 0, RatioDistance = 1;\n\n float x2n(float x){\n float stampInterval = u_Stamp.x;\n int stampMode = int(u_Stamp.w);\n if(stampMode == EquiDistance) return x / stampInterval;\n if(stampMode == RatioDistance){\n float L = distance(p0, p1);\n if(r0 == r1) return x/(stampInterval*r0);\n else return -L / stampInterval / (r0 - r1) * log(1.0 - (1.0 - r1/r0)/L * x);\n }\n }\n\n float n2x(float n){\n float stampInterval = u_Stamp.x;\n int stampMode = int(u_Stamp.w);\n if(stampMode == EquiDistance) return n * stampInterval;\n if(stampMode == RatioDistance){\n float L = distance(p0, p1);\n if(r0 == r1) return n * stampInterval * r0;\n else return L * (1.0-exp(-(r0-r1)*n*stampInterval/L)) / (1.0-r1/r0);\n }\n }\n\n mat2 rotate(float angle){\n return mat2(cos(angle), -sin(angle), sin(angle), cos(angle));\n }\n\n \n \n float random (in vec2 st) {\n return fract(sin(dot(st.xy,\n vec2(12.9898,78.233)))*\n 43758.5453123);\n }\n\n\n // Based on Morgan McGuire @morgan3d\n // https://www.shadertoy.com/view/4dS3Wd\n float noise (in vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n\n // Four corners in 2D of a tile\n float a = random(i);\n float b = random(i + vec2(1.0, 0.0));\n float c = random(i + vec2(0.0, 1.0));\n float d = random(i + vec2(1.0, 1.0));\n\n vec2 u = f * f * (3.0 - 2.0 * f);\n\n return mix(a, b, u.x) +\n (c - a)* u.y * (1.0 - u.x) +\n (d - b) * u.x * u.y;\n }\n\n #define OCTAVES 6\n float fbm (in vec2 st) {\n // Initial values\n float value = 0.0;\n float amplitude = .5;\n float frequency = 0.;\n //\n // Loop of octaves\n for (int i = 0; i < OCTAVES; i++) {\n value += amplitude * noise(st);\n st *= 2.;\n amplitude *= .5;\n }\n return value;\n }\n\n \n void main() {\n float strokeWidth;\n vec4 strokeColor;\n float opacity;\n float strokeOpacity;\n float brushType;\n \n strokeColor = u_StrokeColor;\n strokeWidth = u_ZIndexStrokeWidth.y;\n brushType = u_ZIndexStrokeWidth.z;\n opacity = u_Opacity.x;\n strokeOpacity = u_Opacity.z;\n\n vec2 tangent = normalize(p1 - p0);\n vec2 normal = vec2(-tangent.y, tangent.x);\n\n // The local coordinate orgin at p0, x axis along the tangent direct.\n float len = distance(p1, p0);\n vec2 pLocal = vec2(dot(p-p0, tangent), dot(p-p0, normal));\n vec2 p0Local = vec2(0, 0);\n vec2 p1Local = vec2(len, 0);\n\n float cosTheta = (r0 - r1)/len;\n float d0 = distance(p, p0);\n float d0cos = pLocal.x / d0;\n float d1 = distance(p, p1);\n float d1cos = (pLocal.x - len) / d1;\n\n // Remove corners\n if(d0cos < cosTheta && d0 > r0) discard;\n if(d1cos > cosTheta && d1 > r1) discard;\n \n vec4 color = strokeColor;\n\n if (brushType < 0.5) {\n // Vanilla brush\n if(d0 < r0 && d1 < r1) discard;\n float A = (d0 < r0 || d1 < r1) ? 1.0 - sqrt(1.0 - color.a) : color.a;\n outputColor = vec4(color.rgb, A);\n return;\n } else if (brushType < 1.5) {\n // Stamp brush\n // The method here is not published yet, it should be explained in a 10min video.\n // The footprint is a disk instead of a square.\n // We set a quadratic polynomial to calculate the effect range, the range on polyline edge footprint can touch the current pixel.\n // Two roots of the quadratic polynomial are the effectRangeFront and effectRangeBack.\n // Formulas from SIGGRAPH 2022 Talk - A Fast & Robust Solution for Cubic & Higher-Order Polynomials\n // The quadratic equation\n float a, b, c, delta;\n a = 1.0 - pow(cosTheta, 2.0);\n b = 2.0 * (r0 * cosTheta - pLocal.x);\n c = pow(pLocal.x, 2.0) + pow(pLocal.y, 2.0) - pow(r0, 2.0);\n delta = pow(b, 2.0) - 4.0*a*c;\n if(delta <= 0.0) discard; // This should never happen.\n \n // Solve the quadratic equation\n // Need to learn how to solve a quadratic equation? Check this talk by Cem Yuksel:\n // https://www.youtube.com/watch?v=ok0EZ0fBCMA\n float tempMathBlock = b + sign(b) * sqrt(delta);\n float x1 = -2.0 * c / tempMathBlock;\n float x2 = -tempMathBlock / (2.0*a);\n float effectRangeFront = x1 <= x2 ? x1 : x2;\n float effectRangeBack = x1 > x2 ? x1 : x2;\n\n float stampInterval = u_Stamp.x;\n float noiseFactor = u_Stamp.y;\n float rotationFactor = u_Stamp.z;\n\n // With the distance to the polyline's first vertex, we can compute a \"stamp index\" value.\n // which indicate the number of stamps from the first vertex to current point.\n // We stamp on polyline every time the stamp index comes to an integer.\n float index0 = l0/stampInterval; // The stamp index of vertex0.\n float startIndex, endIndex;\n if (effectRangeFront <= 0.0){\n startIndex = ceil(index0);\n }\n else{\n startIndex = ceil(index0 + x2n(effectRangeFront));\n }\n float index1 = l1/stampInterval;\n float backIndex = x2n(effectRangeBack) + index0;\n endIndex = index1 < backIndex ? index1 : backIndex;\n if(startIndex > endIndex) discard;\n\n // The main loop to sample and blend color from the footprint, from startIndex to endIndex\n int MAX_i = 128; float currIndex = startIndex;\n float A = 0.0;\n for(int i = 0; i < MAX_i; i++){\n float currStampLocalX = n2x(currIndex - index0);\n // Apply roation and sample the footprint.\n vec2 pToCurrStamp = pLocal - vec2(currStampLocalX, 0.0);\n float currStampRadius = r0 - cosTheta * currStampLocalX;\n float angle = rotationFactor*radians(360.0*fract(sin(currIndex)*1.0));\n pToCurrStamp *= rotate(angle);\n vec2 textureCoordinate = (pToCurrStamp/currStampRadius + 1.0)/2.0;\n\n float opacity = 1.0;\n #ifdef USE_FILLIMAGE\n opacity = texture(SAMPLER_2D(u_Texture), textureCoordinate).a;\n #endif\n // Blend opacity.\n float opacityNoise = noiseFactor*fbm(textureCoordinate*50.0);\n opacity = clamp(opacity - opacityNoise, 0.0, 1.0) * color.a;\n A = A * (1.0-opacity) + opacity;\n\n currIndex += 1.0;\n if(currIndex > endIndex) break;\n }\n if(A < 1e-4) discard;\n outputColor = vec4(color.rgb, A);\n }\n \n \n#ifdef USE_WIREFRAME\n vec3 u_WireframeLineColor = vec3(0.0, 0.0, 0.0);\n\n outputColor.xyz = mix(outputColor.xyz, u_WireframeLineColor, (1.0 - edgeFactor()));\n if (any(lessThan(v_Barycentric, vec3(0.01)))) {\n outputColor.a = 0.95;\n }\n#endif\n\n }\n ";