Material Shader for disabling Mesh being picked by Framebuffer

Fellow developers, I once again call upon thee, to ask for help.

I have a working “Model Cutter” which cuts my Model along the X,Y,Z axes based on the AABB of the model. I simply use a shader where I use the opacityPS chunk to discard the alpha if a point is outside of the boundary.

It all works wonderfully. But my new challenge is to pick the correct MeshInstance with the pc.Picker. I have used Framebuffer Picking because it does not require the model to have Collision. Since some models I load are big (around 70MB) creating Collision for them does lower performance and if a certain complexity exists, ammo ultimately fails due to the RAM Excession.

Anyways the problem is, that the Framebuffer Picker does pick MeshInstances that are “invisible”. What I want to achieve is that you could pick “through” objects if one of their sides is “open”. For demonstration I have prepared the following, where you can see that the outer mesh is picked even tough the frontside is “open”:

FrameBufferPick

Is there any other shader chunk I could add to achieve this ?

Thanks a lot!

Hi @Bfischlin,

I think you can solve it in an easier way. The pc.Picker prepare method can accept a list of layers that will use to pick objects from.

You can create a new layer that includes only the pick-able objects and pass that to the method:

https://developer.playcanvas.com/api/pc.Picker.html#prepare

1 Like

@Leonidas thank you for your answer.

How would I dynamically manage the layer-swapping of mesh-instances of one object without it taking a hit on performance on complex models?

For me the simplest solution would be to “clip” the pixels that are not visible just like the camera does. Because when I zoom in with the camera and the front of the cube is clipped by the camera, I can select the cube inside.

FrameBufferPick_2

I see, you can study how to the pc.Picker overrides the colors on each mesh instance rendered and do something similar in a custom shader chunk (e.g. diffuse, if that is used there) to discard pixels based on your logic.

If that happens when the picker layer is rendered it will work.

2 Likes

@Leonidas Thank you again for your insights and ideas!

I got a solution figured out, but it’s a little different :slight_smile:

Instead what I am doing, is I Raymarch from my Cameras-Position up to the far clipping plane and check if I intersect the boundaries of my object. The boundaries are dynamically changed by the sliders. For example with the default cube, the X-Axis the boundaries are -0.5 and 0.5. When I adjust the slider, the boundaries changes to something like -0.5 and 0.2. I then raymarch from my cameras forward direction to the far-clip position and check if I find a position that is within these boundaries. I quickly change the position of my camera to that position and do the framepicking from there, and then switch the position back. With this I was successfully able to create the desired effect.

I thought I’d leave this here, if anyone ever wants to do the same.

var origCameraPosition = this.entity.getPosition().clone();
var stepIncrease = 0.0001; //Resolution of the Raymarches

var cameraEndClipPos = this.camera.screenToWorld(e.x, e.y, this.camera.farClip);
var foundRayMarchPos = cameraEndClipPos.clone(); 

var foundPos = false;

var upperBoundaries = new pc.Vec3(window.ModelCutterSettings.xMax, window.ModelCutterSettings.curYMax, window.ModelCutterSettings.curZMax);
var lowerBoundaries = new pc.Vec3(window.ModelCutterSettings.curXMin, window.ModelCutterSettings.yMin, window.ModelCutterSettings.zMin);

for(var p = 0; p < 1.0 && !foundPos; p += stepIncrease){
    var currentRayMarchPos = new pc.Vec3().lerp(this.entity.getPosition(), cameraEndClipPos, p);
    if(currentRayMarchPos.x > lowerBoundaries.x && currentRayMarchPos.x < upperBoundaries.x && currentRayMarchPos.y > lowerBoundaries.y && currentRayMarchPos.y < upperBoundaries.y && currentRayMarchPos.z > lowerBoundaries.z && currentRayMarchPos.z < upperBoundaries.z){
        foundRayMarchPos = new pc.Vec3().lerp(this.entity.getPosition(), cameraEndClipPos, pc.math.clamp(p - (stepIncrease), 0 , 1));
        foundPos = true;
    }
}

if(foundPos){
    this.entity.setPosition(foundRayMarchPos);
}

picker.prepare(this.entity.camera,this.app.scene);
var selection = picker.getSelection(e.x, e.y, 1, 1);

this.entity.setPosition(origCameraPosition);

//Handle selection
*
*

I know it’s not the pretties or most practical solution, but it worked for me :slight_smile:

3 Likes