The best way will depend on the type of scene you have and your target hw. They are mostly pretty similar, but here is an overview of a few of the popular ones:
- deferred shading, based on generating a gbuffer for opaque objects with all properties needed for both lighting and shading, followed by light pass typically done by rendering geometrical volumes or quad for each light and a fullscreen pass for sunlight, followed by composite pass where lighting and surface properties are combined to get back the final shaded buffer. This is followed by alpha passes, often with forward lighting, and post fx passes, which can use the content of the gbuffer if needed. Full or partial Z prepass is optional. Advantages include potentially rendering your scene only once and
- light prepass/ deferred lighting involves the same kind of steps, only with a minimal gbuffer containing only what you need for the actual lighting ( often just depth buffer + one render target containing normals + spec power ), the same kind of light pass, but then another full scene rendering pass to get the final colour buffer. This means loads more draw calls, but much lighter gbuffers, which can be handy on HW with limited bandwidth, limited support for MRTs, or limited EDRam like the 360. Also gives more flexibility than the previous approach when it comes to object materials, since you are not limited to the information you can store in the gbuffer.
- inferred rendering, which is like light prepass, only with a downsampled gbuffer containing material IDs, downsample light pass, but high res colour pass which uses IDs to pick the correct values from the light buffer without edge artifacts. Kind of neat way of doing gbuffer and light pass much faster at the cost of resolution. Can also be used to store the alpha object properties in the gbuffer with a dithered pattern, and then excluding the samples you don't want / not for that layer during the colour pass. So no more need for forward lighting for alpha objects (up to a point).
- tiled deferred involves not rendering volumes or quads for your lights, which can be pretty extensive when you get alot of light overdraw, especially if your light volumes are not super tight, but instead divide your screen into smaller tiles, generate a frustum per tile, cull your lights on gpu for each tile frustum, and then light only the fragments in the tile by the final list. Usually done in CS, no overdraw issues at all, overall much faster, but requires modern HW and also can generate very large tile frustums when you have large depth discontinuities per tile. The last part can be mitigated by adding a depth division to your tiles ( use 3d clusters instead of 2d tiles ).
- forward+ is similar, but involves z prepass instead of gbuffer generation, then pass to generate light lists per tile, same as above, but instead of lighting at that point, you render your scene again and light forward style using the list of lights intersecting the current tile. Allows for material flexibility and easy MSAA support at the cost of another full geo pass.
There are loads more variations of course, but these are maybe a good starting point.