我正在尝试使用基本的 3D 照明模型来对 3D 参数曲面进行着色,但是这个答案说 TikZ 不支持照明。唯一的选择是使用颜色渐变来为顶点着色。这对于某些图形来说可能是可以接受的,但当您尝试显示 3D 对象的实际形状时则不行。
但是 TikZ 显然拥有所有可用数据来执行此操作。可以为每个顶点计算导数(即用户无需手动为每个表面分析导出它们)。然后可以使用它们来构建法线。让用户指定点光源位置,然后,您就会获得漫射照明。确定相机位置,您也会获得镜面照明。
我知道像 Asymptote 这样的软件包可以创建漂亮的 3D 图像,但是这些解决方案会创建光栅图形,而我需要矢量图形。
如何向 TikZ 添加 3D 灯光和阴影?它不存在有什么原因吗?我不知道 TikZ 是如何实现的,而且我之前也没有写过任何扩展,所以我不知道如何添加这个功能。
答案1
它是通过 TikZ 内部的直接计算获得的。表面是参数化的,但 LaTeX 内存限制几乎已经触及。代码如下,改编自我在在 TikZ 中对圆环进行着色。那里有一些解释。由于我添加了阴影,我再次给出代码。
坐标是无阴影的;尝试一下盎司轴...也许 DJP 用来sagetex
进行计算的想法是合理的。
\documentclass[margin=10pt]{standalone}
\usepackage{ifthen}
\usepackage[rgb]{xcolor}
\usepackage{tikz}
\usetikzlibrary{cd, arrows, matrix, intersections, math, calc}
\xdefinecolor{O}{RGB}{255, 102, 17}
\xdefinecolor{B}{RGB}{17, 87, 221}
\begin{document}
\tikzmath{%
real \slongit, \slatit, \sunx, \suny, \sunz; % towards the light source
real \longit, \latit, \tox, \toy, \toz;
real \newxx, \newxy, \newyx, \newyy, \newzx, \newzy;
\slongit = 100; \slatit = 45;
\sunx = sin(\slongit)*cos(\slatit);
\suny = sin(\slatit);
\sunz = cos(\slongit)*cos(\slatit);
\longit = 25; \latit = 36; % 35;
\tox = sin(\longit)*cos(\latit);
\toy = sin(\latit);
\toz = cos(\longit)*cos(\latit);
\newxx = cos(\longit); \newxy = -sin(\longit)*sin(\latit);
\newyy = cos(\latit);
\newzx = -sin(\longit); \newzy = -cos(\longit)*sin(\latit);
real \ry, \rz;
\ry = 4;
\rz = 1.5;
integer \Ny, \Nz, \j, \k, \prevj, \prevk, \aj, \ak;
% j moves around Oy and k moves around Oz.
% They describe full circles of radii \ry and \rz respectively.
\Nz = 48; % 24; % 60;
\Ny = 80; % 36; % 120;
\ktmp = \Nz-1;
\jtmp = \Ny-1;
\aj = 10;
\ak = 0;
function isSeen(\j, \k) {
let \px = cos(360*(\k/\Nz))*cos(360*(\j/\Ny));
let \py = -sin(360*(\k/\Nz));
let \pz = cos(360*(\k/\Nz))*sin(360*(\j/\Ny));
let \res = \px*\tox + \py*\toy + \pz*\toz;
if \res>0 then {return 1;} else {return 0;};
};
function inLight(\j, \k) {%
let \px = cos(360*(\k/\Nz))*cos(360*(\j/\Ny));
let \py = -sin(360*(\k/\Nz));
let \pz = cos(360*(\k/\Nz))*sin(360*(\j/\Ny));
return {\px*\sunx + \py*\suny + \pz*\sunz};
};
function projX(\j, \k) {%
let \px = \ry+\rz*cos(360*(\k/\Nz))*cos(360*(\j/\Ny));
let \py = -\rz*sin(360*(\k/\Nz));
let \t = -(\rz+\py)/\suny;
return {\px + \t*\sunx};
};
function projZ(\j, \k) {%
let \py = -\rz*sin(360*(\k/\Nz));
let \pz = \ry+\rz*cos(360*(\k/\Nz))*sin(360*(\j/\Ny));
let \t = -(\rz+\py)/\suny;
return {\pz + \t*\sunz};
};
function T(\j, \k) {%
let \py = -\rz*sin(360*(\k/\Nz));
let \pz = \ry+\rz*cos(360*(\k/\Nz))*sin(360*(\j/\Ny));
return {\rz*(-1+sin(360*(\k/\Nz)))/\suny};
};
}
\begin{tikzpicture}[every node/.style={scale=.8},
x={(\newxx cm, \newxy cm)},
y={(0 cm, \newyy cm)},
z={(\newzx cm, \newzy cm)},
evaluate={%
% int \j, \k;
real \tmp;
for \j in {0, 1, ..., \Ny}{%
for \k in {0, 1, ..., \Nz}{%
\test{\j,\k} = isSeen(\j, \k);
if \test{\j,\k}>0 then {%
\tmp{\j,\k} = int(100*inLight(\j,\k)));
if \tmp{\j,\k}>0 then {%
\tmpW{\j,\k}=int(100*inLight(\j,\k)^2);
}
else {%
\tmpK{\j,\k}=-int(100*inLight(\j,\k));
};
} else {};
};
};
}]
% points (P-\j-\k)
\foreach \j in {0, ..., \Ny}{%
\foreach \k in {0, ..., \Nz}{%
\path
( {( \ry+\rz*cos(360*(\k/\Nz)) )*cos(360*(\j/\Ny))},
{-\rz*sin(360*(\k/\Nz))},
{( \ry+\rz*cos(360*(\k/\Nz)) )*sin(360*(\j/\Ny))} )
coordinate (P-\j-\k);
}
}
% shadow
\foreach \k [remember=\k as \prevk (initially 0)] in {1, ..., \Nz}{%
\foreach \j [remember=\j as \prevj (initially 0)] in {1, ..., \Ny}{%
\fill[gray!70!black, opacity={.4*abs(inLight(\j,\k))}]
($(P-\j-\prevk)+T(\j,\prevk)*(\sunx, \suny, \sunz)$)
-- ($(P-\prevj-\prevk)+T(\prevj,\prevk)*(\sunx, \suny, \sunz)$)
-- ($(P-\prevj-\k)+T(\prevj,\k)*(\sunx, \suny, \sunz)$)
-- ($(P-\j-\k)+T(\j,\k)*(\sunx, \suny, \sunz)$) -- cycle;
}
}
% coordinate system $Oxyz$; first layer
\draw[green!50!black]
(0, 0, 0) -- (\ry, 0, 0)
(0, 0, 0) -- (0, 0, \ry);
% "squares"---the mesh
\foreach \k [remember=\k as \prevk (initially 0)] in {1, ..., \Nz}{%
\foreach \j [remember=\j as \prevj (initially 0)] in {1, ..., \Ny}{%
\ifthenelse{\test{\j,\k}=1}{
\ifthenelse{\tmp{\j,\k}>0}{
\filldraw[white!\tmpW{\j,\k}!B]
(P-\j-\prevk) -- (P-\prevj-\prevk)
-- (P-\prevj-\k) --(P-\j-\k) -- cycle;
}{%
\filldraw[black!\tmpK{\j,\k}!B]
(P-\j-\prevk) -- (P-\prevj-\prevk)
-- (P-\prevj-\k) --(P-\j-\k) -- cycle;
}
}{}
}
}
% longitude cycle
\foreach \k [remember=\k as \prevk (initially 0)] in {1, ..., \Nz}{%
\ifthenelse{\test{\aj,\k}=1}{
\draw[red, thick] (P-\aj-\k) -- (P-\aj-\prevk);
}{
\draw[red, very thin, opacity=.4] (P-\aj-\k) -- (P-\aj-\prevk);
}
}
% latitude cycle
\foreach \j [remember=\j as \prevj (initially 0)] in {1, ..., \Ny}{%
\ifthenelse{\test{\j,\ak}=1}{
\draw[red, thick] (P-\j-\ak) -- (P-\prevj-\ak);
}{
\draw[red, very thin, opacity=.3] (P-\j-\ak) -- (P-\prevj-\ak);
}
}
% coordinate system $Oxyz$; second layer
\draw[green!50!black, -{Latex[length=5pt, width=5pt]}]
(\ry+\rz, 0, 0) -- (8, 0, 0) node[right] {$x$};
\draw[green!50!black, -{Latex[length=5pt, width=5pt]}]
(0, 0, 0) -- (0, 6, 0) node[above] {$y$};
\draw[green!50!black, -{Latex[length=5pt, width=5pt]}]
(0, 0, \ry+\rz) -- (0, 0, 8) node[below left] {$z$};
\end{tikzpicture}
\end{document}