学習目標
- 同次座標の概念を理解する。
- 基本的な 2 次元幾何変換行列に精通している。
- コーエン・サザーランド直線セグメント切断アルゴリズムに精通しています。
- 中点セグメンテーション直線セグメント切断アルゴリズムをマスターします。
- Liang-Barsky 直線セグメント クリッピング アルゴリズムをマスターします。
- Sutherland-Hodgman ポリゴン クリッピング アルゴリズムについて学びます。
先駆的な知識
グラフィックスの幾何学的変換には、グラフィックスの平行移動変換、スケール変換、回転変換、反射変換、およびミスカット変換が含まれます。
図形幾何変換は2次元図形幾何変換と3次元図形幾何変換(一応2種類)に分けられ、2次元図形幾何変換は3次元図形幾何変換の基礎となる。
正規化された同次座標
2次元幾何変換行列
オブジェクト変換と座標変換
2D幾何学変換
グラフ頂点セットの正規化された同次座標行列に、特定の変換行列 が乗算されます。変換行列の定義については、式(5-2)を参照してください。
2次元グラフィックスの基本的な幾何変換行列
2 次元グラフィックスの基本的な幾何学的変換とは、座標原点および座標軸に対する相対的な幾何学的な変換を指します。これには、平行移動、スケール、回転、反射、およびせん断の 5 つの変換が含まれます。
オブジェクトの変換はオブジェクト上の各頂点を変換することで実現されるため、点の 2 次元基本幾何変換を例にして 2 次元グラフィックスの基本幾何変換行列を説明します。
変換変換行列
スケール変換行列
回転変換行列
反射変換行列
ミスカット変換行列
たとえば
、これがいかに間違っているかを思い出してください。変換行列だけ覚えておけば大丈夫です。
いくつかのルール:
上で説明した 5 つの変換はすべて、ポイント変換の公式を示しています。ワイヤーフレーム モデルの場合、グラフィックスの変換は実際にはポイント変換を通じて完了できます。
たとえば、直線セグメントは 2 つの頂点の座標を変換し、新しい頂点を接続して新しい変換された直線セグメントを取得することによって変換できます。多角形は、各頂点を変換し、新しい頂点を接続して新しい変換された直線セグメントを取得することによって変換できます。ポリゴン。曲線の変形は、制御ポリゴンの制御点を変形した後に曲線を再描画することで実現できます。
2次元アフィン変換
以下の形式に従う座標変換を2次元アフィン変換(Affine Transformation)といいます。
アフィン変換には、平行線を平行線に変換し、有限点を有限点にマッピングするという一般的な特性があります。
平行移動、スケール、回転、反射、およびクロスカットの 5 つの変換は、すべて2 次元アフィン変換の特殊なケースです。2次元アフィン変換のセットは常に、これら 5 つの変換の組み合わせとして表現できます。
2次元複合変換
原則:
任意の基準点に対する比率変換と回転変換は、複合変換形式として表現される必要があります。
変形方法は(2段階)
- まず、基準点を座標原点に移動し、座標原点を拡大縮小および回転します。
- 次に、逆移動を実行して、基準点を元の位置に移動します。
上の右の図では、まず点 Q と点 P を座標の原点に移動します。
任意の方向に対する 2 次元の幾何学的変換
任意の方向に対する変換方法は次のとおりです。
- まず、変換方向が座標軸と一致するように「任意の方向」に回転変換を行います。
- 次に、座標軸上で 2 次元の基本幾何学変換を実行し、
- 最後に、逆回転変換を実行して、任意の方向を元の方向に戻します。
例 5-2:図に示した三角形を y=kx+b 軸に対して鏡映変換し、各ステップの変換行列を計算します。
まず、変換方向が座標軸と一致するように「任意の方向」に回転変換を行います。↑
次に座標軸上で2次元の基本幾何変換を行います↓
最後に、逆回転変換を実行して、任意の方向を元の方向に戻します。↓
コード:
class CTransform//二维几何变换
{
public:
CTransform();
virtual ~CTransform();
void SetMat(CP2 *,int);
void Identity();
void Translate(double,double);//平移变换矩阵
void Scale(double,double);//比例变换矩阵
void Scale(double,double,CP2);//相对于任意点的比例变换矩阵
void Rotate(double);//旋转变换矩阵
void Rotate(double,CP2);//相对于任意点的旋转变换矩阵
void ReflectO();//原点反射变换矩阵
void ReflectX();//X轴反射变换矩阵
void ReflectY();//Y轴反射变换矩阵
void Shear(double,double);//错切变换矩阵
void MultiMatrix();//矩阵相乘
public:
double T[3][3];
CP2 *POld;
int num;
};
void CTransform::Identity()//单位矩阵
{
T[0][0]=1.0;T[0][1]=0.0;T[0][2]=0.0;
T[1][0]=0.0;T[1][1]=1.0;T[1][2]=0.0;
T[2][0]=0.0;T[2][1]=0.0;T[2][2]=1.0;
}
void CTransform::Translate(double tx,double ty)//平移变换矩阵
{
Identity();
T[2][0]=tx;
T[2][1]=ty;
MultiMatrix();
}
void CTransform::Scale(double sx,double sy)//比例变换矩阵
{
Identity();
T[0][0]=sx;
T[1][1]=sy;
MultiMatrix();
}
void CTransform::Rotate(double beta)//旋转变换矩阵
{
Identity();
double rad=beta*PI/180;
T[0][0]=cos(rad); T[0][1]=sin(rad);
T[1][0]=-sin(rad);T[1][1]=cos(rad);
MultiMatrix();
}
void CTransform::Rotate(double beta,CP2 p)//相对于任意点的旋转变换矩阵
{
Translate(-p.x,-p.y);
Rotate(beta);
Translate(p.x,p.y);
}
void CTransform::ReflectO()//原点反射变换矩阵
{
Identity();
T[0][0]=-1;
T[1][1]=-1;
MultiMatrix();
}
void CTransform::ReflectX()//X轴反射变换矩阵
{
Identity();
T[0][0]=1;
T[1][1]=-1;
MultiMatrix();
}
void CTransform::ReflectY()//Y轴反射变换矩阵
{
Identity();
T[0][0]=-1;
T[1][1]=1;
MultiMatrix();
}
void CTransform::Shear(double b,double c)//错切变换矩阵
{
Identity();
T[0][1]=b;
T[1][0]=c;
MultiMatrix();
}
void CTransform::MultiMatrix()//矩阵相乘
{
CP2 *PNew=new CP2[num];
for(int i=0;i<num;i++)
{
PNew[i]=POld[i];
}
for(int j=0;j<num;j++)
{
POld[j].x=PNew[j].x*T[0][0]+PNew[j].y*T[1][0]
+PNew[j].w*T[2][0];
POld[j].y=PNew[j].x*T[0][1]+PNew[j].y*T[1][1]
+PNew[j].w*T[2][1];
POld[j].w=PNew[j].x*T[0][2]+PNew[j].y*T[1][2]
+PNew[j].w*T[2][2];
}
delete []PNew;
}
2Dグラフィックスのトリミング
世界座標系
現実世界のシーンを記述する固定座標系はワールド座標系と呼ばれます。
ワールド座標系は実数領域座標系です。
アプリケーションのニーズに応じて、直交座標系、円筒座標系を選択できます。 、球座標系、極座標系など。
2. ユーザー座標系
オブジェクトの数学的モデルを記述する座標系はユーザー座標系と呼ばれ、ローカル座標系とも呼ばれます。
ユーザー座標系も実数領域座標系です。
ユーザー座標系は移動可能な座標系であり、オブジェクトの任意の位置に原点を置くことができ、任意の角度に回転することもできます。
立方体の場合、ユーザー座標系の原点を立方体の中心に配置でき、円柱の場合、ユーザー座標系の y 軸を回転軸として使用できます。
観測座標系
観察座標系は、ワールド座標系で定義される座標系であり、視点を原点とし、画面に垂直な方向をZ軸とし、視線方向を正とする。
2次元観測座標系は主にグラフィックスの出力範囲を指定するために使用されます。
3 次元観測座標系は左手系であり、オブジェクトの回転アニメーションを生成するために使用されます。
スクリーン座標系
スクリーン座標系は、実数領域の2次元直交座標系である。原点は画面の中央にあり、x 軸は水平右方向が正、y 軸は垂直上方向が正です。
デバイス座標系
モニターなどのグラフィックス出力デバイス自体は、デバイス座標系と呼ばれる 2 次元の直交座標系を持っています。デバイス座標系は整数領域の 2 次元座標系であり、原点は画面の左上隅にあり、x 軸は水平右方向、y軸は垂直下方向です。単位はピクセルです。[0.0, 0.0] ~ [1.0, 1.0] の範囲にグリッド化されたデバイス座標系を、正規化デバイス座標系と呼びます。
- 正規化されたデバイス座標系は、特定の出力デバイスから独立しています。グラフィックスが正規化されたデバイス座標系に変換されると、単純な乗算操作を使用して特定のデバイス座標系にマッピングできます。
- 標準化されたデバイス座標系は、さまざまなユーザグラフィックスの表示範囲を統一できるため、ユーザグラフィックスを標準化されたデバイス座標系で均一サイズの標準グラフィックスに変換する処理をグラフィックスの論理出力と呼びます。
- 正規化されたデバイス座標系の標準グラフィックスを表示デバイスに送信して出力するプロセスは、グラフィックスの物理出力と呼ばれます。
標準化されたデバイス座標系を使用すると、グラフィックスの出力を抽象的な表示デバイス上で議論できるため、この種のグラフィックスはデバイス非依存グラフィックスとも呼ばれます。
ウィンドウとビューポート
表示内容を決定する観測座標系上で定義される長方形の領域をウィンドウと呼びます。
画面座標で定義された出力グラフィックスの長方形の領域は、ビューポートと呼ばれます。
グラフィックスの出力には、ウィンドウからビューポートへの変換が必要です。
ビューポートにはウィンドウ内のグラフィックスのみを出力でき、
出力の形状はビューポートのサイズに応じて調整する必要があります。これはウィンドウビューポート変換(WVT) と呼ばれます。
ウィンドウの概念はグラフィック システムで広く使用されており、移動、サイズ変更、アクティブ化、または非アクティブ化できる画面上の四角形の領域を指します。
コンピュータ グラフィックス用語では、ウィンドウは、出力グラフィックスの範囲を決定する表示座標系の長方形領域としての元の定義に戻ります。
ウィンドウ変換行列
上記の変換行列の知識が使用されます。
トリミングアルゴリズム
Cohen-Sutherland 直線クリッピング アルゴリズム (Cohen-Sutherland)
最も初期に普及したエンコード アルゴリズム。
2 次元観察では、2 次元グラフィックスを観察座標系のウィンドウ サイズに応じて切り出す必要があり、ウィンドウ内にあるグラフィックスのみがビューポートに変換されて出力されます。
直線セグメントのクリッピングは、2 次元グラフィックスのクリッピングの基礎です。クリッピングの本質は、直線セグメントがウィンドウと交差するかどうかを判断することです。交差する場合、ウィンドウ内に位置する直線セグメントの部分がさらに決定されます。 。
各直線セグメントの端点には、領域コード (RC) と呼ばれる 4 桁のバイナリ コードのセットが割り当てられます。これは、ウィンドウ境界およびその延長線に対する直線セグメントの端点の位置を識別するために使用されます。
ウィンドウ内およびウィンドウ境界上の直線セグメントの端点のコーディングがゼロであることを保証するために、定義規則は次のとおりです。
これは、特にたくさんのことを学んだ後では、思い出すのが難しいです。
切断ステップ
(3) 直線部分が「単純抽出」「単純棄却」の条件を満たさない場合は、ウィンドウとの「交差」判定を行う必要があります。このとき、直線線分はウィンドウ境界と交差するか、ウィンドウ境界の延長線と交差する必要があり、2つの場合に対応できます。
直線がウィンドウ境界と交差する場合
直線が窓境界の延長線と交差する場合
交点計算式
コード
#define LEFT 1 //代表:0001
#define RIGHT 2 //代表:0010
#define BOTTOM 4 //代表:0100
#define TOP 8 //代表:1000
void CTestView::EnCode(CP2 &pt)//编码函数
{
pt.rc=0;
if(pt.x<Wxl)
pt.rc=pt.rc | LEFT;
else if(pt.x>Wxr)
pt.rc=pt.rc | RIGHT;
if(pt.y<Wyb)
pt.rc=pt.rc | BOTTOM;
else if(pt.y>Wyt)
pt.rc=pt.rc | TOP;
}
#define LEFT 0x1 //代表:0001
#define RIGHT 0x2 //代表:0010
#define BOTTOM 0x4 //代表:0100
#define TOP 0x8 //代表:1000
int EnCode(double x , double y , int Wxl , int Wxr , int wyb , int wyt)
{
rc = 0;
if(x < Wxl) rc = rc | LEFT;
if(x > Wxr) rc = rc | RIGHT;
if(y < Wyb) rc = rc | BOTTOM;
if(y > Wyt) rc = rc | TOP;
return rc ;
}
void CTestView::Cohen()//Cohen-Sutherland算法
{
CP2 p;//交点坐标
EnCode(P[0]);//起点编码
EnCode(P[1]);//终点编码
while(P[0].rc!=0 || P[1].rc!=0)//处理至少一个顶点在窗口外
{
if((P[0].rc & P[1].rc)!=0)//简弃之
{
PtCount=0;
return;
}
if(0==P[0].rc)//确保P[0]位于窗口之外
{
CP2 Temp;
Temp=P[0];
P[0]=P[1];
P[1]=Temp;
}
UINT RC=P[0].rc;
double k=(P[1].y-P[0].y)/(P[1].x-P[0].x);//斜率
if(RC & LEFT)//P[0]点位于窗口的左侧
{
p.x=Wxl;//计算交点y坐标
p.y=k*(p.x-P[0].x)+P[0].y;
}
else if(RC & RIGHT)//P[0]点位于窗口的右侧
{
p.x=Wxr;//计算交点y坐标
p.y=k*(p.x-P[0].x)+P[0].y;
}
else if(RC & BOTTOM)//P[0]点位于窗口的下侧
{
p.y=Wyb;//计算交点x坐标
p.x=(p.y-P[0].y)/k+P[0].x;
}
else if(RC & TOP)//P[0]点位于窗口的上侧
{
p.y=Wyt;//计算交点x坐标
p.x=(p.y-P[0].y)/k+P[0].x;
}
EnCode(p);
P[0]=p;
}
}