如何使用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与屏幕上的顶点不匹配,这会阻止光线轨迹的工作。
我们可以从顶点着色器返回点吗?
答案 0 :(得分:2)
我没有深入了解你的顶点着色器实际上做了什么,我认为你有很好的理由在着色器中进行,所以在javascript中重做计算可能不太可行在进行光线投射时。
一种方法可能是对点的位置进行某种估计,将其用于预选,并对最接近光线的点进行更复杂的计算。
如果它不起作用,最好的办法是渲染场景的查找图,其中颜色值是在坐标处渲染的点的id(这也称为GPU选择,示例here,here甚至某些库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并用它做一些事情。