解决 three.js / webGL 中的 gl_PointSize 限制

Posted

技术标签:

【中文标题】解决 three.js / webGL 中的 gl_PointSize 限制【英文标题】:Working around gl_PointSize limitations in three.js / webGL 【发布时间】:2013-02-28 14:21:42 【问题描述】:

我正在使用 three.js 来创建交互式数据可视化。这种可视化涉及渲染 68000 个节点,其中每个不同的节点具有不同的大小和颜色。

最初我尝试通过渲染网格来做到这一点,但事实证明这非常昂贵。我目前的尝试是使用 three.js 粒子系统,每个点都是可视化中的一个节点。

我可以控制点的颜色*大小,但只能控制到某个点。在我的卡片上,gl 点的最大尺寸似乎是 63。当我放大到可视化时,点会变大 - 到一个点,然后保持在 63 像素。

我目前正在使用顶点和片段着色器:

顶点着色器:

attribute float size;
attribute vec3 ca;
varying vec3 vColor;

void main() 
    vColor = ca;
    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
    gl_PointSize = size * ( 300.0 / length( mvPosition.xyz ) );
    gl_Position = projectionMatrix * mvPosition;

片段着色器:

uniform vec3 color;
uniform sampler2D texture;

varying vec3 vColor;

void main() 
    gl_FragColor = vec4( color * vColor, 1.0 );
    gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );

这些几乎是从三个.js 示例之一中逐字复制的。

我对 GLSL 完全陌生,但我正在寻找一种方法来绘制大于 63 像素的点。我可以为任何大于一定大小的点绘制一个网格,但否则使用 gl_point 吗?我可以使用其他解决方法来绘制大于 63 像素的点吗?

【问题讨论】:

【参考方案1】:

您可以通过制作单位四边形数组 + 中心点然后在 GLSL 中按大小扩展来创建自己的点系统。

因此,您将有 2 个缓冲区。一个缓冲区只是一个 2D unitQuad,根据您要绘制的点数重复。

var unitQuads = new Float32Array([
  -0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
  -0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
  -0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
  -0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
  -0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
];

第二个是你的积分,除了每个位置需要重复4次

var points = new Float32Array([
  p1.x, p1.y, p1.z, p1.x, p1.y, p1.z, p1.x, p1.y, p1.z, p1.x, p1.y, p1.z,
  p2.x, p2.y, p2.z, p2.x, p2.y, p2.z, p2.x, p2.y, p2.z, p2.x, p2.y, p2.z,
  p3.x, p3.y, p3.z, p3.x, p3.y, p3.z, p3.x, p3.y, p3.z, p3.x, p3.y, p3.z,
  p4.x, p4.y, p4.z, p4.x, p4.y, p4.z, p4.x, p4.y, p4.z, p4.x, p4.y, p4.z,
  p5.x, p5.y, p5.z, p5.x, p5.y, p5.z, p5.x, p5.y, p5.z, p5.x, p5.y, p5.z,
]);

设置缓冲区和属性

var buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, unitQuads, gl.STATIC_DRAW);
gl.enableVertexAttribArray(unitQuadLoc);
gl.vertexAttribPointer(unitQuadLoc, 2, gl.FLOAT, false, 0, 0);

var buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.enableVertexAttribArray(pointLoc);
gl.vertexAttribPointer(pointLoc, 3, gl.FLOAT, false, 0, 0);

在您的 GLSL 着色器中,计算所需的 gl_PointSize,然后将 unitQuad 乘以视图空间或屏幕空间中的该大小。屏幕空间将匹配 gl_Point 所做的,但人们通常希望他们的点像正常的东西一样在 3D 中缩放,在这种情况下,视图空间就是你想要的。

attribute vec2 a_unitQuad;
attribute vec4 a_position;
uniform mat4 u_view;
uniform mat4 u_viewProjection;

void main() 
   float fake_gl_pointsize = 150;

   // Get the xAxis and yAxis in view space
   // these are unit vectors so they represent moving perpendicular to the view.
   vec3 x_axis = view[0].xyz;
   vec3 y_axis = view[1].xyz;

   // multiply them by the desired size 
   x_axis *= fake_gl_pointsize;
   y_axis *= fake_gl_pointsize;

   // multiply them by the unitQuad to make a quad around the origin
   vec3 local_point = vec3(x_axis * a_unitQuad.x + y_axis * a_unitQuad.y);

   // add in the position you actually want the quad.
   local_point += a_position;

   // now do the normal math you'd do in a shader.
   gl_Position = u_viewProjection * local_point;

我不确定这是否有意义,但有更复杂的但a working sample here

【讨论】:

我已经为这种方法制作了一个可工作的 three.js 示例,我认为最初是受到@gman 这个答案的启发。 :-) discourse.threejs.org/t/… 不同之处在于重复的单位四边形在 z 方向上偏移,以包含围绕指定中心点的“球体”。 您也可以使用InstancedBufferGeometry 来使用更少的数据,尽管在我的测试中 InstancedBufferGeometry 比重复数据慢 20-30%。【参考方案2】:

我可以为任何大于特定大小的点绘制一个网格,否则使用 gl_point 吗?

不在 WebGL 中。

您可以将粒子系统绘制为一系列四边形(即:两个三角形)。但仅此而已。

【讨论】:

以上是关于解决 three.js / webGL 中的 gl_PointSize 限制的主要内容,如果未能解决你的问题,请参考以下文章

图解WebGL和Three.js工作原理

WebGL 和 Three.js 工作原理图解

图解 WebGL & Three.js 工作原理

WebGL 模型查看器使用 Three.js 作为 HTML 中的 DOM 元素

今日推荐WebGL系列之Three.js

WebGL初探—Three.js全景图实战