Frustum culling in LOPS
Frustum culling in LOPs has been a topic since the beginning of the Solaris context. As always, there are many ways to skin a cat, especially when it comes to Houdini. Ideally, we want to keep everything within the LOP context without creating SOP context nodes for intermediate computations, as that could drastically slow our workflows down.
SideFX gave us amazing primitive matching patterns like bound, where we can specify elements that are within the camera frustum:
VEXpression
%bound(/cameras/camera1, fovscale = 1.1)

If we want to keep everything within the LOP context, one way around is to calculate NDC ourselves.
VEXpression
vector toLopNDC(string primpath; string camera; vector position; float frame){
float focalLength = usd_attrib(0, camera, "focalLength", frame);
float horizontalAperture = usd_attrib(0, camera, "horizontalAperture",frame);
float verticalAperture = usd_attrib(0, camera, "verticalAperture", frame);
float screenWindowWidth = tan(horizontalAperture / (2 * focalLength)) * 2;
float screenWindowHeight = screenWindowWidth * verticalAperture / horizontalAperture;
vector pos_world = position * usd_worldtransform(0, primpath, frame);
vector pos_cam = pos_world * invert(usd_worldtransform(0, camera, frame));
vector posCamNorm = set(pos_cam.x/pos_cam.z, pos_cam.y/pos_cam.z, -pos_cam.z);
vector posNDC = set(
0.5 - (posCamNorm.x / screenWindowWidth),
0.5 - (posCamNorm.y / screenWindowHeight),
posCamNorm.z
);
return posNDC;
}
vector ndc = toLopNDC(@primpath, chs("camera"), v@positions, @Frame);
float overscan = 0.1;
if(ndc.x>=0-overscan && ndc.x <=1+overscan)
v@scales *= 0.3;
Download:
lop-frustum-cull-1.hipnc