I just finished a camera perspective projection model using TikZ. Although, while I'm pretty satisfied with the result, it took me a lot of time to do it. It's easy to see from the code below that I'm not very used to Latex and even less to TikZ. So, I'm sure that there are much better ways of drawing this same picture.

I did find a package that supported 3d drawing with TikZ (tikz-3dplot), but at first glance I didn't think it would help me too much. So I didn't bother learning another package. Maybe there are other alternatives which I'm not aware of.

The basic idea of my code is defining three vectores for the picture's coordinate system (x_c, y_c and z_c). Then everything is drawn based on these vectors. So, for example, if I want to move 3 units in the x direction, I simply do something like \draw (0,0) -- (3*\xOne, 3*\xTwo), where \xOne and \xTwo are the first and second values of the defined x vector, respectively (hence the decomposition of this vector in the absolute TikZ x and y direction).

I though that maybe I could learn by finding different solutions from people with more experience. While I would prefer PGF/TikZ solutions since this is the only one that I know something, other alternatives like PSTricks and Metapost would be welcomed too (maybe this is the opportunity for me to learn them).

This is the picture I have:

enter image description here

And this is my code:





% Picture's vectors definition

%\draw[thick,->] (0,0) -- (\xOne,\xTwo) node[anchor=north]{$x$};
%\draw[thick,->] (0,0) -- (\yOne,\yTwo) node[anchor=west]{$y$};
%\draw[thick,->] (0,0) -- (\zOne,\zTwo) node[anchor=north,yshift=-2pt,xshift=3pt]{$z$};
\draw[very thick,->] (-\zOne/2,-\zTwo/2) -- (-\zOne/2+\xOne,-\zTwo/2+\xTwo) node[anchor=north west, xshift=-3pt,font=\footnotesize]{$x_c$};
\draw[very thick,->] (-\zOne/2,-\zTwo/2) -- (-\zOne/2+\yOne,-\zTwo/2+\yTwo) node[anchor=west,font=\footnotesize]{$y_c$};
\draw[very thick,->] (-\zOne/2,-\zTwo/2) -- (\zOne/2,\zTwo/2) node[anchor=north,yshift=-2pt,xshift=3pt,font=\footnotesize]{$z_c$};
\draw (-\zOne/2,-\zTwo/2) node[anchor=north west,font=\footnotesize]{$\mathcal{F}_c$};

\draw[very thin,solid] (-\zOne/2-2*\xOne,-\zTwo/2-2*\xTwo) -- (-\zOne/2+2*\xOne,-\zTwo/2+2*\xTwo); % x elongation
\draw[very thin,solid] (3*\zOne,3*\zTwo) -- (6*\zOne,6*\zTwo); % optical axis behind projection plane

%\draw[thin,dashed] (1.4*\xOne-\zOne/2,1.4*\xTwo-\zTwo/2) -- (6*\zOne+1.4*\xOne,6*\zTwo+1.4*\xTwo); % object x position
\draw[very thin,solid] (6*\zOne-2*\xOne,6*\zTwo-2*\xTwo) -- (6*\zOne+2*\xOne,6*\zTwo+2*\xTwo) node[anchor=west]{}; %object z position

\draw[-latex,line width=3pt,blue,line cap=round] (6*\zOne+1.4*\xOne,6*\zTwo+1.4*\xTwo) -- (6*\zOne+1.4*\xOne,6*\zTwo+1.4*\xTwo+1.1) node[anchor=south,font=\footnotesize]{$ P = (X,Y,Z) $};
\node[circle,inner sep=0pt,minimum size=0.2cm,fill=blue] (object) at (6*\zOne+1.4*\xOne,6*\zTwo+1.4*\xTwo+1.1) {};

\draw[thick,solid,red] (3*\zOne+0.69*\xOne,3*\zTwo+0.7*\xTwo+0.69) -- (6*\zOne+1.4*\xOne,6*\zTwo+1.4*\xTwo+1.1);

\filldraw[fill=gray!20,draw=gray!70,opacity=0.8] (3*\zOne-1.5*\xOne-1.5*\yOne,3*\zTwo-1.5*\xTwo-1.5*\yTwo) -- (3*\zOne+1.5*\xOne-1.5*\yOne,3*\zTwo+1.5*\xTwo-1.5*\yTwo) -- (3*\zOne+1.5*\xOne+1.5*\yOne,3*\zTwo+1.5*\xTwo+1.5*\yTwo) -- (3*\zOne-1.5*\xOne+1.5*\yOne,3*\zTwo-1.5*\xTwo+1.5*\yTwo) -- (3*\zOne-1.5*\xOne-1.5*\yOne,3*\zTwo-1.5*\xTwo-1.5*\yTwo);

\draw[->,thick,green!70!black,dashed] (3*\zOne-1.5*\xOne-1.5*\yOne,3*\zTwo-1.5*\xTwo-1.5*\yTwo) -- (3*\zOne+2*\xOne-1.5*\yOne,3*\zTwo+2*\xTwo-1.5*\yTwo)
     node[anchor=north west, xshift=-3pt,font=\footnotesize]{$u$};
\draw[->,thick,green!70!black,dashed] (3*\zOne-1.5*\xOne-1.5*\yOne,3*\zTwo-1.5*\xTwo-1.5*\yTwo) -- (3*\zOne-1.5*\xOne-1.5*\yOne,3*\zTwo-1.5*\xTwo+2*\yTwo)

\draw[->,thick,cyan,dashed] (3*\zOne-2*\xOne,3*\zTwo-2*\xTwo) -- (3*\zOne+2*\xOne,3*\zTwo+2*\xTwo)
     node[anchor=north west, xshift=-3pt,font=\footnotesize]{$x$};
\draw[->,thick,cyan,dashed] (3*\zOne-2*\yOne,3*\zTwo-2*\yTwo) -- (3*\zOne+2*\yOne,3*\zTwo+2*\yTwo)

\draw[-latex,line width=1.5pt,blue,line cap=round] (3*\zOne+0.69*\xOne,3*\zTwo+0.69*\xTwo) -- (3*\zOne+0.69*\xOne,3*\zTwo+0.69*\xTwo+0.69);
\node[circle,inner sep=0pt,minimum size=0.1cm,fill=blue] (object) at (3*\zOne+0.69*\xOne,3*\zTwo+0.7*\xTwo+0.69) {};

\filldraw[red,opacity=0.6] (3*\zOne+6*0.105*\xOne,3*\zTwo+0.75+6*0.105*\xTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo);

\draw[thick,solid,red] (-\zOne/2,-\zTwo/2) -- (3*\zOne+0.69*\xOne,3*\zTwo+0.7*\xTwo+0.69);

\draw[thin,solid] (0,0) -- (3*\zOne,3*\zTwo);

% z = f
\draw (3*\zOne-1*\xOne+1.3*\yOne,3*\zTwo-1*\xTwo+1.3*\yTwo) node[gray!70,rotate=28] {$ z = f $};
% bar(u)
\draw[to-to, green!70!black] (3*\zOne+0.13*\xOne+0.08*\yOne,3*\zTwo+0.13*\xTwo+0.08*\yTwo) -- (3*\zOne+0.7*\xOne+0.08*\yOne,3*\zTwo+0.7*\xTwo+0.08*\yTwo) node[midway,anchor=north west,xshift=-2pt,yshift=2pt,font=\scriptsize] {$ \bar{u} $};
% bar(v)
\draw[to-to, green!70!black] (3*\zOne-0.1*\xOne-0.04*\yOne,3*\zTwo-0.1*\xTwo-0.04*\yTwo) -- (3*\zOne-0.1*\xOne,3*\zTwo-0.1*\xTwo+0.75) node[midway,anchor=east,xshift=2pt,font=\scriptsize] {$ \bar{v} $};
% (u,v)
\node[green!70!black,anchor=west,font=\scriptsize] at (3*\zOne+0.69*\xOne,3*\zTwo+0.7*\xTwo+0.69) {$ (u,v) $};
% principal point
\draw[very thin] (3*\zOne-0.02*\xOne+0.02*\yOne,3*\zTwo-0.02*\xTwo+0.02*\yTwo) .. controls (3*\zOne-0.1*\xOne+0.3*\yOne,3*\zTwo-0.1*\xTwo+0.3*\yTwo) and (3*\zOne-0.3*\xOne+0.1*\yOne,3*\zTwo-0.3*\xTwo+0.1*\yTwo) ..  (3*\zOne-0.6*\xOne+0.4*\yOne,3*\zTwo-0.6*\xTwo+0.4*\yTwo) node[anchor=north,align=center,font=\sffamily\scriptsize] {ponto \\ principal};
% optical axis
\draw[very thin] (5.5*\zOne-0.02*\xOne+0.02*\yOne,5.5*\zTwo-0.02*\xOne+0.02*\yOne) .. controls (5.5*\zOne-0.1*\xOne+0.3*\yOne,5.5*\zTwo-0.1*\xTwo+0.3*\yTwo) and (5.5*\zOne-0.3*\xOne+0.1*\yOne,5.5*\zTwo-0.3*\xTwo+0.1*\yTwo) ..  (5.5*\zOne-0.6*\xOne+0.4*\yOne,5.5*\zTwo-0.6*\xTwo+0.4*\yTwo) node[anchor=north,align=center,font=\sffamily\scriptsize] {eixo \\ \'otico};

\draw[thin,gray!70] (3*\zOne,3*\zTwo+0.75) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo) -- ++(0.21*\xOne,0.21*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo) -- ++(0.21*\xOne,0.21*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo) -- ++(0.21*\xOne,0.21*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo) -- ++(0.21*\xOne,0.21*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo) -- ++(0.21*\xOne,0.21*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo) -- ++(0.21*\xOne,0.21*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.19*\yOne,-0.19*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo);

  • Have you published this image? Is this the final version? Can I reference that? Thanks
    – Pedro77
    Nov 22, 2016 at 19:16
  • I would also like to use and reference this in my thesis, is that ok? May 10, 2018 at 17:00
  • 1
    I saw this very picture in the book "Computação Gráfica: Teoria e Prática" (2nd edition). However, there's no source reference. Are you the author of that book?
    – LRMAAX
    Dec 11, 2019 at 15:10

2 Answers 2


WOW! This is really quite elegant.
You could have made your life a lot easier though, if you had used the TikZ 3D coordinate system, defined as \draw (0,1,2)--(2,3,4); and, if needed, adjusting the x,y and z coorinates by \begin{tikzpicture}[x={(1cm,0cm)},y={(0cm,1cm)},z={(0.73cm,0.73cm)}] or something alike. This changes the lengths with which the x,y and z are projected onto x and y.

As an alternative, you could have used thetikz-3dplot package to rotate your camera perspective about azimuth and elevation. Really just a two liner:


for 70 degree azimuth and 110 degree of elevation.

When rotating, you have to remember though, that TikZ has no real 3D support, as in:
z-buffering is not (yet?) supported. You might therefore have to adjust some colors after rotation. This is because, with rotation, the drawing order can change. Then things are in front of others, because of the time they are calculated, not where they are in 3D. This is explained in the tikz3dplot example manual, chapter 3.3.

Also, the use of relative coordinates helps to focus on the contents, rather than the creation of the figure. \draw (2,3,4)--++(0,0,1); takes you towards z by one, from where you used to be.

On a rather unrelated note, you should load the TikZ-packages in the preamble, otherwise you would have to reload them in every scope. This would slow down compilation speed, for larger documents.

  • I wasn't aware of this 3D coordinate system support in TikZ. Is it in the manual? A search for 3D doesn't show this. Looks interesting. What do you mean by having to adjust colors? Can't see the relation of it with this topic. I'll try this tikz-3dplot idea. Looks nice too. I used relative coordinates in the pixel squares, but it didn't seem to help much with the other drawings. Maybe with the 3D system this is solved.
    – perr0
    Jan 30, 2013 at 23:00
  • You can find it, in the manual, in chapter 13.2 - Coordinate systems, alongside some other predefined coordinate systems and an explanation on how to define your own coordinate systems. They can all exist in the same picture and you can mix them. Try to avoid mixing 2D and 3D references in one draw command though. Relative coordinate systems really only help to simplify writing a tiny bit, but it can help. On colors: assume you rotate until the blue P=(x,y,z) arrow is partially behind the plane. The color then gets distorted and you might have to rearrange the drawing order or colors.
    – DennisH
    Jan 31, 2013 at 9:20

I'm not sure if this is egregious duplication or not, but I redrew this in order to provide a modified version of this diagram in this answer to Catree's question and thought it might be useful to provide the code for an unmodified version here. I was going to just leave a comment, but then realised that several things needed to be changed in order to produce the original diagram and it got too much for a comment. I hope people will, therefore, forgive the duplication. (If not, I can delete this.)

The approach uses the tikz-3dplot package mentioned in DennisH's answer.

% ail-wneud y llun o gwestiwn perr0: https://tex.stackexchange.com/q/96074/
% côd newydd
    my dashed/.style={dashed, thick, ->, shorten >=-15pt, shorten <=-15pt, every node/.append style={font=\footnotesize}},
    my box/.style={thin, gray!70},
    my blue/.style={blue, line cap=round, -{Triangle[width=3*#1]}, line width=#1, shorten >=#1*1.75pt, every node/.append style={fill, circle, inner sep=0pt, minimum size=#1*3.5pt, anchor=center, outer sep=0pt}},
    my label/.append style={midway, font=\scriptsize},
    my vectors/.style={green!50!black, {Stealth[scale=.75]}-{Stealth[scale=.75]}},
    my red/.style={thick, red, line cap=round},
    my grey/.style={gray!70},
    description/.style={draw=gray!70, thick, line cap=round, every node/.style={align=center, font=\scriptsize\sffamily, anchor=north}},
%   \draw [help lines] (-2,0,0) -- (2,0,0) node[anchor=north west]{$x$} (0,0,0) -- (0,7,0) node[anchor=north east]{$y$} (0,0,0) -- (0,0,2) node[anchor=north]{$z$} (-2,7,0) -- (2,7,0);
  \draw [my grey] (0,4,0) -- (0,7,0) (-2,7,0) -- (2,7,0);
  \coordinate (o) at (0,0,0);
  \path [draw=gray!70, text=gray, fill=gray!20, opacity=0.8, text opacity=1] (-1.5,4,1.75) coordinate (a) -- ++(0,0,-3.5) coordinate (b) -- ++(3,0,0) coordinate (c) -- ++(0,0,3.5) coordinate (d) -- cycle node [pos=.95, above, sloped, anchor=south west] {$z=f$} ;
%   \foreach \i in {a,b,c,d} \node [red, font=\scriptsize] at (\i) {\i};
  \draw [my grey] (-2,0,0) -- (2,0,0) (0,0,0) -- (0,4,0) (0,0,0) -- (0,0,2);
  \draw [thick, ->, every node/.style={font=\footnotesize, inner sep=0pt}] (o) node [anchor=north west] {$F_c$} (o) edge node [pos=1, anchor=north east] {$z_c$} ++(0,1,0) edge node [pos=1, anchor=north] {$y_c$} ++(0,0,1) -- ++(1,0,0) node [anchor=north west] {$x_c$};
  \draw [my box] (o) ++(0,4,-.5) coordinate (p1) -- ++(1,0,0) coordinate (p2) -- ++(0,0,-1.25) coordinate (p3);
  \foreach \i in {0,1,...,4} \draw [my box] (p1) ++(\i*.25,0,0) -- ++(0,0,-.25);
  \foreach \i in {0,1,...,5} \draw [my box] (p2) ++(0,0,-\i*.25) -- ++(-.25,0,0);
  \draw [my box] (p1) ++(0,0,-.25) -- ++(.75,0,0) -- ++(0,0,-1);
  \draw [my dashed, cyan] ($(b)!1/2!(c)$) -- ($(d)!1/2!(a)$) node [below=15pt, anchor=north] {$y$};
  \draw [my dashed, cyan] ($(b)!1/2!(a)$) -- ($(d)!1/2!(c)$) node [above right=17pt, anchor=north west] {$x$};
  \draw [my dashed, green!50!black, <->] (a) node [below=15pt, anchor=north] {$v$} -- (b) -- (c) node [above right=17pt, anchor=north west] {$u$};
  \path [green!50!black, every node/.style={font=\scriptsize, inner sep=0pt}] (p2) node [above right, anchor=south west] {$(u,v)$};
  \path (p2) ++(-.125,0,0) coordinate (q2) ++(0,0,-.125) coordinate (r2);
  \draw [my blue=1] ($(0,4,0)+($(q2)-(p1)$)$) coordinate (s2) -- (r2) node (d1) {};
  \scoped[on background layer]{\draw [my blue=1.75] ($($1.75*($(s2)-(0,4,0)$)$)+(0,7,0)$) -- ++($1.75*($(r2)-(s2)$)$) node (d2) [label={[label distance=-20pt]above:{$P=(X,Y,Z)$}}] {};}
  \draw [my vectors] (0,4,.1) -- ($(s2)+(0,0,.1)$) node [below, my label, sloped] {$\vec{u}$};
  \draw [my vectors] (-.1,4,0) -- ($(q2)-(s2)+(-.1,4,0)$) node [left, my label] {$\vec{v}$};
  \draw [my red] (o) -- (d1.center);
  \scoped[on background layer]{\draw [my red] (d1.center) -- (d2.center);}
  \path [description] (0,4,0) [out=-95, in=95] to (-.75,4,.25) node {ponto\\principal} (0,6.5,0) [out=-95, in=95] to (-.75,6.5,.25) node {eixo\\\'otico};

pinhole camera with <code>tikz-3dplot</code>

  • It looks quite elegant, but from looking at it i got the idea: can you rotate the arrow tips to be parallel to a specific axis? Not that it would be of great importance concering the quality of the drawing, but i think the arrows (and the text) that are placed on the plane would look a bit more natural, if they would be skewed accordingly. Just a thought.
    – MaPePeR
    Aug 8, 2016 at 8:06
  • @MaPePeR I think the answer is that, yes, you could. However, I wouldn't do this for this particular drawing as I think the improvement would be minor, at best, and certainly not worth the effort to code it or the increase in computational time. (This is already quite slow to render for me.) And I'm not convinced that skewed text is helpful in a diagram like this. If it is part of a graphic as such, that's different. But in a diagram like this one, it is meant to be informative and skewed text would be harder to read. For the arrow tips, I suspect you'd end up with a mess, to be honest.
    – cfr
    Aug 8, 2016 at 14:59
  • That is, you can only fake 3D in TikZ. And rotating arrow tips ... no, you can't. You could, basically, draw a customised arrow tip in 2D which emulated one rotated in 3D for a particular plane. But you'd have to do this for every distinct case, which would simply be unmanageable. If you want proper 3D, then just don't use PGF/TikZ. It is important to remember that it only makes sense to do 3D in this when it is simple since fake 3D quickly gets impractically complex (both in terms of code/effort and computational time).
    – cfr
    Aug 8, 2016 at 15:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .