The optixWhitte example is special in that it defines two custom geometric primitives (parallelogram, sphere) which build a super simple scene by hardcoding the sphere and floor parallelogram positions inside the source code. It doesn’t even use the built-in triangles.
The optixPathTracer is building the very little geometry inside its scene by simply hardcoding the vertices for the objects in some global arrays (see g_vertices) and builds triangles from those.
Means you would need to learn how to build your cube from triangles as shown inside the optixPathTracer and then enhance the createGeometry() function to also create a triangle geometry. That in turn cannot be put together with custom primitives into the same geometry accelerations structure (GAS), which means you’d need an instance acceleration structure (IAS) on top of the custom and triangle GAS. That requires understanding of the shader binding table (SBT) a little more, because that needs to be adjusted for the additional geometry as well.
I would recommend looking at the examples which use built-in triangles first and then figure out how to implement a Whitted renderer with those. Then you can still learn how to handle custom geometric primitives like spheres and combine everything in your version of a Whitted renderer.
You would normally build more complex objects with code at runtime or load arbitrary models from scene file formats.
The optixMeshViewer shows how to do the latter. It can load glTF models (most of them), build the acceleration structures of the geometry, and supports the material.
For other examples generating geometry at runtime, maybe have a look at my OptiX examples which have functions creating a simple box from 12 triangles, tessellated planes, spheres and a torus as well as loading arbitrary mesh geometry from any file format supported by ASSIMP.
Look for Box.cpp, Plane.cpp, Sphere.cpp, Torus.cpp here https://github.com/NVIDIA/OptiX_Apps
The Assimp.cpp in the more advanced examples contains the code loading polygonal mesh data.
This data is all loaded into host structures first and then the geometry accelerations structures are built from that.
For the runtime generated objects, that always happens with the same createGeometry() function at the end.
Please take care to not mix and match the different geometry representations in these SDK resp. open-source examples. Some care needs to be taken to match the vertex attributes to the calculations inside the closesthit or anyhit programs accessing these the current hit point.