한때 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을 실제 업무에 적용하면서 정리하는 예가 많아지길 스스로에게 기대한다.
WPF 공부한지 얼마 되지않은 사람입니다.
님의 좋은 예제와 설명이 WPF를 이해하는데 큰 도움이 됩니다.
고맙습니다. ^^
도움이 되셨다니 참 좋습니다~^^
처음 접해서 따라해봤습니다.
아직 머가 먼지는 잘 모르지만 따라하는 걸로도
충분히 WPF 진가를 알수 있네요.
감사합니다.
네, WPF가 매우 강력하고 뛰어나죠? ^^
전체 소스파일 업로드해주시면 안될까요? 초보다보니 힘드네요 ㅠ.ㅠ;;
현재 이 글에 대한 소스코드를 가지고 있지 않습니다. 이 글은 총 3개로 구성되어 있습니다. 단계별로 따라 하시면 될건데, 하시다가 막히는 부분은 질문하시구요~
덕분에 프로젝트에서 안됬던 3D회전을 할수있게 됫네요.. 감사드립니다~
이 글이 도움이 되셨다니 저로써 매우 기쁘네요. 좋은 댓글 감사드립니다.
텍스쳐에 대한 내용은 단박에 읽고… 땡큐베리머취!
감사합니다!!
혹시 이 예제는 어느 사이트나 책에서 참고하셨나요?
안녕하세요, 김형준입니다.
하도 옛날 자료라 기억이 가물하지만..
MS의 WPF 3D의 글을 보고.. 나름대로 작성한 글로 기억됩니다.
해당 url은 기억나질 안구요.