Hey folks, I'm trying to implement a way to cull out instances outside of the camera frustum, in an efficient way (so with a SOP Modify it would work, but with millions instances, quite heavy). By cull out I mean to append them to the invisibleIds Array. It should be like an Shot override for a full sequence layout, so culling the points at the creation level wouldn't be an option.
For SOPs and normal Houdini land the toNDC() VEX function is perfect - take the @P to the NDC space of the given camera and every point outside of the 0-1 range can be deleted. This function doesn't work (yet) for USD Cams.
Is there a way to see into the toNDC() function, what it's technically doing? I looked into the Houdini folders to maybe find the description of this function but no success.
Then I tried to wrap my head around to manually code a way to get the Position vectors of the instances to the ndc space of the given USD cam. Tried the projection() vex function, other ways to build the projection matrix I found online, tried this in the post https://www.sidefx.com/forum/topic/16953/ [www.sidefx.com] but nothing brings the same result as the toNDC() function.
My dirty solution was to create an obj network, LOPimport the USD cam to normal Houdini land and use that cam for the toNDC function. Not super elegant AND that would only work for static cams (with a moving cam you might see shadows popping in/out of elements outside the cam view). With the usd_attrib functions you can also get time sampled values, so culling instances never visible in the full range would be possible.
Thanks in advance!
toNDC() function for USD Cameras
4925 7 5- David Krepelka
- Member
- 15 posts
- Joined: May 2020
- Offline
- rafal
- Staff
- 1454 posts
- Joined: July 2005
- Offline
There was a bug 107793 about `toNDC()` that has been fixed in the development branch, but it's about camera nodes, not USD cameras, so it may or may not work for you in the future releases.
Otherwise, `toNDC()` VEX implementation is a bit scattered, but essentially it obtains the relative transform from objects space (which in case of LOP's is identity) to camera, and then constructs the perspective matrix with HDKs `UT_Matrix4::perspective()` and multiplying them togethr.
Otherwise, `toNDC()` VEX implementation is a bit scattered, but essentially it obtains the relative transform from objects space (which in case of LOP's is identity) to camera, and then constructs the perspective matrix with HDKs `UT_Matrix4::perspective()` and multiplying them togethr.
- Chris Russell
- Member
- 4 posts
- Joined: Jan. 2014
- Offline
- jsmack
- Member
- 8041 posts
- Joined: Sept. 2011
- Offline
- Tim Crowson
- Member
- 249 posts
- Joined: Oct. 2014
- Offline
The prune LOP lets you deactivate prims or cull points from a pointinstancer using camera bounds. See: https://www.sidefx.com/docs/houdini/solaris/pattern.html [www.sidefx.com]
%bound:/path/to/camera/prim
%bound:/path/to/camera/prim
- Tim Crowson
Technical/CG Supervisor
Technical/CG Supervisor
- GrahamDClark
- Member
- 112 posts
- Joined: July 2005
- Offline
- David Krepelka
- Member
- 15 posts
- Joined: May 2020
- Offline
As this topic is still relevant today, I came up with this VEX Code to replicate the camera culling with the NDC-Method. Drop an Attribute Wrangle and select the PointInstancer and give it a camera. Only tested it on PointInstancers because it doesn't delete outside points, it just appends them to the invisibleIds array of a PointInstancer (non-destructive wooooh!).
int n = usd_attriblen(0, @primpath, "positions"); // make sure to have the PointInstancers selected under "Primitves" so @primpath works vector pos[] = usd_attrib(0, @primpath, "positions"); string campath = chs("camera"); // select the camera primitive matrix mcam = usd_worldtransform(0, campath); float focal = usd_attrib(0, campath, "focalLength"); float hap = usd_attrib(0, campath, "horizontalAperture"); float vap = usd_attrib(0, campath, "verticalAperture"); vector2 clipping = usd_attrib(0, campath, "clippingRange"); float near = clipping.x; float far = clipping.y; float o = chf("overscan"); // default could be 0.2 for(int i=0; i<n; i++){ vector pcam = pos[i] * invert(mcam); // get instance positions into camera space float cambackup = o*chf("cam_backup"); pcam.z -= cambackup; // overscan with additional cam -z translation float xclip = focal * pcam.x / (hap/2); float yclip = focal * pcam.y / (vap/2); float wclip = pcam.z; float ndcx = xclip / wclip; float ndcy = yclip / wclip; if(-pcam.z < near || -pcam.z > far-cambackup) append(i[]@invisibleIds, i); // cull outside cam clipping planes if(ndcx+o < -1 || ndcx-o > 1 || ndcy+o < -1 || ndcy-o > 1) append(i[]@invisibleIds, i); } // this creates a NDC-Space between -1 - 1 inside the cam view. toNDC VEX function results in a 0 - 1 range.
Edited by David Krepelka - March 22, 2023 04:39:52
- GrahamDClark
- Member
- 112 posts
- Joined: July 2005
- Offline
edit: sorry I posted wrong code.
thanks again David
thanks again David
//thanks David Krepelka!!! modified camera culling to flatten_to_CAM_9hole int n = usd_attriblen(0, @primpath, "points"); // make sure to have the mesh set under "Primitves" so @primpath works vector pos[] = usd_attrib(0, @primpath, "points"); //string campath = chs("camera"); // select the camera primitive string campath = ch("`opinputpath(".", 1)`"+“/primpath”);//pipe camera into second wrangle input (and must be somewhere upstream in first input). maybe fudgy? matrix mcam = usd_worldtransform(0, campath); matrix mprim = usd_worldtransform(0, @primpath); float focal = usd_attrib(0, campath, "focalLength"); float hap = usd_attrib(0, campath, "horizontalAperture"); float vap = usd_attrib(0, campath, "verticalAperture"); for(int i=0; i<n; i++){ vector pprim = pos[i] * mprim; vector pcam = pprim * invert(mcam); // get point positions into camera space float xcam = (focal * pcam.x / (hap/2))/pcam.z; float ycam = (focal * pcam.y / (vap/2)/(hap/vap))/pcam.z; pcam = set(-xcam,-ycam,pcam.z); pcam.z *= 0; pcam.z-=1; //seems really dumb to just reverse above, better way? pcam.x= ((-pcam.x*focal)*(hap*2))*pcam.z; pcam.y= ((-pcam.y*focal)*(vap*2)*(hap/vap))*pcam.z; pcam = pcam * mcam; pcam = pcam * invert(mprim); usd_setattribelement(0, @primpath, "points", i, pcam); }
Edited by GrahamDClark - March 23, 2023 22:30:38
-
- Quick Links