In such cases, you hash some random value or a salted password or a shared secret value obtained by some kind of Diffie-Hellman exchange, and you must generate a variable number of secret keys, depending on the target protocol. You typically need to generate at least an encryption key and an authentication key, but frequently you need to generate even more keys, e.g. in a communication protocol you may use different keys for the 2 directions of communication, or you may change the keys after a certain amount of time or of transmitted data.
When you have just a hash function with fixed output size, e.g. SHA-512, you must transform it in a hash function with extensible output size, to get all the keys that you need.
Typically this is done by hashing repeatedly the secret value, but each time concatenated with some distinct data, e.g. the value of a counter. Like for any cryptographic operation, every detail of the implementation matters and it is easy to make mistakes.
If you already have available a hash function that has been designed to provide extensible output, then you can use it as it is and you do not have to bother with designing an ad-hoc method for extending the size of its output and with the analysis of its correctness.
This sounds like something I would use HKDF for. But, to your point, it's nice to be able to build the design with a fewer number of primitives, and likely more performant, too.