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.

I want to do so called texture mapping which is to attach an image to a curved surface.

How to attach a 2D rectangular image to a 3D curved surface with TikZ or PSTricks?

For the sake of simplicity, let the curved surface be z=f(x,y)=asin(bx+cy)+d.

share|improve this question

2 Answers 2

up vote 20 down vote accepted
+25

As Christian said, you can use an external program and include the image using pgfplots.

As a proof of concept, here's a command \drapeimageonsurface{<image>}{<equation>} that takes an image filename and an equation and uses the rgl library in R to drape the image over the surface. It removes the background from the image, and computes and sets the necessary mapping to include it in pgfplots.

The whole thing is very, very rough around the edges, there's basically no user interface, but it works.

This image (map taken from http://en.wikipedia.org/wiki/File:Equirectangular_projection_SW.jpg)

is generated by

    \begin{document}
    \begin{tikzpicture}
    \drapeimageonsurface{worldmap.png}{abs(x)+abs(y)}
    \begin{axis}[3d box=complete, zmin=0, zmax=2]
    \addplot3 graphics {draped.png};

    \draw [fill=yellow] (axis cs:-0.67,0.41,1.08) circle [radius=3pt] node [font=\small, right=2pt, fill=white, fill opacity=0.5, text opacity=1] {San Francisco};
    \draw [fill=yellow] (axis cs:0.78,0.39,1.17) circle [radius=3pt] node [font=\small, text depth=0pt,below left=2pt, fill=white, fill opacity=0.5, text opacity=1] {Tokyo};
    \end{axis}
    \end{tikzpicture}
    \end{document}

And

is generated by

\begin{document}
\begin{tikzpicture}
\drapeimageonsurface{worldmap.png}{1+sin(2*x)*(-cos(y))}
\begin{axis}[3d box=complete, zmin=0, zmax=2]
\addplot3 graphics {draped.png};

\end{axis}
\end{tikzpicture}
\end{document}

Here's the complete code:

\documentclass{article}
\usepackage{pgfplots}
\makeatletter
\newcommand{\drapeimageonsurface}[2]{
% Create R script
\newwrite\tempfile
\immediate\openout\tempfile=rscript.r
\immediate\write\tempfile{library(rgl)^^J%
open3d()^^J%
par3d(windowRect=c(0,0,800,800))^^J%
x <- matrix(seq(-1,1,len=50),50,50,byrow=F)^^J%
y <- -matrix(seq(-1,1,len=50),50,50,byrow=T)^^J%
z<-#2^^J%
persp3d(x,y,z,col="white", texture="#1",specular="black",box=F,axes=F,xlab="",zlab="",ylab="")^^J%
view3d(userMatrix= rotate3d(rotationMatrix(-0.3*pi/2, 1,0,0),-pi/9,0,0,1),fov=0, scale=c(1,1,1))^^J%
rgl.snapshot('test.png')^^J%
M = par3d("modelMatrix")^^J%
viewport = par3d("viewport")^^J%
bbox=par3d("bbox")^^J%
v=rbind(matrix(bbox[c(1,3,5,2,3,5,1,4,5,2,4,6)],3),rep(1,4))^^J%
u = M \@percentchar*\@percentchar v^^J%
P = par3d("projMatrix")^^J%
A<-P \@percentchar*\@percentchar u^^J%
a=A / outer(rep(1,4),A[4,])^^J%
(a[1,]+1)/2*(viewport[3]-viewport[1])^^J%
(a[2,]+1)/2*(viewport[4]-viewport[2])^^J%
sink("3dmapping.txt")^^J%
cat("(")^^J%
cat(v[1:3,1],sep=",")^^J%
cat(") => (")^^J%
cat((a[1,1]+1)/2*(viewport[3]-viewport[1]),(a[2,1]+1)/2*(viewport[3]-viewport[1]),sep=",")^^J%
cat(")\@backslashchar n")^^J%
cat("(")^^J%
cat(v[1:3,2],sep=",")^^J%
cat(") => (")^^J%
cat((a[1,2]+1)/2*(viewport[3]-viewport[1]),(a[2,2]+1)/2*(viewport[3]-viewport[1]),sep=",")^^J%
cat(")\@backslashchar n")^^J%
cat("(")^^J%
cat(v[1:3,3],sep=",")^^J%
cat(") => (")^^J%
cat((a[1,3]+1)/2*(viewport[3]-viewport[1]),(a[2,3]+1)/2*(viewport[3]-viewport[1]),sep=",")^^J%
cat(")\@backslashchar n")^^J%
cat("(")^^J%
cat(v[1:3,4],sep=",")^^J%
cat(") => (")^^J%
cat((a[1,4]+1)/2*(viewport[3]-viewport[1]),(a[2,4]+1)/2*(viewport[3]-viewport[1]),sep=",")^^J%
cat(")")}
\immediate\write\tempfile{sink()}
\immediate\write\tempfile{viewport}
\immediate\write\tempfile{u}
\immediate\closeout\tempfile

% Run R script
\immediate\write18{R CMD BATCH rscript.r rscript.log}

% Remove image background
\immediate\write18{convert test.png -alpha set -channel alpha -fill none -floodfill +0+0 white draped.png}

% Set mapping
\everyeof{\relax}
\makeatletter
\def\auxmacro##1\relax{\pgfplotsset{plot graphics/points={##1}}}
\expandafter\auxmacro\@@input 3dmapping.txt 
\makeatother
}


\begin{document}
\begin{tikzpicture}
\drapeimageonsurface{worldmap.png}{1+sin(2*x)*(-2*cos(y))}
\begin{axis}[3d box=complete, zmin=-1, zmax=4]
\addplot3 graphics {draped.png};
\end{axis}
\end{tikzpicture}
\end{document}
share|improve this answer
    
Please wait for a couple of days or weeks or months or maybe years to accept this answer as I still let others think of other solutions (if any) gradually. –  stalking is prohibited Jun 8 '12 at 14:43
    
@Forgiver: Sure! I'd be interested to see other (maybe more native) solutions –  Jake Jun 8 '12 at 14:49

To my knowledge, there are no texture mapping libraries for TikZ.

What you can do is: generate your texture mapping image using an external tool (perhaps matlab) and import the graphics (and only the graphics) into pgfplots. This would result in axis descriptions generated by TikZ. Pgfplots can overlay a correct 3d axis below/on top a 2d projection of some 3d scene (orthogonal projection).

See 3-dimensional histogram in pgfplots for a related example (i.e. something which has been produced by means of 3rd party tools and imported into pgfplots).

share|improve this answer

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.