The physical world has a dedicated function to cast rays into the environment -
RayWorldIntersection. Depending on the material the ray hits and the ray properties, a hit might be "pierceable" or solid. More specifically, a pierceable hit is a hit that has its material pierceability strictly higher than the ray's pierceability (material pierceability and ray pirceability occupy the lowest 4 bits of material flags and RayWorldIntersection flags correspondingly). Pierceable hits don't stop the ray and are accumulated as a sorted (by hit distance) list. The caller provides the function with an array for the hits. A solid hit always takes the slot with index 0 and pierceable hits - slots from 1 to the end. Optionally, the function can separate between 'important' and 'unimportant' pierceable hits (importance is indicated by sf_important in material flags) and can make all important hits have higher priority (regardless of hit distance) than unimportant ones when competing for space in the array. By default RayWorldIntersection checks only entity parts with the geom_colltype_ray flag, but it is possible to specify another flag or combination of flags (in this case all flags should be set in part for it to be tested) by setting rwi_pireceabilitypierceability() in flags.
RayTraceEntity is a more low-level function and checks ray hits with a single entity. It returns only the closest hit.