포스트

[ Opengl ] Projection Transform

Projection transform


면밀한 변환 과정 관찰을 위해 임포트 모델을 2D 모델에서 3D 모델로 변경.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#$Vertex          2904
#$Face            5804
Vertex         1   0.151632  -0.043319   -0.08824
Vertex         2   0.163424  -0.033934   -0.08411
Vertex         3   0.163118  -0.053632  -0.080509
Vertex         4   0.176307  -0.028912  -0.075048
Vertex         5   0.174429  -0.051613  -0.073945
Vertex         6   0.186153  -0.032952  -0.063704
Vertex         7   0.189315  -0.049201  -0.055717
Vertex         8   0.173804  -0.063906  -0.070661
...

Face           1          1          2          3
Face           2          2          4          5
Face           3          5          4          6
Face           4          7          8          5
Face           5          8          9          3
Face           6          9         10         11
...


임포트 코드 수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
void Renderer::readFile(char* pFileName)
{
	char buff[512], buff2[512];
	int i, j;
	FILE* pFile = fopen(pFileName, "rt");
	getNewLine(buff, 512, pFile);
	sscanf(buff, "%s %d", buff2, &m_nNumVertex);
	ASSERT(stricmp(buff2, "$Vertex") == 0);
	getNewLine(buff, 512, pFile);
	sscanf(buff, "%s %d", buff2, &m_nNumFace);
	ASSERT(stricmp(buff2, "$Faces") == 0);

	for (i = 0; i < m_nNumVertex; i++)
	{
		int nNum;
		getNewLine(buff, 512, pFile);
		sscanf(buff, "%s %d %f %f %f", buff2, &nNum, &m_vertex[i][0], &m_vertex[i][1], &m_vertex[i][2]);
		sscanf(buff, "%s ", buff2);
		for (int j = 0; j < 3; j++)	//cow
		m_vertex[i][j] = (m_vertex[i][j]) ;	//cow
		ASSERT(stricmp(buff2, "Vertex") == 0);
		ASSERT((nNum - 1) == i);
	}


	for (i = 0; i < m_nNumFace; i++)
	{
		int nNum;
		int nCurrPos;
		getNewLine(buff, 512, pFile);
		sscanf(buff, "%s %d", buff2, &nNum);		//cow
		m_face[i].m_nNumVertex = 3;					//cow
		m_face[i].m_color[0] = (GLubyte)0;			//cow
		m_face[i].m_color[1] = (GLubyte)0;			//cow
		m_face[i].m_color[2] = (GLubyte)255;		//cow

		ASSERT(stricmp(buff2, "Face") == 0);
		ASSERT((nNum - 1) == i);
		nCurrPos = 0;
		for (j = 0; j < 1; j++)						//cow
		{
			nCurrPos += strcspn(buff + nCurrPos, " \t");
			nCurrPos += strspn(buff + nCurrPos, " \t");
		}
		for (j = 0; j < m_face[i].m_nNumVertex; j++)
		{
			nCurrPos += strcspn(buff + nCurrPos, " \t");
			nCurrPos += strspn(buff + nCurrPos, " \t");
			sscanf(buff + nCurrPos, "%d", &m_face[i].m_vertex[j]);
		}
	}
}


Z-buffering을 통해 출력할 프래그먼트 선택

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
void Renderer::buildEdgetable(int nFace)
{
	float vertices[2][3];
	int ymin;
	float yMax, x, inverseOfSlope, z, zPerY;
	
	for (int i = 0; i < m_face[nFace].m_nNumVertex; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			vertices[0][j] = m_tramsformedVertex[m_face[nFace].m_vertex[i] - 1][j];
			vertices[1][j] = m_tramsformedVertex[m_face[nFace].m_vertex[(i + 1) % m_face[nFace].m_nNumVertex] - 1][j];
		}
		
		if (vertices[0][1] != vertices[1][1])
		{
			inverseOfSlope = (vertices[1][0] - vertices[0][0]) / (vertices[1][1] - vertices[0][1]); //xperY
		}

		zPerY = (vertices[1][2] - vertices[0][2]) / (vertices[1][1] - vertices[0][1]);

		float savedY;
		float savedV;

		if (vertices[0][1] < vertices[1][1]) // 작을때 ceiling 클 때 floor 
		{
			savedY = vertices[0][1];
			ymin = ceil(vertices[0][1]);
			ymin = max(ymin, 0);

			if (ymin > checkImageHeight - 1) continue;
			m_ET[ymin][m_indexCount[ymin]].x = vertices[0][0];
			if (ymin - savedY != 0)
			{
				m_ET[ymin][m_indexCount[ymin]].x += (ymin - savedY) * inverseOfSlope;
			}

			m_ET[ymin][m_indexCount[ymin]].zperY = zPerY;
			m_ET[ymin][m_indexCount[ymin]].yMax = vertices[1][1];
			m_ET[ymin][m_indexCount[ymin]].inverseOfSlope = inverseOfSlope;
			m_ET[ymin][m_indexCount[ymin]].z = vertices[1][2];
			m_indexCount[ymin]++;

		}
		else
		{
			savedY = vertices[1][1];
			ymin = ceil(vertices[1][1]);
			ymin = max(ymin, 0);

			if (ymin > checkImageHeight - 1) continue;
			m_ET[ymin][m_indexCount[ymin]].x = vertices[1][0];

			if (ymin - savedY != 0)
			{
				m_ET[ymin][m_indexCount[ymin]].x += (ymin - savedY) * inverseOfSlope;
			}
			m_ET[ymin][m_indexCount[ymin]].yMax = vertices[0][1];  
			m_ET[ymin][m_indexCount[ymin]].inverseOfSlope = inverseOfSlope;
			m_ET[ymin][m_indexCount[ymin]].z = vertices[0][2];
			m_indexCount[ymin]++;
		}
	}

}

void Renderer::fill(GLubyte color[3])
{
	// AET
	for (int i = 0; i < checkImageHeight; i++)
	{
		//update intersection
		for (int j = 0; j < m_numEdgeInAET; j++)
		{
			m_AET[j].x += m_AET[j].inverseOfSlope;
			m_AET[j].z += m_AET[j].zperY;
		}

		//Add new edge
		for (int j = 0; j < m_indexCount[i]; j++)
		{
			m_AET[m_numEdgeInAET + j] = m_ET[i][j];
		}
		m_numEdgeInAET += m_indexCount[i];

		//Delete edge
		for (int j = 0; j < m_numEdgeInAET; j++)
		{
			if (m_AET[j].yMax < i)
			{
				for (int k = j; k < m_numEdgeInAET; k++)
				{
					m_AET[k] = m_AET[k + 1];
				}
				j--;
				m_numEdgeInAET--;
			}
		}

		//Sort intersections
		Edge temp;
		for (int j = 0; j < m_numEdgeInAET - 1; j++)
		{
			for (int k = j + 1; k < m_numEdgeInAET; k++)
			{
				if (m_AET[j].x > m_AET[k].x)
				{
					temp = m_AET[j];
					m_AET[j] = m_AET[k];
					m_AET[k] = temp;
				}
			}
		}

		//Render
		for (int j = 0; j < m_numEdgeInAET; j += 2)
		{
			int k;
			int xmin = floor(m_AET[j].x);
			int xmax = floor(m_AET[j + 1].x);
			xmin = max(xmin, 0);
			xmax = min(xmax, checkImageWidth - 1);
			float zPerX = (m_AET[j + 1].z - m_AET[j].z) / (xmax - xmin);
			float deltaZ = 0;
		
			for (k = xmin; k < xmax; k++)
			{
				if (m_AET[j].z + deltaZ < zBuffer[i][k])
				{
					checkImage[i][k][0] = (GLubyte)color[0];
					checkImage[i][k][1] = (GLubyte)color[1];
					checkImage[i][k][2] = (GLubyte)color[2];
					zBuffer[i][k] = m_AET[j].z + deltaZ;
					deltaZ += zPerX;
				}
			}
		}
	}
}


NDC 구현

NDC


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 Matrix4 projectMatrix(float fovy, float aspect, float n, float f)
 {
     Matrix4 ret;
     for (int i = 0; i < 4; i++)
     {
         for (int j = 0; j < 4; j++)
         {
             ret.arr[i][j] = 0;
         }
     }
     ret.arr[0][0] = (1 / tan(fovy / 2 * PI / 180)) / aspect;
     ret.arr[1][1] = 1 / tan(fovy / 2 * PI / 180);
     ret.arr[2][2] = - ((f + n) / (f - n));
     ret.arr[2][3] = - ((2 * (n * f)) / (f - n));
     ret.arr[3][2] = -1;
     return ret;
 }


1
2
3
4
g_renderer.m_proj = projectMatrix(90.0f, 1.3333f, 1.0f, 150.0f);
...

Matrix4 mvp = m_proj * m_view * m_world;


출력


Cow 모델 임포트 & projection 적용
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.