What I thought I knew about OOP as taught in college—Encapsulation, Polymorphism, and Inheritance—seemed like a sham.
Perhaps I should go into more detail about that. With polymorphism, the code actually called when a particular object method is invoked depends on the type of object. Different objects that implement the same method signature are interchangeable, although in statically checked languages, they may need to share a base class or interface. But, what is different about this as compared to passing a function directly? Both cases store different code-to-be-called into the same variable, and both cases have to agree on the call's signature. Objects can have internal state, but passed-in functions can achieve the same by closing over lexical variables.
The only thing objects really add above a function is the ability to call several different methods on the object. Even so, this feature is easily replicated by passing a hash table instead of a function, where the keys are strings forming method names, and the values are the corresponding function. Again, some languages provide more guarantees about the structure of an object than the structure of data pretending to be an object, but in others, the two are indistinguishable. You wouldn't be warned until runtime that an object method could not be found.
Functional and OO programming seemed to be equivalent. I demonstrated this by producing a primitive object system using only Perl's functional programming constructs, along with a class whose objects reasonably represented closures in PHP 5. (This was long before PHP 5.3, where they added actual closures, and also a __call method to allow objects to pretend to be functions.)
I eventually discovered Jonathan Rees' thoughts on the meaning of object orientation, which justified my appraisal of OOP-as-it-was-taught-to-me as the Emperor's new clothes. It agreed that that kind of OOP was not the only possible manifestation, nor was it necessarily the One True Way.
FP clearly had a different cadence to it than the OOP it was equivalent to, but that was more of a natural consequence of reducing friction between naming and using functions. Things like map can be written in OO style, but it's cumbersome enough there that nobody does so unless backed into a corner.
Recently, I've been reading about game coding, and how there is very little in the way of OOP, because of the need to keep caches well-utilized. Iterating through an array of x-values, where each index represents a different game object, ends up being faster than iterating through an array of objects, since there are many object properties that need skipped over. (Also, if objects are of differing length, the accesses are harder for hardware to predict.) Unused properties in a loop represent memory that was fetched and paid for, but wasted.
Thus, game coding really strikes me as data-oriented programming. There may be an "object design", but memory is laid out for maximum speed and parallelism of data access.
Where does "procedural programming" fall? I don't think it's a useful distinction anymore, because I don't think there are any purely procedural languages left in wide use. (Niche use, perhaps, like legacy COBOL systems, or physicists writing FORTRAN. But not for anything in my slice of the programming world, certainly.) Even lowly C offers function pointers, which allow for both OOP and FP idioms, as the case may warrant. Pretty much any GTK+ program uses both styles—FP for event callbacks, and OOP for the widgets (and possibly data). C can't, AFAIK, pass pointers to lexical variables outside of the lexical container, so GTK+ has a userdata parameter threaded through every event call to simulate closure, but other than that, the illusion is fairly complete.