2011-09-01

Transformacja normalnej

Promień uderza w skalowaną nierównomiernie sferę, czyli elipsoidę. Nie chcemy jednak pisać osobnego kodu na obsługę elipsy. Promień przed analizą intersekcji poddajemy transformacji świat-obiekt. W wyniku czego nasz promień uderza w sferę o środku w środku układu współrzędnych. Znajdujemy punkt intersekcji. Transformujemy go z powrotem obiekt-świat. To działa. Teraz chcemy wyliczyć normalną w punkcie uderzenia naszej elipsy. Podobnie transformujemy punkt uderzenia świat-obiekt. Wyliczamy normalną dla sfery. Transformujemy ją obiekt-świat. To nie działa.


  
    
      
    
    
      
    
    
      
    
  
  
  
    
      
        image/svg+xml
        
        
      
    
  
  
    
      
      
    
    
      
      
    
    
      
      
    
    
      
      
    
    
      
      
    
    
    
      
      
    
    
      
      
    
    
      
      
    
    
      
      
    
    
      
      
    
    
    
      
      
    
    
      
      
    
    
      
      
    
    
      
      
    
    
      
      
    
    
  


Na pierwszym rysunku widzimy normalne dla sfery. Na drugim normalne sfery zmapowane obiekt-świat błędnie, za pomocą takiej samej metody jak dla punktów intersekcji. Na trzecim widzimy to co chcielibyśmy uzyskać.

Weźmy prostą w 2D $\mathbf{f=ax}$, jej pochodna $\mathbf{f'=a}$, jej normalna (nazwijmy ją tak) $\mathbf{f''=-a}$. Teraz tak jak na naszym rysunku skalujemy zwężając dwukrotnie w osi Y. Równanie naszej prostej $\mathbf{f=\frac{1}{2}ax}$, zaś normalnej $\mathbf{f''=-\frac{1}{2}a}$. Załóżmy teraz dwukrotnie rozciągamy w osi X. Równanie naszej prostej, pochodnej i normalnej zmienia się podobnie. Czyli skalując o a skalujemy normalną o $\mathbf{\frac{1}{2}}$. Podobnie moglibyśmy wykazać wychodząc z definicji pochodnej.

Czyli jeśli nasza macierz skalowania to $\mathbf{S}$, to musimy ją zastąpić w macierzy transformacji obiekt-świat macierzą $\mathbf{S^{-1}}$. Translację powinniśmy pominąć, gdyż mamy do czynienia nie z punktem, a z wektorem. Rotację musimy zachować. Jeśli macierz naszej transformacji dla wierzchołków ma postać $\mathbf{M=TRS}$, to dla normalnych będzie miała postać $\mathbf{M=(TRS^{-1})_{ROT}=RS^{-1}}$

Przy okazji zauważmy, że mając macierz obiekt-świat: $\mathbf{M=TRS}$, macierz transformacji normalnej możemy zapisać jako:

$\begin{split}MN &= (TRS^{-1})_{ROT} \\
&= ((T^{-1})^{T}(R^{-1})^{T}(S^{-1})^T)_{ROT} \\
&= (((XRS)^{-1})^T)_{ROT} \\
&= ((M^{-1})^T)_{ROT} \\
&= ((M^{T})^{-1})_{ROT} \end{split}$

W przekształceniach skorzystaliśmy z faktu, że macierz odwrotna macierzy rotacji jest równa jej transpozycji, macierz transponowana macierzy skalowania jest równa jej samej. X to przekształcona macierz transpozycji. Ponieważ bierzemy część rotacji X ulega ona zniesieniu, równie dobrze możemy wpisać dowolną macierz która w części rotacyjnej 3x3 jest macierzą jednostkową, w szczególności T. Zauważmy, że elementy macierzy T nie wpływają na elementy macierzy rotacji 3x3 w macierzy S i R, ani ich iloczynu. Tak więc macierz transpozycji obiekt-świat dla normalnych jest równa odwrotnej transponowanej macierzy obiekt-świat dla wierzchołków, albo jak kto woli odwrotnej transponowanej macierzy obiekt-świat dla wierzchołków. Czasami może nam się to przydać jeśli nie wiemy jak taka macierz została złożona.

Przykład kodu:
private void UpdateTransformationMatrices()
{
    m_local_to_world = Matrix4.CreateTranslation(Pos) *
        new Matrix4(Right, Up, Forward) * Matrix4.CreateScale(Scale);
    m_world_to_local = m_local_to_world.Inverted;
    m_local_to_world_normal = m_world_to_local.Transposed.Rotation;
}

Brak komentarzy:

Prześlij komentarz