Ray使用Three.js中的自定义顶点着色器跟踪Point Cloud

时间:2016-10-30 22:46:11

标签: three.js webgl

如何使用three.js中的自定义顶点着色器将光线跟踪到点云。

这是我的顶点着色器

void main() {
  vUvP = vec2( position.x / (width*2.0), position.y / (height*2.0)+0.5 );
  colorP = vec2( position.x / (width*2.0)+0.5 , position.y / (height*2.0)  );
  vec4 pos = vec4(0.0,0.0,0.0,0.0);
  depthVariance = 0.0;
  if ( (vUvP.x<0.0)|| (vUvP.x>0.5) || (vUvP.y<0.5) || (vUvP.y>0.0)) {
     vec2 smp = decodeDepth(vec2(position.x, position.y));
     float depth = smp.x;
     depthVariance = smp.y; 
     float z = -depth;
     pos = vec4(( position.x / width - 0.5 ) * z * (1000.0/focallength) * -1.0,( position.y / height - 0.5 ) * z * (1000.0/focallength),(- z + zOffset / 1000.0) * 2.0,1.0);
     vec2 maskP = vec2( position.x / (width*2.0), position.y / (height*2.0)  );
     vec4 maskColor = texture2D( map, maskP );
     maskVal = ( maskColor.r + maskColor.g + maskColor.b ) / 3.0 ;
  }
  gl_PointSize = pointSize;
  gl_Position = projectionMatrix * modelViewMatrix * pos;
}

在Points类中,光线跟踪实现如下:

function testPoint( point, index ) {
    var rayPointDistanceSq = ray.distanceSqToPoint( point );
    if ( rayPointDistanceSq < localThresholdSq ) {
      var intersectPoint = ray.closestPointToPoint( point );
      intersectPoint.applyMatrix4( matrixWorld );
      var distance = raycaster.ray.origin.distanceTo( intersectPoint );
      if ( distance < raycaster.near || distance > raycaster.far ) return;
      intersects.push( {
          distance: distance,
          distanceToRay: Math.sqrt( rayPointDistanceSq ),
          point: intersectPoint.clone(),
          index: index,
          face: null,
          object: object
       } );
    }
 }

var vertices = geometry.vertices;
for ( var i = 0, l = vertices.length; i < l; i ++ ) {
       testPoint( vertices[ i ], i );
}

但是,由于我使用的是顶点着色器,因此geometry.vertices与屏幕上的顶点不匹配,这会阻止光线轨迹的工作。

我们可以从顶点着色器返回点吗?

1 个答案:

答案 0 :(得分:2)

我没有深入了解你的顶点着色器实际上做了什么,我认为你有很好的理由在着色器中进行,所以在javascript中重做计算可能不太可行在进行光线投射时。

一种方法可能是对点的位置进行某种估计,将其用于预选,并对最接近光线的点进行更复杂的计算。

如果它不起作用,最好的办法是渲染场景的查找图,其中颜色值是在坐标处渲染的点的id(这也称为GPU选择,示例herehere甚至某些库here,尽管这并不能真正满足您的需求。

为此,您需要渲染场景两次:在第一遍中创建一个查找地图,并在第二遍中定期渲染它。查找映射将存储在那里渲染粒子的每个像素。

要获取该信息,您需要设置THREE.RenderTarget(这可能会缩小到宽度/高度的一半以获得更好的性能)和不同的材质。顶点着色器保持不变,但片段着色器只会为每个粒子(或任何可用于识别它们的东西)输出单个唯一的颜色值。然后将场景(或更好:只有应该是raycast-targets的部分)渲染到renderTarget中:

var size = renderer.getSize();
var renderTarget = new THREE.WebGLRenderTarget(size.width / 2, size.height / 2);
renderer.render(pickingScene, camera, renderTarget);

渲染后,您可以使用renderer.readRenderTargetPixels方法获取此查找纹理的内容:

var pixelData = new Uint8Array(width * height * 4);
renderer.readRenderTargetPixels(renderTarget, 0, 0, width, height, pixelData);

(此处pixelData的布局与常规画布imageData.data)的布局相同

一旦你拥有了它,raycaster只需要查找单个坐标,读取并将颜色值解释为object-id并用它做一些事情。