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

如何使用Fragment Shader渲染矢量场(电场)的场线?

在GPU片段着色器中实现电场线绘制的解决方案

问题分析

你当前使用的sin(atan(field.y, field.x) * 10.0)方法仅在同号电荷场景有效,核心原因是:异号电荷附近的电场角度会出现从π到-π的突变,导致sin函数的输入连续性断裂,最终渲染出的线条混乱。

可行实现方案

方法1:利用流函数(Stream Function)绘制电场线

静电场是无旋场,流函数的等值线就是电场线,多个点电荷的流函数可直接叠加,能完美适配同号/异号电荷场景。

步骤1:实现流函数计算

const float PI = 3.1415926535;

float stream_function(vec2 pos, PointCharge q) {
    float dx = pos.x - q.pos.x;
    float dy = pos.y - q.pos.y;
    float angle = atan(dy, dx);
    // 单个点电荷的流函数公式
    return (q.charge / (2.0 * PI)) * angle;
}

float total_stream_function(vec2 pos) {
    float psi = 0.0;
    for (int i = 0; i < charge_count; i++) {
        psi += stream_function(pos, charges[i]);
    }
    return psi;
}

步骤2:在片段着色器中渲染电场线

通过流函数的周期性正弦值生成线条,再用平滑函数优化线条边缘:

void main() {
    vec2 resolution = vec2(800.0, 600.0); // 替换为你的画布实际分辨率
    vec2 pos = gl_FragCoord.xy / resolution;
    
    // 计算总流函数
    float psi = total_stream_function(pos);
    
    // 处理电荷附近的奇点(避免噪点)
    float min_charge_dist = 0.02;
    bool near_charge = false;
    for (int i = 0; i < charge_count; i++) {
        if (distance(pos, charges[i].pos) < min_charge_dist) {
            near_charge = true;
            break;
        }
    }
    if (near_charge) {
        gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
        return;
    }
    
    // 生成电场线:调整20.0控制线条密度,值越大线条越密
    float line_pattern = sin(psi * 20.0);
    // 平滑线条边缘,让线条更锐利
    float line_intensity = 1.0 - smoothstep(-0.05, 0.05, line_pattern);
    
    // 可选:结合电场强度调整线条亮度(强电场区域线条更亮)
    vec2 field = electric_field(pos);
    float field_strength = length(field);
    line_intensity *= smoothstep(0.1, 0.5, field_strength);
    
    gl_FragColor = vec4(vec3(line_intensity), 1.0);
}

方法2:修复角度连续性的简易方案

如果你想基于原有角度方法改进,只需将角度范围从[-π, π]转换为连续的[0, 2π),避免突变:

void main() {
    vec2 resolution = vec2(800.0, 600.0);
    vec2 pos = gl_FragCoord.xy / resolution;
    vec2 field = electric_field(pos);
    
    // 处理零电场区域
    if (length(field) < 0.01) {
        gl_FragColor = vec4(1.0);
        return;
    }
    
    // 将角度转换为[0, 2π)的连续范围
    float angle = atan(field.y, field.x);
    angle = angle < 0.0 ? angle + 2.0 * PI : angle;
    
    float line_pattern = sin(angle * 10.0);
    float line_intensity = 1.0 - smoothstep(-0.1, 0.1, line_pattern);
    gl_FragColor = vec4(vec3(line_intensity), 1.0);
}

注意:此方法仅修复了角度连续性,在异号电荷的电场线汇聚/发散区域效果仍不如流函数方法。

优化建议

  • 调整流函数或角度的乘数(如示例中的20.0、10.0)可控制电场线的密度。
  • 增加smoothstep的阈值范围可让线条更柔和,缩小则更锐利。
  • 对于高分辨率画布,可预计算流函数纹理,减少片段着色器的循环计算量。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 02:23:17