10. fejezet - Függelék

Tartalom

Window-viewport transzformációk
Uniform eszköz transzformáció
Window-viewport tarnszformáció 2. változat
Programkódok

Window-viewport transzformációk

A legtöbb grafikus problémát általában Descartes koordináta-rendszerben oldunk meg, tehát derékszögű úgynevezett világkoordináta-rendszerben adjuk meg az algoritmust. A világkoordináta-rendszer speciális, ezért gyakran transzformálni kell a periféria koordináta-rendszerbe. Általában a világ koordináta-rendszer derékszögű ablakát (Window) transzformáljuk az output rendszer képtartományába (Viewport). Gyors algoritmusra van szükségünk, mivel sok pont kezelésével nagy mennyiségű ismétlődő számítást kell elvégeznünk.

10.1. ábra. Window-vieport transzformáció

A geometriai input és output adatokat koordináta-rendszerek közötti leképezéseknek vetjük alá. A következő koordináta-rendszereket különböztetjük meg:

  • világkoordináta-rendszer (World Coordinates, WC) a felhasználó ebben adja meg illetve ebben kapja vissza a geometria információkat.

  • normalizált eszközkoordináta-rendszer (Normalized Device Coordinates, NDC), a különböző grafikus munkahelyek számára egységes koordináta-rendszer.

  • eszközkoordináta-rendszere (Device Coordinates, DC), a munkahely hardverének megfelelő specifikus koordináta-rendszer.

A 10.1. ábra alapján könnyen előállíthatunk egy olyan függvényt, amely világkoordináta-rendszerben lévő pont leképezi a normalizált eszközkoordináta-rendszerbe. A függvénynek rendelkeznie kell a következő tulajdonságokkal:

a világ koordináta-rendszer derékszögű ablakának (Window) határvonalpontjait az output rendszer képtartományának (Viewport) határvonalpontjaiba transzformálja.

Tehát a következő megfeleltetés igaz:

Egyenestartó leképezés, vagyis egyenes képe is egyenes lesz. Az x és y tengelyek a két rendszerben páronként párhuzamosak egymással. Ebből következik, hogy a leképezés lineáris és a 19. ábra alapján a következő összefüggés vezethető le:

és igaz, hogy

ebből adódik, hogy

visszahelyettesítve (10.1)-be:

Új jelöléseket bevezetve: és hasonlóan számítható dvy és dwy.

ami lineáris leképezés.

A másik koordinátára hasonló kifejezést kapunk:

Tehát végeredményként azt kaptuk, hogy

Mivel a leképezés során függetlenek a transzformálandó ponttól ezért ezeket az értékeket csak egyszer kell kiszámítani. Ha vagy  akkor a transzformálandó pont kívül esik az ablakon ezért nem kell vele foglalkozni.

Uniform eszköz transzformáció

10.2. ábra. Világ koordináták

10.3. ábra. Normalizált eszköz koordináták

A 10.2. és 10.3. ábrák egy Window-Viewport transzformációt illusztrálnak, ahol a torzítási tényezők (aspect ratio) nem egyeznek meg. Torzítási tényezőn a magasság és a szélesség arányát értjük. Ilyen estetekben a kör képe ellipszis lesz. A leképezési függvényben szereplő (dvx/dwx) és (dvy/dwy) nem egyenlő. Ez elfogadható, de a programozónak nagyon ügyelnie kell, hogy ne feledkezzen meg a torzulásról. Az uniform transzformációknál követelmény az előbbi hányadosok egyenlősége. is nevezhetjük Az uniform transzformáció valójában hasonlósági transzformáció is, mivel hasonló alakzatok keletkeznek.

Window-viewport tarnszformáció 2. változat

Megoldhatjuk a problémát síkbeli – 3x3-as – transzformációs mátrixok szorzásával is.

  1. Az ablak bal alsó sarkát az origóba toljuk.

  2. Skálázunk az ablak és a képmező (viewport) oldalarányaival, és az y tengelyre tükrözünk.

  3. Az ablak bal felső sarkát a képmező bal felső sarkába toljuk. (Az y tengely mentén többet kell tolnunk a tükrözés miatt.)

Az első lépésben az eltolás vektora

A második lépésben az skálázás mátrixa, amely tartalmazza az y tengelyre való tükrözést is:

A harmadik lépésben vissza kell tolnunk a már skálázott ablakot a képmezőbe, akkor eltolás vektora

Figyelnünk kell arra, hogy a transzformációkat fordított sorrendben kell elvégezni, erre a program mellékletben ügyeltünk. Ha összeszorozzuk a mátrixokat, akkor a 10.2-ban közölt eredményeket kapjuk.

Programkódok

Az identitás mátrixa:

         
// NxN-es egységmátrixot ad vissza 
      public static Matrix Identity(int dimensions) 
      { 
        Matrix ret = new Matrix(dimensions, dimensions); 
        for (int i = 1; i <= dimensions; ++i) 
           { 
            ret[i, i] = 1; 
           } 
        return ret; 
      }
         

Az eltolás mátrixa:

      
// Háromdimenziós eltolási mátrix 
public static Matrix Translate3D(double dx, double dy, double dz) 
{ 
   return new Matrix(4, 4, 
                1, 0, 0, dx, 
                0, 1, 0, dy, 
                0, 0, 1, dz, 
                0, 0, 0, 1); 
}
     

Az elforgatás mátrixa:

      
// 3D-s forgatási mátrix az X tengely körül 
public static Matrix Rotate3Dx(double alpha) 
{ 
  return new Matrix(4, 4, 
               1, 0, 0, 0, 
               0, Math.Cos(alpha), -Math.Sin(alpha), 0, 
               0, Math.Sin(alpha), Math.Cos(alpha), 0, 
               0, 0, 0, 1); 
} 
 
// 3D-s forgatási mátrix az Y tengely körül 
public static Matrix Rotate3Dy(double alpha) 
{ 
   return new Matrix(4, 4, 
                 Math.Cos(alpha), 0, Math.Sin(alpha), 0, 
                0, 1, 0, 0, 
                -Math.Sin(alpha), 0, Math.Cos(alpha), 0, 
                0, 0, 0, 1); 
} 
 
// 3D-s forgatási mátrix a Z tengely körül 
public static Matrix Rotate3Dz(double alpha) 
{ 
   return new Matrix(4, 4, 
                Math.Cos(alpha), -Math.Sin(alpha), 0, 0, 
                Math.Sin(alpha), Math.Cos(alpha), 0, 0, 
                0, 0, 1, 0, 
                0, 0, 0, 1); 
}
     

Rodrigues-féle forgatás mátrixa:

      
// Tetszőleges tengely körüli forgatás 
public static Matrix Rodrigues(double phi, Point3D P1, Point3D P2) 
{ 
  double h = Math.Sqrt((P2.X - P1.X) * (P2.X - P1.X) + 
                  (P2.Y - P1.Y) * (P2.Y - P1.Y) + 
                  (P2.Z - P1.Z) * (P2.Z - P1.Z)); 
  // A normált vektor koordinátái: 
  double cx, cy, cz, d, CosPhi, SinPhi; 
  cx = (x2 - x1) / h; 
  cy = (y2 - y1) / h; 
  cz = (z2 - z1) / h; 
  d = Math.Sqrt(cz * cz + cy * cy); 
  CosPhi = Math.Cos(phi); 
  SinPhi = Math.Sin(phi); 
  Matrix cg = new Matrix(4, 4, 
 
  (-CosPhi + 1) * cx * cx + CosPhi, 
  (-CosPhi + 1) * cx * cy - SinPhi * cz, 
  (-CosPhi + 1) * cx * cz + SinPhi * cy, 0, 
 
  (-CosPhi + 1) * cx * cy + SinPhi * cz, 
  (-CosPhi + 1) * cy * cy + CosPhi, 
  (-CosPhi + 1) * cy * cz - cx * SinPhi, 0, 
 
  (-CosPhi + 1) * cx * cz - SinPhi * cy, 
  (-CosPhi + 1) * cy * cz + cx * SinPhi, 
  (-CosPhi + 1) * cz * cz + CosPhi, 0, 
 
   0, 0, 0, 1); 
   return cg; 
}
     

A koordináta síkra való tükrözés mátrixa:

      
// x-y síkra való tükrözés 
public static Matrix ReflectXY() 
{ 
   return new Matrix(4, 4, 
                1, 0, 0, 0, 
                0, 1, 0, 0, 
                0, 0,-1, 0, 
                0, 0, 0, 1); 
}
     

Tetszőleges síkra való tükrözés mátrixa, 1.változat:

      
\\Tetszőleges síkra való tükrözés 1.változat 
public static Matrix Reflection1( Point3D A, Point3D B, Point3D C) 
{ 
  Point3D v1 = new Point3D(B.X - A.X, B.Y - A.Y, B.Z - A.Z), 
        v2 = new Point3D(C.X - A.X, C.Y - A.Y, C.Z - A.Z), 
        normv = new Point3D(0, 0, 0); 
  // vektoriális szorzat normv=v1xv2 
  normv.X = v2.Y * v1.Z - v2.Z * v1.Y; 
  normv.Y = v2.Z * v1.X - v2.X * v1.Z; 
  normv.Z = v2.X * v1.Y - v2.Y * v1.X; 
  //normalizálás :|normv|=1 
  double h = Math.Sqrt(normv.X * normv.X + normv.Y * normv.Y + 
          normv.Z * normv.Z); 
  //normált vektor koordinátáit segédváltozókban tároljuk 
  double cx, ay, az; 
  cx = normv.X / h; 
  cy = normv.Y / h; 
  cz = normv.Z / h; 
  Matrix cg = new Matrix(4, 4, 
      1 - 2 * cx * cx, -2 * cy * cx , -2 * cz * cx , 0, 
      -2 * cy * cx , 1 - 2 * cy * cy, -2 * cy * cz , 0, 
      -2 * cz * cx , -2 * cy * cz , -2 * cz * cz + 1, 0, 
      0, 0, 0, 1); 
  return cg; 
}
     

Tetszőleges síkra való tükrözés mátrixa, 2.változat:

      
\\Tetszőleges síkra való tükrözés 2.változat 
public static Matrix Reflection2( Point3D A, Point3D B, Point3D C) 
{ 
  Point3D v1 = new Point3D(B.X - A.X, B.Y - A.Y, B.Z - A.Z), 
        v2 = new Point3D(C.X - A.X, C.Y - A.Y, C.Z - A.Z), 
        normv = new Point3D(0, 0, 0); 
  // vektoriális szorzat normv=v1xv2 
  normv.X = v2.Y * v1.Z - v2.Z * v1.Y; 
  normv.Y = v2.Z * v1.X - v2.X * v1.Z; 
  normv.Z = v2.X * v1.Y - v2.Y * v1.X; 
  //normalizálás :|normv|=1 
  double h = Math.Sqrt(normv.X * normv.X + normv.Y * normv.Y + 
          normv.Z * normv.Z); 
  //normált vektor koordinátáit segédváltozókban tároljuk 
  double cx, cy, cz; 
  cx = normv.X / h; 
  cy = normv.Y / h; 
  cz = normv.Z / h; 
  //------- különbség cz 1.változattól -------- 
  // a sík fix pontja legyen cz A pont 
  double x0 = A.X, y0 = A.Y, z0 = A.Z, 
       d =-cx * x0-cy * yo-cz * z0; 
  Matrix cg = new Matrix(4, 4, 
   1 - 2 * cx * cx, -2 * cy * cx , -2 * cz * cx , -2 * cx * d, 
   -2 * cy * cx , 1 - 2 * cy * cy, -2 * cy * cz , -2 * cy * d, 
   -2 * cz * cx , -2 * cy * cz , -2 * cz * cz + 1, -2 * cz * d, 
   0, 0, 0, 1); 
  return cg; 
  //------- különbség vége ------- 
}
     

A skálázás mátrixa:

                                                                                                                                     

                                                                                                                                     
         
// Skálázás 
public static Matrix Scale3D(double kx, double ky, double kz) 
{ 
  return new Matrix(4, 4, 
               kx, 0 , 0 , 0, 
               0 , ky, 0 , 0, 
               0 , 0 , kz, 0, 
               0 , 0 , 0 , 1); 
}
         

A hasonlóság mátrixa 1.változat:

      
// Kicsinyítés vagy nagyítás 
public static Matrix Scale3D(double k) 
{ 
  return new Matrix(4, 4, 
               k, 0, 0, 0, 
               0, k, 0, 0, 
               0, 0, k, 0, 
               0, 0, 0, 1); 
}
     

A hasonlóság mátrixa 2.változat:

      
// Kicsinyítés vagy nagyítás 
public static Matrix Scale3D(double k) 
{ 
  return new Matrix(4, 4, 
               1, 0, 0, 0 , 
               0, 1, 0, 0 , 
               0, 0, 1, 0 , 
               0, 0, 0, 1/k); 
}
     

A vetítés mátrixa:

      
//Merőleges vetítés mátrixa 
public static Matrix OrtogonalProj() 
{ 
  return new Matrix(4, 4, 
               1, 0, 0, 0, 
               0, 1, 0, 0, 
               0, 0, 0, 0, 
               0, 0, 0, 1); 
} 
//Párhuzamos vetítés mátrixa 
public static Matrix ParalellProj(double vx,double vy,double vz) 
{ 
  return new Matrix(4, 4, 
               1, 0, -vx/vz, 0, 
               0, 1, -vy/vz, 0, 
               0, 0,    0, 0, 
               0, 0,    0, 1); 
} 
//Centrális vetítés 1.változat 
// A nézőpont a Z tengely pozitív felén van 
// és negatív irányba néz, a képsík az XY sík. 
public static Matrix CentralProj1(double eyeDistance) 
{ 
  return new Matrix(4, 4, 
               1, 0, 0, 0, 
               0, 1, 0, 0, 
               0, 0, 0, 0, 
               0, 0, -1 / eyeDistance, 1); 
} 
//Centrális vetítés 2.változat 
// A nézőpont az origóban van 
// és pozitív irányba néz, a képsík az z=eyeDistance sík. 
public static Matrix CentralProj2(double eyeDistance) 
{ 
  return new Matrix(4, 4, 
               1, 0, 0, 0, 
               0, 1, 0, 0, 
               0, 0, 1, 0, 
               0, 0, 1 / eyeDistance, 0); 
}
     

Speciális nézeti rendszer:

      
// Viewing system 
// Áttérés világ koordináákról kamera koordinátákra 
// Kamera: gömbi koordinátákkal van megadva; 
// a világkoordináta rendszer origójába néz; 
// a függőleges irány a világ koordináta-rendszer 
// z tengelye jelöli ki; 
// balsodrású a kamera koordináta rendszer. 
public static Matrix SpecViewingSystem(double alpha, double beta, 
                             double r) 
{ 
   double sina = Math.Sin(alpha * Math.PI / 180); 
   double cosa = Math.Cos(alpha * Math.PI / 180); 
   double sinb = Math.Sin(beta * Math.PI / 180); 
   double cosb = Math.Cos(beta * Math.PI / 180); 
 
   return new Matrix(4, 4, 
                -sina, cosa, 0, 0, 
                -cosa * cosb, -sina * cosb, sinb, 0, 
                -cosa * sinb, -sina * sinb, -cosb, r, 
                        0, 0, 0, 1); 
}
     

Általános nézeti rendszer:

      
// General Viewing system u,v,n vektorokkal 
public static Matrix GenViewingSystem( Point3D C, Point3D F, 
                             Point3D Up, string sodras) 
{ 
   // Áttérés világ koordináákról kamera koordinátákra 
   // Kamera: Descartes koordinátákkal, van megadva; 
   // a z tengely iránya : n, F-C; 
   // a y tengely iránya: v=Up-skalarisszorzat(Up,n)*n; 
   // a x tengely iránya: u=vxn->jobbsodrású 
   // -u=nxv->balsodrású koordináta-rendszer. 
   Point3D n = new Point3D(F.X - C.X, F.Y - C.Y, F.Z - C.Z), 
         v = new Point3D(0, 0, 0), 
         u = new Point3D(0, 0, 0); 
   //|n|=1 
   n = Normalizalas(n); 
   v.X = Up.X - SkalariSzorzat(Up, n) * n.X; 
   v.Y = Up.Y - SkalariSzorzat(Up, n) * n.Y; 
   v.Z = Up.Z - SkalariSzorzat(Up, n) * n.Z; 
   v = Normalizalas(v); 
   u = VektorialisSzorzat(v, n); 
   u= Normalizalas(u); 
   if (sodras == "bal") 
   { 
      u.X = -u.X; u.Y = -u.Y; u.Z = -u.Z; 
   } 
   Matrix Tc = new Matrix(4, 4, 
                   u.X, u.Y, u.Z, 0, 
                   v.X, v.Y, v.Z, 0, 
                   n.X, n.Y, n.Z, 0, 
                   0, 0, 0, 1); 
   Matrix T = new Matrix(4,4); 
        T = Matrix.Translate3D(-C.X,-C.Y,-C.Z); 
   Matrix cg = new Matrix(4, 4); 
   cg = Tc * T; 
   return cg; 
}
     

Window-viewport transzformáció mátrixa:

      
// Window to viewport transzformációs mátrix 
public static Matrix WindowToViewPort(RectangleF window, 
                            RectangleF viewport) 
{ 
   // 1. Az ablak bal alsó sarkát az origóba toljuk. 
   // 2. Skálázunk az ablak és a viewport oldalarányaival, 
   //   és az Y tengelyre tükrözünk. 
   // 3. A kép bal felső sarkát a viewport bal felső sarkába toljuk. 
   //   (Az Y tengely mentén többet kell tolnunk a tükrözés miatt.) 
 
   return Matrix.Translate2D(viewport.Left, 
                      viewport.Top + viewport.Height) * 
        Matrix.Scale2D(viewport.Width / window.Width, 
                   -viewport.Height / window.Height) * 
        Matrix.Translate2D(-window.Left, -window.Top); 
}
     

Kavalier-axonometria mátrixa:

      
 
// A Kavalier-axonometria mátrixa. 
// Az X és Y tengelyek egységvektorainak képe önmaga, 
// a Z tengely egységvektorának képe length hosszú, és 
// angle szöget zár be az X tengellyel 
public static Matrix KavalierAxonometry(double length, double angle) 
{ 
   return new Matrix(3, 4, 
                1, 0, -length * Math.Cos(angle), 0, 
                0, 1, -length * Math.Sin(angle), 0, 
                0, 0, 0, 1); 
}
     

A mátrix osztály:

                                                                                                                                     

                                                                                                                                     
         
class Matrix 
{ 
   // a sorok és oszlopok száma 
   private int rows, cols; 
   public int Rows 
   { 
      get 
      { 
         return rows; 
      } 
   } 
   public int Cols 
   { 
      get 
      { 
         return cols; 
      } 
   } 
   // Az elemeket 1-től indexelünk, hogy közelebb legyünk 
   // a matematikai jelöléshez. 
   // Az elemek kívülről nem módosíthatók 
   private double[,] elements; 
   public double this[int i, int j] 
   { 
      get 
      { 
         return elements[i - 1, j - 1]; 
      } 
      private set 
      { 
         elements[i - 1, j - 1] = value; 
      } 
   } 
 
   // Az osztály metodusainak a felsorolása, lásd lentebb. 
   //\dots 
} 
//Metodusok 
//Mátrix létrehozása, az elemek implicite 0-k lesznek 
//Privát a konstruktor, mivel kívülről nics értelme hívni 
//(a hívó nem tudná utólag kitölteni az elemeket) 
private Matrix(int_rows, int_cols) 
{ 
   if (_rows <= 0 ||_cols <= 0) 
   { 
      throw new ArgumentException("Legyen␣pozitív␣a␣dimenzió!"); 
   } 
   rows =_rows; cols =_cols; 
   elements = new double[_rows,_cols]; 
} 
 
//Mátrix létrehozása a dimenziók és az elemek megadásával 
public Matrix(int_rows, int_cols, params double[] values) 
   : this(_rows,_cols) 
{ 
   // ellenőrízzük, hogy a megadott elemek száma megfelelő-e 
   if (values.Length != rows * cols) 
   { 
      throw new ArgumentException("Nem␣megfeleő␣az␣elemek␣száma!"); 
   } 
   // a values tömbből sorfolytonosan töltjük fel a mátrix elemeit 
   int curr = 0; 
   for (int i = 0; i < rows; ++i) 
      for (int j = 0; j < cols; ++j) 
      { 
         elements[i, j] = values[curr]; 
         ++curr; 
      } 
} 
 
// A mátrixszorzás operátora 
public static Matrix operator *(Matrix left, Matrix right) 
{ 
   // Megnézzük, lehet-e egyáltalán szorozni a két mátrixot 
   if (left.cols != right.rows) 
   { 
      throw new ArgumentException("Nem␣összeszorozható␣matrixok!"); 
   } 
   Matrix res = new Matrix(left.rows, right.cols); 
   // Kihasználva azt, hogy a privát konstruktorunk 0-val 
   // töltötte fel a res mátrixot, elég a ciklus 
   // belsejében += operátort használni. 
   for (int i = 1; i <= res.rows; ++i) 
      for (int j = 1; j <= res.cols; ++j) 
         for (int k = 1; k <= left.cols; ++k) 
         { 
            res[i, j] += left[i, k] * right[k, j]; 
         } 
   return res; 
} 
 
 
// Kétdimenziós eltolási mátrix 
public static Matrix Translate2D(double x, double y) 
{ 
   return new Matrix(3, 3, 
                1, 0, x, 
                0, 1, y, 
                0, 0, 1); 
} 
 
// Kétdimenziós skálázási mátrix 
public static Matrix Scale2D(double x, double y) 
{ 
   return new Matrix(3, 3, 
                x, 0, 0, 
                0, y, 0, 
                0, 0, 1); 
} 
 
// NxN-es egységmátrixot ad vissza 
public static Matrix Identity(int dimensions) 
{ 
   Matrix ret = new Matrix(dimensions, dimensions); 
   for (int i = 1; i <= dimensions; ++i) 
   { 
      ret[i, i] = 1; 
   } 
   return ret; 
} 
 
\\A mátrix transzponáltja 
public Matrix Transpose() 
{ 
   Matrix ret = new Matrix(cols, rows); 
   for (int i = 1; i <= rows; ++i) 
      for (int j = 1; j <= cols; ++j) 
      { 
         ret[j, i] = this[i, j]; 
      } 
   return ret; 
}