How to Pass Current Taxonomy Terms into new WP_Query?

Try this:

add_action( 'genesis_loop', 'bds_more_projects_query' );
function bds_more_projects_query() {
    if ( is_singular( 'portfolio' ) ) {
        $categories = get_the_terms( $post->ID, 'portfolio_category' );
        $cat_ids = array();
        foreach ( $categories as $category ) {
            $cat_ids[] = $category->term_id;
        }
        $args = array(
            'post_type' => 'portfolio',
            'posts_per_page' => 4,
            'tax_query' => array(
                array(
                    'taxonomy' => 'portfolio_category',
                    'field' => 'id',
                    'terms' => array( $cat_ids ),
                ),
            ),
        );
        $loop = new WP_Query( $args );
        if ( $loop->have_posts() ) {
            while ( $loop->have_posts() ) : $loop->the_post();
            echo the_title();
            endwhile;
        }
        wp_reset_postdata();
    }
}

Basically, loop over the $categories array, adding the term_id of each to a new array, and then use that new array in the query arguments. The get_the_terms function returns an array of WP_Term objects, rather than just an array of IDs (see the WP Code Reference for more info).

The other thing to check is that $post is giving you the current post inside the function.

To verify, add var_dump($post) just inside the is_singular( 'portfolio' ) condition and make sure it’s giving you the current post object. If it’s not, add global $post inside the bds_more_projects_query function and try again.