# How to draw a shaded sphere?

Andrew Stacey pointed out that the Rosetta Code entry "Draw a Sphere" doesn't have a TikZ entry yet.

Is there a way to draw a "properly" shaded sphere using TikZ? The ball shading would seem an obvious choice, but it uses a circular highlight, which is not what it would look like in reality. So: is there a way to make a sphere that would resemble a white, matte (i.e. non-shiny) ball that is illuminated by a single white light source?

This would mainly require getting a non-circular, correctly oriented highlight, I believe. Both Yori's and Altermundus' answer already do a great job at using a "softer" highlight, which looks more believable, but Altermundus' highlight is still circular, and while Yoris' is elliptic, it's not oriented correctly, I think.

Compare a circle with the ball shading to the example made with Perl 6:

\documentclass{article}

\usepackage{tikz}

\begin{document}

\begin{minipage}{2.5cm}\centering
\scriptsize That's not a sphere.

\tikz{
\fill [black] (-1,-1) rectangle (1,1);
}
\end{minipage}%
%
\begin{minipage}{2.5cm}\centering
\scriptsize \emph{That's} a sphere.

\includegraphics[width=2cm]{Sphere-perl6.png}
\end{minipage}
\end{document}

-
I think that the better sphere is the first one. The second example seems to be a white mushroom. I prefer this one : commons.wikimedia.org/wiki/… or this one webangel.fr/fr/tutaux/sphere.html –  Alain Matthes May 3 '12 at 18:28
I would agree with Altermundus, there is a problem with the second one, he saw a mushroom and I am seeing a knob! –  Yiannis Lazarides May 3 '12 at 19:08
Does this mushroom-y knob require the 3d tag? –  Werner May 3 '12 at 20:05
@Altermundus, @ YiannisLazarides: To me, the first one looks like what you'd see when you're sitting at the bottom of a well looking up, and the sun is not directly overhead. And if I'm not mistaken, the left one can't be correct, because the highlight can't be circular: Think of the extreme case when the light is coming directly from the side: The edge of the highlight would be a straight line. So for light coming at an angle, the edge of the highlight has to be something between a circle and a straight line (an ellipsoid, presumably). –  Jake May 3 '12 at 20:07
I think the contrast is too high in the second picture. You want a sphere realistic ? hyperrealist ? with a impressionism style ? a billiard ball with a soft light ? the moon ? an eclipse ? The question should be more specific ! but it will not be easy to master all the effects. –  Alain Matthes May 3 '12 at 21:29
show 1 more comment

I'm following the earlier post Is there a way to tune ball shading in TikZ ?, particularly Stefan Kottwitz's answer. He showed how to use \pgfdeclareradialshading to change the radial shading. Changing the parameters for the radial and adding some clipping, I can produce this:

Is that sphere enough? Perhaps with some more tweaking it is possible to get an even better result. The code to produce this is:

\documentclass{article}
\usepackage{tikz}

\begin{document}
\makeatletter
color(0bp)=(tikz@ball!0!white);
color(17bp)=(tikz@ball!0!white);
color(21bp)=(tikz@ball!70!black);
color(25bp)=(black!70);
color(30bp)=(black!70)}
\makeatother

\begin{tikzpicture}
\fill [black] (-1,-1) rectangle (1,1);
\begin{scope}
\clip (0,0) circle (1);
\shade [ball color=white] (-0.1,0) ellipse (1.2 and 1);
\end{scope}
\end{tikzpicture}

\end{document}


Or, with a more ellipsoidal shading, one can get this:

The for this is:

\documentclass{article}

\usepackage{tikz}

\begin{document}

\makeatletter
color(0bp)=(tikz@ball!0!white);
color(10bp)=(tikz@ball!0!white);
color(15bp)=(tikz@ball!70!black);
color(20bp)=(black!70);
color(30bp)=(black!70)}
\makeatother

\begin{tikzpicture}

\fill [black] (-1,-1) rectangle (1,1);

\begin{scope}
\clip (0,0) circle (1);
\draw [fill=black!70] (0, 0) circle (1);
\begin{scope}[transform canvas={rotate=45}]
\shade [ball color=white] (0,0.5) ellipse (1.8 and 1.6);
\end{scope}
\end{scope}

\end{tikzpicture}

\end{document}


It's a matter of experimenting and fiddling around with the parameters to see what looks best according to you (judging from the discussion above, this seems quite subjective matter). Notice that you need to specify transform canvas={rotate=45} in the inner scope, not rotate=45 because the latter does not rotate the fill.

-
That looks great! However, is there a way of getting the orientation of the shading right? At the moment, the ellipse is oriented horizontally, but I guess it would need to be at a 45 degree angle? –  Jake May 3 '12 at 21:38
@Jake: see my edit. As said, it's a matter of fiddling around a little bit to find what looks best. I think the second one looks okayish, although not that much different from the first one. –  Yori May 4 '12 at 1:30

If the light source is a long way away, the intensity at each point is the dot product between the position vector and light source vector (in R3), thresholded at 0.

This uses the \pgfdeclarefunctionalshading command from percusse's answer. Of course, we're only given two elements of each vector, so we need to first compute the third.

For some reason, shadings for circles need to be 50bp by 50bp, otherwise this doesn't work.

\documentclass[border=5mm]{standalone}
\usepackage{tikz}

%% calculate unit coordinates
25 div exch
25 div exch
%% copy stack
2 copy
%% compute -z^2 of the current position
dup mul exch
1.0 sub
%% and the -z^2 of the light source
0.3 dup mul
1.0 sub
%% now their sqrt product
mul abs sqrt
%% and the sum product of the rest
%% max(dotprod,0)
%% matte-ify
%% currently there is just one number in the stack.
%% we need three corresponding to the RGB values
dup
0.4
}

\begin{document}
\begin{tikzpicture}
\end{tikzpicture}
\end{document}


-
Nice! I've also tried something similar but for some reason they (including this one) don't work on Sumatra PDF or a few others than Acrobat. That's why I didn't touch the previous answer. Regarding the 50bp case, it seems working if I increase all 25s to 50s too. –  percusse Oct 9 '12 at 16:44
ImageMagick didn't like it too much either. Mac preview handled it fine though. –  Simon Byrne Oct 10 '12 at 10:32
It seems that circles just use the centre 50bp x 50bp rendering. So if you make the bounding box larger in all dimensions, it won't change. However if you also change the 25 div to 50 div you end up with a zoomed in version. –  Simon Byrne Oct 10 '12 at 10:39
This looks really nice. And when I tried it today, SumatraPDF could display it correctly (Windows 7 64bit, SumatraPDF version 2.2.1). I'd like to use this shading in some pictures of molecular systems I want to create with pgf but I don't know anything about postscript. Is there an easy way to get this shading working with different colors or would this require a modification of the whole postscript calculation? –  Philipp Jun 16 '13 at 14:34
You only need to modify the last few lines to change the colour: at the comment %% matte-ify there is 1 number (between 0 and 1) in the stack: you need 3 (being the RGB values). Try playing around with the lines below this point and see how you go (if you get stuck, you can always ask another question...) –  Simon Byrne Jun 18 '13 at 13:03
show 1 more comment

Perhaps something like that

\documentclass[11pt]{scrartcl}
\usepackage{tikz}
\begin{document}

{color(0bp)=(gray!40!white);
color(9bp)=(gray!75!white);
color(18bp)=(gray!70!black);
color(25bp)=(gray!50!black);
color(50bp)=(black)}

\begin{pgfpicture}
\pgfpathcircle{\pgfpoint{0cm}{0cm}}{2cm}
\pgfusepath{}
\end{pgfpicture}

\end{document}


-
Since I've unfamiliar with TikZ, here's a question: Does this ballshading scale depending on the size of the ball? I'm asking because you're using fixed measurements in setting colours. –  Werner May 3 '12 at 19:22
Well ... I don't know ! I used an example from the pgfmanual and I played with the numbers. I changed the radius of the sphere from 1cm to 2cm. –  Alain Matthes May 3 '12 at 19:45
@Altermundus: Nice, the softness looks much better than the default! However, I still think that a circular highlight doesn't look right (and isn't correct physically, assuming your light source is circular). –  Jake May 3 '12 at 21:39

To grease up PS gears, I modified the functional shading given in the PGF manual. I am pretty sure that PSTricks and its foot soldiers are ridiculously better at these type of drawings but Acrobat and Sumatra render pretty impressively.

\documentclass[border=5mm]{standalone}
\usepackage{tikz}
% Compute distance difference (horizontally weighted twice). (50bp,50bp) is the center
65 sub dup mul exch               %Change the coordinate to move vertically
40 sub dup mul 0.5 mul add sqrt   %Change the coordinate to move horizontally
% In MATLAB notation : d=distance diff
% x=1.003^(-d^2)
dup mul neg 1.003 exch exp
% x is the only variable in the stack now but we need 3 values at the top of the stack
% so we duplicate these values putting new values in the stack.
dup % Duplicates with the current value and pushes the stack down (value of green)
dup % Duplicates with the current value and pushes the stack down (value of blue)
}

\begin{document}
\begin{tikzpicture}

You can play around with the last dup commands by replacing with values in the interval [0.0,1.0] from black to white. For example being a colorblind, if I make these values 0.3,0.2 respectively, my brain just quits rendering with a painful error code.
@Jake Indeed, it's quite surprising. As Altermundus commented it's quite laborious to make it flexible and some things are just not possible. I've experimented with the <init code> for colors and there is hope colorwise, not much though. As a side note, for some reason if I do the ->PS->PDF route, the pixelation is too visible. The sampling rate gets too low I suppose. –  percusse May 4 '12 at 5:15