Varying the density based on the proximity to the surface of the mesh is tricky - and requires use of complicated OSL shaders to probe out the nearby surface of the mesh. A much simpler method is to generate the torus mathematically as a procedural volumetric material. This means that we can more easily vary how the density of the torus varies as it moves away from the center.
Firstly, we need to create a volumetric material that varies based on distance. This can be achieved with the following material :
This takes the 'object' coordinates and feeds this into both sockets of a Dot Product vector math node. The Dot Product returns a scalar value based on the magnitudes and relative directions of the input vectors. By using the same vector for both inputs the node returns a value based purely on the distance of the vector (actually distance squared) - this returns a value base on the distance from (0,0,0) - ie, the distance from the center of the mesh. This is passed through a Multiply node to allow the final result to be scaled, subtracted from 1 (clamped) so the result varies from 1 at the center to 0 at its edge, passed through a Power node to allow the falloff to be varied (to make it either more sharp or more fuzzy) and, finally, a Mulltiply node to allow it to be made overall more or less dense. This is then used as the Density of both a Scatter and an Absorption shader node - which are then combined for the final material output.
By creating a mesh (eg, a cube) to act as a 'domain' for the material and varying the Multiply and Power nodes, this can produce an effect similar to the following :
Small 'Power' (eg, 0.1) will produce a very sharp edge to the sphere while large values (eg, 10) will produce a more fuzzy edge. Reducing the first Multiply will increase the sphere's size while increasing it reduces its size.
To turn this into a Torus, we simply need to add some nodes to position the reference point (in the above case, this was the Origin) at various points along a circle - in particular, at the closest point along the circular path. The size of the circle will then dictate the size of the torus and the above Multiply and Power nodes will determine its fuzziness and thickness.
This can be achieved as shown below :
The center of the circle is at the Origin (0,0,0). The orientation of the circle is determined by a Normal vector - in the same way as for the Normal of a surface. Varying the Normal will change the orientation of the torus. The radius of the circle is controlled via an input Value (Radius).
The Cross Product vector math node is used to determine the direction of the closest point of the circle. The Cross Product always returns a vector which is perpendicular to both of the input vectors. By passing in the Normal to the first Cross Product, this will produce a vector that is along the plane of the circle - but at 90 degrees to the direction of the point being sampled. The second Cross Product takes that vector and produces one at -90 degrees to that - this will be along the plane of the circle and in the direction of the point being sampled (which will be the point on the circle that is closest to the point being sampled). This vector is Normalized (so it's magnitude is '1') and then multiplied by the desired Radius. The result of this can then be used to find the actual reference point on the circle that can be measured against the sample point to determine how close that point is to the circle.
We then have the Multiply and Power nodes used to control the falloff of the edge of the torus as in the previous example (but fed from Value nodes and given appropriate names).
This can then produce the following result :
Blend file attached.