한때 3D의 꽃을 뽑으라면 Texture Mapping이였다. 물론 한때다. 지금 다시 뽑으라면 Shader가 되겠지만… 여하튼 오늘 잠시 WPF 3D 쪽으로 눈을 돌리면서 WPF 3D 입문에서 다소 부족했던 부분인, Texutre Mapping 부분을 정리하기로 하겠다.
먼저 Texture Mapping을 하기 위해서는 Texture Mapping 좌표가 필요하다. 이전에 했던 부분은 Texture Mapping 좌표를 제외한 Mesh의 정점과 정점 인덱스 그리고 법선 벡터 만을 지정하였다. 이제 Texture Mapping 좌표를 지정해보는 것을 살펴보자.
우리가 지금까지 구축해 왔던 것에서 시작해보자. Mesh의 정점과 인덱스 그리고 법선 벡터를 지정해 주는 부분을 포함하는 함수가 Windows1.xaml.cs 파일 안의 Window1 클래스에 대한 CreateTriangleModel 함수였다. 이제 이것이 아래와 같이 바뀐다. 파랑색의 코드 부분이 변경이나 추가된 부분이다.
private GeometryModel3D CreateTriangleModel(Point3D p0, Point3D p1, Point3D p2, Point p0t, Point p1t, Point p2t) { MeshGeometry3D mesh = new MeshGeometry3D(); mesh.Positions.Add(p0); mesh.Positions.Add(p1); mesh.Positions.Add(p2); mesh.TriangleIndices.Add(0); mesh.TriangleIndices.Add(1); mesh.TriangleIndices.Add(2); mesh.TextureCoordinates.Add(p0t); mesh.TextureCoordinates.Add(p1t); mesh.TextureCoordinates.Add(p2t); Vector3D normal = CalculateNormal(p0, p1, p2); mesh.Normals.Add(normal); mesh.Normals.Add(normal); mesh.Normals.Add(normal); BitmapImage bi = new BitmapImage(); bi.BeginInit(); bi.UriSource = new Uri(@"y:/face.PNG", UriKind.RelativeOrAbsolute); bi.EndInit(); Brush brush = new ImageBrush(bi); Material material = new DiffuseMaterial(brush); GeometryModel3D model = new GeometryModel3D(mesh, material); return model; }
Texture Mapping 좌표와 Texture Image로 사용될 그림을 재질(Material)로 지정하였다. CreateTriangleModel 함수가 변경되었으니 이 함수를 사용하는 부분도 변경되어야 하지 않겠는가? 그 부분에 대한 코드는 ClickCubeButton 함수 안이며 변경된 코드를 파란색으로 나타내면 다음과 같다.
private void ClickCubeButton(object Sender, RoutedEventArgs e) { if (model != null) return; Model3DGroup cube = new Model3DGroup(); Point3D p0 = new Point3D(-1, -1, -1); Point3D p1 = new Point3D(1, -1, -1); Point3D p2 = new Point3D(1, -1, 1); Point3D p3 = new Point3D(-1, -1, 1); Point3D p4 = new Point3D(-1, 1, -1); Point3D p5 = new Point3D(1, 1, -1); Point3D p6 = new Point3D(1, 1, 1); Point3D p7 = new Point3D(-1, 1, 1); Point t00 = new Point(0, 0); Point t01 = new Point(0, 1); Point t10 = new Point(1, 0); Point t11 = new Point(1, 1); //front side triangles cube.Children.Add(CreateTriangleModel(p3, p2, p6, t00, t10, t11)); cube.Children.Add(CreateTriangleModel(p3, p6, p7, t00, t11, t01)); //right side triangles cube.Children.Add(CreateTriangleModel(p2, p1, p5, t00, t10, t11)); cube.Children.Add(CreateTriangleModel(p2, p5, p6, t00, t11, t01)); //back side triangles cube.Children.Add(CreateTriangleModel(p1, p0, p4, t10, t00, t01)); cube.Children.Add(CreateTriangleModel(p1, p4, p5, t10, t01, t11)); //left side triangles cube.Children.Add(CreateTriangleModel(p0, p3, p7, t10, t00, t01)); cube.Children.Add(CreateTriangleModel(p0, p7, p4, t10, t01, t11)); //top side triangles cube.Children.Add(CreateTriangleModel(p7, p6, p5, t00, t10, t11)); cube.Children.Add(CreateTriangleModel(p7, p5, p4, t00, t11, t01)); //bottom side triangles cube.Children.Add(CreateTriangleModel(p2, p3, p0, t10, t00, t01)); cube.Children.Add(CreateTriangleModel(p2, p0, p1, t10, t01, t11)); model = new ModelVisual3D(); model.Content = cube; mainViewport.Children.Add(model); }
Texture Mapping 좌표에 대한 설명은 이곳 OpenGL의 Texture Mapping에 대한 강좌에 동일하니 그곳을 참고하길 바란다. 그 실행 결과는 다음과 같다.
참고로 텍스쳐 이미지의 크기는 과거의 2의 자승이여야 한다는 제약이 더 이상 적용하지 않으며 동영상(AVI, 동영상 GIF 등)도 쉽게 지원하며 이미지의 경로를 http 프로토콜을 통해서도 쉽게 받아들일 수 있다.
이상으로 WPF를 이용한 간단한 3D 그래픽에 대한 글의 정리를 끝내겠다. 추후에 지속적으로 WPF에 대한 Article을 실제 업무에 적용하면서 정리하는 예가 많아지길 스스로에게 기대한다.