What you describe is not a matrix. These are corners of a cube. The name Box describes it pretty good.
By default there is no chance to get the requested objects without checking each single object within the scene.
Preparation
You can organize your scene in a way that you might be able to reduce the number of considered objects. Be aware this adds restrictions and maybe additional processing time.
For example:
- You could assume that all cloud objects are above z=0. This allows you to ignore them when the box is below z=0.
Sounds to abstract? how about the opposite you assume that all amphibia are below z=0.
Caching
You still need an efficient method to get the remaining objects. Filtering will not work that well as you would still need to touch all objects. But you can reduce the amount of filtering. E.g. you filter the object just once and reuse the result again and again. This reduces the effort from O(n²) to O(n).
The assumptions are:
A) the criteria does not change on an object
B) the criteria has no influence on the box checking (= no position, rotation, scale, not even parent relationship when the parent isn’t fixed in place).
C) rarely adding and removing objects as this requires to update the cache
Hierarchical organization (tree)
You could structure your scene in a way that it is very efficient to identify objects that can be ignored (or be considered).
The idea is you cut the scene into pieces. A simple and efficient way are cubes. Example:
Root = cube
= 8x lvl 1 Branches
= 8x8 lvl 2 Branches (each Branch consists of 8 sub-branches)
…
Here we go into the direction of the already mentioned tree datastructure. We look top down from lowest detail to highest detail.
Root:
A) if the bounding box of the root does not intersects the box -> there is no object inside
B) otherwise look at the branches
Branch:
A) if the bounding box of the branch does not intersects the box -> there is no object inside -> continue with the other branches
B) otherwise look at the branches of the branch
Branch:
A) if the bounding box of the branch does not intersects the box -> there is no object inside -> continue with the other branches
B) otherwise look at the branches of the branch
…
This way you do not iterate over all objects but the cubes. Which are much less.
The problem here is, that objects can move from one cube to another. This could be an expensive operation as the tree needs to be restructured (find object, remove from branch, find new branch, add object). So this is efficient on many static (non-moving) objects.
Remarks
I’m sure there are many other ways to reduce the number of objects to consider. It is important that the additional effort does not exceed the efford you need to spend without it.
What method is effective for you depends on your use case. So why do you think you need this operation? And why do you think it needs to be very fast?