Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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);
    \shade [ball color=white] (0,0) circle [radius=1cm];
}
\end{minipage}%
%
\begin{minipage}{2.5cm}\centering
\scriptsize \emph{That's} a sphere.

\includegraphics[width=2cm]{Sphere-perl6.png}
\end{minipage}
\end{document}
share|improve this question
    
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
1  
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

4 Answers 4

up vote 16 down vote accepted

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
\pgfdeclareradialshading[tikz@ball]{ball}{\pgfqpoint{-20bp}{20bp}}{%
 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
\pgfdeclareradialshading[tikz@ball]{ball}{\pgfqpoint{0bp}{0bp}}{%
 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.

share|improve this answer
    
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}

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

\begin{document}
\begin{tikzpicture}
  \shade[shading=sphere] (0,0) circle [radius=5cm];
\end{tikzpicture}
\end{document}

sphere

share|improve this answer
    
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
1  
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
1  
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

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}
\pgfdeclarefunctionalshading{eightball}{\pgfpointorigin}{\pgfpoint{100bp}{100bp}}{}{
% 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}
\shade[shading=eightball] (0,0) circle (5cm);
\end{tikzpicture}
\end{document}

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.

enter image description here

share|improve this answer
    
Ooooh! I didn't know you could use PS code for the shadings. Looks really impressive! –  Jake May 4 '12 at 4:58
    
The list of the ps operations are given in the pgfmanual (shading part). You can use the basic operations but you can't program. It's the big difference between ps and PDF. Very fine result from percusse !! –  Alain Matthes May 4 '12 at 5:06
    
@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
    
@Altermundus Thank you for your kind words. –  percusse May 4 '12 at 5:16
    
I recently saw this in the PGF manual and so when thinking about this question wondered if this would a way to get a right solution - I'm pleased to see someone figure it out! Really nice. I think I'd add some "ambient light" so that it doesn't shade to black. –  Loop Space May 4 '12 at 10:00

Perhaps something like that

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

\pgfdeclareradialshading{ballshading}{\pgfpoint{-10bp}{10bp}}
 {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}
  \pgfshadepath{ballshading}{20}
  \pgfusepath{}
\end{pgfpicture}  

\end{document}

enter image description here

share|improve this answer
    
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

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.