Iluminação e sombreamento 3D com TikZ/PGFPlots

Iluminação e sombreamento 3D com TikZ/PGFPlots

Estou tentando sombrear superfícies paramétricas 3D usando um modelo básico de iluminação 3D, masesta respostadiz que o TikZ não tem suporte para iluminação. A única opção é usar gradientes de cores para sombrear os vértices. Isso pode ser aceitável para determinados gráficos, mas não quando você tenta mostrar a forma real de um objeto 3D.

Mas o TikZ claramente tem todos os dados disponíveis para fazer isso. As derivadas podem ser calculadas numericamente para cada vértice (ou seja, sem que o usuário precise derivá-las analiticamente manualmente para cada superfície). Eles podem então ser usados ​​para construir as normais. Deixe o usuário especificar um local de luz pontual e pronto, você terá iluminação difusa. Prenda a posição da câmera e você também terá iluminação especular.

Eu sei que pacotes como o Asymptote podem criar belas imagens 3D, mas essas soluções criam gráficos rasterizados e preciso de gráficos vetoriais.

Como posso adicionar iluminação e sombreamento 3D ao TikZ?Existe uma razão pela qual ainda não existe? Não sei como o TikZ é implementado e não escrevi nenhuma extensão antes, então não tenho ideia de como adicionaria esse recurso.

Responder1

insira a descrição da imagem aqui

É obtido por cálculos diretos dentro do TikZ. A superfície é fornecida parametricamente, mas as limitações de memória do LaTeX já foram quase tocadas. O código está abaixo, adaptado da resposta que dei emSombreando um toro no TikZ. Existem algumas explicações aí. Forneço o código novamente desde que adicionei a sombra.

As coordenadas não têm sombra; é tentador experimentar a sombra projetada doonçaeixo... Talvez a ideia do DJP de usar sagetexpara realizar cálculos seja a mais razoável.

\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}

informação relacionada