There appears to be no other way than to calculate the surface area of the three sections of the cylinder (top cap, body, bottom cap) and then use the random number generator to decide which section of the cylinder our point will be on:
/// <summary>Returns a random point on the surface of a cylinder</summary>
/// <param name="randomNumberGenerator">Random number generator that will be used</param>
/// <param name="orientation">Orientation of the cylinder</param>
/// <param name="radius">Radius of the cylinder</param>
/// <param name="length">Length of the cylinder</param>
/// <returns>A random point on the volume's surface</returns>
public static Vector3 GenerateRandomPointOnSurface(
System.Random randomNumberGenerator,
Matrix orientation, float radius, float length
) {
// Calculate the surface areas of the three sections our cylinder has:
// Upper cap, side and lower cap
float capArea = MathHelper.Pi * (radius * radius);
float sideArea = 2.0f * MathHelper.Pi * radius * length;
float capAndSideArea = capArea + sideArea;
// We need a phi value (angle of the random point) in any of the cases
float phi = (float)randomNumberGenerator.NextDouble() * MathHelper.TwoPi;
// Choose the section that the random point will be generated on in relation
// to its surface area so the probability is constant on the entire surface
float section = (float)randomNumberGenerator.NextDouble() * (capArea * 2.0f + sideArea);
// Depending on the section, these two values are calculated differently
float randomRadius;
float randomZ;
// Upper cap: Generate a random radius
if(section < capArea) {
randomZ = length / 2.0f;
randomRadius = (float)Math.Sqrt(randomNumberGenerator.NextDouble()) * radius;
// Side: Generate a random height
} else if(section < capAndSideArea) {
randomZ = ((float)randomNumberGenerator.NextDouble() - 0.5f) * length;
randomRadius = radius;
// Lower cap: Generate a random radius
} else {
randomZ = -length / 2.0f;
randomRadius = (float)Math.Sqrt(randomNumberGenerator.NextDouble()) * radius;
}
// Now transform the point to cartesian coordinates and rotate it into
// the global coordinate frame
return Vector3.Transform(
new Vector3(
randomRadius * (float)Math.Cos(phi),
randomRadius * (float)Math.Sin(phi),
randomZ
),
orientation
);
}
/// <param name="randomNumberGenerator">Random number generator that will be used</param>
/// <param name="orientation">Orientation of the cylinder</param>
/// <param name="radius">Radius of the cylinder</param>
/// <param name="length">Length of the cylinder</param>
/// <returns>A random point on the volume's surface</returns>
public static Vector3 GenerateRandomPointOnSurface(
System.Random randomNumberGenerator,
Matrix orientation, float radius, float length
) {
// Calculate the surface areas of the three sections our cylinder has:
// Upper cap, side and lower cap
float capArea = MathHelper.Pi * (radius * radius);
float sideArea = 2.0f * MathHelper.Pi * radius * length;
float capAndSideArea = capArea + sideArea;
// We need a phi value (angle of the random point) in any of the cases
float phi = (float)randomNumberGenerator.NextDouble() * MathHelper.TwoPi;
// Choose the section that the random point will be generated on in relation
// to its surface area so the probability is constant on the entire surface
float section = (float)randomNumberGenerator.NextDouble() * (capArea * 2.0f + sideArea);
// Depending on the section, these two values are calculated differently
float randomRadius;
float randomZ;
// Upper cap: Generate a random radius
if(section < capArea) {
randomZ = length / 2.0f;
randomRadius = (float)Math.Sqrt(randomNumberGenerator.NextDouble()) * radius;
// Side: Generate a random height
} else if(section < capAndSideArea) {
randomZ = ((float)randomNumberGenerator.NextDouble() - 0.5f) * length;
randomRadius = radius;
// Lower cap: Generate a random radius
} else {
randomZ = -length / 2.0f;
randomRadius = (float)Math.Sqrt(randomNumberGenerator.NextDouble()) * radius;
}
// Now transform the point to cartesian coordinates and rotate it into
// the global coordinate frame
return Vector3.Transform(
new Vector3(
randomRadius * (float)Math.Cos(phi),
randomRadius * (float)Math.Sin(phi),
randomZ
),
orientation
);
}