OpenGL ES2.0 기초 강좌

Similar documents
PowerPoint 프레젠테이션

Łø·ŸÕ=¤ ¬ ÇX±xÒ¸ 06 - Èpº– 1

Microsoft PowerPoint - lecture15-ch6.ppt

Microsoft PowerPoint - 13prac.pptx

Microsoft PowerPoint - lecture16-ch6

Microsoft PowerPoint - lecture3-ch2.ppt [호환 모드]

04_오픈지엘API.key

Microsoft PowerPoint - lecture11-ch4

<322EBCF8C8AF28BFACBDC0B9AEC1A6292E687770>

Microsoft PowerPoint - lecture11-ch4.ppt

Microsoft Word - cg07-midterm.doc

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx

Open GL

Poison null byte Excuse the ads! We need some help to keep our site up. List 1 Conditions 2 Exploit plan 2.1 chunksize(p)!= prev_size (next_chunk(p) 3

PowerPoint 프레젠테이션

임베디드시스템설계강의자료 6 system call 2/2 (2014 년도 1 학기 ) 김영진 아주대학교전자공학과

서피스셰이더프로그램 셰이더개발을쉽게! Thursday, April 12, 12

(Microsoft PowerPoint - \301\24608\260\255 - \261\244\277\370\260\372 \300\347\301\372)

금오공대 컴퓨터공학전공 강의자료

Microsoft PowerPoint - chap01-C언어개요.pptx

WebGL 레슨 5 - 텍스쳐에 대하여

PowerPoint 프레젠테이션

Microsoft PowerPoint - ch07 - 포인터 pm0415

Microsoft PowerPoint - lecture17-ch8.ppt [호환 모드]

iii. Design Tab 을 Click 하여 WindowBuilder 가자동으로생성한 GUI 프로그래밍환경을확인한다.

Ⅱ. Embedded GPU 모바일 프로세서의 발전방향은 저전력 고성능 컴퓨팅이다. 이 러한 목표를 달성하기 위해서 모바일 프로세서 기술은 멀티코 어 형태로 발전해 가고 있다. 예를 들어 NVIDIA의 최신 응용프 로세서인 Tegra3의 경우 쿼드코어 ARM Corte

Microsoft PowerPoint - additional01.ppt [호환 모드]

PowerPoint 프레젠테이션

PowerPoint Presentation

KNK_C_05_Pointers_Arrays_structures_summary_v02

A Hierarchical Approach to Interactive Motion Editing for Human-like Figures

C# Programming Guide - Types

(Microsoft PowerPoint - \301\24613\260\255 - oFusion \276\300 \261\270\274\272)

PowerPoint Template

Microsoft PowerPoint - 04-Model Class.pptx

Microsoft PowerPoint - 3ÀÏ°_º¯¼ö¿Í »ó¼ö.ppt

Microsoft PowerPoint - Java7.pptx

JAVA 프로그래밍실습 실습 1) 실습목표 - 메소드개념이해하기 - 매개변수이해하기 - 새메소드만들기 - Math 클래스의기존메소드이용하기 ( ) 문제 - 직사각형모양의땅이있다. 이땅의둘레, 면적과대각

쉽게 풀어쓴 C 프로그래밍

슬라이드 1

歯Lecture2.PDF

SBR-100S User Manual

C++ Programming

OCW_C언어 기초

chap 5: Trees

지난시간에... 우리는 kernel compile을위하여 cross compile 환경을구축했음. UBUNTU 12.04에서 arm-2009q3를사용하여 간단한 c source를빌드함. 한번은 intel CPU를위한 gcc로, 한번은 ARM CPU를위한 gcc로. AR

서강대학교공과대학컴퓨터공학과 CSE4170 기초컴퓨터그래픽스기말고사 (2/8) 다음과같이설정되어있는데, cam.pos[0] = 0.0, cam.pos[1] = 0.0, cam.pos[2] = 500.0; 이때의 cam.naxis[] 벡터의세원소값을기술하라. Figure

[ 마이크로프로세서 1] 2 주차 3 차시. 포인터와구조체 2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Functi

<443A5C4C C4B48555C B3E25C32C7D0B1E25CBCB3B0E8C7C1B7CEC1A7C6AE425CBED0C3E0C7C1B7CEB1D7B7A55C D616E2E637070>


untitled

서강대학교 공과대학 컴퓨터공학과 CSE4170 기초 컴퓨터 그래픽스 중간고사 (1/7) [CSE4170: 기초 컴퓨터 그래픽스] 중간고사 (담당교수: 임 인 성) 답은 연습지가 아니라 답안지에 기술할 것. 답 안지 공간이 부족할 경우, 답안지 뒷면에 기술 하고, 해당

Microsoft PowerPoint - chap11-포인터의활용.pptx

BMP 파일 처리

슬라이드 1

adfasdfasfdasfasfadf

Microsoft PowerPoint - chap06-2pointer.ppt

2005CG01.PDF

슬라이드 1

PowerPoint Presentation

1. 안드로이드개발환경설정 안드로이드개발을위해선툴체인을비롯한다양한소프트웨어패키지가필요합니다 툴체인 (Cross-Compiler) 설치 안드로이드 2.2 프로요부터는소스에기본툴체인이 prebuilt 라는이름으로포함되어있지만, 리눅스 나부트로더 (U-boot)

11장 포인터

Microsoft Word - 3부A windows 환경 IVF + visual studio.doc

목차 포인터의개요 배열과포인터 포인터의구조 실무응용예제 C 2

PowerPoint 프레젠테이션

Microsoft Word - Crackme 15 from Simples 문제 풀이_by JohnGang.docx

PowerPoint 프레젠테이션

Microsoft PowerPoint - lecture16-ch8.ppt [호환 모드]

슬라이드 1

1부

PowerPoint 프레젠테이션

Speaker MVP (Visual C++) 팁스웨어대표 tipssoft.com 개발커뮤니티운영자 한이음 IT 멘토 tipsware blog.naver.com/tipsware

쉽게 풀어쓴 C 프로그래밍

Structure and Interpretation of Computer Programs: Assignment 3 Seung-Hoon Na October 4, George (아래 3개의 문제에 대한 구현이 모두 포함된 george.rkt파일을 제출하시오.

Microsoft PowerPoint - java1-lab5-ImageProcessorTestOOP.pptx

Microsoft PowerPoint - IP11.pptx

윤성우의 열혈 TCP/IP 소켓 프로그래밍

UI TASK & KEY EVENT

JUNIT 실습및발표

쉽게 풀어쓴 C 프로그래밍

Microsoft PowerPoint - chap10-함수의활용.pptx

<4D F736F F F696E74202D20B8B6C0CCC5A9B7CEC7C1B7CEBCBCBCAD202839C1D6C2F7207E203135C1D6C2F >

Dialog Box 실행파일을 Web에 포함시키는 방법

Microsoft PowerPoint - 07-Data Manipulation.pptx

gnu-lee-oop-kor-lec06-3-chap7

PowerPoint Presentation

example code are examined in this stage The low pressure pressurizer reactor trip module of the Plant Protection System was programmed as subject for

1

서강대학교 공과대학 컴퓨터공학과 CSE4170 기초 컴퓨터 그래픽스 중간고사 (1/8) [CSE4170: 기초 컴퓨터 그래픽스] 중간고사 (담당교수: 임 인 성) 답은 연습지가 아니라 답안지에 기술할 것. 있는 변환 행렬은 일반적으로 어떤 좌표계 에서 어떤 좌표계로의

Microsoft PowerPoint - ch09 - 연결형리스트, Stack, Queue와 응용 pm0100

MVVM 패턴의 이해

A Dynamic Grid Services Deployment Mechanism for On-Demand Resource Provisioning

ISP and CodeVisionAVR C Compiler.hwp

쉽게 풀어쓴 C 프로그래밍

API STORE 키발급및 API 사용가이드 Document Information 문서명 : API STORE 언어별 Client 사용가이드작성자 : 작성일 : 업무영역 : 버전 : 1 st Draft. 서브시스템 : 문서번호 : 단계 : Docum

Orcad Capture 9.x

API 매뉴얼

슬라이드 1

쉽게 풀어쓴 C 프로그래밍

PowerPoint Presentation

Transcription:

OpenGL ES2.0 기초강좌 나의별

소개글 OpenGL ES 2.0 의 Shader Programming 을이용하여, MFC 와 Android 용기초강좌 1. MFC EGL 구성 2. Android EGL 구성

목차 1 [GLES20] 01. Android 3D Programming을위한 MFC 프로젝트설정 4 2 [GLES20] 02. Android 3D Programming을위한 Android 프로젝트설정 12 3 [GLES20] 03. Color Shading 23 4 [GLES20] 04. Geometry Transform 32 5 [GLES20] 05. Vertex Position 처리 (1) 44 6 [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 53 7 [GLES20] 07. Parametric Surface 69 8 [GLES20] 08. 조명적용하기 - 확산광, Diffuse Vertex Light 77 9 [GLES20] 09. 조명적용하기 - 주변광, Ambient Vertex Light 86 10 [GLES20] 10. 조명적용하기 - 경면광, Specular Vertex Light 93 11 [GLES20] 11. 조명적용하기 - Pixel Light 102 12 [GLES20] 12. 조명적용하기 - Flat Shading과 Smooth Shading 110 13 [GLES20] 13. 조명적용하기 - Point Light와감쇠방정식 117 14 [GLES20] 14. 조명적용하기 - Spot Light 126 15 [GLES20] 15. 조명적용하기 - Toon Shading 136 16 [GLES20] 16. Q/A 몇가지와 FBO 사용시유의점정리 141 17 [GLES20] 17. 텍스쳐적용하기앞서서준비사항정리 147 18 [GLES20] 18. 텍스쳐적용하기 - 2D Texture 155

[GLES20] 01. Android 3D Programming 을위한 MFC 프로젝트설정 2011.11.02 20:20 OGLES20Template 네이버카페 3D Programming 의매니저이신바른생활님이 Android 용으로 GLSL 강좌를정리해보는것도좋을것같다는추천이있어 서한번도전해봅니다 ^^; 구성은바른생활님이 http://cafe.naver.com/gld3d/401 페이지에서만들고있는내용위주로진행할까생각중입니다. 중간중간 NeHe Tutorial 과 PowerVR SDK 의내용도추가해볼생각입니다. 이곳에정리하는내용은 3D의기본적인사항인 PROJECTION MATRIX, MODEL MATRIX, VIEW MATRIX, EULER/QUATERNION TRASFORMATION, FRAME BUFFER OBJECT, VERTEX BUFFER OBJECT, COLOR BUFFER ARRAY, DEPTH BUFFER ARRAY, STENCIL BUFFER ARRAY, VERTEX, TEXTURE, LIGHT, BLEND 등에대해서일일히서술하지는않도록하겠습니다. 위의내용은바른생활님 3D Programming 카페나기타다른 OpenGL ES 용도서를참고하셔서병행공부해나가면좋을것같습니다. 저는 OpenGL Super bible 과 iphone 3D Programming Developing Graphical Applications With OpenGL ES(Oreilly), OpenGLES 2.0 Programming Guide(Addison Wesley), OpenGL Shading Language 3/E(Addison Wesley) 등의 책을추천합니다. [GLES20] 01. Android 3D Programming 을위한 MFC 프로젝트설정 4

참고로제가구현한 Android Native Renderer 엔진은위의 iphone 3D Programming 책에서소개한 Native Renderer를기초로만들었으며, 차후혹여나상용화를하실경우라이센스상에문제가발생할수있습니다. ( 책에보면, 저자가소스에대해서 O'REILLY 출판사에권한이있음으로, 소스사용시문의는해달라는요청글등이있었던것같네요. ^^;) 들어가기에앞서서밝혀두지만, 저또한 3D 를마스터한상태는아닌 1 인이기때문에, 내용자체가저의주관적으로이해한것을바탕으로 정리될것입니다. 혹틀린부분이있다면바로바로지적해주십시요 ^^; 1. 폴더구성 기본적으로프로젝트를 Android Java 3D 가아닌, Native Renderer 로 3D 를구성할예정입니다. Java 로짤경우, Debugging 이쉬운장점이있지만, Native Renderer 보다속도가좀느리고, 아이폰이나윈모등다른플랫폼으로이식 하기에는어렸다는단점도있습니다. 이에 Native Renderer 를 C++ 로짜서 Android, ios, Windows Mobile 등에이식하게편하게만들어볼예정입니다. Native Renderer 의경우, Debugging 이어려운점이있지만, Renderer 부분은 MFC 로호환되게금해서 Visual Studio 로연계할예 정입니다. 프로젝트의기본구성은아래와같습니다. [GLES20] 01. Android 3D Programming 을위한 MFC 프로젝트설정 5

폴더설명 1. Builds : OpenGL, EGL Library 와 android ndk-r5b 에서사용할 stlport 등이있다. 향후추가적인 prebuilt library 등은이폴더에추가할예정이다. 2. Android : Android Project 폴더이다. 안드로이드로 App 개발시필요한 Activity 등이있으며, Native Renderer 용 JNI 도이곳에있다. 3. Windows : MFC Project 폴더이다. MFC 을구성하는 Frame, View, Document 파일등과 GLView, EGLView, Native Renderer Interface 등이있다. 4. Classes : Renderer 소스가있는폴더이다. 이폴더는 MFC 와 Android 에서공용으로사용한다. 5. Shaders : Shader Language 인 vertex shader 와 fragment shader 가있는폴더이다. 이폴더는 MFC 와 Android 에서공용으로사용한다. [GLES20] 01. Android 3D Programming 을위한 MFC 프로젝트설정 6

6. Utils : Renderer 를구성하는데필요한수학함수, 리소스매니저등과같은도우미모듈등이있는폴더이다. 이폴더는 MFC 와 Android 에서공용으로사용한다. 2. MFC 프로젝트구성 프로젝트패키지설명 1. Classes : 렌더링클래스가모여있다. 템플릿예제에서는 Grid 형의바닥과 X-Y-Z 축을그리고있다. 실제사용되는파일은 RenderingEngine.ES1.cpp와 RenderingEngine.ES2.cpp이며, EGLRenderer와 OGLRenderer는더이상사용되지는않는다. ( 렌더러모듈은 iphone 3D Programming 책내용과유사한구성을따르고있으며, 책의 1장내용과비교해서보시면됩니다.) 이폴더는 MFC와 Android에서공용으로사용한다. [GLES20] 01. Android 3D Programming 을위한 MFC 프로젝트설정 7

2. OpenGL.ES : MFC 에서사용할 EGLView 로서 libegl.lib 구현화한부분이다. EGView.cpp 는 MFC 의 CView 를상속받은 User View 이다. EGLSurface.cpp 는 EGL 을생성한 Surface 모듈이다. 3. Shaders : GLSL 용 Shader 파일들이있다. 이폴더는 MFC 와 Android 에서공용으로사용한다. 4. Utils : Renderer 를구성하는데필요한수학함수, 리소스매니저등과같은도우미모듈등이있다. 이폴더는 MFC 와 Android 에서공용으로사용한다. 5. 소스파일 : MFC 의 Frame, View, Document 의소스부분이다. 이중 NativeInterface.cpp 는 ResourceManager 에서 bmp 와 tga 파일을읽어올때거쳐가는 Bridge 역활을한다. 6. 헤더파일.gles : OpenGL ES 1.0 용 header 파일이다. 7. 헤더파일.gles2 : OpenGL ES 2.0 용 header 파일이다. 3. 실행결과 MFC 에서실행한결과는다음과같습니다. [GLES20] 01. Android 3D Programming 을위한 MFC 프로젝트설정 8

4. 예외사항정리 GLES 2.0 은 GLES1.0 의하위호환성을배제하고만들어졌습니다. 이때문에, GLES2.0 세서는기존 GLES1.0 에서지원하는 glmatrixmode, glpushmatrix, glpopmatrix, glrotatef, gltranslatef, glscalcef 등등등많은 API 가지원하지않습니다. 즉, 라이브러리에담겨있는 header 파일내의내용도서로다릅니다. 하나의 OGLES20Template 프로젝트내에 GLES1.0 과 GLES2.0 을담다보니서로충돌이나는문제가발생합니다. 이는 ios 개발시에도동일하며보통 Macro 처리를통해서컴파일시점에 GLES1.0 으로할지 GLES2.0 으로할지결정합니다. MFC 프로젝트에서는다음과같이수정해줘야두버전을사용할수있습니다. 4.1 GLES 2.0 으로빌드하기 프로젝트속성창의 " 링크 -> 입력 -> 추가종속성 " 탭에서 libglesv2.lib 를추가해줘야합니다. [GLES20] 01. Android 3D Programming 을위한 MFC 프로젝트설정 9

또한 EGLView.cpp 의 CEGLView::OnCreate() 에서렌더러생성을 RenderingEngine.ES2.cpp 로맞춰줘야합니다. 이부분은 iphone 3D Programming 책에서 GLView.mm 에서처리하는부분과유사하게처리하였습니다. 4.2 GLES 1.0 으로빌드하기 GLES1.0 으로빌드하기위해서는프로젝트속성창의 " 링크 -> 입력 -> 추가종속성 " 탭에서 libglesv2.lib 를삭제해준뒤에 또한 EGLView.cpp 의 CEGLView::OnCreate() 에서렌더러생성을 RenderingEngine.ES1.cpp 로맞춰줘야합니다. 이부분은 iphone 3D Programming 책에서 GLView.mm 에서처리하는부분과유사하게처리하였습니다. 다음에는 Android Project 설정에대해서다뤄보도록하겠습니다. 프로젝트는 16MB 정도로커져버려서, myastro google code 에올렸습니다. OGLES20 Template Application ported to mfc and android http://code.google.com/p/myastro/downloads/list [GLES20] 01. Android 3D Programming 을위한 MFC 프로젝트설정 10

http://code.google.com/p/myastro/downloads/detail?name=ogles20application.v10.zip&can=2&q=#makechanges [GLES20] 01. Android 3D Programming 을위한 MFC 프로젝트설정 11

[GLES20] 02. Android 3D Programming 을위한 Android 프로젝트설정 2011.11.04 13:59 OGLES20Template 1. Android 프로젝트구성 [GLES20] 02. Android 3D Programming 을위한 Android 프로젝트설정 12

프로젝트패키지설명 1. com.myastro.gles.demo : Android App 의 Main Activity 이다. 2. com.myastro.gles : List Activity 이다. GLES20.Lesson 과 GLES10.Lesson 두가지 List Activity 가있다. 대체로 GLES20 에대해서다룰예정이지만, 시간여유가생기면, GLES10 용도업데이트할예정이다. 3. com.myastro.gles.view : EGL View용모듈이다. EGLView.java : GLSurfaceView를상속받은추상클래스 (abstract class) 이다. 또한 EGLView에는사용자로부터의이벤트도수신함으로 Touch 이벤트을통한 Gesture 기능구현은이곳에이루어져있다. EGLView20.java : EGLView를상속받아서 abstract method 들을구현한다. GLES20 용 EGL Configuration으로구성되어있다. [GLES20] 02. Android 3D Programming 을위한 Android 프로젝트설정 13

EGLView10.java : EGLView 를상속받아서 abstract method 들을구현한다. GLES10 용 EGL Configuration 으로구성되어있다. 4. com.myastro.main.jni : C++ 로구현한 NativeRenderer 와통신하는 Java native interface 이다. NativeRenderer.java : GLES10용 NativeRenderer.cpp와 GLES20용 NativeRender20.cpp를호출하기위한 native method로구성되어있다. CBJavaHandler.java : Java 단의 Main Activity Thread가아닌 Native 단의 NativeRenderer 로부터 Androd SDK내의 API를이용할때사용하기위한모듈이다. 이모듈에는 "utf-8 to ecu-kr" converting 함수, "bmp, png, jpg, tga" 등과같은이미지파일로딩을위한함수가있다.. 5. com.myastro.utils : TGA 이미지파일로딩클래스인 TGA.java(cocos2d java 버전에서가져옴 ) 와이미지로딩후 Integer 형을 Byte 형으로변환하기위한 ArrayCopy.java 클래스가있다. TGA.java : From cocos2d(zhouweikuan-cocos2d-2eb179b) 6. jni : Native Renderer를구성하는 Java Native Interface layer 이다. com_myastro_main_jni_nativerenderer.h : JNI의헤더파일이다.(Java Native Interface 참고 ) Anroid.mk : jni.gles10에있는 NativerRenderer.cpp 와앞장에서설명한 RenderingEngine.ES1.cpp 와 RenderingEngine.ES2.cpp를컴파일한다. 빌더는 ndk-r5b을이용하였으며, STL 라이브러리의경우, crystax 라이브러리가아닌 stlport 라이브러리를이용하였습니다. stlport library 는이전페이지에서언급했듯이 "OGLES20Application->Builds->Android->stlport" 폴더에있습니다. Application.mk : native module 생성파일로 ndk-r4b까지는그다지의미가없었지만, ndk-r5b에서는 stlport 를설정해주기도합니다.(APP_STL := stlport_static) 7. jni.gles10 : OpenGL ES 1.0 용 Native Renderer 이다. 8. jni.gles20 : OpenGL ES 2.0 용 Native Renderer 이다. 9. libs.armeabi : 빌드한 Shared Library 인 libnative.renderer.so 와 libnative.renderer20.so 가있다. 기본적으로 Android 프로젝트는위와같이구성되어있습니다. 그런데, Android 프로젝트에는 MFC 프로젝트에서설명했던 Renderer 클래스인 RenderingEngine.ES1.cpp와 RenderingEngine.ES2.cpp 등이보이지않습니다. 또한도우미라이브러리인 Interfaces.hpp, Matrix.hpp, Quaternion.hpp, Vector.hpp 등도보이지않습니다. 이프로젝트의구성을멀티플렛폼에맞춰서구성하다보니, 소스의위치를 Android Project 보다상위디렉토리레벨에배치하였습니다. [GLES20] 02. Android 3D Programming 을위한 Android 프로젝트설정 14

Eclipse 내에서프로젝트내에배치할경우, jni 폴더하위에두면되긴하지만, mfc 와 ios등과디렉토리레벨이틀어지기때문에현재와같이상위디렉토리레벨에배치하였습니다. 어차피 GDB을이용해서 native 단에서의 android debugging이쉽지않기때문에, mfc 에서디버깅할수있도록배치하였습니다. ( 이구성은 Imagination Technology 사의 PowerVR SDK 와유사한방식입니다.) 2. 실행결과 Android Galaxy Tab 에서실행하였으며, 결과는다음과같습니다. [GLES20] 02. Android 3D Programming 을위한 Android 프로젝트설정 15

Perspective Projection 으로 X 축 Red, Y 축 Green, Z 축 Blue 로표현되어있다. 3. 렌더러설명 제가여기에소개하는 Nativer Renderer 는기본적으로 iphone 3D Programming 책에설명이다나와있습니다. 그럼으로이책을사서보시는게이해는더빠르실거라생각됩니다. 또한렌더러설계에대해서는제블로그 http://blog.daum.net/aero2k/41 에서한번소개했었습니다. 렌더링엔진구성은다음과같습니다. 제가 Android 3D 로포팅한렌더러엔진도 ios 프레임워크부분을 Android Java 프레임워크로변경하였을뿐위에있는구조와유 [GLES20] 02. Android 3D Programming 을위한 Android 프로젝트설정 16

사합니다. Classes/RenderingEngine.ES2.cpp 에있는 Render 함수의구성에대해서간단하게설명하도록하겠습니다. PROJECTION MATRIX if (width > height) ratio = (GLfloat)width / (GLfloat)height; else ratio = (GLfloat)height / (GLfloat)width; mat4 projectionmatrix = mat4::perspective(45.0f, ratio, 1.0f, 100.0f); gluniformmatrix4fv(m_simple.uniforms.projection, 1, 0, projectionmatrix.pointer()); CAMERA MATRIX /* Setting the camera */ vec3 eye(10, 10, 10); vec3 target(0, 0, 0); vec3 up(0, 1, 0); mat4 camera = mat4::lookat(eye, target, up); [GLES20] 02. Android 3D Programming 을위한 Android 프로젝트설정 17

틀릴수도있지만, 저는위의코드를아래와같이해석합니다. 카메라설정기능으로머리위인상향벡터 (up(0, 1,0 ) 로두었으며, 물체는원점 (target(0, 0, 0)) 으로설정되었고, 시점은 X, Y, Z 축으로 -10 만큼떨어져있다. 만약 3차원좌표계가아닌 2D 좌표계인 X-Y 기준으로보고싶다면, eye(0, 0, 10) 을하시면 X와 Y축만남게되고, Z축은시점에서정면이됩니다. VERTICES /* Draw Grid and Axis */ /* 20x20 의격자형바닥을그린다. */ GLfloat gridvertices[] = { -10.0f, 0.0f, 0.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, -10.0f, 0.0f, 0.0f, 10.0f, ; /* X(Red)-Y(Green)-Z(Blue) 축을그린다. */ GLfloat axisvertices[] = { // x 0.0f, 0.0f, 0.0f, 6.0f, 0.0f, 0.0f, // y 0.0f, 0.0f, 0.0f, 0.0f, 4.0f, 0.0f, // z 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 6.0f, ; MODELVIEW MATRIX // 배경화면 Shader 인 Simple shader 프로그램바인딩. gluseprogram(m_simple.program); // GLES10에있는 glenableclientstate(gl_vertex_array) 를대체하는함수. glenablevertexattribarray(m_simple.attributes.position); // Draw Grid. // Grid Vertices Buffer Array 등록 [GLES20] 02. Android 3D Programming 을위한 Android 프로젝트설정 18

// GLES10에있는 glvertexpointer(3, GL_FLOAT, 0, gridvertices); glvertexattribpointer(m_simple.attributes.position, 3, GL_FLOAT, GL_FALSE, 0, gridvertices); gllinewidth(1.0f); GLint grid_axis_diffuse = m_simple.attributes.diffusematerial; // OGLES20에서는색을칠하는 Color Buffer Array 나 glcolor4f등의 API는지원하지않는다. // 이를대체하는방법으로 model에재질을입혀서색을표현해야한다. // diffuse material( 난반사재질 ) 색상을회색으로칠했다. vec3 grid_axis_material_dif = vec3(0.5f, 0.5f, 0.5f); glvertexattrib3fv(grid_axis_diffuse, grid_axis_material_dif.pointer()); mat4 grid_axis_modelview; for (int i = -10; i <= 10; ++i) { grid_axis_modelview = mat4::translate(0, 0, (GLfloat)i); grid_axis_modelview = grid_axis_modelview * camera; gluniformmatrix4fv(m_simple.uniforms.modelview, 1, 0, grid_axis_modelview.pointer()); gldrawarrays(gl_lines, 0, 2); for (int i = -10; i <= 10; ++i) { grid_axis_modelview = mat4::translate((glfloat)i, 0, 0); grid_axis_modelview = grid_axis_modelview * camera; gluniformmatrix4fv(m_simple.uniforms.modelview, 1, 0, grid_axis_modelview.pointer()); gldrawarrays(gl_lines, 2, 2); // Draw Axis. // Axis Vertices Buffer Array 등록 // GLES10에있는 glvertexpointer(3, GL_FLOAT, 0, axisvertices); 를대체하는함수. glvertexattribpointer(m_simple.attributes.position, 3, GL_FLOAT, GL_FALSE, 0, axisvertices); gllinewidth(1.5f); // Axis X // 빨간색난반사재질입히기 grid_axis_material_dif = vec3(1.0f, 0.0f, 0.0f); glvertexattrib3fv(grid_axis_diffuse, grid_axis_material_dif.pointer()); // gltranslatef() 를대체하는함수. grid_axis_modelview = mat4::translate(0.05f, 0.05f, 0.05f); [GLES20] 02. Android 3D Programming 을위한 Android 프로젝트설정 19

grid_axis_modelview = grid_axis_modelview * camera; gluniformmatrix4fv(m_simple.uniforms.modelview, 1, 0, grid_axis_modelview.pointer()); gldrawarrays(gl_lines, 0, 2); // Axis Y // 녹색난반사재질입히기 grid_axis_material_dif = vec3(0.0f, 1.0f, 0.0f); glvertexattrib3fv(grid_axis_diffuse, grid_axis_material_dif.pointer()); gldrawarrays(gl_lines, 2, 2); // Axis Z // 파란색난반사재질입히기 grid_axis_material_dif = vec3(0.0f, 0.0f, 1.0f); glvertexattrib3fv(grid_axis_diffuse, grid_axis_material_dif.pointer()); gldrawarrays(gl_lines, 4, 2); // GLES10 에있는 gldisableclientstate(gl_vertex_array) 를대체하는함수. gldisablevertexattribarray(m_simple.attributes.position); GLES20 용 RenderingEngine.ES2.cpp 와 GLES10 용 RenderingEngine.ES1.cpp 파일두개를비교해서보면, 양자간에 API 들의구성이어떻게변경되었는지이해하기편할것같습니다. 4. Shader Language 바른생활님이작성한 http://cafe.naver.com/gld3d/402 강좌플보면, EGLSL과달리 GLSL에서는 OpenGL 1.1 스펙에있는 API들과같이사용할수잇는것을볼수있습니다. 하지만, Embedded Graphic Library Shader Language 에서는임베디드용으로최적화되면, OpenGL 1.1 스펙에서 Shader 언어로처리할수있는 API 들에대해서는전부제거되었습니다. 이에대한스펙을이해하기위해서는앞장에서소개했던 OpenGL ES 2.0 Programming Guide 책을한번보시는것도좋습니다. 그런데이책에대한서평들을읽어보시면알겠지만, 너무않좋습니다. 저같은경우는원서로봤는데, 책이출판된시점이크로노스그룹에서 GLES2.0이만들어진지얼마되지않은시점에나와서그런지예제는아주많이부실하고왠지스펙문서를읽는듯한느낌이듭니다 ^^; [GLES20] 02. Android 3D Programming 을위한 Android 프로젝트설정 20

그래도한번쭉읽어보는것이도움이됨으로추천은합니다. ( 솔직히 OpenGL ES 2.0 관련된다른책이없어서추천할대체제가없습니다. 전돈주고샀지만인터넷에 PDF 파일이많음으로블랙마켓에서구해서보는것도... ㅠㅠ ) 각설하고,... 아래와같이배경화면그리는수준의 Shader 언어에서는별내용이없습니다. 이부분은바른생활님이설명하신 http://cafe.naver.com/gld3d/402 장과비교하시면서한번쭈욱보시면, 이해하실수있을겁니다. Simple.vert : Vertex Shader tatic const char* SimpleVertexShader = STRINGIFY( precision highp float; precision highp int; // Attributes attribute vec4 a_position;//position; attribute vec3 a_diffusematerial; uniform mat4 u_modelviewprojectionmatrix;//projection; uniform mat4 u_modelviewmatrix;//modelview; varying vec3 v_diffuse;//diffuse; void main(void) { v_diffuse = a_diffusematerial; gl_position = u_modelviewprojectionmatrix * u_modelviewmatrix * a_position; Simple.frag : Fragment Shader static const char* SimpleFragmentShader = STRINGIFY( varying lowp vec3 v_diffuse;//diffuse; void main(void) { gl_fragcolor = vec4(v_diffuse, 1.0); [GLES20] 02. Android 3D Programming 을위한 Android 프로젝트설정 21

이장은프로젝트설정부분임으로여기까지만소개하겠습니다. ^^; 다음장에서는 Model 에 Material 재질값을입히고색상을표현하는방법에대해서알아보도록하겠습니다. ( 솔직히이미아시는분들은알겠지만, 이글에서소개한정도만으로도 glcolor4f 를대체하는색상그리는방법은이해하실수있을 겁니다.) OGLES20 Template Application ported to mfc and android http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail?name=ogles20application.v10.zip&can=2&q=#makechanges [GLES20] 02. Android 3D Programming 을위한 Android 프로젝트설정 22

[GLES20] 03. Color Shading 2011.11.06 22:50 1. Color Shading 앞절에서간략하게설명한 RenderingEngine.ES2.cpp 에대해서좀더풀어서설명하도록하겠습니다. Classes/RenderingEngine.ES2.cpp 1. Create the GLSL program m_simple.program = BuildProgram(SimpleVertexShader, SimpleFragmentShader); BuildProgram 함수에서는 GLSL source 인 Vertex Shader text(simple.vert) 와 Fragment Shader text(simple.frag) 파일 을아래의 GLES 함수를이용해서컴파일합니다. Vertex Shader 생성 GLuint vertexshader= glcreateshader(gl_vertex_shader); glshadersource(vertexshader, 1, &source, 0); [GLES20] 03. Color Shading 23

glcompileshader(vertexshader); Fragment Shader 생성 GLuint fragmentshader= glcreateshader(gl_fragment_shader); glshadersource(fragmentshader, 1, &source, 0); glcompileshader(fragmentshader); Vertex Shader와 Fragment Shader 링크 GLuint programhandle = glcreateprogram(); glattachshader(programhandle, vertexshader); glattachshader(programhandle, fragmentshader); gllinkprogram(programhandle); 2. Program 선택 바탕화면이하나짜리를사용할경우에는아래와같이생성한 Program 한개를사용한다고 GLES 라이브러리에알립니다. gluseprogram(m_simple.program); 만약한개이상의 Program 을사용할경우에는필요할때마다 gluseprogram 을이용해서변경가능합니다. 이는 glpushmatrix()-glpopmatrix() 쌍으로묶여있는렌더러구성과유사하게응용할수있습니다. GLES10에서는바탕화면을그리고상자를그린다고하면다음과같이그릴수있다. glpushmatrix(); drawbackground(); glpopmatrix(); glpushmatrix(); drawcubebox(); glpopmatrix(); GLES20에서는 glpushmatrix()-glpopmatrix() 함수를지원하지않는다. gluseprogram(m_simple.program); drawbackground(); gluseprogram(m_cubebox.program); drawcubebox(); 와같이처리할수있다. [GLES20] 03. Color Shading 24

3. PROJECTION MATRIX m_simple.uniforms.projection = glgetuniformlocation(program, "u_modelviewprojectionmatrix"); m_simple.uniforms.projection = glgetuniformlocation(program, "u_projectionmatrix"); if (width > height) ratio = (GLfloat)width / (GLfloat)height; else ratio = (GLfloat)height / (GLfloat)width; mat4 projectionmatrix = mat4::perspective(45.0f, ratio, 1.0f, 100.0f); gluniformmatrix4fv(m_simple.uniforms.projection, 1, 0, projectionmatrix.pointer()); m_simple.uniforms.projection 은 glgetuniformlocation() 함수를이용해서 simple.vert 내에선언된 uniform mat4 u_projectionmatrix;//projection; 과연결되는 ID 를얻을수있습니다. 이 ID 에 Perspective, Frustum, Ortho 투영행렬을 gluniformmatrix4fv() 함수를이용해서저장합니다. 4. MODELVIEW MATRIX 4.1 CAMERA MATRIX /* Setting the camera */ vec3 eye(10, 10, 10); vec3 target(0, 0, 0); vec3 up(0, 1, 0); mat4 camera = mat4::lookat(eye, target, up); 틀릴수도있지만, 저는위의코드를아래와같이해석합니다. 카메라설정기능으로머리위인상향벡터 (up(0, 1,0 ) 로두었으며, 물체는원점 (target(0, 0, 0)) 으로설정되었고, 시점은 X, Y, Z 축으로 -10 만큼떨어져있다. 만약 3차원좌표계가아닌 2D 좌표계인 X-Y 기준으로보고싶다면, eye(0, 0, 10) 을하시면 X와 Y축만남게되고, Z축은시점에서정면이됩니다. [GLES20] 03. Color Shading 25

camera 행렬을 modelview 행렬에곱해서사용합니다. (camera 행렬은 Option으로서없어도 3D를표현할수있습니다. 하지만명확하게 3D를표현할때는 Camera 행렬을이용하는것이좋습니다.) camera에대해서는바른생활님기초강좌님다음페이지들을참고하십시요. http://cafe.naver.com/gld3d/366 http://cafe.naver.com/gld3d/368 http://cafe.naver.com/gld3d/371 4.2 VERTICES /* Draw Grid and Axis */ /* 20x20 의격자형바닥을그린다. */ GLfloat gridvertices[] = { -10.0f, 0.0f, 0.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, -10.0f, 0.0f, 0.0f, 10.0f, ; /* X(Red)-Y(Green)-Z(Blue) 축을그린다. */ GLfloat axisvertices[] = { // x 0.0f, 0.0f, 0.0f, 6.0f, 0.0f, 0.0f, // y 0.0f, 0.0f, 0.0f, 0.0f, 4.0f, 0.0f, // z 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 6.0f, ; 20x20 격자형바닥을표현할 gridvertices 배열과 X-Y-Z 축을표현할 axisvertices 배열입니다. 이정점들은 simple.vert 내의 attribute vec4 a_position; 에값들을저장합니다. [GLES20] 03. Color Shading 26

m_simple.attributes.position = glgetattriblocation(program, "a_position"); m_simple.attributes.position 은 glgetattriblocation(program, "a_position"); 함수를이용해서 simple.vert 내에선언된 uniform mat4 attribute vec4 a_position; 과연결되는 ID 를얻을수있습니다. a_position 에정점값을저장할려면, glenablevertexattribarray() 과 glvertexattribpointer() 함수를이용합니다. 이함수는 GLES1.0 과비교하면다음과같습니다. GLES10 glenableclientstate(gl_vertex_array); gldisableclientstate(gl_vertex_array); glvertexpointer(3, GL_FLOAT, 0, gridvertices); GLES20 glenablevertexattribarray(m_simple.attributes.position) gldisablevertexattribarray(m_simple.attributes.position glvertexattribpointer(m_simple.attributes.position, 3, G GL_FALSE, 0, gridvertices); glvertexpointer(3, GL_FLOAT, 0, axisvertices); glvertexattribpointer(m_simple.attributes.position, 3, G GL_FALSE, 0, axisvertices); 4.3 MODELVIEW MATRIX // 배경화면 Shader 인 Simple shader 프로그램바인딩. gluseprogram(m_simple.program); // GLES10에있는 glenableclientstate(gl_vertex_array) 를대체하는함수. glenablevertexattribarray(m_simple.attributes.position); // Draw Grid. // Grid Vertices Buffer Array 등록 // GLES10에있는 glvertexpointer(3, GL_FLOAT, 0, gridvertices); glvertexattribpointer(m_simple.attributes.position, 3, GL_FLOAT, GL_FALSE, 0, gridvertices); gllinewidth(1.0f); GLint grid_axis_diffuse = m_simple.attributes.diffusematerial; // OGLES20에서는색을칠하는 Color Buffer Array 나 glcolor4f등의 API는지원하지않는다. // 이를대체하는방법으로 model에재질을입혀서색을표현해야한다. // diffuse material( 난반사재질 ) 색상을회색으로칠했다. [GLES20] 03. Color Shading 27

vec3 grid_axis_material_dif = vec3(0.5f, 0.5f, 0.5f); glvertexattrib3fv(grid_axis_diffuse, grid_axis_material_dif.pointer()); mat4 grid_axis_modelview; for (int i = -10; i <= 10; ++i) { grid_axis_modelview = mat4::translate(0, 0, (GLfloat)i); grid_axis_modelview = grid_axis_modelview * camera; gluniformmatrix4fv(m_simple.uniforms.modelview, 1, 0, grid_axis_modelview.pointer()); gldrawarrays(gl_lines, 0, 2); for (int i = -10; i <= 10; ++i) { grid_axis_modelview = mat4::translate((glfloat)i, 0, 0); grid_axis_modelview = grid_axis_modelview * camera; gluniformmatrix4fv(m_simple.uniforms.modelview, 1, 0, grid_axis_modelview.pointer()); gldrawarrays(gl_lines, 2, 2); // Draw Axis. // Axis Vertices Buffer Array 등록 // GLES10에있는 glvertexpointer(3, GL_FLOAT, 0, axisvertices); 를대체하는함수. glvertexattribpointer(m_simple.attributes.position, 3, GL_FLOAT, GL_FALSE, 0, axisvertices); gllinewidth(1.5f); // Axis X // 빨간색난반사재질입히기 grid_axis_material_dif = vec3(1.0f, 0.0f, 0.0f); glvertexattrib3fv(grid_axis_diffuse, grid_axis_material_dif.pointer()); // gltranslatef() 를대체하는함수. grid_axis_modelview = mat4::translate(0.05f, 0.05f, 0.05f); grid_axis_modelview = grid_axis_modelview * camera; gluniformmatrix4fv(m_simple.uniforms.modelview, 1, 0, grid_axis_modelview.pointer()); gldrawarrays(gl_lines, 0, 2); // Axis Y // 녹색난반사재질입히기 grid_axis_material_dif = vec3(0.0f, 1.0f, 0.0f); [GLES20] 03. Color Shading 28

glvertexattrib3fv(grid_axis_diffuse, grid_axis_material_dif.pointer()); gldrawarrays(gl_lines, 2, 2); // Axis Z // 파란색난반사재질입히기 grid_axis_material_dif = vec3(0.0f, 0.0f, 1.0f); glvertexattrib3fv(grid_axis_diffuse, grid_axis_material_dif.pointer()); gldrawarrays(gl_lines, 4, 2); // GLES10 에있는 gldisableclientstate(gl_vertex_array) 를대체하는함수. gldisablevertexattribarray(m_simple.attributes.position); 위에코드을이용해서 20x20 격자형바닥을그린후 X-Y-Z축을그립니다. 여기서색상 Color Shading 에대해서나옵니다. GLES10에서는물체에색상을입힐때, COLOR BUFFER ARRAY 또는 glcolor3f, glcolor4f 등을이용합니다. 하지만, GLES20에서는 glcolor를지원하지않습니다. 그럼으로위에서사용했던방법과같이 Position 정점을구하는방법과마찬가지로, Color에대해서 Attribute 로설정합니다. 저는난반사재질인 Diffuse material 로정하고회색 (0.5f, 0.5f, 0.5f) 을입혔습니다. ( 보통불투명색상을 diffuse 로정한다고합니다.) m_simple.attributes.diffusematerial = glgetattriblocation(program, "a_diffusematerial");... vec3 grid_axis_material_dif = vec3(0.5f, 0.5f, 0.5f); glvertexattrib3fv(grid_axis_diffuse, grid_axis_material_dif.pointer()); Color Shading 은다음과같이 vertex shader 인 simple.vert 에서는 a_diffusematerial 로받은색상값을 varying keyword 를이용해서 fragment shader 인 simple.frag 에전달합니다. simple.vert varying vec3 v_diffuse;//diffuse; void main(void) [GLES20] 03. Color Shading 29

{ v_diffuse = a_diffusematerial; gl_position = u_modelviewprojectionmatrix * u_modelviewmatrix * a_position; varying keyword 로전달받은 v_diffuse 는 gl_fragcolor 에저장합니다. (gl_fragcolor 는 (r, g, b, a) 임으로 vec4 형태로변경해줘야합니다. 물론 varying 통해서전달받는값자체를 vec4 형으로받아와도됩니다.) simple.frag varying lowp vec3 v_diffuse;//diffuse; void main(void) { gl_fragcolor = vec4(v_diffuse, 1.0); 이장에서는간단하게 glcolor4f 를이용해서색상을입히는방법에대해서알아봤습니다. 색상에대한상세한정리는 Light 조명효과로유명한 Phong Shader 를다룰때설명하도록하겠습니다. OGLES20 Template Application ported to mfc and android http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail?name=ogles20application.v10.zip&can=2&q=#makechanges [GLES20] 03. Color Shading 30

[GLES20] 03. Color Shading 31

[GLES20] 04. Geometry Transform 2011.11.11 12:37 기하변환 이번장에서는 CubeBox 를뛰운후에이를확대, 회전, 이동시키는변환방법에대해서알아보도록하겠습니다. 1. 회전변환 GLES10 에서는모델에대해서회전변환을적용하기위해서 glrotatef() 함수를이용합니다. glrotatef(90.0f, 1.0f, 0.0f, 0.0f) : X 축을기준으로 90 도회전 glrotatef(90.0f, 0.0f, 1.0f, 0.0f) : Y 축을기준으로 90 도회전 glrotatef(90.0f, 0.0f, 0.0f, 1.0f) : Z 축을기준으로 90 도회전 하지만, GLES20 에서는 glrotatef() 함수를지원하지않습니다. 즉이를대체하는 Function Set 를직접만들어야합니다. 구현방법은예전에정리했었던, " 오일러회전 " 장에보시면기본원리가있습니다. ( 변환으로가는길 #2 : 오일러회전 ) [GLES20] 04. Geometry Transform 32

Utils/Matrix.hpp -. 오일러변환 X 축 static Matrix4<T> RotateX(T degrees) { T radians = degrees * 3.14159f / 180.0f; T s = std::sin(radians); T c = std::cos(radians); Matrix4 m; m.x.x = 1; m.x.y = 0; m.x.z = 0; m.x.w = 0; m.y.x = 0; m.y.y = c; m.y.z =-s; m.y.w = 0; m.z.x = 0; m.z.y = s; m.z.z = c; m.z.w = 0; m.w.x = 0; m.w.y = 0; m.w.z = 0; m.w.w = 1; return m; -. 오일러변환 Y 축 static Matrix4<T> RotateY(T degrees) { T radians = degrees * 3.14159f / 180.0f; T s = std::sin(radians); T c = std::cos(radians); Matrix4 m; m.x.x = c; m.x.y = 0; m.x.z = s; m.x.w = 0; m.y.x = 0; m.y.y = 1; m.y.z = 0; m.y.w = 0; m.z.x = -s; m.z.y = 0; m.z.z = c; m.z.w = 0; m.w.x = 0; m.w.y = 0; m.w.z = 0; m.w.w = 1; return m; -. 오일러변환 Z 축 static Matrix4<T> RotateZ(T degrees) { T radians = degrees * 3.14159f / 180.0f; T s = std::sin(radians); [GLES20] 04. Geometry Transform 33

T c = std::cos(radians); Matrix4 m; m.x.x = c; m.x.y = -s; m.x.z = 0; m.x.w = 0; m.y.x = s; m.y.y = c; m.y.z = 0; m.y.w = 0; m.z.x = 0; m.z.y = 0; m.z.z = 1; m.z.w = 0; m.w.x = 0; m.w.y = 0; m.w.z = 0; m.w.w = 1; return m; 사용방법은다음과같습니다. X 축회전 void RenderingEngine::drawCubebox(int texturemode) { mat4 euler_x = mat4::rotatex(xrot); mat4 euler_y = mat4::rotatey(yrot); mat4 euler_z = mat4::rotatez(yrot); mat4 rotation = euler_x * m_orientation.tomatrix(); mat4 modelview = scale * rotation * translation; [GLES20] 04. Geometry Transform 34

Y 축회전 void RenderingEngine::drawCubebox(int texturemode) { mat4 euler_x = mat4::rotatex(xrot); mat4 euler_y = mat4::rotatey(yrot); mat4 euler_z = mat4::rotatez(yrot); mat4 rotation = euler_y * m_orientation.tomatrix(); mat4 modelview = scale * rotation * translation; [GLES20] 04. Geometry Transform 35

Z 축회전 void RenderingEngine::drawCubebox(int texturemode) { mat4 euler_x = mat4::rotatex(xrot); mat4 euler_y = mat4::rotatey(yrot); mat4 euler_z = mat4::rotatez(yrot); mat4 rotation = euler_z * m_orientation.tomatrix(); mat4 modelview = scale * rotation * translation; [GLES20] 04. Geometry Transform 36

2. 축척변환 GLES10 에서는모델에대해서축척변환을적용하기위해서 glscalef() 함수를이용합니다. 하지만, GLES20 에서는 glscalef() 함수를지원하지않습니다. 축척을변환하는방법은위의함수를사용하지않고도간단하게구현할수있습니다. 왜냐하면, 확대축소는수학적으로벡터량이아닌스칼라량이기때문에곱하기만하면됩니다. iphone 3D Programming 책에서제공하는모듈에서는이에대한함수를제공함으로이를이용하면됩니다. Utils/Matrix.hpp static Matrix4<T> Scale(T s) { Matrix4 m; m.x.x = s; m.x.y = 0; m.x.z = 0; m.x.w = 0; m.y.x = 0; m.y.y = s; m.y.z = 0; m.y.w = 0; [GLES20] 04. Geometry Transform 37

m.z.x = 0; m.z.y = 0; m.z.z = s; m.z.w = 0; m.w.x = 0; m.w.y = 0; m.w.z = 0; m.w.w = 1; return m; static Matrix4<T> Scale(T x, T y, T z) { Matrix4 m; m.x.x = x; m.x.y = 0; m.x.z = 0; m.x.w = 0; m.y.x = 0; m.y.y = y; m.y.z = 0; m.y.w = 0; m.z.x = 0; m.z.y = 0; m.z.z = z; m.z.w = 0; m.w.x = 0; m.w.y = 0; m.w.z = 0; m.w.w = 1; return m; 사용방법은다음과같습니다. void RenderingEngine::drawCubebox(int texturemode) { mat4 euler_x = mat4::rotatex(xrot); mat4 euler_y = mat4::rotatey(yrot); mat4 euler_z = mat4::rotatez(yrot); // mat4 scale = mat4::scale(1.0f); mat4 scale = mat4::scale(3.0f*cos(interpolation_z)); mat4 translation = mat4::translate(x, y, z); mat4 rotation = euler_x * m_orientation.tomatrix(); mat4 modelview = scale * rotation * translation; interpolation_z += (2*Pi) * 0.0008f; 보간값을계산할때삼각함수인 cos을이용했습니다. cos에치역은 -1.0f ~ 1.0f 사이입니다. 위의 3.0f*cos(interpolation_z) 의경우, interpolation_z가 0에서부터시작함으로, cos(0) = 1 입니다. +3 ~ 0 ~ -3 ~ 0 ~ +3 의값을반복해서반환하게됩니다. 결국아래의동영상에서볼수있듯이 CubeBox 가원점을기준으로 +3 에큰크기에서시작했다가 원점으로수렴해가면서작아집니다. 이후 -3 에점점수렴해가기때문에그모양이뒤집혀서커지게됩니다. ((2*Pi) * 0.0008f; 증가수치는 PowerVR SDK 내의보간증가수치를가져왔습니다. ^^;) [GLES20] 04. Geometry Transform 38

3. 이동변환 GLES10 에서는모델에대해서이동변환을적용하기위해서 gltranslatef() 함수를이용합니다. 하지만, GLES20 에서는 gltranslatef() 함수를지원하지않습니다. 이동변환의원리는 4x4 행렬상에서 4번째행에값을저장하면됩니다. 예를들어아래와같은회전행렬이있다고한다면, 1 0 0 0 0 1 0 0 0 0 1 0 wx wy wz 1 wx, wy, wz 요소에이동변환값을저장한후 model matrix 에곱하면됩니다. 구현함수는다음과같습니다. [GLES20] 04. Geometry Transform 39

Utils/Matrix.hpp static Matrix4<T> Translate(T x, T y, T z) { Matrix4 m; m.x.x = 1; m.x.y = 0; m.x.z = 0; m.x.w = 0; m.y.x = 0; m.y.y = 1; m.y.z = 0; m.y.w = 0; m.z.x = 0; m.z.y = 0; m.z.z = 1; m.z.w = 0; m.w.x = x; m.w.y = y; m.w.z = z; m.w.w = 1; return m; 사용방법은다음과같습니다. void RenderingEngine::drawCubebox(int texturemode) { mat4 euler_x = mat4::rotatex(xrot); mat4 euler_y = mat4::rotatey(yrot); mat4 euler_z = mat4::rotatez(yrot); mat4 scale = mat4::scale(1.0f); // mat4 translation = mat4::translate(x, y, z); mat4 translation = mat4::translate(x, y, 15.0f*cos(interpolation_z)-7.5f); mat4 rotation = euler_x * m_orientation.tomatrix(); mat4 modelview = scale * rotation * translation; interpolation_z += (2*Pi) * 0.0008f; 보간값은축척변환에적용했던삼각함수 cos을이용했습니다. cos에치역은 -1.0f ~ 1.0f 사이입니다. 15.0f*cos(interpolation_z)-7.5f 의경우, interpolation_z가 0에서부터시작함으로, cos(0) = 1 입니다. +7.5 ~ -7.5 ~ -22.5 의값을반복해서반환하게됩니다. 특이사항은 cos(theta) 가 0 ~ -1 일경우, -7.5 에가중치를받기때문에, 아주약간 EaseOut 효과처럼가속를받게됩니다. ((2*Pi) * 0.0008f; 증가수치는 PowerVR SDK 내의보간증가수치를가져왔습니다. ^^;) [GLES20] 04. Geometry Transform 40

4. vertex shader 내에서변환적용하기 이와같은변환의적용은 vertex shader 내에서적용해도됩니다. shader language 내에서처리할경우, 그연산처리를 GPU 에서하기때문에, CPU 에대한부하를줄일수있다고합니다. ( 저도실제측정해보지는않았습니다.) Shaders/SimpleLighting.vert // Attributes attribute vec4 a_position;//position; // Uniforms uniform mat4 u_projectionmatrix;//projection; uniform mat4 u_modelviewmatrix;//modelview; uniform mat3 u_normalmatrix;//normalmatrix; uniform float u_interpolation_z; varying vec3 v_eyespacenormal;//eyespacenormal varying vec3 v_diffuse;//diffuse; varying vec2 v_texturecoordout; [GLES20] 04. Geometry Transform 41

void main(void) { v_eyespacenormal = u_normalmatrix * a_normal; v_diffuse = a_diffusematerial; v_texturecoordout = a_texturecoordin; // Vertex Position vec4 newpos = vec4(a_position); mat4 translation = mat4(1.0); mat4 modelview = mat4(u_modelviewmatrix); translation[3][2] = (15.0*cos(u_interpolation_z) - 7.5); modelview *= translation; gl_position = u_projectionmatrix * modelview * newpos; translation[3][2] 는이동변환에서 Z 축을기준으로변환시키는요소입니다. [GLES20] 04. Geometry Transform 42

저는아직까지는 vertex shader 내에서논리연산을많이사용하지는않습니다. 튜닝적인요소가있을경우에는적용할가치가있을것같지만, 아직까지는 shader 코드내에서의 helper 함수를새로구성해야하는점과디버깅이불편하다는점때문에잘사용하지는않습니다. OGLES20 Template Application ported to mfc and android http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail?name=01_geometrytransform.v1.1.zip&can=2&q= [GLES20] 04. Geometry Transform 43

[GLES20] 05. Vertex Position 처리 (1) 2011.11.11 21:24 Vertex Shader 내에서 Vertex Position 조정 이번장에서는 Vertex shader 내에서정점위치를조정하는방법에대해서정리해보도록하겠습니다. 정리에앞서서 OpenGL GLSL 로만들어진바른생활님강좌를한번읽어보시면도움이될것같습니다.. http://cafe.naver.com/gld3d/405 http://cafe.naver.com/gld3d/406 OpenGL ES 에서는 OpenGL 에서는지원하는즉시모드 (Immediate Mode) 형태의 glvertex3f() 를지원하지않습니다. 그럼으로당연히 GLSL 내에있는 gl_vertex 도지원하지않습니다. 정점을얻기위해서는 attribute keyword 형태로선언해서 OpenGL App( 이하본 App ^^) 에서정점값을얻어와야합니다. ( 나중에다루겠지만, Normal vector, Texture vector 값도 attribute 으로선언해서본 App 으로부터얻어옵니다.) 1. Cubebox 그리기 여기서구현한 Cubebox 는 GL_TRIANGLE_STRIP 으로구현하는방법으로정점을구성하는방법중하나일뿐입니다. 예를들어, vertices, normals, texcoords 배열을초기화시점에미리다설정해서구성해도됩니다. [GLES20] 05. Vertex Position 처리 (1) 44

GLfloat texcoords[4][2]; GLfloat vertices[4][3]; GLfloat normals[4][3]; GLubyte indices[4]={0, 1, 3, 2; /* QUAD to TRIANGLE_STRIP conversion; */ -- (0) gluseprogram(m_cubebox.program); // GLES10에있는 glenableclientstate(gl_vertex_array) 를대체하는함수. -- (1) glenablevertexattribarray(m_cubebox.attributes.position); glenablevertexattribarray(m_cubebox.attributes.normal); glenablevertexattribarray(m_cubebox.attributes.texturecoord); // GLES10에있는 glvertexpointer(3, GL_FLOAT, 0, gridvertices); ----------- (2) glvertexattrib3fv(diffuse, box_material_dif.pointer()); glvertexattribpointer(position, 3, GL_FLOAT, GL_FALSE, 0, vertices); glvertexattribpointer(normal, 3, GL_FLOAT, GL_FALSE, 0, normals); glvertexattribpointer(texcoord, 2, GL_FLOAT, GL_FALSE, 0, texcoords); /* Front Face */ ------------------------------------------------- (3) /* Normal Pointing Towards Viewer */ // 법선벡터를 Z축방향으로앞으로나오는면이므로, Z축열에 +1.0을넣는다. normals[0][0] = normals[1][0] = normals[2][0] = normals[3][0] = 0.0f; normals[0][1] = normals[1][1] = normals[2][1] = normals[3][1] = 0.0f; normals[0][2] = normals[1][2] = normals[2][2] = normals[3][2] = 1.0f; /* Point 1 (Front) */ texcoords[0][0] = 1.0f; texcoords[0][1] = 0.0f; vertices[0][0] = -1.0f; vertices[0][1] = -1.0f; vertices[0][2] = 1.0f; /* Point 2 (Front) */ texcoords[1][0] = 0.0f; texcoords[1][1] = 0.0f; vertices[1][0] = 1.0f; vertices[1][1] = -1.0; vertices[1][2] = 1.0f; /* Point 3 (Front) */ texcoords[2][0] = 0.0f; texcoords[2][1] = 1.0f; vertices[2][0] = 1.0f; vertices[2][1] = 1.0; vertices[2][2] = 1.0f; /* Point 4 (Front) */ texcoords[3][0] = 1.0f; texcoords[3][1] = 1.0f; vertices[3][0] = -1.0f; vertices[3][1] = 1.0; vertices[3][2] = 1.0f; [GLES20] 05. Vertex Position 처리 (1) 45

/* Blue Color */ ------------------------------------------------- (4) if (texturemode == 0) { box_material_dif = vec3(0.f, 0.f, 1.f) * 0.75f; glvertexattrib3fv(diffuse, box_material_dif.pointer()); /* Draw one textured plane using two stripped triangles */ ----------- (5) gldrawelements(gl_triangle_strip, 4, GL_UNSIGNED_BYTE, indices); /* Back Face */ ----------------------------------------------- (6) /* Normal Pointing Away From Viewer */ // 법선벡터를 Z축방향으로뒤로들어가는면이므로, Z축열에 -1.0을넣는다. normals[0][0] = normals[1][0] = normals[2][0] = normals[3][0] = 0.0f; normals[0][1] = normals[1][1] = normals[2][1] = normals[3][1] = 0.0f; normals[0][2] = normals[1][2] = normals[2][2] = normals[3][2] = -1.0f; /* Point 1 (Back) */ texcoords[0][0] = 0.0f; texcoords[0][1] = 0.0f; vertices[0][0] = -1.0f; vertices[0][1] = -1.0f; vertices[0][2] = -1.0f; /* Point 2 (Back) */ texcoords[1][0] = 0.0f; texcoords[1][1] = 1.0f; vertices[1][0] = -1.0f; vertices[1][1] = 1.0f; vertices[1][2] = -1.0f; /* Point 3 (Back) */ texcoords[2][0] = 1.0f; texcoords[2][1] = 1.0f; vertices[2][0] = 1.0f; vertices[2][1] = 1.0f; vertices[2][2] = -1.0f; /* Point 4 (Back) */ texcoords[3][0] = 1.0f; texcoords[3][1] = 0.0f; vertices[3][0] = 1.0f; vertices[3][1] = -1.0; vertices[3][2] = -1.0f; /* Black Color */ if (texturemode == 0) { box_material_dif = vec3(0.f, 0.f, 0.f) * 0.75f; glvertexattrib3fv(diffuse, box_material_dif.pointer()); /* Draw one textured plane using two stripped triangles */ gldrawelements(gl_triangle_strip, 4, GL_UNSIGNED_BYTE, indices); [GLES20] 05. Vertex Position 처리 (1) 46

/* Top Face */ ------------------------------------------------- (7) /* Normal Pointing Up */ // 법선벡터를 Y축방향으로위로올라가는면이므로, Y축열에 +1.0을넣는다. normals[0][0] = normals[1][0] = normals[2][0] = normals[3][0] = 0.0f; normals[0][1] = normals[1][1] = normals[2][1] = normals[3][1] = 1.0f; normals[0][2] = normals[1][2] = normals[2][2] = normals[3][2] = 0.0f; /* Point 1 (Top) */ texcoords[0][0] = 1.0f; texcoords[0][1] = 1.0f; vertices[0][0] = -1.0f; vertices[0][1] = 1.0f; vertices[0][2] = -1.0f; /* Point 2 (Top) */ texcoords[1][0] = 1.0f; texcoords[1][1] = 0.0f; vertices[1][0] = -1.0f; vertices[1][1] = 1.0f; vertices[1][2] = 1.0f; /* Point 3 (Top) */ texcoords[2][0] = 0.0f; texcoords[2][1] = 0.0f; vertices[2][0] = 1.0f; vertices[2][1] = 1.0f; vertices[2][2] = 1.0f; /* Point 4 (Top) */ texcoords[3][0] = 0.0f; texcoords[3][1] = 1.0f; vertices[3][0] = 1.0f; vertices[3][1] = 1.0f; vertices[3][2] = -1.0f; /* Green Color */ if (texturemode == 0) { box_material_dif = vec3(0.f, 1.f, 0.f) * 0.75f; glvertexattrib3fv(diffuse, box_material_dif.pointer()); /* Draw one textured plane using two stripped triangles */ gldrawelements(gl_triangle_strip, 4, GL_UNSIGNED_BYTE, indices); /* Bottom Face */ ------------------------------------------------- (8) /* Normal Pointing Down */ // 법선벡터를 Y축방향으로위로올라가는면이므로, Y축열에 -1.0을넣는다. normals[0][0] = normals[1][0] = normals[2][0] = normals[3][0] = 0.0f; normals[0][1] = normals[1][1] = normals[2][1] = normals[3][1] = -1.0f; normals[0][2] = normals[1][2] = normals[2][2] = normals[3][2] = 0.0f; /* Point 1 (Bottom) */ texcoords[0][0] = 0.0f; texcoords[0][1] = 1.0f; vertices[0][0] = -1.0f; vertices[0][1] = -1.0f; vertices[0][2] = -1.0f; [GLES20] 05. Vertex Position 처리 (1) 47

/* Point 2 (Bottom) */ texcoords[1][0] = 1.0f; texcoords[1][1] = 1.0f; vertices[1][0] = 1.0f; vertices[1][1] = -1.0f; vertices[1][2] = -1.0f; /* Point 3 (Bottom) */ texcoords[2][0] = 1.0f; texcoords[2][1] = 0.0f; vertices[2][0] = 1.0f; vertices[2][1] = -1.0f; vertices[2][2] = 1.0f; /* Point 4 (Bottom) */ texcoords[3][0] = 0.0f; texcoords[3][1] = 0.0f; vertices[3][0] = -1.0f; vertices[3][1] = -1.0f; vertices[3][2] = 1.0f; /* Yellow Color */ if (texturemode == 0) { box_material_dif = vec3(0.8f, 0.8f, 0.f) * 0.75f; glvertexattrib3fv(diffuse, box_material_dif.pointer()); /* Draw one textured plane using two stripped triangles */ gldrawelements(gl_triangle_strip, 4, GL_UNSIGNED_BYTE, indices); /* Right face */ ------------------------------------------------- (9) /* Normal Pointing Right */ // 법선벡터를 X축방향으로오른쪽으로이동하는면이므로, X축열에 +1.0을넣는다. normals[0][0] = normals[1][0] = normals[2][0] = normals[3][0] = 1.0f; normals[0][1] = normals[1][1] = normals[2][1] = normals[3][1] = 0.0f; normals[0][2] = normals[1][2] = normals[2][2] = normals[3][2] = 0.0f; /* Point 1 (Right) */ texcoords[0][0] = 0.0f; texcoords[0][1] = 0.0f; vertices[0][0] = 1.0f; vertices[0][1] = -1.0f; vertices[0][2] = -1.0f; /* Point 2 (Right) */ texcoords[1][0] = 0.0f; texcoords[1][1] = 1.0f; vertices[1][0] = 1.0f; vertices[1][1] = 1.0f; vertices[1][2] = -1.0f; /* Point 3 (Right) */ texcoords[2][0] = 1.0f; texcoords[2][1] = 1.0f; vertices[2][0] = 1.0f; vertices[2][1] = 1.0f; vertices[2][2] = 1.0f; /* Point 4 (Right) */ texcoords[3][0] = 1.0f; texcoords[3][1] = 0.0f; vertices[3][0] = 1.0f; vertices[3][1] = -1.0f; vertices[3][2] = 1.0f; [GLES20] 05. Vertex Position 처리 (1) 48

/* Red Color */ if (texturemode == 0) { box_material_dif = vec3(1.0f, 0.f, 0.f) * 0.75f; glvertexattrib3fv(diffuse, box_material_dif.pointer()); /* Draw one textured plane using two stripped triangles */ gldrawelements(gl_triangle_strip, 4, GL_UNSIGNED_BYTE, indices); /* Left Face*/ ------------------------------------------------- (10) /* Normal Pointing Left */ // 법선벡터를 X축방향으로왼쪽으로이동하는면이므로, X축열에 -1.0을넣는다. normals[0][0] = normals[1][0] = normals[2][0] = normals[3][0] = -1.0f; normals[0][1] = normals[1][1] = normals[2][1] = normals[3][1] = 0.0f; normals[0][2] = normals[1][2] = normals[2][2] = normals[3][2] = 0.0f; /* Point 1 (Left) */ texcoords[0][0] = 1.0f; texcoords[0][1] = 0.0f; vertices[0][0] = -1.0f; vertices[0][1] = -1.0f; vertices[0][2] = -1.0f; /* Point 2 (Left) */ texcoords[1][0] = 0.0f; texcoords[1][1] = 0.0f; vertices[1][0] = -1.0f; vertices[1][1] = -1.0f; vertices[1][2] = 1.0f; /* Point 3 (Left) */ texcoords[2][0] = 0.0f; texcoords[2][1] = 1.0f; vertices[2][0] = -1.0f; vertices[2][1] = 1.0f; vertices[2][2] = 1.0f; /* Point 4 (Left) */ texcoords[3][0] = 1.0f; texcoords[3][1] = 1.0f; vertices[3][0] = -1.0f; vertices[3][1] = 1.0f; vertices[3][2] = -1.0f; /* White Color */ if (texturemode == 0) { box_material_dif = vec3(1.f, 1.f, 1.f) * 0.75f; glvertexattrib3fv(diffuse, box_material_dif.pointer()); /* Draw one textured plane using two stripped triangles */ gldrawelements(gl_triangle_strip, 4, GL_UNSIGNED_BYTE, indices); // GLES10 에있는 gldisableclientstate(gl_vertex_array) 를대체하는함수. -- (11) gldisablevertexattribarray(position); [GLES20] 05. Vertex Position 처리 (1) 49

gldisablevertexattribarray(normal); gldisablevertexattribarray(texcoord); ---------------------------------------------------------------------------------------- ------ (0) : 사각형을 gldrawarrays 가아닌 gldrawelements 로그리기위한 indices 순서입니다. (1) : glenablevertexattribarray() 는 vertex shader 에선언된 attribute keyword 를활성화합니다. 이는 GLES10 의 glenableclientstate() 를대체합니다. (2) : glvertexattribpointer() 를이용해서 vertex shader에선언된 attribte keyword 벡터변수에정정, 법선또는텍셀정보를저장합니다. 이는 GLES10의 glvertexpointer(), glnormalpointer(), gltexcoordpointer() 를대체합니다. 이는 OpenGL의 glvertex3f(), glnormal3f(), gltexcoord3f() 를대체합니다. (3) : Front Face 즉앞부분사각면을그리는부분입니다. 파란색으로칠하고있으며, 아직 normals 와 texcoords 는사용하지는않습니다. (4) : 파란색으로칠하기위해서본 App 에서 vertex shader 로접근하는부분입니다. 정점당색상값인 Color Buffer Array 을대체하는부분니다. (5) : (0) 에서설정한색인값인 indices 순서로사각형을그립니다. (6) : Back Face 즉뒷부분사각면을그리는부분입니다. 검정색으로칠하고있으며, 아직 normals 와 texcoords 는사용하지는않습니다. (7) : Top Face 즉윗부분사각면을그리는부분입니다. 녹색으로칠하고있으며, 아직 normals 와 texcoords 는사용하지는않습니다. (8) : Bottom Face 즉아랫부분사각면을그리는부분입니다. 노란색으로칠하고있으며, 아직 normals 와 texcoords 는사용하지는않습니다. (9) : Right Face 즉우측부분사각면을그리는부분입니다. 빨간색으로칠하고있으며, 아직 normals 와 texcoords 는사용하지는않습니다. (10) : Left Face 즉뒷좌측부분사격면을그리는부분입니다. 흰색으로칠하고있으며, 아직 normals 와 texcoords 는사용하지는않습니다. [GLES20] 05. Vertex Position 처리 (1) 50

(11) : (1) 에서활성화한정점, 법선, 텍셀을비활성화합니다. 이는 GLES10 의 gldisableclientstate() 를대체합니다. 결과는다음과같습니다. 2. Vertex Position Vertex shader 는본 App 으로부터 Position 정점정보를 a_position 으로전달받게됩니다. attribute 와 uniform 으로선언된변수들은 shader 내에서는 constant( 상수?) 로동작합니다. 즉 shader 내에서는 data 를읽기 (read) 는가능하지만쓰기 (write) 를할수는없습니다. 그럼으로 vertex 위치를임의로변경하기위해서는다음과같이 vec4 newpos 변수를만들어주고이에저장합니다. /Shaders/SimpleLighting.vert // Vertex Position vec4 newpos = vec4(a_position); newpos.z = newpos.z + sin(2.0 * newpos.x); gl_position = u_projectionmatrix * u_modelviewmatrix* newpos; [GLES20] 05. Vertex Position 처리 (1) 51

3. 결론 Cubebox 의정점이변경하기전과약간변화된것을볼수있습니다. 변형시키는 sin 함수내의가중치값 2.0 을더크게하면변화가눈에 " 뛰게보이지만, 주전자만큼크게찌그러지지는않습니다. newpos.z = newpos.z + sin(2.0 * newpos.x); 이는상자의경우정점의갯수가많지않아서그런것으로보입니다. 바른생활님예제 (http://cafe.naver.com/gld3d/406) 와같이주전자로그림을그리면, 얼추비슷한결과가나올겁니다. 그런데, OpenGL ES 에서는 glut lib 를지원하지않기때문에, OpenGL 과같이 glutsolidteapot() 함수를통해서쉽게주전자를그릴수는없습니다. ㅠㅠ 다음장에서는주전자를그리기위해서 wavefront obj 파일을 header 로추출한파일을이용해서, 주전자를그리고 Vertex Position 보간값을적용해보도록하겠습니다. (wavefront obj 파일은 3DMax 나 Blender 와같은 3D 그래픽도구를이용해서그린후 export 를 obj 형태로하면됩니다. ^^;) [GLES20] 05. Vertex Position 처리 (1) 52

[GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 2011.11.15 19:46 2011.11.18 : teapot을그릴때, normal 값에대한 offset이빠져있었습니다. ^^; Wireframe 형태일때는잘못느껴지겠지만, 면으로그려보면느껴지네요. 참고하십시요. ( 이전에 Render To Texture 로그릴때뺀이후로계속빠져있었던듯싶네요ㅎㅎ ) Vertex Shader 내에서 Vertex Position 조정 앞장에이어서이번장에서는 Wavefront Obj 포맷으로구성된 Obj 파일에서 mesh data 를추출한후, 이를 Vertex Buffer Object 형으로구성한뒤, Wireframe 형식의주전자 (Teapot) 를그려보도록하겠습니다. 그리고이 OpenGL App( 이하본 App) 의결과를 Vertex Shader 내에서 Vertex Position 을조정해보겠습니다. 1. Wavefront Obj mesh data 주전자 Teapot 를그리는방법은구글링하면많이나옵니다. 예를들어, 애플에서발표한 WWDC 2010 에서발표했던 CorMotionTeapot 를참조해도되고, [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 53

Wavefront Obj 모델파일을구해서, header 로변환해서이용해도됩니다. Obj 스펙은구글링하면손쉽게얻을수있으며, iphone3d Programming 책의 4 장에도설명은나와있습니다. 그런데, iphone3d Programming 책의경우 OBJ 파일포맷이 3dsMax 나 Blender 와는다른형태의 mesh 구조입니다. 작명에도나와있지만, Insanely Simple ObJ File( 직역하면, " 제정신이아니게간단한 OBJ File) - ㅁ -; 입니다. 또한 vertex 와면만을구하고 normal 과 texture coordinate 은고려하지않고만들어진파서입니다. 책에서는 normal 값을 Triangle 를구성하는 3 개의정점이한개의면을이루게됩니다. 같은평면상에있는두개의벡터의외적을구하면법선을계산할수있습니다. 즉, 면을구성하는 3 개의정점이 a, b, c 라고한다면, N = (b - a).cross(c - a) 가됩니다. [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 54

혹이책을보시고 Obj 포맷파싱을책과같이하면되겠구나오해하시면안되며, Example 4.23 과같이 f(face) 값같이숫자사이에 '/' 으로구분자를인식하게금만드셔야합니다. 3ds Max 나 Blender 로만든 Obj 파일은 Example 4.23 과같은포맷을얻을수있습니다. 그럼으로이책에서소개하는 OBJ 파서는이런포맷도있다고이해하시고다른파서를구하시는것이좋습니다. 저는다음과같이 prebuilt header로이미파싱된데이타를이용합니다. http://heikobehrens.net/2009/08/27/obj2opengl/ obj2opengl 이라고 perl script 형태이며, 손쉽게헤더파일을생성해줍니다. (Material 까지파싱은하지못하기때문에, 이값은수작업으로설정해야합니다. ^^;) 주전자하나그리는데너무삼천포로빠져버렸네요ㅠㅠ 그만큼 Opengl es 는 opengl 보다뭔가를그리기가쉽지가않습니다. 자기만의 utility function 등을구현해서 Module 화해놓는것이좋습니다. 그럼다시 Teapot 주전자그리기로돌아가도록하겠습니다. [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 55

Mesh data 는정점 (teapot2verts) 과법선정보 (teapot2normals) 두가지만담겨있습니다.( 텍스쳐정보는없음 ) Models/teapot2.h /* ------------------------------------ (1) created with obj2opengl.pl source file :./teapot2.obj vertices : 3644 faces : 6320 normals : 5995 texture coords : 0 // include generated arrays #import! "./teapot2.h" // set input data to arrays glvertexpointer(3f, GL_FLOAT, 0, teapot2verts); glnormalpointer(gl_float, 0, teapot2normals); // draw data gldrawarrays(gl_triangles, 0, teapot2numverts); */ unsigned int teapot2numverts = 18960; // ------- (2) float teapot2verts [] = { // ------------------- (3) // f 2909//1 2921//1 2939//1 0.204248774988595f, 0.110553208769824f, -0.0353058869814484f, 0.206408240328974f, 0.105045437554406f, -0.0356647617094558f, 0.209210851457354f, 0.105045437554406f, 3.80670129563477e-05f,... ; float teapot2normals [] = { // ----------------- (4) // f 2909//1 2921//1 2939//1-0.926910681623383f, -0.368160873543573f, 0.0727609750079555f, -0.926910681623383f, -0.368160873543573f, 0.0727609750079555f, -0.926910681623383f, -0.368160873543573f, 0.0727609750079555f,... [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 56

; ---------------------------------------------------------------------------- (1) : obj2opengl.pl 스크립트를이용해서생성된 Obj 파일에대한정보입니다. GLES 1.0 에서사용하는 Example 을보여줍니다. (2) : 주전자의정점갯수를나타냅니다. teapot2numverts (18960) 이값은면의갯수가 6320 개임으로, 한개의면당 3 개의정점이있기때문에 18960 개가됩니다. (3) : 주전자정점의위치입니다. = teapot2verts 배열정보는 1차원배열이지만, 한줄당면한개를표현하는데필요한정점 3개를나타냅니다. 그럼으로, 면의갯수가 6320임으로전체줄수는 6320줄이며한줄당 3개의정점을포함하고있기때문에, 18960 요소가있습니다. 이는일차원배열로는 [18960] 이며이차원배열로는 [6320][3] 으로정의할수있습니다. 아래의정보는 (1) 번의 "obj2opengl.pl" 로생성한정점좌표입니다. // f 2909//1 2921//1 2939//1 0.204248774988595f, 0.110553208769824f, -0.0353058869814484f, 0.206408240328974f, 0.105045437554406f, -0.0356647617094558f, 0.209210851457354f, 0.105045437554406f, 3.80670129563477e-05f, 간단하게살펴보면, "// f v/vt/vn" 한개의면을구성하는데필요한 vertex/texture/normal 정보를나타냅니다. 예를들어 "//f 2909//1" 값을보면 teapot2.obj 에는 texture 정보는없다는것을알수있습니다. 2909 : 면을표현하는데필요한 vertices 정보는 2909번째줄에있는면정점 VOID : slash 사이에 0을채우는게아니라비워둡니다. 1 : 면을표현하는데필요한 normal 정보는 1번째줄에있는면의법선을나타냅니다. 다시 teapot2.h 를보면 teapot2verts 의경우, 첫째줄의정점 x, y, z 정보는 teapot2.obj 의 2909 번째줄의정점값을나타내야합니다. 그런데, teapot2.obj 의 2909 줄의 vertex 정보는 "v 1.368074 2.435437-0.227403" 으로그값이다릅니다. 이는 obj2opengl.pl 스크립트를만드신분의정점에대한정보를최적화? 시키면서변경된사항입니다. 대략적인알고리즘은각각의정점 x, y, z 중중간값 center(x, y, z) 정보를구하고, scale factor 값을 x, y, z 정점중각각의차중가장큰값을 1로나눠서정규화 scale factor를구합니다. 이 center 정보와 scalefac에의해서계산되었기때문에다른겁니다. teapot2.obj 나위의 obj2opengl.pl 패키지에포함된 banana.obj 는정점이많은모델이기때문에이해가어려울수있습니다. [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 57

obj2opengl.pl 패키지내에같이포함된 cube.obj와이를컨버팅한 cube.h를비교해서보시면좀더쉽게해석할수있을것같습니다. cubeverts[] = { // f 1//2 7//2 5//2-0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, ; cube.obj v 0.0 0.0 0.0 v 0.0 0.0 1.0 v 0.0 1.0 0.0 v 0.0 1.0 1.0 v 1.0 0.0 0.0 v 1.0 0.0 1.0 v 1.0 1.0 0.0 v 1.0 1.0 1.0 Cube의경우, 정점의 range는 0 ~ 1.0 임으로 center(x, y, z) 는 (0.5, 0.5, 0.5) 입니다. scale factor는 xmax - min = xdiff 가 1임으로 1.0 / 1.0이되서 1.0입니다. (x_token - center.x) / 1.0 = (0.0-0.5) / 1.0 = -0.5 (y_token - center.y) / 1.0 = (0.0-0.5) / 1.0 = -0.5 (z_token - center.z) / 1.0 = (0.0-0.5) / 1.0 = -0.5 첫번째줄의값이됩니다. 일곱번째줄의 face 값은 x(1.0), y(1.0), z(0.0) 임으로컨버팅값은 vx(0.5), vy(0.5), vz(-0.5) 가됩니다. 다섯번째줄의 face 값은 x(1.0), y(0.0), z(0.0) 임으로컨버팅값은 vx(0.5), vy(-0.5), vz(-0.5) 가됩니다. (4) : 주전자법선정보입니다. = teapot2normals 앞의 (3) 정점정보와같은방식으로계산할수있습니다. // f 2909//1 2921//1 2939//1-0.926910681623383f, -0.368160873543573f, 0.0727609750079555f, -0.926910681623383f, -0.368160873543573f, 0.0727609750079555f, -0.926910681623383f, -0.368160873543573f, 0.0727609750079555f, 엄하게 Vertex Position 을다룰려고했는데 Obj Parser 를다루게되어버렸네요 - ㅁ -; [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 58

책수준에서공부할때는이부분을넘겨도되지만, 차후상용프로젝트을진행할경우에는 3D 디자이너와협업을하게되는데, 이때 3D 디자이너는 3ds Max( 유료 ) 나 Blender( 무료 ) 툴을이용해서 Model 을그려서 Obj 포맷으로배포할수있습니다. 그럼으로 Obj spec(http://www.martinreddy.net/gfx/3d/obj.spec) 을읽어보시고직접 Uitility module 을개발하시는것도좋 은공부가될수있습니다. 다음에 Texture 장을다룰때는텍스쳐정보가같이있는 Obj 파일인 banana.obj 와 banana.jpg 을다뤄보도록하겠습니다. 2. Vertex Buffer Object 앞절에서모델을 header로변환하는방법에대해서설명하였습니다. 이번절은주전자정보를 stl의 vector 클래스에보관한후이를 VBO에저장하는방법에대해서다루겠습니다. iphone 3D Programming 예제중 ModelViewer.ObjViewer 프로젝트가있습니다. ISurface interface를상속받아서 ObjSurface라는모듈을구현하고있습니다. 앞절에서소개했었던 Example 4.22 Insanely Simple Obj 파일로부터데이타를추출하고, vertices 정보를저장하고 normals 값은계산해서저장하고있습니다. 저는이파서를사용하지않고다음과같은 ObjSurfaceFromData 모듈을만들어서사용합니다. Utils/ObjSurfaceFromData.cpp 이모듈은 Obj 파일로부터추출한 vertices, textures, normals 의정보를 stl 의 vector 클래스를이용해서저장합니다. 특이사항은 obj2opengl.pl 이 indices 색인정보를이용해서삼각형을그리는 gldrawelements() 방식이아닌, 정점으로삼각형을그리는 gldrawarrays() 방식임으로 indices 정보는저장하지않습니다. ObjSurfaceFromData(int numverts, float* vertices, float* normals); 정점과법선정보한묶음 (stride) 으로만들어서 stl 의 vector 클래스에저장합니다. vertices(x, y, z) 와 normal(x, y, z) 임으로 stride 는 float 형으로 6 개가배치됩니다. ObjSurfaceFromData(int numverts, float* vertices, float* normals, float* texcoords); 정점, 법선그리고텍스쳐정보를한묵음 (stride) 으로만들어서 stl 의 vector 클래스에저장합니다. vertices(x, y, z), normal(x, y, z), texcoords(s, t) 임으로 stride 는 float 형으로 8 개가배치됩니다. void GenerateVertices(vector<float>& vertices, unsigned char flags) const; [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 59

저장된정점 (x, y, z), 법선 (x, y, z), 텍스쳐 (s, t) 정보를포함하는 vector 클래스의포인터를반환합니다. Vertex Buffer Object로 Data를옮기기위해서는 CreateDrawable() 함수를이용합니다. VBO에대해서는 iphone 3D Programming 책의 "3장정점과터치점, 3-3 정점버퍼객체를사용하여성능향상시키기 " 장에서설명이되어있으며, 바른생활님이정리하신다음포스트도읽어보시면도움이될것같습니다. http://cafe.naver.com/gld3d/129 // Create the VBO for the vertices. ------------------------- (1) struct Drawable { GLuint VertexBuffer; GLuint IndexBuffer; int IndexCount; int VertexCount; int Flags; ; // Create the VBO for the vertices. ------------------------- (2) struct Drawables { Drawable teapot; // ; void RenderingEngine::Initialize(int width, int height) { // Create the teapot drawable ----------------------------- (3) int flags = VertexFlagsNormals;//VertexFlagsNormals VertexFlagsTexCoords; m_drawables.teapot = CreateDrawable(ObjSurfaceFromData(teapot2NumVerts, teapot2verts, teapot2normals), flags);... Drawable RenderingEngine::CreateDrawable(const ObjSurfaceFromData& surface, int flags) { // Create the VBO for the vertices. ------------------------- (4) vector<float> vertices; surface.generatevertices(vertices, flags); GLuint vertexbuffer; [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 60

glgenbuffers(1, &vertexbuffer); glbindbuffer(gl_array_buffer, vertexbuffer); glbufferdata(gl_array_buffer, vertices.size() * sizeof(vertices[0]), &vertices[0], GL_STATIC_DRAW); // Create a new VBO for the indices if needed. -------------- (5) int vertexcount = surface.getvertexcount(); int indexcount = surface.gettriangleindexcount(); GLuint indexbuffer; // 인덱스는사용하지않도록예외처리. ------------------------ (6) if (indexcount < 0) { indexbuffer = 0; indexcount = -1; else { // 이아래부분은이장에서는사용되지않는처리임. ------------ (7) vector<glushort> indices(indexcount); surface.generatetriangleindices(indices); glgenbuffers(1, &indexbuffer); glbindbuffer(gl_element_array_buffer, indexbuffer); glbufferdata(gl_element_array_buffer, indexcount * sizeof(glushort), &indices[0], GL_STATIC_DRAW); // Fill in the data into the drawable structure. ---------------- (8) Drawable drawable = {0; drawable.vertexbuffer = vertexbuffer; drawable.indexbuffer = indexbuffer; drawable.vertexcount = vertexcount; drawable.indexcount = indexcount; drawable.flags = flags; // GL_ARRAY_BUFFER 에 binding 했던것을해제하자. -------------- (9) glbindbuffer(gl_array_buffer, 0); [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 61

return drawable; --------------------------------------------------------------------------- (1) : Drawable 구조체입니다. VBO 로생성한 VertexBuffer ID, IndexBuffer ID, Index 갯수, Vertex 갯수, 그리고 Buffer 에저장된데이타가 Normal 이포함되어있는지 TexCoord 가포함되어있는지알려주는 Flag 값이있습니다. (2) : 여러개의 Drawable 포함하는 Drawables 구조체입니다. 현재는 teapot 하나이지만, 여러개의모델이있을경우여기에추가할예정입니다. (3) : teapot drawable를생성하는 CreateDrawable() 함수를호출합니다. 이함수는 Vertex Buffer ID, Index Buffer ID 등이포함된 Drawable 객체를반환해줍니다. 또한 ObjSurfaceFromData가 ISurface를상속받기때문에, 추후에 iphone 3D Programming에서이용하는 Parametric Surface 형으로만든기하구조와호환가능합니다. (Parametric Surface는매개변수방정식을이용해서만든 Geometry Sample( 구, 원뿔, 뫼비우스띠등 ) 입니다.) (4) : 정점정보를저장하는 VBO 을생성합니다. (5) : Vertex 의갯수를반환합니다. Index 의갯수는 Parametric Surface 에서이용되며, 여기서는 gldrawarrays() 로그리기때문에사용되지않습니다. (6) : IndexBuffer 와 IndexCount 를사용하지않는값으로설정합니다. (7) : VBO 의 Index Buffer 를등록하는부분입니다. gldrawelements() 로그릴때필요합니다. 주전자를그릴때는사용하지않습니다. 나중에 Parametric Surface 를이용해서기하구조를그릴때사용할예정입니다. (8) : Drawable 구조체에 VBO 로생성한 ID, Vertex count 등을저장합니다. [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 62

(9) : VBO 의 Binding 을해제합니다. 여기는초기화부분이지실제 Rendering 하는부분이아님으로, VBO 를사용하는시점에 Binding 해서사용합니다. 만약 Vertex Buffer Array(VBA) 와번갈아가면서모델을그릴경우에는 VBO 을 UnBind 해놔야충돌이발생하지않습니다. 3. Teapot 그리기 이제드디어주전자를그릴준비가다되었습니다. OpenGL 에서는 glutsolidteapot() 함수하나로해결이되지만, OpenGL ES 에서는많은준비가필요합니다. ^^; 주전자를그리는방법은앞장에서배운 Cubebox 그리기와유사하며, Vertex Shader 도 Cubebox 에서사용했던 SimpleLighting.vert 를재사용하겠습니다. 차이가나는부분은바탕화면인 Grid Axis 와 1 차모델인 Cubebox 는 VBA 형태로그렸지만, Teapot 은 VBO 로그릴것임으로이부분에대해서만정리하도록하겠습니다. void RenderingEngine::drawTeapot(float size, int texturemode) {... /* stride 설정 */ --------------------------------------------- (1) int stride = sizeof(vec3); if (drawable.flags & VertexFlagsNormals) { stride += sizeof(vec3); if (drawable.flags & VertexFlagsTexCoords) { stride += sizeof(vec2); const GLvoid* offset = (const GLvoid*) sizeof(vec3); /* Load teapot object as VBO */ -------------------------------- (2) glbindbuffer(gl_array_buffer, drawable.vertexbuffer); glvertexattribpointer(position, 3, GL_FLOAT, GL_FALSE, stride, 0); glvertexattribpointer(normal, 3, GL_FLOAT, GL_FALSE, stride, 0); [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 63

glvertexattribpointer(normal, 3, GL_FLOAT, GL_FALSE, stride, offset); /* Select A Texture Based On filter */ --------------------------- (3) glbindtexture(gl_texture_2d, 0); gluniform1i(m_cubebox.uniforms.texturemode, 0); /* Draw teapot */ -------------------------------- (4) //gldrawarrays(gl_triangles, 0, drawable.vertexcount); gllinewidth(1); gldrawarrays(gl_lines, 0, drawable.vertexcount); //gldrawarrays(gl_points, 0, drawable.vertexcount);... ------------------------------------------------------ (1) : stride 를설정합니다. size(vec3) 는 float x, y, z 임으로 3 * 4byte = 12 입니다. 주전자는 normal 값만있음으로 stride 는 24 입니다. offset 은 normal 값에대한간격입니다. vertices 위치다음임으로 sizeof(vec3) 으로설정합니다. (2) : 앞에서생성했던 Teapot VBO 객체를 Bind 합니다. VBA에서는 vertices와 normals의배열포인터를넘기지만, VBO는 stride 형태로 Vertex Buffer 의묶음의크기를정하고, 주소는 NULL(0) 을넣습니다. normal 값에대해서는 (1) 에서계산한 offset을설정해줍니다. glvertexattribpointer(position, 3, GL_FLOAT, GL_FALSE, stride, 0); glvertexattribpointer(normal, 3, GL_FLOAT, GL_FALSE, stride, 0); glvertexattribpointer(normal, 3, GL_FLOAT, GL_FALSE, stride, offset); (3) : 텍스쳐는사용하지않을것임으로 UnBind 시킵니다. (4) : Wireframe 형태로주전자를그립니다. Face 면으로그릴려면, GL_TRIANGLES 부분의주석을해제해줍니다. Point 점으로그릴려면, GL_POINTS 부분의주석을해제해줍니다. [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 64

4. Vertex Position 이제앞장에서적용했던 Vertex Position 을적용해보겠습니다. Shaders/SimpleLighting.vert // Vertex Position vec4 newpos = vec4(a_position); newpos.z = newpos.z + sin(2.0 * newpos.x); gl_position = u_projectionmatrix * u_modelviewmatrix* newpos; 변화가생기기는했지만, 바른생활님예제만큼크게느껴지지는않습니다. [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 65

가중치값을 2.0 에서 4.0 으로올려서할경우, newpos.z = newpos.z + sin(4.0 * newpos.x); Cubebox 에비해서확연히찌브러지는것을확인할수있습니다. 카메라방향을다음과같이변경해서바라볼경우, 좀더효과를잘볼수있는것같습니다. vec3 eye(0, 0, 10); // ----------- (1) vec3 target(0, 0, 0); // ------------ (2) vec3 up(0, 1, 0); // --------------- (3) m_camera_0 = mat4::lookat(eye, target, up); ------------------------------------------------- (1) : eye vector 로 camera 의 Z 축을 World Coordinate 의원점을바라보게설정한다. (2) : target(center) vector 로 camera 의중심을 World Coordinate 기준으로원점으로설정한다. (3) : up vector 로 camera 의 Y 축은 World Coordinate 의 X-Y 평면위에있게한다. [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 66

5. 결론 RenderingEngine::Render() 함수에서다음과같은순서로호출하면됩니다. // VBO 을 UnBind 해야 VBA 와충돌이없다. glbindbuffer(gl_array_buffer, 0); glbindbuffer(gl_element_array_buffer, 0); /* ------------------------------------------------------------------- */ // 배경화면 Shader 인 Simple shader 프로그램바인딩. drawgridandaxis(); /* ------------------------------------------------------------------- */ /* Use cubebox shader */ drawcubebox(1.0f); /* ------------------------------------------------------------------- */ /* Use cubebox shader */ drawteapot(6.0f); 이상으로 Wavefront Obj 파일을파싱하고, 이데이타를 VBO 에저장해서 Renderer 에서는 VBA 와 VBO 를섞어서 사용하는방법에대해서알아보았습니다. [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 67

또한 Shaders/SimpleLighting 쉐이더를재사용해서사용할수있음을확인할수있었습니다. 다음장에는 Pixel Lighting 를에정리하기앞서서 Parametric Surface 로 " 구 (Sphere), 원뿔 (Cone), 토러스 (Torus), 클레인병 (Klein bottle), 뮈비우스띠 (Mobius strip)" 모델을그려보도록하겠습니다. OGLES20 Template Application ported to mfc and android http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail?name=01_geometrytransform.v1.1.1.zip&can=2&q=#makechanges [GLES20] 06. Vertex Position 처리 - Obj 와 VBO (2) 68

[GLES20] 07. Parametric Surface 2011.11.16 22:19 Parametric Surface iphone 3D Programming 책의 4 장에소개된 Parametric Surface 는 Parametric representation( 매개변수방정식표현법 ) 을 이용해서기하구조를그리는방법이서술되어있습니다. 저는처음 iphone 3D Programming 4 장의매개변수방정식을봤을때, 이런수식법이있었나가물가물했었습니다. 구 (Sphere) 에대해서 x^2 + y^2 + z^2 = r^2 인것은기억하지만, cosine 과 sine 을써서표현하는방법은생소했었습니다. 그런데, 역시나고등학교정석책을찾아보니 - ㅁ -; 매개변수방정식으로된문제가있었습니다. ㅠㅠ 매개변수방정식에대해서는선형보간법 Lerp(Linear Interpolation) 장에서소개한직선과원을표현하는방법을읽으면, 혹도움이될지도모르겠네요. 매개변수방정식에대해서더궁금하신분은아래의싸이트의수식을참고하십시요. http://www.euclideanspace.com/maths/geometry/surfaces/parameterisation/index.htm http://tutorial.math.lamar.edu/classes/calciii/parametricsurfaces.aspx http://www.econym.demon.co.uk/isotut/parametric.htm 위의수식을 iphone 3D Programming 책의예제인 ParametricEquations.hpp 에실제적용해보는것도 좋은공부될것같습니다. 제가원본소스에서수정한사항은다음과같습니다. [GLES20] 07. Parametric Surface 69

1. Cylinder 관련 Parametric Surface 추가 2. Teapot, Banana, Cubebox Obj Surface 추가 나머지부분은책 ( 한글번역서도있음 ) 의 4 장내용과동일함으로따로설명을하지는않겠습니다. ^^; (http://ofps.oreilly.com/titles/9780596804824/chrealism.html) 1. Cylinder 관련 Parametric Surface 추가 아쉽게도 iphone 3D Programming 책에는실린더를그리는예제는없습니다. 이에실린더를매개변수방정식에맞춰서그려봤습니다. (http://tutorial.math.lamar.edu/classes/calciii/parametricsurfaces.aspx 참조 ) Utils/ParametricEquations.hpp class Cylinder : public ParametricSurface { public: Cylinder(float radius) : m_radius(radius) { ParametricInterval interval = { ivec2(20, 20), vec2(pi, TwoPi), vec2(20, 20) ; // ----------------- (1) SetInterval(interval); vec3 Eval!uate(const vec2& domain) const { float u = domain.x, v = domain.y; // ------------------------------------------------- (2) float x = u - Pi / 2; // 중앙에오도록조정하기위해서 Pi / 2 를빼주었다. // ----------------------- (3) float y = m_radius * sin(v); float z = m_radius * cos(v); return vec3(x, y, z); private: float m_radius; ; ----------------------------------------------------------------- 기존의매개변수방정식에맞춰서구현하였습니다. 실린더의수식은원뿔인 Cone 방정식과유사합니다. [GLES20] 07. Parametric Surface 70

즉원뿔모양에서위와아래둘다같은반지름을갖고있으면, 실린더입니다. (1) : 매개변수의간격을정합니다. 첫번째 ivec2(20, 20) 중앞에 20 은실린더를몇조각으로나눌지결정하며, 뒤에 20 은 19 등분한도형 ( 원 ) 을결정합니다. 만약뒤에 20 을 5 로한다면 4 각형이됨으로앞 - 뒤가열려있는 Cube box 가그려집니다. 두번째 vec2(pi, TwoPi) 중앞에 Pi 는실린더의길이를결정하며, 뒤에 TwoPi 는호의길이를결정합니다. 앞에 Pi 는 3.14159f 의길이를뜻하며, 2.0f 여도상관없습니다. 뒤에인자가 Pi 이면원형의반만그려집니다. 세번째 vec2(20, 20) 텍셀좌표를구할때사용되며가로세로조각수를결정합니다. (2) : 정의역값이며 Parametric Surface 에서정해집니다. (3) : 실린더의위치를중앙에서부터시작하기위해서전체길이 (Pi;3.14159f) 의반을빼주었습니다. 매개변수값이 "ParametricInterval interval = { ivec2(20, 20), vec2(pi, TwoPi), vec2(20, 20) ;" 일경우, 매개변수값이 "ParametricInterval interval = { ivec2(20, 20), vec2(pi, Pi), vec2(20, 20) ;" 일경우, [GLES20] 07. Parametric Surface 71

매개변수값이 "ParametricInterval interval = { ivec2(20, 5), vec2(pi, TwoPi), vec2(20, 20) ;" 일경우, 2. Teapot, Banana, Cubebox Obj Surface 추가 매개변수방정식은재미난공부는될수있겠지만, 한계가분명히있습니다. 아무래도수식을아무리적용해도한계가있는부분이분명히있으며, 디자이너가그린것보다예쁘지도않습니다. ㅠㅠ 거의대부분의실무에서는디자이너가 3ds Max나 Blender 등으로그려서추출해서준 3ds나 obj 파일을임포트해서사용하게됩니다. ( 물론 Obj등의파일을그대로이용하지않고이미지용량을줄이기위해서압축해서사용하기도합니다. 또한리소스를외부에서사용하지못하도록막기위해인코딩을해서사용하기도합니다. ^^;) 이전장에서주전자 Teapot Obj 파일을해석해서사용하는방법에대해서다뤘음으로앞장을참고하십시요. 앞장과의차이점은 RenderingEngine.CH02.ES2.cpp 이아닌, ApplicationEngine.CH02.cpp 에구현되어있습니다. Classes/ApplicationEngine.CH02.cpp [GLES20] 07. Parametric Surface 72

void ApplicationEngine::Initialize(int width, int height) { m_textureindex = 3; // LoadTexture(); vector<isurface*> surfaces(surfacecount); surfaces[0] = new Sphere(1.4f); surfaces[1] = new Cone(3, 1); surfaces[2] = new Torus(1.4f, 0.3f); surfaces[3] = new TrefoilKnot(1.8f); surfaces[4] = new KleinBottle(0.2f); surfaces[5] = new MobiusStrip(1); surfaces[6] = new Quad(1, 1); surfaces[7] = new Cylinder(.5f); surfaces[8] = new ObjSurfaceFromData(CH02::teapot2NumVerts, CH02::teapot2Verts, CH02::teapot2Normals); surfaces[9] = new ObjSurfaceFromData(CH02::bananaNumVerts, CH02::bananaVerts, CH02::bananaNormals); surfaces[10] = new ObjSurfaceFromData(CH02::cubeNumVerts, CH02::cubeVerts, CH02::cubeNormals); m_renderingengine->initialize(surfaces); for (int i = 0; i < SurfaceCount; i++) delete surfaces[i]; ResizeWindow(width, height); ----------------------------------------------------------------------- 0~7 번까지는 Parametric Surface 이며, 8 ~ 10 번까지는 Obj Surface 입니다. Classes/RenderingEngine.CH02.ES2.cpp void RenderingEngine::Render(const vector<visual>& visuals) {... ProgramHandles handler = m_pixellight; handler = m_vertexlight; // -------------------------------------- (1) [GLES20] 07. Parametric Surface 73

// Set the light Mode : 0(Diffuse Mat Color), 1(DIFFUSE), 2(AMBIENT_DIFFUSE), 3(AMBIENT_DIFFUSE_SPECULAR) gluniform1i(handler.uniforms.lightmode, 0); // ------------------------- (2) ------------------------------------------------------------------- (1) : VertexLighting 으로실행합니다. Diffuse Material 만으로볼것임으로 Pixel Light 이든 Vertex Light 이든크게상관은없습니다. (2) : 확산광 Diffuse Material 재질값만으로그립니다. 이는 Light Off 상태입니다.... Teapot Obj 결과 Banana Obj 결과 [GLES20] 07. Parametric Surface 74

Cubebox Obj 결과 3. 결론 iphone 3D Programming 책의 4 장에서예제로제공된 Parametric Surface 를 mfc 와 android 에변경해서작업해보았습니다. 조명을적용하기에앞서서조명을투영할다양한기하구조를그려보았습니다. 다음장에서는 Ambient Light( 주변광 ), Diffuse Light( 확산광 ), Specular Light( 경면광 ) 등에대해서다루도록하겠습니다. OGLES20 Template Application ported to mfc and android [GLES20] 07. Parametric Surface 75

http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail?name=02_parametricsurface.v.1.2.0.zip&can=2&q=#makechanges [GLES20] 07. Parametric Surface 76

[GLES20] 08. 조명적용하기 - 확산광, Diffuse Vertex Light 2011.11.20 17:04 조명적용하기 - Vertex Light 여기서다루는조명적용하기는 iphone 3D Programming 책의 "4-5 장조명적용하기 " 의 Vertex Light 부분을다룹니다. 상세한설명은책을보시면이해가잘될겁니다. 또한바른생활님의정리해놓으신 GLSL 부분강좌와비교해서보시면 GLES20 과의변경사항을확인하실수있을것같습니다. 1. Diffuse Light : 확산광적용 확산광은실시간조명을나타내는가장일반적인형태라고하며, 람베르트반사 (Lambertian Reflection) 를이용합니다. 책에도이미다잘나와있고, 바른생활님도정리한내용을다시정리하는것은중복작업이될것같아서 구체적인기술은생략하도록하겠습니다. ^^; ( 바른생활님 GLSL 17 강 : Diffuse 조명의구현 (http://cafe.naver.com/gld3d/435)) [GLES20] 08. 조명적용하기 - 확산광, Diffuse Vertex Light 77

확산율 (Diffuse Factor) 은위의그림에서보이는 df 값입니다. df = max(0, N dot L) 정점을통해서구성된면 (face) 의법선벡터 (N) 와광원의위치벡터 (L) 간에사잇각이 df 입니다. (dot 은사잇각을구하는내적 ) (1) 법선과광원의위치가직각 (90) 을이루면 cos(90) 이됨으로 df 는 0.0 이됩니다. df = N * L *cos(90) = 0.0 (2) 법선과광원의위치가수평 (0) 을이루면 cos(0) 이됨으로 df 는 1.0 이됩니다. df = N * L *cos(0) = 1.0 (N 과 L 은 normalize 되어있기때문에단위값으로볼수있다.) (3) 법선과광원의위치가 60 도를이루면 cos(60) 이됨으로 df 는 0.5 가됩니다. (4) 법선과광원의위치가 90 도보다크다면, 뒷면에있다고볼수있음으로 df 는 0.0 으로합니다. 이에대한 Diffuse Light Shader 구현코드는다음과같습니다. const lowp float INTENSITY = 1.0; [GLES20] 08. 조명적용하기 - 확산광, Diffuse Vertex Light 78

// diffuse light vec3 calclightdiffuse() { vec3 N = normalize(eyespacenormal); // ------------------- (5) vec3 L = normalize(directionlight - vec3(positionlight)); // --- (6) float df = max(0.0, dot(n, L)); // ------------------- (7) return INTENSITY * a_diffusematerial * df; // ------------------- (8) ---------------------------------------------------------- (5) N : eyespacenormal 은관측자관점에서의법선벡터입니다. 이값은면당법선벡터값 a_normal 과본App 에서이미설정되어있는 NormalMatrix값을곱한값입니다. 본App에서보면 NormalMatrix 값은 modelview matrix 값이그대로적용되어있습니다. 이는이예제에서의좌표계가직교정규화좌표계이이기때문에 modelview에대한역전치가따로필요하지않습니다. (6) L : 광원의위치입니다. Directional Light 와 Positional Light 의개념이들어가는부분입니다. Directional Light : 방향성조명으로실세계를예로들어서태양이광원이라고할경우, 모델위치로부터광원이멀리떨어져있기때문에, 모델의정점값인 Vertex Position은원점 (0, 0, 0) 으로볼수있습니다. 즉광원의빛은모델전체의 Vertex에균등하게뿌려주게됩니다. 이런상태가될려면, "- vec3(positionlight)" 부분이 0 벡터가되어야합니다. 책이나기타다른소스를보면, 광원의위치값 L를 directionlight 값만사용하고 vertex postion 값을사용하지않는것은이때문입니다. directionlight는 main() 함수에서구현하였으며, 본App으로부터전달받은 u_lightposition에서값을받아옵니다. 기본값은 (10.0, 10.0, 10.0, 0.0) 임으로오른쪽-위-전면에있게됩니다. ( 참고로바른생활님예제는광원이 (1, 1, 1, 0) 인데, 제예제는 (10, 10, 10, 0) 인이유는바른생활님예제는 1.0 단위의직교좌표계프로젝션를사용하고있으며, 저는 Perspective Projection에카메라를사용해서 10만큼뒤로땡겼기때문에광원도같이 10만큼보정해주었습니다. <-- 혹이부분이틀렸다면댓글로지적해주세요 ^^;) Positional Light : 위치기준조명으로모든 Vertices 정점들에균등하게뿌려주는것이아니라, [GLES20] 08. 조명적용하기 - 확산광, Diffuse Vertex Light 79

방향과위치기준으로해당하는부분만뿌려줍니다. ( 해당하는기준은 "directionlight - vec3(positionlight)" 와같이벡터의차로구하고있음으로 directionlight 벡터와 postionlight 벡터 ( 정점 ) 을이어주는선이되겠습니다.) 더상세한내용은바른생활님의정리하신, "21 강 Positional Light 에대한이해 " 장을읽어보시면도움이될겁니다. ^^; (5), (6) 번에 normalize() 함수를처리하고있는데중요한부분입니다. 바른생활님강좌중 "14강 Fragment shader를이용한 pixel별연산 " 부분에설명이나와있으며, 제블로그에서도 " 선형보간법 Lerp" 의단위사원수를 Normalize 하는부분에왜? 정규화가필요한지설명이있습니다. 참고하십시요. (7) Diffuse Factor 확산율을구합니다. (8) INTENSITY : 빛의강도를뜻합니다. 더밝게또는약하게할수있습니다. 여기서는 1.0 으로설정해서적용하지는않고있습니다. Diffuse Light main() 함수 Shaders/VertexLighting.vert void main(void) { eyespacenormal = u_normalmatrix * a_normal; // Calculate Color vec3 color = a_diffusematerial; // Calculate Light // Directional Light // ----------------------------- (1) positionlight = vec4(0, 0, 0, 0); directionlight = vec3(u_lightposition); // Positional Light // ----------------------------- (2) //positionlight = u_modelviewmatrix * a_position; //directionlight = vec3(u_lightposition); [GLES20] 08. 조명적용하기 - 확산광, Diffuse Vertex Light 80

//directionlight *= u_interpolation_z; if (u_lightmode == DIFFUSE_LIGHT) // ----------------- (3) color = calclightdiffuse(); else if (u_lightmode == AMBIENT_DIFFUSE_LIGHT) color = calclightambdif(); else if (u_lightmode == AMBIENT_DIFFUSE_SPECULAR_LIGHT) color = calclightambdifspec(); v_destinationcolor = vec4(color, 1.0) * u_color; // Vertex Position gl_position = u_projectionmatrix * u_modelviewmatrix * a_position; ------------------------------------------------------------------- (1) : Directional Light 를설정해줍니다. positionallight 는 0 벡터여야하며, Positional Light (2) 번부분은주석으로막아주셔야합니다. (2) : Positional Light 를설정해줍니다. positionallight 의설정부분입니다. 적용을위해서는주석을풀어주면됩니다. (3) : 확산광계산을실행시킵니다. u_lightmode 정보는본 App 으로부터얻어옵니다. < 좌측은 Directional Light 상태이며, 우측은 Positional Light 상태의결과 > [GLES20] 08. 조명적용하기 - 확산광, Diffuse Vertex Light 81

2. 본 App 에서 Diffuse Light 실행하기 (Vertex Lighting) // Classes/RenderingEngine.CH02.ES2.cpp void RenderingEngine::Initialize(int width, int height) {... // Set up some default material parameters. vec4 diffuselight = vec4(1, 1, 1, 1); // 백색광 vec4 ambientlight = vec4(0.05f, 0.05f, 0.05f, 1.0f); // 흑색광 vec3 diffusematerial = vec3(1, 1, 1); // 백색재질 vec3 ambientmaterial = vec3(1.0f, 0.f, 0.f); // 적색재질 // vec3 ambientmaterial = vec3(0.0f, 0.f, 0.f); // 주변광끄기 vec3 specularmaterial = vec3(1, 1, 1); // 백색재질... void RenderingEngine::Render(const vector<visual>& visuals) {... ProgramHandles handler = m_pixellight; handler = m_vertexlight; // -------------------------------------- (1) GLfloat weight = cos(interpolation_z) * 1.5f; // -------------------------- (2) gluniform1f(handler.uniforms.interpolation_z, (GLfloat) weight); LOG_PRINT("interpolation_z:%f, cos(%f)", interpolation_z, weight); interpolation_z += (2*Pi) * 0.0008f; // Set the light Mode : 0(Diffuse Mat Color), 1(DIFFUSE), 2(AMBIENT_DIFFUSE), 3(AMBIENT_DIFFUSE_SPECULAR) gluniform1i(handler.uniforms.lightmode, 1); // -------------------------- (3) [GLES20] 08. 조명적용하기 - 확산광, Diffuse Vertex Light 82

... ------------------------------------------------------------------- (1) : VertexLighting 으로실행합니다. pixellight 보다성능이떨어지지만 Diffuse 만으로볼경우에는크게차이가나지는않습니다. ^^; (2) : Postional Light 시에광원의위치를이동시키기위해서사용되는가중치값입니다. (3) : 확산광 Diffuse Light 으로실행합니다. <Directional Light> ( 용량 1.3MB) <Positional Light> [GLES20] 08. 조명적용하기 - 확산광, Diffuse Vertex Light 83

( 용량 1.6MB) 3. 결론 mfc 와 android app 으로 Vertex Light 확산광을적용해보았습니다. 광원에위치는아래와같으며 ^^; Directional Light 적용시에는아래와같습니다. Positional Light 적용시에는반대방향으로이동해서어둡게나오게됩니다 ^^; 다음시간에는 Ambient Light 에대해서다뤄보겠습니다. ^^; [GLES20] 08. 조명적용하기 - 확산광, Diffuse Vertex Light 84

OGLES20 Template Application ported to mfc and android http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail?name=02_parametricsurface.v.1.2.0.zip&can=2&q=#makechanges [GLES20] 08. 조명적용하기 - 확산광, Diffuse Vertex Light 85

[GLES20] 09. 조명적용하기 - 주변광, Ambient Vertex Light 2011.11.20 17:09 조명적용하기 - Ambient Light 1. Ambient Light : 주변광적용 주변광은깊이있게들어가면실세계의주변 ( 환경?) 을그대로표현해야하는조명표현임으로무지어렸다고합니다. - ㅁ -; 하지만, 일반적으로 OpenGL 에서는 ' 균일한단색 ' 을나타낸다고하니쉽게처리할수있습니다. ^^; 빛의강도, 광원의위치 (Light Source Position), 표면의방향에의해서그색상이변하지않기때문에, 단지대상물체의주변색상을나타낸다고말할수있습니다. 그런데, 아무래도주변광만사용할경우, 주변색상과물체의색상이구분되지않기때문에, 그림자효과를넣지않는한,.. 물체와주변이구분되지않게됩니다. 주변광은확산광과경면광등과결합해서조명효과를더욱현실감있게만드는데사용됩니다. 만약주변광만사용해서그리면다음과같이단색으로만나오게됩니다. [GLES20] 09. 조명적용하기 - 주변광, Ambient Vertex Light 86

( 어두운적색원이그려집니다. 않보이면뚫어지게보십시요 ^^;) 위에그림에서보듯이주변광만을사용하는경우는거의없으며, 다음과같이확산광 (Diffuse), 경면광 (Ambient) 등과 결합해서사용합니다. 주변광에대해서 iphone 3D Programming 책에서는크게다루지는않습니다. 이부분은바른생활님강좌중 "18 강 Ambient 조명의구현 " 을참고하시면도움이될것같습니다. 수식은다음과같습니다. 제예제를기준으로보면주변광은바탕화면의색인검정색이주변색이됩니다. 하지만, 검정색을유지하면, 주변광이잘안보임으로적색으로변경해서시험해보도록하겠습니다. Shaders/VertexLighting.vert // ambient + diffuse light vec3 calclightambdif() { vec3 N = normalize(eyespacenormal); vec3 L = normalize(directionlight - vec3(positionlight)); float df = max(0.0, dot(n, L)); // (1) vec3 globalambient = u_ambientmaterial * vec3(0.05); // * gl_lightsource[0].ambient vec3 ambient = u_ambientmaterial * vec3(u_ambientlight); // ------ (2) [GLES20] 09. 조명적용하기 - 주변광, Ambient Vertex Light 87

return (globalambient + ambient) + (INTENSITY * a_diffusematerial * df); // ----- (3) ----------------------------------------------------------------------- (1) : 전역주변광 (Ga) 색을구합니다. Ma * Ga globalambient는 Ambient Material * gl_lightsource[0].ambient와결합해서구합니다. 하지만, GLES20에서는멀티광원처리시사용되는 gl_lightsource 를지원하지않습니다. 따로 uniform 형태로만들어서사용해야하며여기서는 u_ambientlight를사용해도되고, 바탕화면색이검정색임으로 vec3(0.05) 로처리하였습니다. (2) : 주변광 (La) 색을구합니다. Ma * La ambient 는 Ambient Material 과 Ambient Light 를곱해주면됩니다. (3) : 전역주변광과주변광을더해주면됩니다. 주변광은확산광과더해지기때문에, 그값이 0 벡터여도확산광값에영향을미치지않습니다. 즉, 주변광을꺼도확산광으로표현되게됩니다. ( 바탕이검정색이기때문에주변광을검정색이아닌적색으로변경하였습니다.) [GLES20] 09. 조명적용하기 - 주변광, Ambient Vertex Light 88

2. 본 App 에서 Ambient Light 실행하기 (Vertex Lighting) Classes/RenderingEngine.CH02.ES2.cpp void RenderingEngine::Initialize(int width, int height) {... // Set up some default material parameters. vec4 diffuselight = vec4(1, 1, 1, 1); // 백색광 vec4 ambientlight = vec4(0.05f, 0.05f, 0.05f, 1.0f); // 흑색광 vec3 diffusematerial = vec3(1, 1, 1); // 백색재질 vec3 ambientmaterial = vec3(1.0f, 0.f, 0.f); // 적색재질 // vec3 ambientmaterial = vec3(0.0f, 0.f, 0.f); // 주변광끄기 vec3 specularmaterial = vec3(1, 1, 1); // 백색재질... void RenderingEngine::Render(const vector<visual>& visuals) {... ProgramHandles handler = m_pixellight; handler = m_vertexlight; // -------------------------------------- (1) GLfloat weight = cos(interpolation_z) * 1.5f; // -------------------------- (2) gluniform1f(handler.uniforms.interpolation_z, (GLfloat) weight); LOG_PRINT("interpolation_z:%f, cos(%f)", interpolation_z, weight); interpolation_z += (2*Pi) * 0.0008f; // Set the light Mode : 0(Diffuse Mat Color), 1(DIFFUSE), 2(AMBIENT_DIFFUSE), 3(AMBIENT_DIFFUSE_SPECULAR) gluniform1i(handler.uniforms.lightmode, 2); // -------------------------- (3)... [GLES20] 09. 조명적용하기 - 주변광, Ambient Vertex Light 89

------------------------------------------------------------------- (1) : VertexLighting 으로실행합니다. pixellight 보다성능이떨어지지만 Diffuse 만으로볼경우에는크게차이가나지는않습니다. ^^; (2) : Postional Light 시에광원의위치를이동시키기위해서사용되는가중치값입니다. (3) : 주변광과확산광결합으로실행합니다. 원래주변광은검정색이지만, 물체와바탕화면을구분하기위해서적색재질을사용하였습니다. vec3 ambientmaterial = vec3(1.0f, 0.f, 0.f); 계산수식을적용할때주의할점은검정색이 (0, 0, 0) 이라고해서 0 으로만전부곱하면, 곱셈법칙에의해서대상값도 0 이되어버립니다. 그럼으로, 주변바탕이검정색이더라도 0 에가까운색 (0.05?) 으로설정해야합니다. ^^; 만약주변광을끄고싶다면, AmbientMaterial 재질값을 (0, 0, 0) 으로설정하면됩니다. <Positional Light> ( 용량 1.0MB) [GLES20] 09. 조명적용하기 - 주변광, Ambient Vertex Light 90

3. 결론 mfc 와 android app 으로 Vertex Light 주변광확산광합성을적용해보았습니다. 광원에위치는앞장과같으며, Directional Light 는생략하고 Positional Light 만적용하였습니다. < 좌측은 Diffuse Only 이며, 우측은 Ambient + Diffuse 상태 > 주변광의재질을바탕색인검정색으로하면 Diffuse 로했을때와크게차이가느껴지지는않습니다. 다음과같이주변광재질을적색으로할경우에는어두운적색테두리가만들어지게됩니다. < 좌측은 Diffuse Only 이며, 우측은 Ambient + Diffuse 상태 > 다음시간에는 Specular Light 에대해서다뤄보겠습니다. ^^; [GLES20] 09. 조명적용하기 - 주변광, Ambient Vertex Light 91

OGLES20 Template Application ported to mfc and android http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail?name=02_parametricsurface.v.1.2.0.zip&can=2&q=#makechanges [GLES20] 09. 조명적용하기 - 주변광, Ambient Vertex Light 92

[GLES20] 10. 조명적용하기 - 경면광, Specular Vertex Light 2011.11.21 23:22 조명적용하기 - Specular Light 1. Specular Light : 경면광적용 앞에서다뤘던확산광과주변광가는달리 Specular 경면광의경우, 벡터의사칙연산, 정사영 (Projetion Vector), 반사벡터 (Reflection Vector), 반벡터 (Half Vector) 에대한선행학습이필요합니다. 다음의블로그를참고하십시요. 벡터의사칙연산과정사영은제블로그의 " 변환으로가는길 #1 : 벡터와행렬 " 에설명이되어있습니다. 반사벡터의경우에는바른생활님의 " 쉐이더로구현하는 specular 조명 " 과 감자님의 " 반사벡터 " 블로그를참고하시면도움이될것같습니다. Specular Light 경면광에대한자료를찾아보면무지많기때문에, 이론에대한원류보다는해석에좀더집중하겠습니다. ( 바른생활님의 "GLSL 19 장쉐이더로구현하는 specular 조명 " 를먼저보시면더좋을것같습니다.) 일반적으로 Specular 을설명할때 Phong Model 과 Blinn Model 두가지를많이소개합니다. [GLES20] 10. 조명적용하기 - 경면광, Specular Vertex Light 93

1.1 퐁모델 specular 수식은다음과같으며, R 은반사벡터를의미합니다. 관측자의시점 (Eye), 조명 (L) 과법선벡터 (N) 에대해서는알고있으며, 이를기준으로반사벡터 R 을유도할수있습니다. 반사벡터 R 을바른생활님이그려놓으신그림을기준으로해석하면다음과같습니다. 반산벡터 R 을구하는데유의할점은 "?" 부분입니다. -N*Dot(L, N) 이유도되기위해서는 다음과같은정사영 ( 투영벡터 ) 을이해하고있어야합니다. [GLES20] 10. 조명적용하기 - 경면광, Specular Vertex Light 94

조명을 -L 벡터로만든후법선벡터 N 에투영할경우, 투영벡터 n 을구할수있습니다. ( 아래의수식이이해가가지않는다면, " 변환으로가는길 #1 : 벡터와행렬 " 의 7 페이지를다시한번살펴보십시요 ^^;) 위와같은정사영식에서 A 와 B 벡터를 -L 과 N 벡터로교체해서보면 -Dot(L, N)*N 을구할수있습니다. ( 분모였던, Dot(N, N) 의경우법선벡터의크기는 1 이기때문에 1*1 이되어서생략되게됩니다.) 분모를 Dot(N, N) 을만들어주어야함으로내적부분의순서는조명 L 이앞에있고법선 N 은뒤에있어야합니다. 이제반사벡터 R 을유도하기위해서투영벡터 n 에 2 를곱하고조명 L 벡터와합해주면 ^^; 이를통해서 Specular 의 Phong 모델인다음의수식에적용할수있습니다. ( 경면지수 Shininess 는바른생활님강좌 "GLSL 19 장쉐이더로구현하는 specular 조명 " 을참고하십시요 ^^;) 참고로반사벡터 R 은 OpenGL ES 20 의 shader 함수에서지원을하고있으며 다음과같이 "vec3 R = reflect(-l, N);" 함수를이용해도됩니다. ^^; 1.2 Blinn 모델 블린모델은퐁모델에비해서수식이복잡하지않습니다. 그런데효과는퐁모델만큼나기때문에거의대부분블린모델을이용 [GLES20] 10. 조명적용하기 - 경면광, Specular Vertex Light 95

합니다. 사람에게복잡한수식인만큼컴퓨터에게도똑같이연산프로세스를높이기때문에성능을하락시키게됩니다. (iphone 3D Programming, OpenGL ES 2.0 Programming Guide 책에서도퐁모델이아닌블린모델로 Specular 를설명하고있습니다. <OpenGL ES 2.0 Programming Guide 책에서의블린모델 Specular> < 바른생활님의그린블린모델 Specular> 블린모델이입력값중조명과관측자의시점으로풀이합니다. 앞의퐁모델에서는반사벡터 R 을구하는것이핵심이라면, 블린모델에서는반각벡터 (Half Vector) 를구하는것이핵심입니다. 반각벡터는다음과같이조명의위치와관측자의시점의벡터합으로구할수있습니다. 아주간단하죠 ^^; 제예제는 iphone 3D Programming 과 OpenGL ES 2.0 Programming Guide 를참고하였음으로, [GLES20] 10. 조명적용하기 - 경면광, Specular Vertex Light 96

블린모델로구현되어있습니다. Shaders/VertexLighting.vert // ambient + diffuse + specular light vec3 calclightambdifspec() { vec3 N = normalize(eyespacenormal); vec3 L = normalize(directionlight - vec3(positionlight)); // ---------- (1) //vec3 L = -normalize(vec3(positionlight) - directionlight); vec3 E = vec3(0, 0, 1); // -------------------------------------- (2) vec3 H = normalize(l + E); // ------------------------------------ (3) float df = max(0.0, dot(n, L)); float sf = max(0.0, dot(n, H)); // --------------------------------- (4) sf = pow(sf, u_shininess); // ----------------------------------- (5) vec3 globalambient = u_ambientmaterial * vec3(0.05); // * gl_lightsource[0].ambient vec3 ambient = u_ambientmaterial * vec3(u_ambientlight); // ------------------------------------------------------------- (6) return (globalambient + ambient) + (INTENSITY * a_diffusematerial * df) + (u_specularmaterial * sf); ----------------------------------------------------------------------- (1) : Positional Light 구하기수식를보면, normalize(directionlight - vec(positionlight)); 인데 directionlight는광원의위치이고, positionlight는광원이조사되는정점입니다. 이를벡터로보면, world space를기준으로벡터의차로본다면, (D - P) 라하면, P --> D로연결하는벡터가생성됩니다. 즉위의그림에서보면 L 벡터는정점을향해서조사되는데이와반대방향인 -L 벡터가구해집니다. (2) : 관측자의시점 ( 시선 ) 또는관찰자여기서는 vec(0, 0, 1) 로했는데무한히멀리떨어져있는관측자 (infinite viewer) 입니다. 만약 E값을사용할경우에는근접관측자 (local viewer) 라합니다. 왠지앞장에서 Positional Light를구하는방법을이용해서하면근접관측자가가능할것같은데, 근접관측자에대한예제는한번구해봐야할것같네요. [GLES20] 10. 조명적용하기 - 경면광, Specular Vertex Light 97

(3) : 반각벡터구하기 OpenGL ES 20 에서는 OpenGL 과달리반각벡터 H 를직접구해줘야합니다. 조명과관찰자간의벡터의합을통해서쉽게구할수있습니다. ^^; (4) : 경면율 (Specular Factor) 구하기 확산율 df 는법선 (N) 과조명 (L) 간의사잇각을이용해서구하지만, 경면율 sf 는법선 (N) 과반각벡터 (H) 간의사잇각을이용해서구합니다. (5) : 경면지수구하기경면지수는상수값이며, 본App에서 uniform을이용해서저장하였습니다. 경면지수 Shininess 값이크면 Specular 가비춰지는백색광점의반경은작아지지만색은환하게보입니다. 값이작으면백색광점의반경이넓게퍼집니다. 제예제에서는 128을사용하였습니다. (6) : 주변광, 확산광, 경면광반영 2. 본 App 에서 Specular Light 실행하기 (Vertex Lighting) Classes/RenderingEngine.CH02.ES2.cpp void RenderingEngine::Initialize(int width, int height) {... // Set up some default material parameters. vec4 diffuselight = vec4(1, 1, 1, 1); // 백색광 vec4 ambientlight = vec4(0.05f, 0.05f, 0.05f, 1.0f); // 흑색광 vec3 diffusematerial = vec3(1, 1, 1); // 백색재질 vec3 ambientmaterial = vec3(1.0f, 0.f, 0.f); // 적색재질 // vec3 ambientmaterial = vec3(0.0f, 0.f, 0.f); // 주변광끄기 vec3 specularmaterial = vec3(1, 1, 1); // 백색재질 [GLES20] 10. 조명적용하기 - 경면광, Specular Vertex Light 98

gluniform4fv(m_vertexlight.uniforms.diffuselight, 1, diffuselight.pointer()); gluniform4fv(m_vertexlight.uniforms.ambientlight, 1, ambientlight.pointer()); gluniform3fv(m_vertexlight.uniforms.ambientmaterial, 1, ambientmaterial.pointer()); gluniform3fv(m_vertexlight.uniforms.specularmaterial, 1, specularmaterial.pointer()); gluniform1f(m_vertexlight.uniforms.shininess, 128);... void RenderingEngine::Render(const vector<visual>& visuals) {... ProgramHandles handler = m_pixellight; handler = m_vertexlight; // -------------------------------------- (1) GLfloat weight = cos(interpolation_z) * 1.5f; // -------------------------- (2) gluniform1f(handler.uniforms.interpolation_z, (GLfloat) weight); LOG_PRINT("interpolation_z:%f, cos(%f)", interpolation_z, weight); interpolation_z += (2*Pi) * 0.0008f; // Set the light Mode : 0(Diffuse Mat Color), 1(DIFFUSE), 2,(AMBIENT_DIFFUSE), 3(AMBIENT_DIFFUSE_SPECULAR) gluniform1i(handler.uniforms.lightmode, 3); // -------------------------- (3)... ------------------------------------------------------------------- (1) : VertexLighting 으로실행합니다. pixellight 보다성능이떨어져서나옵니다. ^^; 나중에 Pixel Light(Fragment Light) 를다룰때비교를해보겠습니다. (2) : Postional Light 시에광원의위치를이동시키기위해서사용되는가중치값입니다. [GLES20] 10. 조명적용하기 - 경면광, Specular Vertex Light 99

(3) : 주변광, 확산광, 경면광결합으로실행합니다. 원래주변광은검정색이지만, 물체와바탕화면을구분하기위해서적색재질을사용하였습니다. 경면광은백색으로처리되어있습니다. vec3 ambientmaterial = vec3(1.0f, 0.f, 0.f); vec3 specularmaterial = vec3(1, 1, 1); // 백색재질 만약주변광을끄고싶다면, AmbientMaterial 재질값을 (0, 0, 0) 으로설정하면됩니다. <Specular Light> 3. 결론 mfc 와 android app 으로 Vertex Light 주변광확산광경면광합성을적용해보았습니다. Vertex Light 로경면광을표현할경우, 바른생활님예제인 "GLSL 19 장쉐이더로구현하는 specular 조명 " 과마찬가지로 표현이뭔가이상하게보입니다. Pixel Light 로표현할경우다음과같이정상적이화면이나옵니다. [GLES20] 10. 조명적용하기 - 경면광, Specular Vertex Light 100

다음장에는 Pixel Light 로표현해서살펴보도록하겠습니다. OGLES20 Template Application ported to mfc and android http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail?name=02_parametricsurface.v.1.2.0.zip&can=2&q=#makechanges [GLES20] 10. 조명적용하기 - 경면광, Specular Vertex Light 101

[GLES20] 11. 조명적용하기 - Pixel Light 2011.11.26 13:23 조명적용하기 - Pixel Light 1. Pixel Light : 주변광, 확산광, 경면광적용 Pixel Light 는픽셀단위조명 (Per Pixel Light) 을의미합니다. 또한 Fragment Shader 에서처리됨으로 Fragment Light 라고도 불립니다. 일반적으로 Fragment Light 보다는 Pixel Light 라고더많이불리는것같네요. (Direct X 의경우, Fragment Shader 를 Pixel Shader 라고부릅니다.) 픽셀단위조명은앞장에서배웠던정점단위조명 (Vertex Shader 에서처리하는 Vertex Light) 을그대로 Pixel Shader 에서구현 하면됩니다. 이때 Vertex( 정점 ) 처리와관련된부분만 Vertex Shader 에서 varying 형변수로선언해서 Fragment Shader 로넘겨주면됩니다. Specular Light 를기준으로보면다음과같습니다. Shaders/PixelLighting.vert [GLES20] 11. 조명적용하기 - Pixel Light 102

// Varying // --------------------------------------- (1) varying vec3 v_eyespacenormal;//eyespacenormal varying vec3 v_lightposition;//eyespacenormal varying vec3 v_diffuse;//diffuse; void main(void) { // ---------------------------------------------------- (2) v_eyespacenormal = u_normalmatrix * a_normal; v_diffuse = a_diffusematerial; v_texturecoordout = a_texturecoordin; // -------------------------------------- (3) // Directional Light vec4 positionlight = vec4(0, 0, 0, 0); vec3 directionlight = vec3(u_lightposition); // Positional Light positionlight = u_modelviewmatrix * a_position; directionlight = vec3(u_lightposition); directionlight *= u_interpolation_z; // Varying v_lightposition = directionlight - vec3(positionlight); // Vertex Position gl_position = u_projectionmatrix * u_modelviewmatrix * a_position; ----------------------------------------------------------------------- (1) : varying 형변수선언 Vertex Shader에서정점관련된정보를이용해서계산한후, Fragment Shader로전달할예정인변수입니다. v_eyespacenormal, v_lightposition, v_diffuse 등이있습니다. 색상정보중 diffuse color를빼고, ambient, diffuse, specular material 과 color는 Fragment Shader에서 uiform 형으로본App으로부터전달받았습니다. (2) : 법선벡터, Vertex Color 정점정보와관련되어서계산되는부분입니다. [GLES20] 11. 조명적용하기 - Pixel Light 103

(3) : 방향성또는위치성조명벡터 Light Vector를계산합니다. 이정보를이용해서 Vertex Shader에서 Half Vector를생성한후 varying 형으로 Fragment Shader로전달해도됩니다. 제예제에서는 Half Vector등은 Fragment Shader에서처리하였습니다. Shaders/PixelLighting.frag // ambient + diffuse + specular light vec3 calclightambdifspec() { vec3 N = normalize(v_eyespacenormal); // ---------- (1) vec3 L = normalize(v_lightposition); // -------------- (2) vec3 E = vec3(0, 0, 1); // ------------------------ (3) vec3 H = normalize(l + E); // --------------------- (4) float df = max(0.0, dot(n, L)); float sf = max(0.0, dot(n, H)); // ------------------- (5) sf = pow(sf, u_shininess); // ------------------- (6) // ------------------------------------------- (7) vec3 globalambient = u_ambientmaterial * vec3(0.05); // * gl_lightsource[0].ambient vec3 ambient = u_ambientmaterial * vec3(u_ambientlight); return (globalambient + ambient) + (INTENSITY * v_diffuse * df) + (u_specularmaterial * sf); ----------------------------------------------------------------------- FragmentShader 의 Pixel Light 계산은앞장에서처리했던 Vertex Light 부분을그대로옮겨오기만하면됩니다. (1) : 법선벡터구하기 (2) : Positional Light 구하기 수식를보면, normalize(directionlight - vec(positionlight)); 인데 directionlight 는광원의위치이고, positionlight 는 광원이조사되는정점입니다. [GLES20] 11. 조명적용하기 - Pixel Light 104

이를벡터로보면, world space 를기준으로벡터의차로본다면, (D - P) 라하면, P --> D 로연결하는벡터가생성됩니다. 즉위의그림에서보면 L 벡터는정점을향해서조사되는데이와반대방향인 -L 벡터가구해집니다. (3) : 관측자의시점 ( 시선 ) 또는관찰자여기서는 vec(0, 0, 1) 로했는데무한히멀리떨어져있는관측자 (infinite viewer) 입니다. 만약 E값을사용할경우에는근접관측자 (local viewer) 라합니다. 왠지앞장에서 Positional Light를구하는방법을이용해서하면근접관측자가가능할것같은데, 근접관측자에대한예제는한번구해봐야할것같네요. (4) : 반각벡터구하기 OpenGL ES 20 에서는 OpenGL 과달리반각벡터 H 를직접구해줘야합니다. 조명과관찰자간의벡터의합을통해서쉽게구할수있습니다. ^^; (5) : 경면율 (Specular Factor) 구하기 확산율 df 는법선 (N) 과조명 (L) 간의사잇각을이용해서구하지만, 경면율 sf 는법선 (N) 과반각벡터 (H) 간의사잇각을이용해서구합니다. (6) : 경면지수구하기경면지수는상수값이며, 본App에서 uniform을이용해서저장하였습니다. 경면지수 Shininess 값이크면 Specular 가비춰지는백색광점의반경은작아지지만색은환하게보입니다. 값이작으면백색광점의반경이넓게퍼집니다. 제예제에서는 128을사용하였습니다. (7) : 주변광, 확산광, 경면광반영 [GLES20] 11. 조명적용하기 - Pixel Light 105

2. 본 App 에서 Pixel Light 실행하기 Classes/RenderingEngine.CH02.ES2.cpp void RenderingEngine::Initialize(int width, int height) {... // Set up some default material parameters. vec4 diffuselight = vec4(1, 1, 1, 1); // 백색광 vec4 ambientlight = vec4(0.05f, 0.05f, 0.05f, 1.0f); // 흑색광 vec3 diffusematerial = vec3(1, 1, 1); // 백색재질 vec3 ambientmaterial = vec3(1.0f, 0.f, 0.f); // 적색재질 // vec3 ambientmaterial = vec3(0.0f, 0.f, 0.f); // 주변광끄기 vec3 specularmaterial = vec3(1, 1, 1); // 백색재질 gluniform4fv(m_vertexlight.uniforms.diffuselight, 1, diffuselight.pointer()); gluniform4fv(m_vertexlight.uniforms.ambientlight, 1, ambientlight.pointer()); gluniform3fv(m_vertexlight.uniforms.ambientmaterial, 1, ambientmaterial.pointer()); gluniform3fv(m_vertexlight.uniforms.specularmaterial, 1, specularmaterial.pointer()); gluniform1f(m_vertexlight.uniforms.shininess, 128);... [GLES20] 11. 조명적용하기 - Pixel Light 106

void RenderingEngine::Render(const vector<visual>& visuals) {... ProgramHandles handler = m_pixellight; //handler = m_vertexlight; // -------------------------------------- (1) GLfloat weight = cos(interpolation_z) * 1.5f; // -------------------------- (2) gluniform1f(handler.uniforms.interpolation_z, (GLfloat) weight); LOG_PRINT("interpolation_z:%f, cos(%f)", interpolation_z, weight); interpolation_z += (2*Pi) * 0.0008f; // Set the light Mode : 0(Diffuse Mat Color), 1(DIFFUSE), 2,(AMBIENT_DIFFUSE), 3(AMBIENT_DIFFUSE_SPECULAR) gluniform1i(handler.uniforms.lightmode, 3); // -------------------------- (3)... ------------------------------------------------------------------- (1) : PixelLighting 으로실행합니다. Pixel Light 로동작하도록 vertexlight 는주석으로막아줍니다. (2) : Postional Light 시에광원의위치를이동시키기위해서사용되는가중치값입니다. (3) : 주변광, 확산광, 경면광결합으로실행합니다. 원래주변광은검정색이지만, 물체와바탕화면을구분하기위해서적색재질을사용하였습니다. 경면광은백색으로처리되어있습니다. vec3 ambientmaterial = vec3(1.0f, 0.f, 0.f); vec3 specularmaterial = vec3(1, 1, 1); // 백색재질 만약주변광을끄고싶다면, AmbientMaterial 재질값을 (0, 0, 0) 으로설정하면됩니다. [GLES20] 11. 조명적용하기 - Pixel Light 107

3. 결론 mfc 와 android app 으로 Pixel Light 주변광확산광경면광합성을적용해보았습니다. Pixel Light 로경면광을표현하는것이 Vertex Light 보다훨씬예쁘게나오는것을확인할수있었습니다. 결국확산광, 주변광을처리할때는 Vertex Light 로처리해도좋지만, Specular Light 경면광을처리할때는 Pixel Light 로처리 해야훨씬예쁘게나오는것을볼수있었습니다. 다음에다룰 Spot Light 나 Toon Light 도 Vertex Light 가아닌 Pixel Light 로처리해야훨씬예쁘게나옵니다. 그런데 Specular Light 로돌려보시면알겠지만, 두가지이상한점이발견됩니다. 첫번째로다른물체들은다예쁘게나오는데유독주전자 Teapot 만이상하게도 평평한 Flat Model 로만나오는것을볼수있을겁니다. [GLES20] 11. 조명적용하기 - Pixel Light 108

두번째로는현재적용한조명방식이 Directional Light 가아닌 Positional Light 임에도불구하고, Light 의위치를가까이해도, 전체적으로뿌려지는것처럼보입니다. 이는감쇠수식 (attenuation) 를반영하지않고있기때문에발생한현상입니다. 다음장에서는이부분들에대해서해결하는방안에대해서알아보도록하겠습니다. OGLES20 Template Application ported to mfc and android http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail?name=02_parametricsurface.v.1.2.0.zip&can=2&q=#makechanges [GLES20] 11. 조명적용하기 - Pixel Light 109

[GLES20] 12. 조명적용하기 - Flat Shading 과 Smooth Shading 2011.11.27 11:09 조명적용하기 - Flat Shading 과 Smooth Shading 1. Flat Shading OpenGL ES 2.0 에서는 glshademodel() API 를지원하지않습니다. 이때문에현재제가사용하고있는주전자 teapot2.bj 파일을이용해서처리할경우에는원본자체가 Flat 형으로평평하기때문에, Phong 을적용하든 Blinn 을적용하든 Smooth 효과가먹지않습니다. 기본적으로퐁과블린은 Normal 값인법선벡터, Light 벡터, 관측자시선벡터등이주요인자입니다. 그런데, Flat Shade Model 의경우에는한면을이루는세정점의 Normal 값이같은방향을바라보고있습니다. 이때문에법선에의한조명이먹긴하지만평평한모습이보이는겁니다. Models/teapot2.h double teapot2normals [] = { // f 2909//1 2921//1 2939//1-0.926910681623383f, -0.368160873543573f, 0.0727609750079555f, [GLES20] 12. 조명적용하기 - Flat Shading 과 Smooth Shading 110

-0.926910681623383f, -0.368160873543573f, 0.0727609750079555f, -0.926910681623383f, -0.368160873543573f, 0.0727609750079555f, // f 2939//2 2931//2 2909//2-0.926913284595327f, -0.368154113036399f, 0.0727620223405273f, -0.926913284595327f, -0.368154113036399f, 0.0727620223405273f, -0.926913284595327f, -0.368154113036399f, 0.0727620223405273f, // f 2869//3 2877//3 2921//3-0.903724329229544f, -0.368523134254108f, 0.217883079375474f, -0.903724329229544f, -0.368523134254108f, 0.217883079375474f, -0.903724329229544f, -0.368523134254108f, 0.217883079375474f, // f 2921//4 2909//4 2869//4-0.903723860047075f, -0.36852494292931f, 0.217881966258257f, -0.903723860047075f, -0.36852494292931f, 0.217881966258257f, -0.903723860047075f, -0.36852494292931f, 0.217881966258257f, ----------------------------------------------------------------- 법선벡터의데이타를보면한면을이루는 Set 가동일한방향을바라보고있음을알수있습니다. 이와같은플랫쉐이딩은표면이굴곡이보이지않습니다. 대신플랫쉐이딩을이용할경우비현실적인화면처럼보이지만, 연산처리가 Smooth 에비해서많이들지않기때문에 많이사용된다고합니다. 또한다각형간에윤곽선이보이는것처럼보이기도합니다. ^ ㅁ ^; OpenGL ES 1.0 의경우에는 glshademodel(gl_smooth) 를이용해서표면을부드럽게표현할수있습니다. 이는고정파이프라인기능으로 CPU 연산에부담을많이준다고합니다. [GLES20] 12. 조명적용하기 - Flat Shading 과 Smooth Shading 111

이에 OpenGL ES 2.0 에서는빠졌습니다. - ㅁ -; 다시말하면예제의 teapot2.obj 를그대로이용하기위해서는 GL_SMOOTH 을직접구현해줘야합니다. 2. Smooth Shading - Gouraud Shading OpenGL ES 2.0 에서 Smooth Shading 을구현하기위해서는 Gouraud Shading 기법 ( 고라우드? 고로? 구로? 등의발음을함 ) 을 이용합니다. 다각형내부를서로다른색으로채우는방법으로표면위에한정점을잡아서이를둘러싸고있는면의법선벡터를계산한후평균 값을구한다고합니다. - ㅁ -; 표면위에있는임의의한정점 lp 를구하는방법입니다. 의외로이론은많지만, OpenGL ES 2.0 에서는구현코드가잘보이지않습니다. 아마도정점이아주많을경우에는이와같은정점에대한보간처리가 CPU 에부담을많이주기때문이아닌가생각되네요. ( 공부차원에서구현을한번해보는것도좋을것같네요. ^^;) [GLES20] 12. 조명적용하기 - Flat Shading 과 Smooth Shading 112

참고페이지 http://m.blog.naver.com/postview.nhn?blogid=darkedge81&logno=120040318294 http://www.stack.nl/~dimitri/3dsview/gouraud.html http://omega.di.unipi.it/web/ium/waterloo/node84.html#section001520000000000000000 예제의 teapot2.obj 파일을블렌더를이용해서 Smooth 를걸어서변경해보겠습니다. Models/teapot2_smooth.h double teapot2_smoothnormals [] = { // f 2909//1 2921//2 2939//3-0.94681898769934, -0.284130897478523, 0.151008071406948, -0.91365422609069, -0.369282579203481, 0.169900947152883, -0.926916056314328, -0.368148199769382, 0.0727566323661089, // f 2939//3 2931//4 2909//1-0.926916056314328, -0.368148199769382, 0.0727566323661089, -0.96417026254053, -0.254257716714598, 0.0756882971377034, -0.94681898769934, -0.284130897478523, 0.151008071406948, // f 2869//5 2877//6 2921//2-0.911147515387562, -0.284468468461901, 0.298140731289795, -0.875375967008971, -0.369676898357678, 0.311537970725489, -0.91365422609069, -0.369282579203481, 0.169900947152883, // f 2921//2 2909//1 2869//5 [GLES20] 12. 조명적용하기 - Flat Shading 과 Smooth Shading 113

-0.91365422609069, -0.369282579203481, 0.169900947152883, -0.94681898769934, -0.284130897478523, 0.151008071406948, -0.911147515387562, -0.284468468461901, 0.298140731289795, ----------------------------------------------------------------- 법선벡터의데이타를보면한면을이루는 Set 내의법선의방향이다른것을볼수있습니다. 왼쪽은원본이며, 오른쪽은블렌더를이용해서 Smooth 를적용한화면입니다. 3. 결론 이번장에서는 Flat Shading 과 Smooth Shading 처리에사용되는 glshademodel() API 가 OpenGL ES 2.0 에서는지원하지않는 다는것을확인하였습니다. [GLES20] 12. 조명적용하기 - Flat Shading 과 Smooth Shading 114

또한인터넷또는디자이너로부터얻은이미지데이타가 Flat model 일경우이를수정하는방법에대해서간단하게알아보았습 니다. 그런데, 이예제의 teapot2.obj 로는 smooth 로처리해도위 - 경도사이에경계선이그려져있는문제점과 Texture 좌표가 Obj 내에 없다는문제가있습니다. 가장좋은방법은인터넷에서더좋은 Source 를구하거나디자이너로부터예쁜주전자이미지를받는것이가장좋을것같습니다. 참고로 3ds Max 에는기본으로 Teapot 이있으며이를이용해서 Smooth 를정해주고단일 Group 으로선택해서그리면다음과같 은더욱괜찮은주전자가탄생합니다.( 회사디자이너가그려줌 ^^;) Gouraud Shading 에대해서는시간이나면구현을한번해봐야할것같네요. ^^; 우선은조명과텍스쳐의기초과정을다르고있으니이진행이끝나고나서시작해봐야할것같네요. ㅎㅎ OGLES20 Template Application ported to mfc and android [GLES20] 12. 조명적용하기 - Flat Shading 과 Smooth Shading 115

http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail?name=02_parametricsurface.v.1.2.1.zip&can=2&q=#makechanges [GLES20] 12. 조명적용하기 - Flat Shading 과 Smooth Shading 116

[GLES20] 13. 조명적용하기 - Point Light 와감쇠방정식 2011.11.28 01:09 조명적용하기 - Positional Light 와감쇠효과 앞에장에서는방향성조명을 Directional Light 라고하고위치성조명은 Positional Light 라고하였습니다. 그런데, 3D 소스나책등을보면 Directional Light 는 Direction Light 라고하고, Positional Light 는 Point Light 라고도부릅니다. 같은의미이기때문에혼동이없으시길바랍니다.^^; 또한 Positional Light 에대한선행지식을쌓고싶으시다면, 바른생활님의설명하신 "[GLSL] 21. Positional Light 에대한이해 " 장을한번읽어보시면도움이될겁니다. 1. Positional Light "[GLES20] 08. 조명적용하기 - 확산광, Diffuse Vertex Light" 장에서광원의위치에따라서확산과경면광의 조사되는위치가변경되는것을보았습니다. 그런데, 이상하게도광원이모델로부터멀어지면빛이모델에닿는거리가멀어지기때문에모델이어두어져야하는데앞의예제 에서는그렇지가않습니다. 다음화면을보면, 기존의 Specular 를 Pixel Light 구현했을때의화면입니다. [GLES20] 13. 조명적용하기 - Point Light 와감쇠방정식 117

왼쪽의광원위치는 (2, 0, -7) 인상태로물체에가까이다가갔을때화면입니다. ( 카메라의 eye vector 를 (0. 0. 10) 으로설정한상태임으로 Z 가 -7 이면물체에가까이다가간것입니다.) 반대로오른쪽의광원위치는 (2, 0, 0) 인상태로물체에멀어졌을때화면입니다. 그런데, 오른쪽구의광원을받은정도를보면더많은광량을보여주고있습니다. 물론광원이모델로부터떨어졌기때문에방사되는영역이넓어져서그렇다고도볼수있지만, Z값을 0~100으로올려도점점더밝아지게됩니다. 이는마치 Directional Light 을 (1, 1, 1, 0) 으로놓고 Positional Light 를적용하지않음으로서, 무한히멀리떨어진태양처럼광원 에의해서모델전체에비춰지는현상과유사합니다. 아무튼광원이계속뒤로떨어져가는데도물체가더밝아지는것은이상한상태입니다. - ㅁ -; 이는 08~10 장에서다룬 Diffuse, Ambient, Specular Light 에광원의위치이동에따른방향정보 (Directional Light) 는정상적으로 적용이되었지만, 광원과모델간의거리에따른정보 (Positional Light) 는정상적으로적용되지않았기때문에나타나는현상입니 다. 이와같이거리정보에따른 Positional Light 적용을위해서는감쇠 (attenuation) 방정식을적용해야합니다. 2. Attenuation - 감쇠방정식 감쇠율방정식 [GLES20] 13. 조명적용하기 - Point Light 와감쇠방정식 118

Kc = GL_CONSTANT_ATTENUATION, 감쇠상수 Kl = GL_LINEAR_ATTENUATION, 감쇠일차방정식 Kq = GL_QUDRATID_ATTENUATION, 강쇠이차방정식 d = 광원과표면의정점사이거리 OpenGL ES 2.0에서는 OpenGL 에서지원하는다음과같은함수을지원하지않습니다. gllightf(gl_light0, GL_CONSTANT_ATTENUATION, 1.5); gllightf(gl_light0, GL_LINEAR_ATTENUATION, 0.2); gllightf(gl_light0, GL_QUADRATIC_ATTENUATION, 0.5); 왼쪽은감쇠율은 (1.0, 0.0, 0.0) 이며, 오른쪽은 (1.5. 0.5, 0.2) 인상태입니다. 기본 Default 감쇠율은 Kc만 1.0이고 Kl과 Kq는 0.0 이기때문에 attenuation factor는 1이되며감쇠효과를사용하지않게됩니다. 하지만, 오른쪽 Kc(1.5), Kl(0.5), Kq(0.2) 의경우에는, 거리값 d가 1을기준으로할경우, 0.4545 가되며, 전체적인 Light 값에서반정도빠지게됩니다. 물론광원과정점사이의거리인 d가 0 ~ 1 사이라면더밝아질것이고, 1 ~? 사이라면점점어두어지게됩니다. 결국 Kc, Kl, Kq 에의해서계산된결과값인 attenuation factor 는그값이 1.0 에가까운값이라면, 감쇠를적용하지않는것이며, 0.0 에가깝게되면감쇠율에영향을크게받게되며 Light 가어두워지게됩니다. 결국다음의방정식은 [GLES20] 13. 조명적용하기 - Point Light 와감쇠방정식 119

분모가 1 보다큰값이면, 0.0 에가까워지며, 분모가 1 보다작은값이면오히려감쇠효과는사라지고더밝아지게됩니다. Kc, Kl, Kq 는상수이기때문에광원과표면의정점사이의거리인 d 가작아서서로가까이있다면, att 값은 0.0 보다커지게되며, 거리가멀어져서 d 값이커지면, att 값은 0.0 에가까워집니다. 위와같은감쇠율을계산하기위한인자들은 uniform 형으로본 App 으로부터전달받아서사용해야합니다. 또는다음의 Post 와같이수식으로정리할수도있습니다. Light attenuation http://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/ 이분이정리한방식은광원의반지름을임의로설정할경우, 조사되는광량을조절하고있습니다. 대단 ^^; 위와같은수식에서광원과표면의정점사이의거리인 d 는기존정보로계산할수있으며, 여기에임의의반지름 r 을 1, 2, 4, 8 로놓고계산할경우다음과같은그래프가나옵니다. 이수식을넣어서풀어보면반지름이 1 일경우에는거리가 1.5 만넘어가도 att 가 0.2 가되어버리기때문에아주어두어져버립니다. 이경우에는광원이표면에가까이있을경우에만밝게비춰집니다. 반면반지름을 8 로할경우에는거리가 10 정도는떨어졌을경우에 att 가 0.2 에도달함으로어두어지는현상이천천히발생합니다. 또한위의수식을 2 차방정식으로풀면다음과같이나옵니다. [GLES20] 13. 조명적용하기 - Point Light 와감쇠방정식 120

이제방정식을 OpenGL 의 attenuation factor 로유도하면, Kc = 1, Kl = 2/r, Kq = 1/(r^2) 을얻을수있습니다. ^^; 구현코드는기존의 Specular 부분에 attenuation factor를추가하였습니다. // ambient + diffuse + specular + attenuation light vec3 calclightambdifspecatt() { float dist = v_distance; //vec3 N = normalize(2.0 * v_eyespacenormal - c_one); // 보정및정규화 : from ShaderX2 vec3 N = normalize(v_eyespacenormal); vec3 L = normalize(v_lightposition); vec3 E = vec3(0, 0, 1); vec3 H = normalize(l + E); vec3 R = reflect(-l, N); //vec3 R = -normalize(-2.0 * N * dot(l, N) + L); //-2*N*(dot(L, N)) + L; //vec3 R = normalize(2.0 * N * dot(n, L) - L); // from ShaderX2 float df = max(c_zero, dot(n, L)); float sf = max(c_zero, dot(n, H)); // Blinn model //float sf = max(c_zero, dot(r, E)); // Phong model sf = pow(sf, u_shininess); vec3 globalambient = u_ambientmaterial * vec3(0.05); // * gl_lightsource[0].ambient vec3 ambient = u_ambientmaterial * vec3(u_ambientlight); vec3 color = globalambient; // GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION, GL_QUADRATIC_ATTENUATION float r = 4.0; //lightradius; float d = max(dist - r, c_zero); //L = normalize(l / dist); // calculate basic attenuation float denom = d/r + c_one; float att; float Kc = c_one; float Kl = 2.0/r; float Kq = c_one/(r * r); [GLES20] 13. 조명적용하기 - Point Light 와감쇠방정식 121

// r이 4.0일때... //Kc = 1.0; //Kl = 0.5; //Kq = 0.0625; //Kc = 1.5; //Kl = 0.5; //Kq = 0.2; // Default //Kc = 1.0; //Kl = 0.0; //Kq = 0.0; if (df > c_zero) { // att = c_one / (denom*denom); att = c_one / (Kc + (Kl * d) + (Kq * d * d)); color += att * (v_diffuse * df + ambient); color += att * (u_specularmaterial * sf); return color; ----------------------------------------- 수식중아래와같은주석부분을이용할경우좀더최적화가될것같습니다. attenuation factor를 Kc, Kl, Kq로구분하지않고바로아래와같이분모를 (d/r +1) 로구해서계산하고있습니다. // att = c_one / (denom*denom); 해가동치임으로결과도동일합니다. [GLES20] 13. 조명적용하기 - Point Light 와감쇠방정식 122

<radius 4 로세팅한결과 > <Kc=1.0, Kl=0.5, Kq=0.2> [GLES20] 13. 조명적용하기 - Point Light 와감쇠방정식 123

3. 결론 이번장은 Spot Light 에들어가기앞서서필요한감쇠율 Attenuation factor 를적용하는법에대해서알아보았습니다. OpenGL ES 2.0 Programming Guide 책의 "8장 Vertex Shaders" 에서 Spot Light 부분에 attenuation factor 가나오는데, K0, K1, K2 만언급하고생략되어있습니다. 혹이책은기본적으로 OpenGL Super Bible 이나 Red Book 을숙지하고있다는전제로시작하고있기때문에세세한부분을설명하지는않습니다. -ㅁ-; 결국 OpenGL ES 2.0에서는다음의 gllightf() 를지원하지않기때문에직접구현해주셔야합니다. gllightf(gl_light0, GL_CONSTANT_ATTENUATION, 1.5); gllightf(gl_light0, GL_LINEAR_ATTENUATION, 0.2); gllightf(gl_light0, GL_QUADRATIC_ATTENUATION, 0.5); 3가지정보다 Fixed Pipe line 기준으로본다면 3개다상수임으로 uniform 형으로전달받아서처리해줘도됩니다. 또는 "light attenuation" 을구하는방정식을이용해서직접 shader 내에서구현해도됩니다. 향상된버전인 "Improved light attenuation" 도제공하고있음으로추가적인공부가필요하신분은참고하십시요. 참고로 OGL ES 2.0 Programming Guide 책에대해서좀더설명한다면이책에서제공하는예제는아주많이부실합니다. 하지만, Windows, ios, Android, WebGL 까지두루예제로제공하면서 MIT License 임으로뜯어고쳐서실무에응용해도됨으로분석은하는것이좋습니다. 이코드와더불어동작가능한소스 (?) 는아니지만, ios 용으로 OpenGL 과호환가능하도록 API Set을만들어놓은작업이있습니다.(http://code.google.com/p/gles2-bc/) 이소스도같이겸해서보시는것을추천합니다. ^ ㅁ ^; 또한이장을통해서 "I'm doing it wrong" 블로그의저자님이적용한 Light Attenuation 으로도비슷한효과가나는것을확인하 였습니다. OGLES20 Template Application ported to mfc and android http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail?name=02_parametricsurface.v.1.2.1.zip&can=2&q=#makechanges [GLES20] 13. 조명적용하기 - Point Light 와감쇠방정식 124

[GLES20] 13. 조명적용하기 - Point Light와감쇠방정식 125

[GLES20] 14. 조명적용하기 - Spot Light 2011.11.28 23:31 조명적용하기 - Spot Light 이번장은기초조명과정중마지막장이라할수있는 Spot Light 입니다. Spot Light 에대해서는 iphone 3D Programming 책에서는다루지않고있습니다. (attenuation 감쇠율부분도빠져있음 ) 이부분에대해서는 OpenGL ES 2.0 Programming Guide 책의 "8 장 Vertex Shader" 을참고하는것이좋습니다. 물론 OpenGL ES2.0 책은독자가 OpenGL 조명에대해서어느정도이해한상태에서설명을합니다. 그렇기때문에 OpenGL ES 부터시작하는개발자들은구현함수인 spot_light() 함수만봐서는이런수식을이용하면되겠구나할 수는있지만실제동작시켜볼수는없습니다. 하지만, 13장까지잘이해하고따라오셨다면 14장의 Spot Light도책내의 spot_light() 함수만보고도적용하실수있을것이라고생각합니다. 추가적인참조자료로는바른생활님의 "[GLSL]23 Spot Light 의구현 " 장과 PowerVR SDK Training Course 내에있는 "ComplexLighting" 코드를분석해보시면도움이될겁니다. 여기서는 Spot Light 의수식과관련된핵심부분만정리하도록하겠습니다. [GLES20] 14. 조명적용하기 - Spot Light 126

1. Spot Light 기본적으로 Spot Light 는다음과같은그림으로설명할수있습니다. 1. 광원으로부터 Spot( 지점 ) 방향으로향하는벡터 Spot direction 과광원의표면의정점으로향하는 Light direction 간의사잇 각을구한다. 2. 그사잇각이 Spot cutoff angle 보다작다면 Spot Effect 를계산하고, 크다면감쇠율을 0.0 으로둔다. 3. Spot factor 가 Cutoff angle 보다작다면, 아래와같은 Spot Effect 수식에따라서계산한다. 위의수식을완성하기위해서는다음과같은값들을본 App 으로부터 uniform 형으로받아와야합니다. (spotexp 의기본값은 0 으로이값이높아지면 spoteffect 가활성화되는영역과그밖에감쇠되는영역이부드러워집니다.) 13 장까지정리한것을기준으로 14 장을보면 Spot Light 는쉽게해결할수있습니다. Spot Light 에대한구현코드는다음과같습니다. [GLES20] 14. 조명적용하기 - Spot Light 127

Shaders/PixelLighting.frag // ambient + diffuse + specular + attenuation + Spot light vec3 calclightambdifspecattspot() {... if (u_spotcutoffangle < 180.0) { vec3 S = (vec3(u_spotdirection)); float spot_factor = dot(-l, S); // ----------------------------- (1) if (spot_factor >= cos(radians(u_spotcutoffangle))) { // ------- (2) spot_factor = pow(spot_factor, u_spotexponent); // ------- (3) else { spot_factor = c_zero; // ---------------------------------- (4) att *= spot_factor; // -------------------------------------- (5)... -------------------------------------------------------------------------- (1) Spot direction과 Light direction 사잇각구하기지점방향을나타내는 Spot direction 벡터와광원방향을나타내는 Light Direction 간의사잇각을구합니다. Spot direction은본app에서기본값인 (0, 0, -1) 로설정되어있으며, Light Direction은 vertex shader에서광원과정점의벡터차를통해서구한 Positional Light벡터를이용합니다. dot(-l, S) 에서 -L인이유는광원과정점의벡터차를구했음으로그벡터방향은 " 정점-> 광원 " 으로향합니다. 저희는 " 광원-> 정점 " 방향으로구해야함으로 -L을해줘야합니다. (2) Spot factor와 cut off angle 비교 Spot Light는원뿔형으로빛이비춰집니다. 이때원뿔의영역을정하는기준이 Spot direction을중심으로 spot cut off angle( 절단각 ) 을통해서구해집니다. 즉이 cut off angle 내에만 Spot Effect를계산해주고, 나머지영역은감쇠율값에 0.0을해줌으로서빛을전부빼줘야합니다. 이때 cos() 함수가이용되는데이는 spot_factor 가 -L과 S간의내적을통해서구해짐으로그결과는 cos(theta) 입니다. 그런데절닥각보다작아야한다고하는데, 비교식은 " 크거나같다 " 입니다. [GLES20] 14. 조명적용하기 - Spot Light 128

이는아래와같이 Cosine 함수가 Theta 각도 (Range) 가커질수록그결과값 ( 치역 ) 은작아지기때문에그렇습니다. 결국내적의결과인 spot_factor 가 cos(cutoffangle) 의결과보다크다면, 그사잇각은작다는것을의미합니다. ( 이부분은제가이렇게이해했다는의미입니다. - ㅁ -; 혹다른의미가있다면댓글달아주세요 ^^;) (4) 절단각에서벗어나는영역은감쇠율을최대치로높인다. (5) 감쇠율에 Spot factor 을곱해줘서뒤에오는주변광, 확산광, 경면광에적용해준다. < 왼쪽은 Vertex Light 를이용한 Spot Light 이며, 오른쪽은 Pixel Light 를이용한 Spot Light> 2. 본 App 에서 Spot Light 실행하기 Shader 내의코드는수학적논리가단순해서어렸지는않습니다. 그런데, GLES20 책은응용 App 단부분은생략되어있기때문에, 도대체 Spot direction은왜? (0, 0, -1) 을넣어야하고, Spot Exponent는몇으로잡아줘야하는지? Cut Off Angle 절단각은 180도를넘어가면어떻게되는지? Light direction은몇으로잡아야하는지? 등에대해서는책만봐서는알수없습니다. [GLES20] 14. 조명적용하기 - Spot Light 129

그럼실제응용 App 에서는어떻게호출해줘야하는지보도록하겠습니다. Classes/RenderingEngine.CH02.ES2.cpp void RenderingEngine::Initialize(int width, int height) {... m_pixellight.uniforms.attenuationfactor = glgetuniformlocation(program, "u_attenuationfactor"); m_pixellight.uniforms.isattenuation = glgetuniformlocation(program, "u_computeattenuation"); m_pixellight.uniforms.lightradius = glgetuniformlocation(program, "u_lightradius"); m_pixellight.uniforms.spotdirection = glgetuniformlocation(program, "u_spotdirection"); m_pixellight.uniforms.spotexponent = glgetuniformlocation(program, "u_spotexponent"); m_pixellight.uniforms.spotcutoffangle = glgetuniformlocation(program, "u_spotcutoffangle");... ----------------------------------------------------------------------------------- Attenuation 과 Spot Factor 에대한신규 uniform 형들이추가되었음으로이를초기화함수에추가해줍니다. ----------------------------------------------------------------------------------- void RenderingEngine::Render(const vector<visual>& visuals) {... // -------------------------------------------- // PixelLight bool bispixellight = true; // -------------------------------------- (1) ProgramHandles handler; if (bispixellight) handler = m_pixellight; else handler = m_vertexlight; gluseprogram(handler.program); [GLES20] 14. 조명적용하기 - Spot Light 130

// Set the light Mode // -------------------------------------- (2) // 0 : MENU_DIFFUSE_MATERIAL -> DIFFUSE_MATERIAL // 1 : MENU_DIFFUSE_LIGHT -> DIFFUSE_LIGHT // 2 : MENU_AMBIENT_DIFFUSE_LIGHT -> AMBIENT_DIFFUSE_LIGHT // 3 : MENU_AMBIENT_DIFFUSE_SPECULAR_LIGHT -> AMBIENT_DIFFUSE_SPECULAR_LIGHT // 4 : MENU_AMBIENT_DIFFUSE_SPECULAR_ATT_LIGHT -> AMBIENT_DIFFUSE_SPECULAR_ATT_LIGHT // 5 : MENU_AMBIENT_DIFFUSE_SPECULAR_ATT_SPOT_LIGHT -> AMBIENT_DIFFUSE_SPECULAR_ATT_SPOT_LIGHT gluniform1i(handler.uniforms.lightmode, MENU_AMBIENT_DIFFUSE_SPECULAR_ATT_SPOT_LIGHT); //gluniform4f(handler.uniforms.ambientlight, 0, 0, 0, 0); // Initialize various state. glenablevertexattribarray(handler.attributes.position); glenablevertexattribarray(handler.attributes.normal); GLfloat weight = sin(interpolation_z) * 3;//12.0f;//1.5f; // ------------------- (3) //weight = 0.0f; //gluniform1f(handler.uniforms.interpolation_z, (GLfloat) weight); //LOG_PRINT("interpolation_z:%f, cos(%f)", interpolation_z, weight); interpolation_z += (2*Pi) * 0.0015f; // Set the attenuation factor. // -------------------- (4) //vec3 attenuationfactor(1.5f, 0.5f, 0.2f); vec3 attenuationfactor(1.0f, 0.0f, 0.0f); gluniform3fv(handler.uniforms.attenuationfactor, 1, attenuationfactor.pointer()); gluniform1i(handler.uniforms.isattenuation, 1); gluniform1f(handler.uniforms.lightradius, 4.0f); // Set the light position. //vec4 lightposition(0.f, 0.f, -1.f, 1.0f); // for ortho vec4 lightposition(0.f, 0.f, -6.f, 1.0f); // --------------------------------- (5) //lightposition.x += weight; lightposition.z = max(-6.5f, lightposition.z+weight*2.f); // --------------- (6) lightposition.y += weight; //LOG_PRINT("lightPosition:(%f, %f, %f) + weight(%f)", lightposition.x, lightposition.y, lightposition.z, weight); gluniform4fv(handler.uniforms.lightposition, 1, lightposition.pointer()); // Set the spot direction. vec4 spotdirection(0, 0, -1, 1); // ---------------- (7) [GLES20] 14. 조명적용하기 - Spot Light 131

gluniform4fv(handler.uniforms.spotdirection, 1, spotdirection.pointer()); gluniform1f(handler.uniforms.spotexponent, 0.0f); gluniform1f(handler.uniforms.spotcutoffangle, 25.0f); //gluniform1f(handler.uniforms.spotexponent, 40.0f); //gluniform1f(handler.uniforms.spotcutoffangle, 51);... ------------------------------------------------------------------- (1) : bispixellight 변수설정합니다. Pixel Light 로동작할지 Vertex Light 로동작할지선택할수있습니다. (2) : Spot Light 가동작하도록설정합니다. ; MENU_AMBIENT_DIFFUSE_SPECULAR_ATT_SPOT_LIGHT (3) : Light Position 의위치를변경시켜주기위한가중치값입니다. 이값은 sin() 함수로동작함으로 0~1~-1~0 를반복합니다. 여기에세배를가해줌으로 0 ~ 3 ~ 0 ~ -3 ~ 0 값이가중치값으로이용합니다. (4) : Attenuation factor 인감쇠율값을설정합니다. 기본값인 (1.0, 0.0, 0.0) 으로설정되어있지만, 앞장에서배웠던광원의반지름공식에의해서감쇠율이적용됩니다. (5) : Light Position을설정합니다. Light Position 설정은 (0, 0, -6) 으로시작합니다. 예제는 Camera를통해서 Z축을정면으로보는상태에서카메라를 10만큼뒤로뺀상태입니다. (eye(0, 0, 10)) 다시말하면, 모델객체는월드좌표계를기준으로 Z축을기준으로 -10 만큰뒤에있는상태입니다. 그럼으로광원위치를모델객체 ( 로컬좌표 ) 에가까이다가가게하기위해서 -6 만큼빼주었습니다. (6) : (3) 번에계산한 weight값으로광원위치를변경해줍니다. y와 z값을변경해줍니다. Z값의경우, 시작설정값이 -6인데모델객체 ( 로컬좌표 ) 의원점은 -10입니다. 그런데 Z값이 -6.5 이하로더내려가면, 광원이모델객체와충돌을일으키는지광원이사라져버립니다. ^^ 그래서 Z값의최대한멀어지는위치는 -6.5로한정시켰습니다. [GLES20] 14. 조명적용하기 - Spot Light 132

(7) : Spot Direction을설정합니다. 기본값인 (0, 0, -1, 1) 로설정합니다. w값 1은 vertex positon의 w와동일하다고본다고합니다. 이예제에서는 w값은실제 shading 중에사용되지는않습니다. Spot Exponent는기본값인 0으로하였으며, 이값을높이면 Spot Light 경계지점이부드러지네요 ^^; Spot Cut Off Angle은 25도로설정하였으며, 이값을높이면 Spot Light 가커지게됩니다. (180도이상올리면 Spot Light가동작하지않게됩니다.) ----- 여기서부터는제주관적인관점이기때문에틀릴수도있습니다. 저도독학이어서 ^ㅁ^; ----- 여기서중요한값은 -1 입니다. 스팟라이트가정상적으로동작하기위해서는이값은음수여야합니다. 광원의위치인 Light Positon은음수방향으로보고있습니다. Spot Light도같은방향인음수방향으로보고있어야 L과 S간의내적을통해서구하는사잇각이예각내에서구해지게됩니다. 만약 Spot Light가 (0, 0, 1) 이면 L과 S의벡터방향이반대가되어버려서내적을통해서계산되는사잇각은예각이아닌둔각이되어버립니다. 그럼으로이부분은 (0, 0, -1) 이되어야하는것같습니다. ( 아닐수도있습니다. 어쨋든양수로하면효과가원하는바대로나오지않습니다.) [GLES20] 14. 조명적용하기 - Spot Light 133

<Vertex shader 로구현된 Spot Light> <Pixel shader 로구현된 Spot Light, Exponent 0> <Pixel shader 로구현된 Spot Light, Exponent 36> [GLES20] 14. 조명적용하기 - Spot Light 134

3. 결론 여기서설명한부분들은 iphone 3D Programming 책과 OpenGL ES 2.0 Programming Guide 책에이미나와있는내용입니 다. 책을우선보시고여기서의내용은레퍼런스로정도로이용하시면될것같습니다. 위의두책에서아쉬운부분은그림자효과에대해서는다루지않았습니다. 그림자효과에대해서는 Shader X2:Direct X9 쉐이더프로그래밍책에설명이나와있던데, 궁금하신분은 X2 책을참고하셔도좋을것같네요. 이상으로조명과관련한기초과정을마무리하도록하겠습니다. 어느새강좌를쓰기시작한지도한달이되었습니다. 보통한달에 4 개정도포스팅했는데, 14 개를하다니엄청나게폭풍집필 ^^; 을했네요. 큭 앞으로는속도조절을하면서글을써야할것같네요. - ㅁ -; 지금까지여기서설명한방법들은책을보고실행해봐서이렇게되겠구나하고생각하는점들을정리한것입니다. 실제강의를듣거나마스터분한테사사받은것이아니어서오류가있을수도있습니다. ^^; 혹논리에문제가있거나정정이필요한부분이있다면댓글로지적해주십시요. 다음장부터는조명기법중 Cartoon 효과를주는 Toon Shading 을간단하게정리한후에 Texture 처리로넘어가도록하겠습니다. OGLES20 Template Application ported to mfc and android http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail?name=02_parametricsurface.v.1.2.1.zip&can=2&q=#makechanges [GLES20] 14. 조명적용하기 - Spot Light 135

[GLES20] 15. 조명적용하기 - Toon Shading 2011.11.30 23:51 조명적용하기 - Toon Shading ( 불연속적인쉐이딩표현 ) 툰쉐이딩은다른말로 Cell Shading 이라고도부릅니다. 다음의게시물이있는데정의가잘표현되어있습니다. http://bbs2.ruliweb.daum.net/gaia/do/ruliweb/default/101/read?bbsid=g005&articleid=8749477&itemid=421 에보시면, cartoon rendering 에대해서 " 불연속적인쉐이딩표현 " 또는 " 실루엣외곽선묘사 " 둘중에한가지가적용되어있다면, Toon Shading이라고정의하고있습니다. iphone 3D Programming 책에서는 " 불연속적인쉐이딩표현 " 방식으로구현을하고있습니다. 1. Toon Shading 이란? 3D Programming의목적은화면에비춰지는것이마치실세계의그것과비슷하게표현하기위한방법입니다. 볼륨감이느껴지고깊이감이느껴지는사람의시각적으로느끼는것입니다. 그런데, 만화처럼느껴지게처리하면, 비현실적인느낌이되어버립니다. Toon Shading의렌더링기법을 Non photo realistic rendering(npr) 비사실적인렌더링방식이라합니다. iphone 3D Programming 책의경우 "4 장깊이와현실감향상시키기 " 장에서 Toon Shading 을설명하고있습니다. [GLES20] 15. 조명적용하기 - Toon Shading 136

이책에서는코드는있지만, 설명은없습니다. 정말멋지게처리된다? 정도 - ㅁ -; Toon Shading 에대해서좀더설명해줬으면하는부분이긴하지만, 이렇게처리하면되는구나정도로도괜찮을것같네요. 저는저자가설명하지않고넘어간부분에대해서부연설명을하도록하겠습니다. 2. 불연속적인쉐이딩표현 14 장까지저희가배운조명처리방법은주변광 Ambient, 확산광 Diffuse, 경면광 Specular, 지점광 Spot 등이있었습니다. 이중확산광과경면광에는 Diffuse factor 와 Specular factor 가있었습니다. Diffuse factor 는빛의각도와표면의각도의의해서표현되는값이며, Specular factor 는신선의각도와빛의각도, 표면의각도에 의해표현되는하일라이트값입니다. 조명을표현할때중요한 factor 는 Diffuse factor 입니다. 빛이표면에 100% 밝기로보여주기위해서는빛의각도와표면의각도의사잇각이 0도를이루어야합니다.(acos(1) = 0도 ) 빛이표면에 50% 밝기로보여주기위해서는빛의각도와표면의각도의사잇각이 60도를이루어야합니다. (acos(0.5) = 60도 ) 빛이표면에 0% 밝기로보여주기위해서는빛의각도와표면의각도의사잇각이 90도이상을이루어야합니다. (acos(0) = 90도 ) 확산광은위와같이 consine 함수를통해서구한 diffuse factor을이용해서쉐이딩을스무스하게표현합니다. [GLES20] 15. 조명적용하기 - Toon Shading 137

iphone 3D Programming 책의예제에서는다음과같이 4 단계로이루어져있습니다. Shaders/PixelLighting.frag vec3 calclighttoon() {... float df = max(c_zero, dot(n, L)); if (df < 0.1) df = 0.0; else if (df < 0.3) df = 0.3; else if (df < 0.6) df = 0.6; else df = 1.0;... --------------------------------------------------------- -. 확산율결과가 100% ~ 60% 면 100% 로만확산율을적용한다. -. 확산율결과가 59% ~ 30% 면 60% 로만확산율을적용한다. -. 확산율결과가 29% ~ 10% 면 30% 로만확산율을적용한다. -. 확산율결과가 9% ~ 0% 면 0% 로만확산율을적용한다. 4 단계영역으로끊겨서표현됨으로불연속적인쉐이딩표현입니다. 확산율과더불어하일라이트를표현하는 Specular factor 에도두단계로제약을가해줍니다. Shaders/PixelLighting.frag vec3 calclighttoon() {... float sf = max(c_zero, dot(n, H)); // Blinn model sf = pow(sf, u_shininess); sf = step(0.5, sf);... --------------------------------------------------------- step(a, b) 함수는다음과같은함수입니다. [GLES20] 15. 조명적용하기 - Toon Shading 138

float step(float edge, float x) { return x < edge? 0.0 : 1.0; 다시말하면, step(0.5, sf) 는 0.5보다크면 Specular Light를 100% 반영하고, 0.5보다작다면 0% 반영하라는의미입니다. 결과는다음과같이비현실적인예쁜영상이됩니다. ^ ㅁ ^; 3. 결론 Toon Shading 은이방법말고도더예쁘게표현되는많은방법들이있습니다. 먹물 (Ink) 효과, 외곽선 (Outline), 실루엣외곽선, Hatching 등정말공부가많이있네요 ^^; OGLES20 Template Application ported to mfc and android http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail?name=02_parametricsurface.v.1.2.1.zip&can=2&q=#makechanges [GLES20] 15. 조명적용하기 - Toon Shading 139

[GLES20] 15. 조명적용하기 - Toon Shading 140

[GLES20] 16. Q/A 몇가지와 FBO 사용시유의점정리 2012.01.01 15:43 제가정리한 Android 3D Programming 강좌에서궁금한질문사항이있어서정리를해보았습니다. 1. Q&A [ 질문 1] 왜안드로이드는 glgen glbind 안시키죠? [ 답변 ] glgen과 glbind는어떤부분을말씀하시는지잘모르겠네요. FBO, VBO, Texture 등 glgen과 glbind를접두사로취하는것들이많아서. 우선제가여기에올린예제는 Android와 Mfc를혼합해놓았습니다. 제프로젝트에서는 Eclipse로 Android project만열었을경우에는 NDK(jni) 쪽파일들은 build용 Android.mk와 JNI용 NativeRenderer.cpp 만있지실제 Renderer 구현부분은보이지않습니다. MFC쪽프로젝트를여서서보시면관련파일들을볼수있으며이쪽에 glgenxxx, glbindxxx 관련부분이있습니다. [ 질문 2] 단말기에서가로모드 / 세로모드로변경될경우, 해상도처리는어디서하나요? [GLES20] 16. Q/A 몇가지와 FBO 사용시유의점정리 141

[ 답변 ] 안드로이드의경우, Java 단에서처리해주셔야합니다. 안드로이드에서는 EGLView를 GLSurfaceView로구현합니다. 그리고이 GLSurfaceView를 Android Activie 의 Main View에 add 시킵니다. 이부분은안드로이드기본서적를참고하시는게좋을것같습니다. ( 제소스코드상에서는 GLRenderActivity.java에보시면 setcontentviw(mglview) 부분에서추가합니다.) GLSurfaceView가정상적으로생성되었다면, 단말기의 Orientation 이변경될때마다, onsurfacechanged() 함수가호출됩니다. 저는 NativeRenderer.java에서이부분을처리하고있으며, nativeresize() 함수를호출합니다. 이함수의구현부분은 JNI로구성되어있으며, NativeRenderer.cpp의 Java_com_myastro_main_jni_NativeRenderer_nativeResize 와메칭됩니다. 참고로제가올린예제에서는 ResizeWindow() 내에 glgen과 glbind가있는데, 이를해제하는부분이빠져있습니다. 또한단말기으방향이변경될때마다계속생성되게됩니다. 최적화를시킨예제는아니기때문에조정이필요합니다. 예를들어아래와같이... void RenderingEngine::ResizeWindow(int width, int height) { if (m_screensize.x!= width && m_screensize.y!= height) { // Create Off-screen FBO glgenframebuffers(1, &m_offscreen_framebuffer); glbindframebuffer(gl_framebuffer, m_offscreen_framebuffer); // Create Off-screen FBO 물론이방법도이전 fbo를삭제는안하고있기때문에완전히최적화되어있지는않습니다. ^^;. [ 질문 3] GLSurfaceView 는더블버퍼링을자기가알아서해주는가? [ 답변 ] 더블버퍼링을해주지는않습니다. 제가알기로는아이폰도더블버퍼링을지원하지않는것으로알고있습니다. 일반적으로 OGLES2.0 에서는더블버퍼링을지원한다고도하는데 ( 책에서는 ), 아직저는성공하지못햇습니다. [GLES20] 16. Q/A 몇가지와 FBO 사용시유의점정리 142

( 물론아직시도도안하고있고요 ^^;) 하지만이미아실지모르겠는데, 더블버퍼링을대체하는기술로 Framebuffer Object를이용합니다. FBO를 OnScreen 한개, OffScreen 한개씩.. 만들어서사용하는방법입니다. 아이폰 3D programming 책에도설명이나와있으니이미접해보셨을것같네요. ( 제블로그에도 RenderToTexture 장 (http://blog.daum.net/aero2k/53) 에기본적인설명은되어있습니다.) [ 질문 4] 아이폰에서는 gldraw 하고나서 [m_context presentrenderbuffer:gl_renderbuffer]; 함수 (ios 기준으로는 message 라 고도부르죠.) 를호출하는데, 안드로이드는 GLSurfaceView 만등록하면되나요? [ 답변 ] 아이폰에서는 OpenGLES를처리하는데 EGL를사용하지않고 EAGL이라고 Apple 용으로만든버전을사용합니다. 다음의 [m_context presentrenderbuffer:gl_renderbuffer]; 이부분은아이폰에만해당하는사항임으로처리해줄필요는없습니다. 물론안드로이드나윈도우즈시스템에서 OpenGL 를이용하기위해서 EGL 객체를만들어야합니다. 이 EGL를만드는위치가안드로이드의경우, GLSurfaceView임으로이객체를활성화해서처리해줘야합니다. ( 윈도우즈의경우, MFC 기준으로는 CView 클래스를상속받아서 CEGLView 를만들어야합니다. EGL 에대해서는관련자료들이많으며, 제소스와비교해서보시면이해가되시리라생각됩니다.) GLSurfaceView에서호출되어지는 ondrawframe() 함수 (Listener method) 에제작한 Renderer를호출해주시면됩니다. ( 이렌더러는 Java로작성할수도있으며, 제예제와같이 Native C++ 로작성할수도있습니다.) 제예제에서는 NDK단의 nativerender() -> Java_com_myastro_main_jni_NativeRenderer_nativeRender 함수를호출합니다. Java Renderer와 Native Renderer의사용법에대해서는이미안드로이드서적에많이나와있습니다. 책을참고해서제예제를보시면도움이더잘될것같네요 ^^; 2. FBO 사용시유의점정리 마지막으로질문에는없지만첨언을달자면, 아이폰과안드로이드 (Windows MFC) 에서의 Framebuffer Object 의사용방법에있어 서차이가있습니다. [GLES20] 16. Q/A 몇가지와 FBO 사용시유의점정리 143

Framebuffer 란아래의그림과같이맨마지막단에위치한버퍼로 OpenGL 에서 LCD Buffer 단으로넘어가기직전위치입니다. ( 좀더상세한설명은바른생활님이정리하신 http://cafe.naver.com/gld3d/130 을읽어보십시요 ^^;) 아이폰소스를안드로이드소스로포팅하는중에가장첫번째로부딪히는부분이아마도 Framebuffer Object 사용법이아닐까생각합니다. 제가올린 Android 3D 예제에서도이문제와연관된논리상오류가있습니다. ( 물론동작하는데는문제가없습니다. ^^) 논리상문제가되는부분은렌더러를초기화하는 Initialize 함수에서발생합니다. void RenderingEngine::Initialize(int width, int height) { // Create the depth buffer. glgenrenderbuffers(1, &m_depthrenderbuffer); glbindrenderbuffer(gl_renderbuffer, m_depthrenderbuffer); glrenderbufferstorage(gl_renderbuffer, GL_DEPTH_COMPONENT16, width, height); // Create the framebuffer object. glgenframebuffers(1, &m_framebuffer); glbindframebuffer(gl_framebuffer, m_framebuffer); glframebufferrenderbuffer(gl_framebuffer, [GLES20] 16. Q/A 몇가지와 FBO 사용시유의점정리 144

GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_colorrenderbuffer); glframebufferrenderbuffer(gl_framebuffer, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthrenderbuffer); // Bind the color buffer for rendering. glbindrenderbuffer(gl_renderbuffer, m_colorrenderbuffer); ------------------------ #ifndef PLATFORM_IOS glbindframebuffer(gl_framebuffer, 0); #endif 초기화함수를 FBO 로 m_framebuffer 를생성했는데, 안드로이드와 Windows 에서는이를사용하지않고있습니다. glbindframebuffer(gl_framebuffer, 0); 호출해서 m_framebuffer 를사용하지않고 "0" 번 ID 를사용합니다. 아이폰의경우에는 On Screen Buffer 쪽 FBO ID의경우, System Frame buffer Object 인 0번이아닌, 개발자가 glgen한 ID를사용해야합니다.( 아이폰 3D 프로그래밍책에서도언급이되어있습니다.) 하지만, 안드로이드와 Windows 플랫폼에서는 System Frame buffer Object는 0번으로이미정해져있습니다. 그럼으로 On Screen Buffer 값은 0번이어야합니다. 안드로이드와윈도우즈플랫폼에서는 System Frame buffer Object를확인하기위해서이용되는 glgetintegerv(gl_framebuffer_binding, &m_framebuffer); 이용해서얻을수도있습니다. 이값은 0번이나옵니다. 정리하면, 아이폰소스를안드로이드소스로포팅할경우에는 On Screen Buffer 즉 LCD Buffer 에그리는객체를처리할경우에는 FBO ID 가 0 번이어야정상적으로화면이디스플레이됩니다. -- 추가설명 1 -- On Screen Buffer 라는용어가잘이해가안가신다면, 화면에렌더링한 3D 객체를그려주기위해서, gldrawarray 또는 gldrawelement 함수를호출합니다. 이함수를호출하기직전에지정된 FBO ID가 On Screen Buffer 를가리키는 ID 입니다. [GLES20] 16. Q/A 몇가지와 FBO 사용시유의점정리 145

-- 추가설명 2 -- 그렇다면 FBO를이용해서 Render To Texture 를구현할경우에는더블버퍼링과같이 On Screen Buffer 뿐만아니라 Off Screen Buffer를 FBO로생성합니다. 이때는 On Screen Buffer에해당하는 FBO ID는 0번이되어야하고, 더블버퍼리을위해생성한 Off Screen Buffer용 FBO ID는 glgenframebuffers(1, &m_offscreen_framebuffer); 와같이만들어서, glbindframebuffer(gl_framebuffer, m_offscreen_framebuffer); 함수로바인딩해서그림을그리면됩니다. 좀더상세한설명은 Render To Texture 장을다룰때다시정리해보도록하겠습니다. [GLES20] 16. Q/A 몇가지와 FBO 사용시유의점정리 146

[GLES20] 17. 텍스쳐적용하기앞서서준비사항정리 2012.01.16 13:18 16장부터는 Texture 관련해서정리해보도록하겠습니다. 기본적인목차는다음과같습니다. 1. 이미지파일로딩하기 2. Single Texture 3. Multi Texture 4. Bumpmap Texture 5. Cubemap Texture 6. Render To Texture 7. Blur(Bruteforce blur and Gaussian blur) ( 물론위의목차는제개인적인사정또는갑작스럽게재미분야가생기면변경될수도있습니다. ^^;) 텍스쳐적용하기앞서서준비사항정리 3D 에서 Texture 를적용하기위해서는 PNG, JPG, BMP, TGA, PVR 파일등과같은 2D image 파일들을 decoding 해서, RGBA 형태의 byte array 로변환해줘야합니다. [GLES20] 17. 텍스쳐적용하기앞서서준비사항정리 147

예를들어바나나를 3D 로그리기위해서는다음과같은 2D 이미지파일이필요합니다. ( 이파일은 "http://heikobehrens.net/2009/08/27/obj2opengl/" 에서배포된바나나파일입니다.) 이미지자체는바나나의앞 - 뒷면을보여주는화면입니다. 1. MFC 에서 2D 이미지로딩하기 MFC 에서이미지파일을로딩하는인터넷에서검색하면다양한방법을찾을수있습니다. BMP, TGA 등의 Decoder 는제블로그에서도소개를했었지만, PNG 나 JPG 등을 Decoding 하기위해서는추가라이브러리가있 어야합니다. 여기서작성하는예제들은테스트용임으로 CxImage 라이브러리를이용해서작업하도록하겠습니다.(Version 5.99c) CxImage 라이브러리가 zlib 라이센스를따르는 open source 임으로저작자에대한라이센스명기만해준다면상용화에응용해도 크게문제가되지는않기때문에선택했습니다. 기본적으로사용법도간담합니다. 다운로드받은 CxImage 프로젝트를 Release 로빌드하면, 다음과같은라이브러리를얻을수있습니다. cximage.lib jasper.lib jbig.lib Jpeg.lib png.lib Tiff.lib zlib.lib [GLES20] 17. 텍스쳐적용하기앞서서준비사항정리 148

이미지파일을로딩하는방법도간단합니다. /OGLES2/Platforms/Windows/NativeInterface.cpp -. CxImage 라이브러리호출부분 ImageData CBLoadImage(char* filename, int type) { ImageData imgdata; CxImage image; char resourcepath[256]; sprintf(resourcepath, "../../Textures/%s", filename); image.load(resourcepath, type); // ---- (1) if (image.isvalid()) { int length = (int)image.getsize(); long size = 0; BYTE* buffer = 0; image.encode2rgba(buffer, size); // ---- (2) //CxMemFile memfile; //memfile.open(); //image.encode(&memfile, CXIMAGE_FORMAT_BMP); //BYTE* buffer = memfile.getbuffer(); //long size = memfile.size(); m_pimagedata = new BYTE[size]; memcpy(&m_pimagedata[0], buffer, sizeof(byte)*size); imgdata.size = ivec2((int)image.getwidth(), (int)image.getheight()); imgdata.length = size; [GLES20] 17. 텍스쳐적용하기앞서서준비사항정리 149

imgdata.image_data.resize(size); vector<unsigned char>::iterator data = imgdata.image_data.begin(); for (int i=0; i<size; ++i) { *data++ = (unsigned char)m_pimagedata[i]; // ---- (3) image.freememory(buffer); return imgdata; (1) : CXIMAGE_FORMAT_BMP, CXIMAGE_FORMAT_JPG, CXIMAGE_FORMAT_PNG, CXIMAGE_FORMAT_TGA 와같은 type 에따른이미지파일열기 (2) : 로딩한이미지파일을 RGBA 형태로변환 (3) : RGBA 데이타를 Open GL 에서사용할 byte array 형으로변환한다. -. BMP 파일로딩 ImageData CBLoadImageBmp(string filename) { ImageData imgdata; int len = strlen(filename.c_str()); char* text_utf = new char[len*2+1]; strcpy(text_utf, filename.c_str()); imgdata = CBLoadImage(text_utf, CXIMAGE_FORMAT_BMP); memset(text_utf, 0x00, len*2+1); delete[] text_utf; return imgdata; -. JPG 파일로딩 ImageData CBLoadImageJpg(string filename) {... imgdata = CBLoadImage(text_utf, CXIMAGE_FORMAT_JPG);... [GLES20] 17. 텍스쳐적용하기앞서서준비사항정리 150

-. PNG 파일로딩 ImageData CBLoadImagePng(string filename) {... imgdata = CBLoadImage(text_utf, CXIMAGE_FORMAT_PNG);... -. TGA 파일로딩 ImageData CBLoadImageTga(string filename) {... imgdata = CBLoadImage(text_utf, CXIMAGE_FORMAT_TGA);... CxImage에대해서더궁금하신분은네이버블로그중다음글이잘설명되어있습니다. ^^; http://blog.naver.com/kdha83?redirect=log&logno=98673605 사용법은다음의페이지를참고하십시요. http://www.codeproject.com/kb/graphics/cximage.aspx 2. Android 에서 2D 이미지로딩하기 아마도이강좌를보시는분들은기본적으로안드로이드에서 BMP 나 PNG 이미지로딩은이미경험해보셨을것으로보입니다. 그럼으로 2D 이미지로딩에대한구체적인내용은생략하도록하겠습니다. ^^; 단지 Java 가아닌 NDK 에서 Open GL 를처리하고있음으로, Android API 인 Drawable 이나 InputStream 을이용해얻은 Bitmap 객체를생성한후 RGBA byte array buffer 로변경한후 NDK 단으로내려보내야합니다. 제엔진 (?) 에서는 com.myastro.gles.resourcemanager 에서 bmp, png, tga, jpg 등의이미지파일을디코딩하도록구현되어 [GLES20] 17. 텍스쳐적용하기앞서서준비사항정리 151

있습니다. RGBA byte array buffer 의경우 Java Activity 의초기화시에생성해둘수도있으며, 제가구현한방법과같이 Activity 초기화시 점이아닌, NDK 단에서 Open GL 이초기화되는시점에처리해줄수도있습니다. 즉 NDK와의통신은 Activity main thread에서호출되는형태가아닌, Native 단에 Activity 초기화시점에저장한 g_vm->attachcurrentthread(&env, NULL) 에서 Activity main thread로호출하는방식으로구현되어있습니다. ( 일반적으로 JNI을통한통신이 Java -> JNI -> Native layer 로통신되지만, g_vm를이용해서, Native layer -> JNI -> Java 로도호출도가능합니다. 이부분에대해서는구글링을하시거나제엔진에서 NativeRenderer20.cpp <--> CBJavaHander.java 간의관계를참고하십시요 ^^;) 이미지파일이로딩을위한순서는다음과같이전개됩니다. jni/ogles20/nativerenderer20.cpp ImageData CBLoadImage(string filename) {... JNIEnv* env; g_vm->attachcurrentthread(&env, NULL); // -------------- (1) jmaincls = (*env).findclass(cb_class_main); mloadimage = (*env).getstaticmethodid(jmaincls, CB_CLASS_LOADIMAGE_FUNC, CB_CLASS_LOADIMAGE_SIG); [GLES20] 17. 텍스쳐적용하기앞서서준비사항정리 152

jobject res_texture_description = (jobject)(*env).callstaticintmethod(jmaincls, mloadimage, (*env).newstringutf(text_utf)); // -- (2)... // ---- (3) int width = (int)env->getintfield(res_texture_description, tex_description_field_id.width); int height = (int)env->getintfield(res_texture_description, tex_description_field_id.height); int length = (int)env->getintfield(res_texture_description, tex_description_field_id.length); jobject array_obj = (jobject)env->getobjectfield(res_texture_description, tex_description_field_id.image_data); jboolean iscopy; jbyte* array_element = env->getbytearrayelements((jbytearray)array_obj, &iscopy); imgdata.size = ivec2(width, height); imgdata.length = length; imgdata.image_data.resize(length); vector<unsigned char>::iterator data = imgdata.image_data.begin(); for (int i=0; i < length; i++) { *data++ = (unsigned char)array_element[i]; // ------------------ (4) int len2 = sizeof(array_element); env->releasebytearrayelements((jbytearray)array_obj, array_element, 0); return imgdata; ---------------------------------------------------------------------------- (1) g_vm->attachcurrentthread(&env, NULL); 를통한 Native thread 객체정보획득 (2) CallStaticIntMethod() 함수를통해서 Java 단의 CBJavaHandler에서구현된 _loadimage() 함수호출이지점에서 Native thread는 _loadimage() 함수를통해서해당이미지파일을로딩하기전까지 Lock 된다. NativeRenderer20.CBLoadImage.CallStaticInMethod() --> com.myastro.main.jni.cbjavahandler._loadimage() --> com.myastro.gles.resourcemanager.resourcemanager() --> _loadimage() --> CBLoadImage() 로이동합니다. (3) 로딩한이미지에대한 width, height, byte array buffer 얻기 com.myastro.main.jni.cbjavahandler._loadimage() 함수를통해서얻은 tex_description_field_id 로부터정보를얻습니 다. (4) RGBA 데이타를 Open GL 에서사용할 byte array 형으로변환 [GLES20] 17. 텍스쳐적용하기앞서서준비사항정리 153

이제 Open GL 로 Texture 를그릴때필요한 width, height, RGBA byte array buffer 얻었습니다. 3. 결론 NDK 단에 OpenGL 를구성할경우에는이미지프로세스모듈을 Android.mk 내에구현해서사용하는경우도많습니다. 저는공부차원에서 Android API 이용해서구현해봤으며, 효율성으로따진다면그다지추천하지는못하겠네요 ^^; 굳이 JNI 에오버로드를줄필요는없을테니깐요ㅎㅎ 추가로아이폰과달리안드로이드의경우에는단말마다 GPU 칩이다다릅니다. ( 아이폰은아직까지 PowerVR 칩만사용 ) 이때문에 NDK 에 Open GL 를구성할경우, A 단말에서는동작하는앱이 B 단말에서는동작하지않는문제가발생합니다. 대표적인예가갤럭시 S 와갤럭시탭의경우 Power VR 칩을사용하지만, 갤럭시 S2 의경우 ARM Mali 칩을사용합니다. Power VR 사의 GPU 의경우아이폰에서도사용하기때문에아무래도아이폰에서구현한코드가수월하게포팅이되지만, 갤럭시 S2 의경우에는 FBO 가정상적으로동작하지않는경우가발생합니다. 반대로갤럭시 S2에서는수월하게구현한사항이갤럭시 S나탭에서는비정상동작하는경우도많습니다. -ㅁ-; 예를들어 RTT 구현시 Offscreen size를 NPOT 크기로설정할경우, 갤럭시 S2에서는정상동작하던것이갤럭시탭에서는검정화면만뜨는경우가발생합니다. 이는 Power VR 칩의경우이미지의가로-세로크기가 POT(Power Of Two, 2의거듭제곱, 2x2, 4x4, 64x128) Size여야합니다. 하지만갤럭시 S2의경우 NPOT Size도정상적으로지원합니다. 갤 S2 에서 POT 에신경쓰지않고구현하다가갤탭에서텍스쳐나오지않는다면, POT 를첫번째로의심해보는것도좋습니다. [GLES20] 17. 텍스쳐적용하기앞서서준비사항정리 154

[GLES20] 18. 텍스쳐적용하기 - 2D Texture 2012.01.18 00:41 텍스쳐적용하기 - 2D Texture 아마도이글을읽는분들의경우, 기본적으로 OpenGL 또는 OpenGL ES 1.x에서텍스쳐구현을해보셨을것이라생각됩니다. OpenGL 책을사면텍스쳐에대한설명은필수적이기때문에관련책을읽어보는것을추천합니다. 바른생활님의설명하신 "[ios GLView 만들기 ]13. Texturing" 장에서도설명이잘되어있습니다. All about OpenGL ES 2.x (part 2/3) (http://db-in.com/blog/2011/02/all-about-opengl-es-2-x-part-23/) 여기서는 OpenGL ES 2.0 에서 2D Texture 을사용하기위해서는어떻게처리해야하는지에대해서좀더집중하도록하겠습니다. 1. Texture 그리기전준비사항 텍스쳐의종류에는 2D Texture, Multi Texture, Cubemap Texture, Bumpmap Texture 등등다양한방식이있습니다. (3D Texture 도있지만이는 GL_TEXTURE_3D_OES 인 extension 임으로생략...) 일반적으로가장많이사용하는 2D Texture 에대해서알아보도록하겠습니다. 텍스쳐를그리기위해서는일차적으로는해당이미지파일을로딩한후 buffer array 로변경해야하며, 그다음에는 Texture ID 를생성한후이 ID 에 buffer array 를저장해야합니다. [GLES20] 18. 텍스쳐적용하기 - 2D Texture 155

텍스쳐 ID 생성및이미지 RGBA 버퍼저장 GLuint RenderingEngine::createTexture(const string& file, bool bpot, int wrapmode) { GLuint tex; glgentextures(1, &tex); glbindtexture(gl_texture_2d, tex); loadimage(m_resourcemanager->loadimage(file)); GLenum minfilter = (bpot? GL_LINEAR : GL_LINEAR_MIPMAP_LINEAR); gltexparameteri(gl_texture_2d, GL_TEXTURE_MIN_FILTER, minfilter); gltexparameteri(gl_texture_2d, GL_TEXTURE_MAG_FILTER, GL_LINEAR); if (bpot) { if (wrapmode == 1) { gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); else { gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_S, GL_REPEAT); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_T, GL_REPEAT); else { //gltexparameteri(gl_texture_2d, GL_GENERATE_MIPMAP, GL_TRUE); glgeneratemipmap(gl_texture_2d); return tex; ---------------------------------------------------------- 코드의구성은 OpenGL ES 1.x 와유사합니다. (1) glgentexture() 함수를이용해서 Texture ID를생성하고, (2) glbindtexture() 함수를이용해서생성한 Texture ID를방인딩하고, (3) glteximage2d() 함수를이용해서 "16장 " 에서얻은 RGBA byte array을저장하고, (4) gltexparameteri() 함수를이용해서축소필터 GL_TEXTURE_MIN_FILTER 과확대필터 GL_TEXTURE_MAG_FILTER 를설정할수있습니다. (5) gltexparameteri() 함수를이용해서 GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T 를이용해서 Vertex 점정의경계지점을부드럽게보간시켜주는 GL_CLAMP_TO_EDGE 와텍스쳐를계속반복시킬 GL_REPEAT를설정할수있습니다. 밉맵을사용할경우에는 glgeneratemipmap(gl_texture_2d) 를이용해주고요 축소 / 확대필터 [GLES20] 18. 텍스쳐적용하기 - 2D Texture 156

GL_NEAREST : 인접축소필터로, 가장근접한텍셀이색상을사용한다. 단순하고거칠게표현된다. gltexparameteri(gl_texture_2d, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gltexparameteri(gl_texture_2d, GL_TEXTURE_MAG_FILTER, GL_NEAREST); GL_LINEAR : 양방향선형 (bilinear) 필터링알고리즘으로, 텍셀에서인접한 2x2 점을샘플링하여가중치평균값을구한다. gltexparameteri(gl_texture_2d, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gltexparameteri(gl_texture_2d, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GL_LINEAR_MIPMAP_LINEAR : 삼중선형 (TRILINEAR) 필터링으로가장근접한밉맵두개를찾아서각각 GL_LINEAR 필터링한한결과값을섞는다. OpenGL은이필터링을위해서 8개의샘플을추출하므로, 가장높은품질의값을생성하는필터이다. gltexparameteri(gl_texture_2d, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); gltexparameteri(gl_texture_2d, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 밉맵은축소필터에만영향을미치며, OpenGL ES 2.0에서사용할경우에는자동밉맵함수인 glgeneratemipmap(gl_texture_2d); 이용해서처리가능하다. 이제 Texture ID 를생성하였음으로, 생성한 Texture ID 를이용해서화면에어떻게렌더링하는지에대해서알아보도록하겠습니다. 2. 2D Texture 앞장까지의강좌에서는 Vertex Coordination( 정점좌표 ) 와 Normal Coordination( 법선좌표 ) 를다뤘었습니다. 정점좌표를이용해서삼각형, 큐브, 원뿔, 구, 뫼뷔우스띠등과같은 Geometry( 기하 ) 를그렸으며, 법선좌표를이용해서삼각형의각면등에조명 (Light) 효과를보여줬었습니다. 마찬가지로텍스쳐를 Geometry에표현하기위해서는텍스쳐좌표가필요합니다. ( 텍스쳐좌표를 Texture Element라부르며짧게텍셀 (texel) 이라고합니다.) OpenGL에서 texel은기존 pixel과다릅니다. 다음의그림을보시면, 픽셀은 Left-Top이원점 (base point) 입니다. 하지만, Open GL에서의원점은 Left-Bottom 입니다. ( 원점을 Anchor point 라고도부르기도합니다.) [GLES20] 18. 텍스쳐적용하기 - 2D Texture 157

상자와에디이미지를예로들어보면정상 (normal) 이미지 (pixel 순서 ) 의경우왼쪽과같으며, 이이미지를 OpenGL 에저장하면, 오른쪽과같이뒤집혀서들어가기때문에 OpenGL 로화면을그리면뒤집혀있게됩니다. [GLES20] 18. 텍스쳐적용하기 - 2D Texture 158

해결방법은여러가지가있습니다. (1) 이미지자체를수직 ( 세로 ) 방향으로뒤집어서저장해둔다. (2) 이미지를읽은후 Affine coordination( 아핀좌표계 ) 를 api 등을이용해서뒤집어서 OpenGL에전달한다. (3) OpenGL에서이용할 texel의 y 좌표에 -1.0f 를곱해줌으로서항상뒤집어지게한다. (4) Vertex Shader에서 vertex position에해당하는값 ("gl_vertex.y" to "a_position.y") 에 -1.0f를곱해줌으로서항상뒤집어지게한다. ("a_position" 은제가이용하는 attribute vec4 a_position; 변수로서변수명은바꿔서사용해도됩니다. ^^;) ----------------------------- 여기서주의할점은 (3) 번과 (4) 번의경우 Render To Texture 를이용해서화면을표현해줄때 RTT한이미지가 Left-Top 으로그려진상태에서텍셀의 y축에음수가곱해져서 Left-Bottom 순서로다시뒤집어지는문제가발생할수도있음으로, 제개인적인의견으로는 (1) 번과 (2) 번방법이적당할것같습니다. 3. Draw Banana 이제바나나를기준으로실제 Shader 을이용해서 Texture Rendering 하는부분에대해서보겠습니다. 렌더링의절차를그림으로표현하면다음과같습니다. [GLES20] 18. 텍스쳐적용하기 - 2D Texture 159

( 소스코드분석에앞서서다음장에서다룰 MultiTexture 와혼합되어있기때문에일반적인방법과는좀다를수있습니다.) Classes/RenderingEngine.CH03.ES20.cpp 렌더링하기전초기화부분정리 void RenderingEngine::Initialize(const vector<isurface*>& surfaces) {... m_textures.banana = createtexture(texturefiles[banana], true); // ---- (1) [GLES20] 18. 텍스쳐적용하기 - 2D Texture 160

// Initialize OpenGL ES STATE Initialize(width, height); // ------------------- (4)... // ------------------- (2) GLuint RenderingEngine::createTexture(const string& file, bool bpot, int wrapmode) { GLuint tex; glgentextures(1, &tex); glbindtexture(gl_texture_2d, tex); loadimage(m_resourcemanager->loadimage(file)); // ------------------- (3) GLenum minfilter = (bpot? GL_LINEAR : GL_LINEAR_MIPMAP_LINEAR); gltexparameteri(gl_texture_2d, GL_TEXTURE_MIN_FILTER, minfilter); gltexparameteri(gl_texture_2d, GL_TEXTURE_MAG_FILTER, GL_LINEAR); if (bpot) { if (wrapmode == 1) { gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); else { gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_S, GL_REPEAT); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_T, GL_REPEAT); else { //gltexparameteri(gl_texture_2d, GL_GENERATE_MIPMAP, GL_TRUE); glgeneratemipmap(gl_texture_2d); return tex; ------------------------------------------------------ (1) : 바나나텍스쳐생성 createtexture() 함수를이용해서 "banana.jpg" 파일을읽은후 Texture ID를생성합니다. 이생성한 Texture ID로바나나를 3D로렌더링하기전에 glbindtexture(gl_texture_2d, tex_id) 를해주면됩니다. ( 렌더링하기전이란.. gldrawarrays나 gldrawelements 를호출하기전이라고보면될것같습니다.) (2) : createtexture() 함수위에서도설명했었지만텍스쳐파일을로딩한후 Texture ID를생성하는함수입니다. 주의할실점은바른생활님블로그에도언급되었다싶이, 밉맵사용시에는이미지파일을로딩한후 OpenGL에저장하는함수인 glteximage2d() 함수처리를앞에서먼저해줘야정상적으로동작합니다. [GLES20] 18. 텍스쳐적용하기 - 2D Texture 161

(3) : loadimage() 함수 파일을로딩하고 array buffer 를 glteximage2d() 함수를이용해서 OpenGL 에저장하는역활을합니다. (4) : Initialize() 함수 OpenGL ES20과관련한 VERTEX SHADER, FRAGMENT SHADER STATE를초기화는부분입니다. 이전장과다른점은 texture 를사용할것임으로다음과같이두가지가추가됩니다. VERTEX SHADER에선언된 texel 과관련한 attribute 형인 "attribute vec2 a_texturecoordin;" 와연결되는 m_pixellight.attributes.texturecoord = glgetattriblocation(program, "a_texturecoordin"); 부분과 FRAGMENT SHADER에선언된 sampler2d 형인 "uniform sampler2d Sampler0;" 와연결되는 gluniform1i(m_pixellight.uniforms.sampler0, 0); 를추가되었습니다. (Texture 를다룰때 sampler2d 형인 Sampler0 에 0 값으로초기화해주지않는책이나코드가간혹있습니다. Single Texture 일경우에는 0 으로초기화해주지않아도정상적으로동작은합니다. 하지만 Multi Texture 까지고려해서프로그램을짤것임으로 Sampler0 에는 0 으로초기화해주는것이좋습니다.) Classes/RenderingEngine.CH03.ES20.cpp 렌더링부분정리 void RenderingEngine::Render(const vector<visual>& visuals) {... // PixelLight bool bispixellight = true; // ---- (1) ProgramHandles handler; handler = m_pixellight; gluseprogram(handler.program); // Initialize various state. glenablevertexattribarray(handler.attributes.position); glenablevertexattribarray(handler.attributes.normal); glenablevertexattribarray(handler.attributes.texturecoord); // ---- (2)... [GLES20] 18. 텍스쳐적용하기 - 2D Texture 162

// Set the Texture Mode if (bispixellight) { glactivetexture(gl_texture0 + TEXTURE_0); if (visualindex == 9) { glbindtexture(gl_texture_2d, m_textures.banana); // --------- (3) gluniform1i(handler.uniforms.sampler0, TEXTURE_0); else { glbindtexture(gl_texture_2d, m_textures.crate); gluniform1i(handler.uniforms.sampler0, TEXTURE_0);... // Draw the surface. int stride = 2 * sizeof(vec3) + sizeof(vec2); // --------- (4) GLvoid* offset = (GLvoid*) sizeof(vec3); // --------- (5) GLint position = handler.attributes.position; GLint normal = handler.attributes.normal; GLint texture = handler.attributes.texturecoord; // --------- (6) vec4 white(1, 1, 1, 1); if (drawable.indexcount < 0) { glbindbuffer(gl_array_buffer, drawable.vertexbuffer); glvertexattribpointer(position, 3, GL_FLOAT, GL_FALSE, stride, 0); glvertexattribpointer(normal, 3, GL_FLOAT, GL_FALSE, stride, offset); offset = (GLvoid*) (sizeof(vec3) * 2); // --------- (7) glvertexattribpointer(texture, 2, GL_FLOAT, GL_FALSE, stride, offset); // --------- (8) gldrawarrays(gl_triangles, 0, drawable.vertexcount); // --------- (9) else { // --------- (10) glbindbuffer(gl_array_buffer, drawable.vertexbuffer); glvertexattribpointer(position, 3, GL_FLOAT, GL_FALSE, stride, 0); glvertexattribpointer(normal, 3, GL_FLOAT, GL_FALSE, stride, offset); offset = (GLvoid*) (sizeof(vec3) * 2); glvertexattribpointer(texture, 2, GL_FLOAT, GL_FALSE, stride, offset); glbindbuffer(gl_element_array_buffer, drawable.indexbuffer); gldrawelements(gl_triangles, drawable.indexcount, GL_UNSIGNED_SHORT, 0); gldisablevertexattribarray(handler.attributes.position); gldisablevertexattribarray(handler.attributes.normal); gldisablevertexattribarray(handler.attributes.texturecoord); // --------- (11) // VBO을 UnBind 해야 VBA와충돌이없다. // --------- (12) glbindbuffer(gl_array_buffer, 0); [GLES20] 18. 텍스쳐적용하기 - 2D Texture 163

glbindbuffer(gl_element_array_buffer, 0); /* Flush all drawings */ glfinish(); error = checkglerror(); if (!m_spinning) { xrot += xspeed; /* Add xspeed To xrot */ yrot += yspeed; /* Add yspeed To yrot */ glbindtexture(gl_texture_2d, 0); // --------- (13) gluseprogram(0); // --------- (14) ------------------------------------------------------ (1) : bispixellight 변수설정합니다. bool bispixellight = true; handler = m_pixellight; gluseprogram(handler.program); (2) Texture Coordination Array 를활성화해줍니다. "glenablevertexattribarray(handler.attributes.texturecoord);" 기존 GLES1.1 기준으로는 "glenableclientstate(gl_texture_coord_array);" 함수를대체하는부분입니다. 활성화와반대되는비활성화는 (11) 번함수 " gldisablevertexattribarray(handler.attributes.texturecoord);" 가쌍으로있어야합니다. (3) Banana texture id를바인딩해줍니다. 생성한텍스쳐를 3D 렌더링객체에적용하기위해서는해당 texture id를바인딩해줘야합니다. glbindtexture(gl_texture_2d, m_textures.banana); gluniform1i(handler.uniforms.sampler0, TEXTURE_0); (Sampler0에대해서 0번으로재설정해주는부분은없어도상관은없지만, 명시적표현으로나뒀습니다.) ( 간혹 Shader에정의된 sampler2d 변수에 glgentexture() 함수를이용해서생성한 texture id에 -1를한후넣어주는코드가있는경우가있습니다. 이는 glgen() 시에처음생성된 ID가 1이기때문에그렇게하는경우입니다. 문제는 Galaxy Tab과같은 PowerVR GPU의경우, 처음생성시 1이아닌 3xxxxx 번등과같은높은숫자가나오는경우가있습니다. 그럼으로명시적으로그냥첫번째 sampler2d 변수에는 0을두번째는 1를넣어주는것이좋을것같습니다.) (4) Vertex, Normal, Texture coord 에대한 memory chunk 영역을설정해줍니다. banana.obj를해석한후 Vertex Buffer Object(VBO) 에저장해놓았음으로, OpenGL에게메모리가한묶음에해당하는크기를알려줘야합니다. 이역할을하는부분이 stride 입니다. int stride = 2 * sizeof(vec3) + sizeof(vec2); [GLES20] 18. 텍스쳐적용하기 - 2D Texture 164

"VertexCoord(12) + NormalCoord(12) + TextureCoord(8) = 32" 가됩니다. OpenGL ES 2.0 Programming Guide 책에서는이부분을 Array Of Structures(Page 104) 라고표현합니다. (5) Array Of Structures 에서 Normal Coord에대한 offset 을설정합니다. GLvoid* offset = (GLvoid*) sizeof(vec3); VertexCoord 에대한크기만큼이동시켜줄것임으로 12 bytes 입니다. 전달은 GLvoid* 형태로캐스팅해서주소로넘겨줍니다. (6) VERTEX SHADER내의 Texture Attrbute 와연결된 ID 입니다. GLint texture = handler.attributes.texturecoord; PixelLighting.vert 내에선언된 "a_attribute vec2 a_texturecoordin;" 연결가능한 Attribute ID 입니다. 이 ID를이용해서 (8) 번항목에서 Vertex Attribute Pointer 등록시사용하게됩니다. (7) Array Of Structures 에서 Texture Coord에대한 offset 을설정합니다. offset = (GLvoid*) (sizeof(vec3) * 2); VertexCoord 와 NormalCoord에대한크기만큼이동시켜줄것임으로 24 bytes 입니다. 전달은 GLvoid* 형태로캐스팅해서주소로넘겨줍니다. (8) 텍셀정보를저장합니다. 텍셀좌표에대한정보를 "glvertexattribpointer(texture, 2, GL_FLOAT, GL_FALSE, stride, offset);" 함수를이용해서저장합니다. 기존 GLES1.1 기준으로는 "gltexcoordpointer(2, GL_FLOAT, 0, texcoords);" 함수를대체하는부분입니다. (9) Vertices 방식으로렌더링을합니다. gldrawarrays(gl_triangles, 0, drawable.vertexcount); banana.obj 는 gldrawarray 방식으로표출되게금 header 형태로만들었기때문에이부분에서그려지게됩니다. (10) Indices 방식으로렌더링을합니다. gldrawelements(gl_triangles, drawable.indexcount, GL_UNSIGNED_SHORT, 0); ParametricSurface 로만들어진 Geometry 들은 Indices 로만들어졌음으로이부분에서그려지게됩니다. (11) Texture Coordination Array 를비활성화해줍니다. "gldisablevertexattribarray(handler.attributes.texturecoord);" [GLES20] 18. 텍스쳐적용하기 - 2D Texture 165

함수는기존 GLES1.1 기준으로는 "gldisableclientstate(gl_texture_coord_array);" 함수를대체하는부분입니다. 비활성화와반대되는활성화는 (2) 번함수 "glenablevertexattribarray(handler.attributes.texturecoord);" 가 쌍으로있어야합니다. (12) VBO 객체를 UnBind 합니다. Vertex Buffer Array 형태로그려지는렌더러와충돌하지않도록 VBO 를해제해줍니다. (13) 바인딩했던텍스쳐를해제해줍니다. glbindtexture(gl_texture_2d, 0); (14) PixelLighting 쉐이더를해제해줍니다. gluseprogram(0); 4. 좀더파고들기 추가적으로이미지뒤집기기능을좀더정리해보도록하겠습니다. 이미지뒤집기 API 정리 앞에서언급했지만, 이미지파일를로딩한후 raw data 를뒤집을필요가있습니다. [GLES20] 18. 텍스쳐적용하기 - 2D Texture 166

이때이용하는것이 Affine Transformation API 입니다. 일반적으로아이폰에서는다음과같이적용하면됩니다. TextureDescription LoadImage(const string& file) { NSString* basepath = [NSString stringwithutf8string:file.c_str()]; NSString* resourcepath = [[NSBundle mainbundle] resourcepath]; NSString* fullpath = [resourcepath stringbyappendingpathcomponent:basepath]; UIImage* uiimage = [UIImage imagewithcontentsoffile:fullpath]; TextureDescription description; description.size.x = CGImageGetWidth(uiImage.CGImage); description.size.y = CGImageGetHeight(uiImage.CGImage); description.bitspercomponent = 8; description.format = TextureFormatRgba; description.mipcount = 1; int bpp = description.bitspercomponent / 2; int bytecount = description.size.x * description.size.y * bpp; unsigned char* data = (unsigned char*)calloc(bytecount, 1); CGAffineTransform transform = CGAffineTransformIdentity; //if (flipimage) { transform = CGAffineTransformTranslate(transform, 0, description.size.y); transform = CGAffineTransformScale(transform, 1.0, -1.0); CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); CGBitmapInfo bitmapinfo = kcgimagealphapremultipliedlast kcgbitmapbyteorder32big; CGContextRef context = CGBitmapContextCreate(data, description.size.x, description.size.y, description.bitspercomponent, bpp * description.size.x, colorspace, bitmapinfo); CGContextConcatCTM(context, transform); CGColorSpaceRelease(colorSpace); CGRect rect = CGRectMake(0, 0, description.size.x, description.size.y); CGContextDrawImage(context, rect, uiimage.cgimage); CGContextRelease(context); m_imagedata = [NSData datawithbytesnocopy:data length:bytecount freewhendone:yes]; return description; [GLES20] 18. 텍스쳐적용하기 - 2D Texture 167

안드로이드에서는다음과같이적용할수있습니다. com.myastro.gles.resourcemanager.java public ResourceManager(Context context, int res) { // Get the texture from the Android resource directory Bitmap bitmap = null; InputStream is = context.getresources().openrawresource(res); try { // BitmapFactory is an Android graphics utility for images bitmap = BitmapFactory.decodeStream(is); finally { //Always clear and close try { is.close(); is = null; catch (IOException e) { // 비트맵변환을위한행렬을만든다. Matrix mirrormatrix = new Matrix(); // 행렬좌표를 Y축으로뒤집는다. mirrormatrix.prescale(1.0f, -1.0f); // 생성된변환행렬을이용해서새로 bitmap을생성한다. 속도는??? Bitmap tranformedbitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getwidth(), bitmap.getheight(), mirrormatrix, false); bitmap.recycle(); if (tranformedbitmap!= null) { initbitmap(tranformedbitmap); 5. 결론 텍스쳐적용하기를다루면서어떻게정리할까고민했는데, 대략적인정리가된것같네요. [GLES20] 18. 텍스쳐적용하기 - 2D Texture 168

텍스쳐장은책에서이미많이소개하고있는부분이어서 OpenGL 책을보시는게이해가더빠를수도있습니다. 여기에정리된사항은책을보고만들어보시고이런방식으로 ES2.0 으로적용도가능하다는관점으로보시면좋을것같습니다. 또한제가정리한방식은최적화되어있지않음으로참조만하시기바랍니다. ^^; OGLES20 Template Application ported to mfc and android http://code.google.com/p/myastro/downloads/list http://code.google.com/p/myastro/downloads/detail? name=ogles20application.release.v.1.3.0.zip&can=2&q=#makechanges [GLES20] 18. 텍스쳐적용하기 - 2D Texture 169