如何使用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
相关产品推荐
相关产品推荐

