You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在GLSL中实现细胞噪声(Voronoi图)交点的定位与可视化?

Pure GLSL Implementation to Find and Draw Voronoi Cell Intersection Points

I totally get the frustration of trying to nail down Voronoi vertex detection in GLSL—those intersection points where three cells meet can be tricky to compute without external libraries. Let's break this down, starting with the math behind Voronoi vertices, then modifying your existing code to detect these points and draw circles on them entirely in GLSL.

Key Background: What are Voronoi Vertices?

Voronoi vertices are the points where three Voronoi cells meet, and they're equidistant from the three corresponding generator points. Mathematically, this means the vertex lies at the intersection of the perpendicular bisectors of the line segments connecting each pair of the three generators. We need to:

  • Track nearby generator points in each grid cell
  • Check all triplets of generators to find potential vertices
  • Verify the vertex is indeed a valid Voronoi vertex (closer to these three generators than any others)
  • Draw circles around valid vertices

Modified GLSL Code with Vertex Detection

Here's the updated code that builds on your original Voronoi implementation. I'll walk through the key changes after the code:

#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

// Random generator function (same as your original)
vec2 rand(vec2 co){
    return vec2(
        fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453),
        fract(sin(dot(co, vec2(78.9898, 32.233))) * 237374.5453)
    );
}

// Calculate the perpendicular bisector intersection of three points (Voronoi vertex)
// Returns whether the vertex is valid, and the vertex position if so
bool getVoronoiVertex(vec2 p1, vec2 p2, vec2 p3, out vec2 vertex) {
    // Perpendicular bisector of p1-p2: ax + by = c
    float a1 = 2.0 * (p2.x - p1.x);
    float b1 = 2.0 * (p2.y - p1.y);
    float c1 = p2.x*p2.x - p1.x*p1.x + p2.y*p2.y - p1.y*p1.y;
    
    // Perpendicular bisector of p2-p3: dx + ey = f
    float a2 = 2.0 * (p3.x - p2.x);
    float b2 = 2.0 * (p3.y - p2.y);
    float c2 = p3.x*p3.x - p2.x*p2.x + p3.y*p3.y - p2.y*p2.y;
    
    // Solve linear system: a1x + b1y = c1; a2x + b2y = c2
    float det = a1 * b2 - a2 * b1;
    if (abs(det) < 0.001) return false; // Lines are parallel, no unique intersection
    
    vertex.x = (b2 * c1 - b1 * c2) / det;
    vertex.y = (a1 * c2 - a2 * c1) / det;
    
    // Verify the vertex is equidistant to all three points (within epsilon)
    float d1 = distance(vertex, p1);
    float d2 = distance(vertex, p2);
    float d3 = distance(vertex, p3);
    if (abs(d1 - d2) > 0.001 || abs(d2 - d3) > 0.001) return false;
    
    return true;
}

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution.xy;
    st *= 10.0; // Scale up to see larger Voronoi cells
    vec2 i_st = floor(st);
    vec2 f_st = fract(st);
    float intensity = u_mouse.x / u_resolution.x;
    
    // Store nearby generator points (relative to current fragment's grid cell)
    vec2 generators[9];
    int genCount = 0;
    
    // Collect all generators in 3x3 neighborhood
    for (int x = -1; x <= 1; ++x) {
        for (int y = -1; y <= 1; y++){
            vec2 neighbor = vec2(x, y);
            vec2 point = rand(i_st + neighbor);
            // Convert generator to fragment-local coordinates
            generators[genCount] = neighbor + point * intensity;
            genCount++;
        }
    }
    
    // Base Voronoi distance (for original visualization)
    float m_dist = 1.0;
    for (int i = 0; i < genCount; i++) {
        float dist = distance(generators[i], f_st);
        m_dist = min(m_dist, dist);
    }
    
    // Check all triplets of generators for Voronoi vertices
    float vertexCircle = 0.0;
    float circleRadius = 0.02; // Adjust size of circles here
    
    for (int i = 0; i < genCount; i++) {
        for (int j = i+1; j < genCount; j++) {
            for (int k = j+1; k < genCount; k++) {
                vec2 vertex;
                if (getVoronoiVertex(generators[i], generators[j], generators[k], vertex)) {
                    // Verify this vertex is closer to these three generators than any others
                    bool isValidVertex = true;
                    float distToVertex = distance(vertex, f_st);
                    for (int l = 0; l < genCount; l++) {
                        if (l == i || l == j || l == k) continue;
                        if (distance(generators[l], vertex) < distToVertex - 0.001) {
                            isValidVertex = false;
                            break;
                        }
                    }
                    
                    // If valid, draw a circle around the vertex
                    if (isValidVertex) {
                        float circle = smoothstep(circleRadius + 0.005, circleRadius, distance(f_st, vertex));
                        vertexCircle = max(vertexCircle, circle);
                    }
                }
            }
        }
    }
    
    // Combine Voronoi visualization with vertex circles
    vec3 color = vec3(m_dist);
    color = mix(color, vec3(1.0, 0.0, 0.0), vertexCircle); // Red circles for vertices
    
    gl_FragColor = vec4(color, 1.0);
}

Key Changes Explained

Let's break down the critical parts of the code:

  • Generator Collection: Instead of just tracking the minimum distance, we now collect all 9 generators in the 3x3 neighborhood around the current grid cell. This gives us all potential candidates for forming Voronoi vertices.
  • Vertex Calculation Function: The getVoronoiVertex function solves the linear system of perpendicular bisectors for three points to find their intersection. It also checks if the intersection is equidistant to all three points (to avoid edge cases like parallel lines).
  • Vertex Validation: After calculating a potential vertex, we check if it's closer to the three generators than any other nearby generator. This ensures we only keep valid Voronoi vertices (not intersections that lie inside another cell).
  • Circle Drawing: For each valid vertex, we use smoothstep to draw a sharp circle around it, mixing the red circle color with the original Voronoi distance color.

Tuning Tips

  • Adjust circleRadius to make the vertex circles larger or smaller.
  • Modify the scaling factor (st *= 10.0) to change the size of the Voronoi cells.
  • Adjust the epsilon values (like 0.001) if you notice false positives/negatives—these account for floating-point precision errors.

内容的提问来源于stack exchange,提问作者Abu Hurairah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 19:48:16