요즘 핫한 Node.js 환경설정을 해봅시다.


뭐, 시작하기 앞서 또 잡설이지만,,,

솔직히 이녀석이 웹시장에서 뜰줄은 알고는 있었지만 이정도 일줄은 예상치 못했다.

내가 Node.js 를 처음 접했을 때가 2012년도 였는데 그 때엔 v0 버전대의 알파버전이 나오던 시기였다.

이 녀석을 처음 접했을 때 가장 편했던(?) 점은 역시 자바스크립트를 사용한다는 점이었다.

PHP나 JSP 처럼 새로운 문법을 배울 필요가 없다는 점은 분명 정말 엄청난 메리트였다.

또한 C/C++로 짜여진 네이티브 프로그램을 모듈로 실행시킬수 있다는 점 또한 다른 서버사이드 언어들과의 큰 차별점이지 않나 싶다. 

뭐 처음에는 이정도 감상으로 정말 꽤 괜찮은 물건이 나왔네 하며 감탄을 하였었다.

하지만 요즘은... 쏟아지는 패키지에 모듈에,,, 어디서부터 손대야 할지를 모르겠다.

분명 개발은 쉬워졌지만, 익혀야 할게 너무 늘었다는게 문제다.

게다가 한번 이러한 모듈들이 익히고 나서 계속 써먹을 수 있는게 아닌게 문제다.

나중에 써먹으려고 다시 보면 뭔가 메이저한 업데이트가 있어서 많이 달라져 있다거나, 더 좋은 모듈이 나오거나 해서 완전히 새로 익혀야하는 경우가 쉽상이기 때문이다...

그렇지 않은 모듈 생태계가 어디있겠냐만은 Node.js 쪽은 특히나 심한 것 같다...


위에서 너무 단점만 말한 것 같지만 뭐, 그래도 아무튼 좋은 녀석이고 웹 생태계 변화에 엄청난 공을 한 놈은 틀림없는 사실이다.




1. 왜 NVM인가

node.js를 설치하는 가장 간단한 방법은 역시 apt 패키지 관리자를 사용하는 것이다.


root@glassylife:~# apt-get install nodejs npm

캬~ 넘나 간단한 것.

근데 이방법에는 약간의 문제가 있다.

사실 짚으려는 문제의 원인은 npm을 통해 설치할 모듈에 있는거라

여기서 문제라고 언급하기엔 조금 지나친 감이 있지만...

어떤 것이냐면 다음과 같다.


'고오오올져스한 웹서버'를 만들기 위해 필요한 어떤 노드 모듈 A 가 있다.

근데 얘가 오래전에 만들어진거라 v4 이하의 Node 에서만 동작한다.

참고로 Node.js 최신 버전은 LTS 기준 v6.9.4 이다.

당연히 난 더 이상 업데이트도 안하는 이 망할 모듈을 버리고 더욱 세련된 다른놈을 사용하고 싶다.

그렇지만 내가 원하는 기능을 제공하는 모듈은 저것 하나뿐이다...

어떡하겠는가.

선택지는 셋 중 하나다.

1. 새로 만들거나

2. 모듈을 사용할 수 있는 버전대로 node.js를 다운그레이드하거나

3. 때려치거나...


1번 선택지도 충분히 고려해볼만 하지만 이 포스팅의 목적은 NVM을 사용하는 것이기 때문에 나는 2번 선택지를 고른다.

그래서 node.js 를 v4.0 으로 다운그레이드했다.

좋다 여기까진.


근데 이번엔 '그레에에에잇트한 웹서버'를 만들고 싶다.

여기에 필요한건 단지 최신버전의 node.js 이다.

기껏 다운그레이드 시켜놧더니 최신버전이라니...

어디의 금수저씨는 리소스 제한도 있겠다 서버 한 대 새로 장만하시면 되겠지만 흙수저는 그럴 수 없다.(억지스럽지만 그냥 그렇다 하자)

'고오오올져스한 웹서버'가 돌고있는 동일한 머신에서 '그레에에에잇트한 웹서버' 소스를 작성하고 실행시키려는데 문제가 발생한다.

node.js 패키지는 글로벌로 깔려있어서. 최신버전으로 다시 업그레이드하면 '고오오올져스한 웹서버'는 못돌린다..


역시 서버 한 대를 새로 장만하고 싶은 마음이 굴뚝 같지만 앞서 말했듯이 난 흙수저라 어떻게든 한 대의 자원으로 해결을 봐야 한다

이 때 쓸 수 있는게 바로 NVM(Node Version Manager) 이다.

NVM 통해 각각 다른 버전의 node 로 서버를 돌리면 된다.

사실 예로 든게 억지스러울 순 있다.

NVM의 컨셉이해를 위한 예시니 그러려니하자.

그렇다고 아주 허무맹랑한 예시는 아니다

최근에 겪은건 아니지만 2년전인가 5년전에 확실히 나도 위와 같은 상황에 당면한 경험이 있다.


나는 그 때 NVM을 이용해본 이후로 혹시 모를 상황에 대비해 그냥 습관적으로 설치하게 되었다.




2. NVM 설치

뭐 암튼 각설하고 NVM을 설치해보자.


curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.0/install.sh | bash


현재 18 / 01 / 2017 (GMT) 를 기준으로 0.33 버전이 최신인가 보다.

또는 다음 링크에서 최신버전을 확인 및 다운 받을 수 있다.


https://github.com/creationix/nvm#install-script



윗 명령어를 사용하면 알아서 쉘스크립트 실행하여 NVM를 설치 해준다.

완료가 되었다면 putty를 다시 껏다가 키거나 모종의 방법으로 세션을 다시 열자.

또는 그렇게 하지않고 source 명령어로 NVM을 바로 실행시킬 수 있다.


root@glassylife:~# source ~/.bashrc

nvm --version 명령어로 잘 설치되었나 확인!


root@glassylife:~# nvm --version
안 되는 분이 계신다면... 리포트 부탁드린다...
나는 한번에 되서 문제점을 인지할 수가 없당.
여기까지 node version을 컨트롤해주는 NVM설치를 마쳤다.



3. node & npm 설치

nvm --help 명령어로 자세한 사용법을 알아볼 수 있다.

하지만 원하는건 최신버전 및 특정버전의 node를 설치 및 사용하는 것이므로 자세한 사용법은 스스로 찾아 공부하도록 하자.


최신버전 및 특정버전 node.js 를 설치하는 방법은 각각 다음과 같다.

npm은 해당 노드버전이 배포될 때 같이 배포되었던 버전으로(아마도?) 같이 설치되므로 신경쓸 필요가 없다.


root@glassylife:~# nvm install node
root@glassylife:~# nvm install v0.12.18

nvm install node 하면 현재 최신버전의 node를 받게 되고, LTS 버전이 필요하다면 --lts 옵션을 붙여준다.



그리고 nvm으로 설치된 노드 버전들을 다음 명령어로 확인할 수 있다.


root@glassylife:~# nvm ls


다음 명령어로 디폴트 node 버전을 선택할 수 있다.


root@glassylife:~# nvm use v0.12.18

또는 특정 버전으로 node를 실행 시킨다.

root@glassylife:~# nvm run 0.12.18 app.js



이렇게 설치했던 각각의 노드버전들은 ~/.nvm/versions/node 폴더에 잘 정리되어있다. 심심하면 한번 ~/.nvm의 구조를 들여다봐도 좋을 듯 싶다.







Posted by nfyfamraa
,

서버쪽 개발을 하는 사람들을 보면 다들 자기만의 툴사용 방식이 있는데,

나같은 경우 putty로 직접 서버에 붙어서 하는 편이다.


들어가기 앞서 내가 보았던 서버 개발 방식을 몇가지 이야기하려하는데 잡설이므로 스크롤을 내려도 좋다.

조금내려서 1. Vundle 다운로드 항목부터 보시길.


가장 기본적인 방법으로는 로컬컴에서 코딩을 한 후 그 수정본을 ftp로 업로드해서

확인해보는 것이다.

이 방법은 '서브x임' 같이 자신이 평소 사용하던 편집기를 사용할 수 있다는 점을 장점으로 꼽을 수 있다.

그런데 이건 매번 수정할 때마다 업로드를 위해 '파일x라'같은 ftp 툴을 이용해야 한다는 점이 매우 번거롭다.


이보다 좀 더 진화한 방법은, 로컬컴에서도 같은 서버를 만들어 놓고, 로컬에서 테스트를 거치면서 개발을 하다가 메이저한 업데이트가 있을 때에만 서버에 소스를 업로드하여 동기화하는 것이다. 

이 방법은 처음에 언급했던 방법에서 장점은 그대로 보존하고 단점은 보완한 방식이라고 할 수 있다. 

하지만 이러한 방식은 윈도우-리눅스 간의 크로스 플랫폼을 지원하지 않는 서버 프로그램엔 적용할 수 없다는 단점이 있다.

뭐 단점이라고 해봤자 대부분 지원하니까 크게 느껴지진 않을테지만 말이다.

대신 크로스 플랫폼을 지원하는 프로그램이더라도 플랫폼마다 설정방식의 차이가 있어 버그가 발생할 수 있는 경우를 고려해야 한다.

그리고 이 방식은 서버에 직접 붙어서 작업을 하는 것이 아니므로 작업하다 뭔가를 날리거나 하는 등의 실수를 하더라도 안전하다는 장점이 있다.

이런 점에선 내가 사용하는 방법보다 더 좋다고 볼 수 있다.


마지막으로 내가 사용하는 방법은 앞에서 말했듯이 그냥 바로 서버에 붙어 작업하는 것이다.

오로지 콘솔만으로... 문자로만 이루어진 스크린을 앞에 두고 과거 선인들이 느꼈던 고뇌(?)를 몸소 체험해 가면서 말이다.

이러한 방식의 장점은, 개발할 수 있는 장소가 물리적으로 한정되어 있지 않다는 점이다.

위에서 서술했던 두 방법은 평소 자신이 작업하던 환경이 아닌 새로운 곳에서 작업을 하려면 개발환경 세팅을 처음부터 다시해야 한다.

하지만 서버에 직접 붙는다면 처음 한번만 하고나선 나중에 터미널로 붙기만 하면되니, 직장에서든 집에서든 PC방(?)에서든 작업을 이어서 할 수 있다. 

PC방같이 공개장소에서 접속하는 경우 PK관리같은 각별한 보안 관리가 필요하겠지만...

뭐 암튼 그런 장점이 있다.

단점이라면... 그 외 모든것?

ㅋㅋㅋ 그래도 일단 익혀두면 그 성취감과 편리함이 단점들을 모두 커버하는 것 같다.



1. Vundle 다운로드

vi 플러그인 관리자 툴인 Vundle 을 다운로드하자.

먼저 git이 필요하다. git을 설치하자


root@glassylife:~# apt-get update
root@glassylife:~# apt-cache search git | grep '^git '
root@glassylife:~# apt-get install git

일단 나는 root 로 접속했기 때문에 그냥 했는데 일반계정으로 접속했다면


NormalAccount@glassylife:~# sudo apt-get update

와 같이 실행하려는 명령어 앞에 sudo를 붙이면 된다.

만에 하나 sudo를 사용해도 권한에러가 발생한다면 구글에 "sudoers"라는 키워드를 이용해 검색해보라.


일반 유저래도 흙수저 은수저가 있다. sudo를 사용할 수 있는 은수저가 되기 위해선 sudoers 라는 그룹에 속해 있어야 한다.

update는 로컬과 원격의 패키지 저장소의 데이터를 싱크시켜주는데 패키지를 설치할 때 습관적으로 한번씩 해주자.

패키지 저장소라고는 했는데, 말에 조금 어폐가 있지만 딱히 적당한 표현이 생각나질 않아서 일단 그냥 두고 나중에 수정해야겠다.

여기서 말하는 패키지 저장소는 실제로 패키지의 바이너리 데이터를 보관하는 게 아니라 패키지의 바이너리 데이터가 어디에 있는지, 버전은 몇인지와 같은 메타데이터를 관리하는 저장소이다.


apt-cache search 는 로컬 패키지 저장소에 찾으려는 패키지가 있는지 검색한다. 

없으면 패키지가 있는 원격저장소를 직접 찾아 등록시켜줘야한다.

대부분 잘 될것이기 때문에 이 명령어는 사용하지 않아도 되지만, 혹시라도 다음과정에서 git 패키지 설치가 안 되는 분은 로컬 저장소에서 git을 검색해 있는지 확인해 보고 없다면 갓-구글에게 "restore ubuntu default repo"나 이와 비스무리한 키워드로 검색해보길 바란다.

다음으로 apt-get install git 명령어로 git을 설치한다.


이제 git으로 Vundle 소스를 clone 한다. 


root@glassylife:~# git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim




2. Vundle 설정 및 플러그인 설치

별거 없다. Vundle 깃헙에 있는 Quick Start 항목을 그대로 따라한다.

Vundle 소스를 받은 후 ~/.vimrc 파일을 수정해준다. 없으면 만들어 준다.


root@glassylife:~# vi ~/.vimrc


일종의 vim 스크립트 파일인데 다음과 같은 코드를 넣어줘야 vi를 사용할 때 Vundle이  작동한다.


set nocompatible " be iMproved, required filetype off " required " set the runtime path to include Vundle and initialize set rtp+=~/.vim/bundle/Vundle.vim call vundle#begin() " alternatively, pass a path where Vundle should install plugins "call vundle#begin('~/some/path/here') " let Vundle manage Vundle, required Plugin 'VundleVim/Vundle.vim' " The following are examples of different formats supported. " Keep Plugin commands between vundle#begin/end. " plugin on GitHub repo Plugin 'tpope/vim-fugitive' " plugin from http://vim-scripts.org/vim/scripts.html Plugin 'L9' " Git plugin not hosted on GitHub Plugin 'git://git.wincent.com/command-t.git'

" The sparkup vim script is in a subdirectory of this repo called vim. " Pass the path to set the runtimepath properly. Plugin 'rstacruz/sparkup', {'rtp': 'vim/'} " All of your Plugins must be added before the following line call vundle#end() " required filetype plugin indent on " required " To ignore plugin indent changes, instead use: "filetype plugin on " " Brief help " :PluginList - lists configured plugins " :PluginInstall - installs plugins; append `!` to update or just :PluginUpdate " :PluginSearch foo - searches for foo; append `!` to refresh local cache " :PluginClean - confirms removal of unused plugins; append `!` to auto-approve removal " " see :h vundle for more details or wiki for FAQ " Put your non-Plugin stuff after this line


나중에 플러그인을 추가할 때 여기에서 플러그인을 눈치껏 추가해주면 된다. 

소스 중간쯤에 보이는 Plugin 'plugin_name' 과 같은 형식으로 추가해주면 된다.

그럼 플러그인 추가 방법도 알았겠다 NERDTree를 추가해보자


참고로 NERDTree 플러그인은 pwd를 기준으로 디렉토리 구조를 비주얼라이징 해주거나 파일을 열 수 있도록 도와준다.

코딩을 하다보면 여기저기에 나눠진 소스들을 확인하고 수정하게 되는데,

이 때 소스경로를 직접 입력하지 않고도 파일에 접근 할 수 있게 도와준다.

써보면 안다 얼마나 편한지를...


다음과 같이 rstacruz/sparkup 다음줄에 밑의 소스 2줄을 추가해준다.

참고로 쌍따옴표(")는 주석이다.


...
Plugin 'rstacruz/sparkup', {'rtp': 'vim/'}
" NERDTree Plugin
Plugin 'scrooloose/nerdtree'


코드를 넣었으면 저장을 하고, 닫는다.

그리고 다음 명령어로 플러그인 설치를 한다.


root@glassylife:~# vim +PluginInstall +qall


또는 vim의 명령어 모드에서 :PluginInstall 로도 설치할 수 있다.


이제 vim에 들어가서 명령어 모드에서 :NERDTreeToggle 를 입력해 보자

다음과 같이 왼쪽에 뭔가 불쑥 튀어 나온다. 오오...

나는 현재 빈 폴더에서 vi 를 실행시켜서 특이할만한게 없다.



이미지나 직접 확인해 볼 수 있듯이 사용설명을 보려면 '?'을 입력하면 된다.

그 외에 기본적인 사용 방법은 


Ctrl+h,j,k,l 로 윈도우를 옮겨다니거나

:q 로 윈도우를 닫을 수 있다.


3. Vi 설정

매번 명령어를 입력해 NERDTree를 불러오기 귀찮으니까 단축키로 등록해 놓자.

F7키에 등록 시켜놓는게 일반적이다.


다시 ~/.vimrc 파일을 열고 맨 끝에 다음 소스를 추가한다.


...
" map NERT Tree to key 'F7'
nmap <F7> :NERDTreeToggle<CR>


추가한 김에 간단한 vi 설정도 넣자...

마찬가지로 맨 끝에 넣는다


...
" vim settings
set number           " show line number
set ts=2             " tab size is 2 spaces
set autoindent       " use auto indent
set cindent          " use C style indent
set smartindent      " use smart indent
set shiftwidth=2     " shifting size is 2 spaces
set paste            " turn off autoindent when you paste code
set hlsearch         " hilight searching results
set noexpandtab      " turn off auto replacing tab with spaces

뭐... 그럭저럭 쓸만한데 이쁘지가 않다.

color scheme을 지정해보자


다시 .vimrc로 돌아가서.. 플러그인을 추가해준다.

NERDTree 플러그인 추가해줬던 줄 밑에 다음 소스를 추가해 준다


...
Plugin 'scrooloose/nerdtree'
" solarized color scheme
Plugin 'altercation/vim-colors-solarized'


그리고 맨 끝에 다음과 같이 설정을 추가해 준다...


...
" setting soarized color scheme
set t_Co=256
set background=dark
highlight Normal ctermbg=NONE
highlight nonText ctermbg=NONE
let g:solarized_termcolors=256
hi Comment term=bold cterm=bold ctermfg=2
colorscheme solarized


다시 다음 명령어로 플러그인을 설치해주고...


root@glassylife:~# vim +PluginInstall +qall


vi을 열면...! 



알록달록한게 매우 맘에 든다ㅋㅋㅋ 매우 흡족.




4. Plugin 검색

이제 능동적으로 필요한 플러그인을 찾고 설치해보자

플러그인 검색은 vi의 명령어 모드에서 :PluginSearch 를 입력한다.



무슨 플러그인 4000개 씩이나...


그럼 왼쪽에 윈도우가 하나 생기는데 vi에서 일반적으로 단어 찾는 단축키인 / 를 이용해 적당한 검색 키워드를 입력한다.



taglist 라고 검색하니 'taglist-plus'라는 플러그인을 찾을 수 있었다.

이제 이걸 그대로 NERDTree 추가했듯이 ~/.vimrc 파일에 넣어준다.

그리고 :PluginInstall 해주는거 까먹지 말고!!





Posted by nfyfamraa
,

이번엔 셰이더를 적용해 봅시다.

뭔가 초장부터 중간보스를 만난 느낌이네요.



들어가기 앞서 기본 개념인 버텍스와 픽셀에 대해서 짚어봅시다.

3D 그래픽을 조금이라도 접해본 사람들은 다 알만한 개념이긴 한데 원문에서 이 개념을 설명하기 위해 한 강의를 투자하였기 때문에 간단히 짚고 갑니다.


버텍스: 3차원 그래픽 공간에서 한 점을 의미하는 가장 기본적인 개념입니다. 카르테시안 좌표계에서 x, y, z 요소로 나타낼 수 있죠.

픽셀: 물리적으로 화면에서 한 점을 나타내는 최소 단위입니다. 하나의 픽셀은 RGBA를 조합한 한가지 색만을 표현할 수 있습니다.



쉐이더

쉐이더도 일종의 프로그램입니다. 일반적인 C/C++ 네이티브 프로그램과 다른점은 쉐이더는 CPU가 아니라 GPU에서 처리되는 프로그램이라는 점이죠(그치만 쿠다를 사용한다면?!). 쉐이더는 여러가지가 있는데 책 "OpenGL Super Bible 6th" 에서 발췌한 렌더링 파이프라인 다이어그램에서 그 중 몇가지를 볼 수 있습니다.




이미지에서 볼 수 있듯이 렌더링 파이프라인에서 가장 처음과 끝에 버텍스 쉐이더와 프레그먼트 쉐이더가 위치합니다. 3D를 렌더링하는데 빠져서는 안 될 엄청 중요한 놈들이죠. 이것들 외에 추가로 사용할 수 있는 쉐이더가 몇 개 더 있는데요 테설레이션 쉐이더와 지오메트리 쉐이더입니다. 렌더링 파이프라인에서 얘네들이 없이도 충분히 3D를 화면을 렌더링 할 수 있지만, 사용한다면 더욱 정교하게 픽셀을 관리하여 멋진 효과를 줄 수 있죠. 이러한 쉐이더들은 프로그래밍으로 직접 컨트롤 할 수 있는데 반해, 버텍스 패치(Vertex fetch), 레스터라이제이션(Rasterization), 프레임버퍼 오퍼레이션(Buffer operations)은 직접 컨트롤 할 수 없습니다. 


예제 코드를 작성해보기 전에 쉐이더간의 기본동작에 대해 알아봅시다.

파이프라인에서 한 쉐이더에서 다음 쉐이더로 넘어갈 때 변수를 이용해서 데이터를 전달할 수 있습니다. 이 때 변수들은 다음의 특정 규칙을 따라야 합니다.


1. 변수의 이름과 타입은 앞 쉐이더의 것과 뒷 쉐이더의 것이 일치해야 합니다(뭐 당연한거 같네요).

2. 변수는 파이프라인상에서 바로 다음에 위치한 쉐이더에게만 전달할 수 있습니다. 예를 들어 버텍스 쉐이더, 테설레이션, 프레그먼트 쉐이더를 사용한다고 할 때 버텍스 쉐이더에서 프레그먼트 쉐이더로 데이터를 전달하고 싶다면, 먼저 버텍스 쉐이더에서 테설레이션 쉐이더로 변수를 전달한 다음 다시 테설레이션에서 프레그먼트 쉐이더로 변수를 전달해야 합니다.


또한 CPU에서 보내지는 버텍스 데이터들은 버퍼를 통해 전달되는데, 버텍스 쉐이더에서 밖에 접근할 수 없습니다. 반면 쉐이더간 데이터전달에 사용되는 유니폼 변수들은 어느 쉐이더에서든 접근이 가능합니다(위의 2번 조건과 함께 생각해보면 각 쉐이더가 실행될 때 유니폼 변수들이 같은 메모리 공간을 사용하는거 같아 보이네요. 같은 메모리 주소를 사용하니까 변수 생존 기간이 한 쉐이더에서 다음 쉐이더까지인가 봅니다).


후 드디어 코딩입니다. 쉐이딩 언어로는 GLSL을 사용합니다. OpenGL Shading Language 의 약자에요ㅋ


예제프로그램에서는 쉐이더 소스를 파일로 작성하고, 메인 루틴에서 실행중에 동적으로 이 쉐이더 소스를 읽어와 컴파일하고 렌더링 파이프 라인에 적용합니다. 먼저 이러한 동작을 하는 C++ 모듈을 만들어 보죠.


프로젝트에 Core라는 폴더를 만들고 그 안에 Shader_Loader.h 와 Shader_Loader.cpp 파일을 작성합니다.


// Shader_Loader.h
#pragma once

#include <GL/glew.h>
#include <GL/freeglut.h>
#include <iostream>

namespace Core
{

    class Shader_Loader
    {
    private:

        std::string ReadShader(char *filename);
        GLuint CreateShader(GLenum shaderType,
            std::string source,
            char* shaderName);

    public:

        Shader_Loader(void);
        ~Shader_Loader(void);
        GLuint CreateProgram(char* VertexShaderFilename,
            char* FragmentShaderFilename);

    };
}


// Shader_Loader.cpp
#include "Shader_Loader.h" 
#include <iostream>
#include <fstream>
#include <vector>

using namespace Core;

Shader_Loader::Shader_Loader(void){}
Shader_Loader::~Shader_Loader(void){}

std::string Shader_Loader::ReadShader(char *filename)
{

    std::string shaderCode;
    std::ifstream file(filename, std::ios::in);

    if(!file.good())
    {
        std::cout<<"Can't read file "<<filename<<std::endl;
        std::terminate();
    }

    file.seekg(0, std::ios::end);
    shaderCode.resize((unsigned int)file.tellg());
    file.seekg(0, std::ios::beg);
    file.read(&shaderCode[0], shaderCode.size());
    file.close();
    return shaderCode;
}

GLuint Shader_Loader::CreateShader(GLenum shaderType, 
                                   std::string source, char* shaderName)
{

    int compile_result = 0;

    GLuint shader = glCreateShader(shaderType);
    const char *shader_code_ptr = source.c_str();
    const int shader_code_size = source.size();

    glShaderSource(shader, 1, &shader_code_ptr, &shader_code_size);
    glCompileShader(shader);
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_result);

    //check for errors
    if (compile_result == GL_FALSE)
    {

        int info_log_length = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);
        std::vector<char> shader_log(info_log_length);
        glGetShaderInfoLog(shader, info_log_length, NULL, &shader_log[0]);
        std::cout << "ERROR compiling shader: " << shaderName << std::endl << &shader_log[0] << std::endl;
        return 0;
    }
    return shader;
}

GLuint Shader_Loader::CreateProgram(char* vertexShaderFilename,
                                    char* fragmentShaderFilename)
{

    //read the shader files and save the code
    std::string vertex_shader_code = ReadShader(vertexShaderFilename);
    std::string fragment_shader_code = ReadShader(fragmentShaderFilename);

    GLuint vertex_shader = CreateShader(GL_VERTEX_SHADER, vertex_shader_code, "vertex shader");
    GLuint fragment_shader = CreateShader(GL_FRAGMENT_SHADER, fragment_shader_code, "fragment shader");

    int link_result = 0;
    //create the program handle, attatch the shaders and link it
    GLuint program = glCreateProgram();
    glAttachShader(program, vertex_shader);
    glAttachShader(program, fragment_shader);

    glLinkProgram(program);
    glGetProgramiv(program, GL_LINK_STATUS, &link_result);
    //check for link errors
    if (link_result == GL_FALSE)
    {

        int info_log_length = 0;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);
        std::vector<char> program_log(info_log_length);
        glGetProgramInfoLog(program, info_log_length, NULL, &program_log[0]);
        std::cout << "Shader Loader : LINK ERROR" << std::endl << &program_log[0] << std::endl;
        return 0;
    }
    return program;
}


gl 라이브러리 함수 및 Shader_Loader 클래스의 설명입니다.


GLuint: 

쉐이더와 프로그램 핸들들을 갖습니다. GLuint 변수는 기본적으로 이러한 엔티티(쉐이더와 프로그램 핸들들)를 갖는 빈 객체로 존재합니다. OpenGL 에서는 '프로그램' 이라는 용어를 좀 헷갈리게 사용하는데요, OpenGL에서 정의하는 '프로그램'이라는 용어는 쉐이더들(vertex, fragment, tessellation, geometry)을 담을 수 있는 컨테이너를 의미합니다.


glCreateShader(GLenum shader_type):

인자로 전달받는 shader_type 으로 비어있는 쉐이더 객체를 생성하고 핸들을 반환합니다.


glShaderSource(GLuint shader, GLsizei count, 

                     const GLchar **shader_code, const GLint *legth):

shader 객체에 shader_code 를 로드합니다. *GLchar 은 캐릭터형 배열이며 이 배열의 갯수를 count 로 알려줍니다. length 는 각 *GLchar 의 길이 정보를 갖는 배열입니다. 예제에선 소스코드를 한개씩 전달하였으므로 count 를 1로 전달하였죠.


glCompileShader(GLuint shader):

소스코드를 컴파일합니다.


glGetShaderiv(GLuint shader, GLenum pname, GLint *prarams):

에러를 확인하고 콘솔로 검출해냅니다


glCreateProgram():

프로그램 객체를 생성하고 그 핸들을 반환합니다.


glAttachShader(GLuint program, GLuint shader):

프로그램에 쉐이더를 붙입니다.


glLinkProgram(GLuint program):

프로그램 객체를 링킹합니다.


glGetProgramiv(GLuint program, GLenum pname, GLint *params):

glGetShaderiv과 마찬가지로 에러를 확인하고 콘솔로 검출해냅니다.


glUseProgram(GLuint program):

렌더링 루프에서 program 객체를 이용하여 렌더링 하로독 지정합니다.


Shader_Loader: 

생성자 함수...


~Shader_Loader: 

소멸자 함수...


CreateShader: 

쉐이더를 생성하고 컴파일.


ReadShader: 

쉐이더 파일의 소스를 읽는 메서드


CreateProgram: 

내부적으로 ReadShader, CreateShader 메서드를 호출 하여 버텍스와 프레그먼트 쉐이더를 불러오고 프로그램에 담은 후 링킹하는 메서드



버텍스 쉐이더와 프레그먼트 쉐이더

쉐이더에서 가장 중요한 것은 각 쉐이더들은 버텍스를 다루는데 자신만의 고유의 역할을 갖는다는 것입니다. 버텍스 쉐이더는 x,y,z 3차원 좌표로 이루어진 버텍스들이 2차원 화면에서는 어떤 위치에 그려질지 정사영하는 일을, 프래그먼트 쉐이더는 각 픽셀들이 어떤 색상을 갖는지 정하는 일을 하는 식이죠.


버텍스 쉐이더에서 여러분이 꼭 기억해야할 변수는 gl_Position 입니다. 이 변수는 미리정의 되어 있으며 현재 버텍스의 작업이 다 끝난 후의 스크린상의 점의 위치를 가리킵니다.


프레그먼트 쉐이더의 가장 큰 역할은 각각의 프레그먼트들의 색상을 결정하는 것입니다. 이 쉐이더에서 반환값은 색상버퍼뿐이며, 이 색상버퍼에 표현되지 않은 오브젝트들은 전부 검정색으로 표현됩니다.


다음으로 버텍스 쉐이더의 코드를 작성해 보죠. Core 폴더와 같이 Shaders 폴더도 하나 만들고 Vertex_Sahder.glsl 과 Fragment_Shader.glsl 파일을 작성합니다.


// Vertex_Shader.glsl
#version 330 core

void main(void)
{
    const vec4 vertices[3] = vec4[3](vec4( 0.25, -0.25, 0.5, 1.0),
                                     vec4(-0.25, -0.25, 0.5, 1.0),
                                     vec4( 0.25, 0.25, 0.5, 1.0));
    gl_Position = vertices[gl_VertexID];
}


// Fragment_Shader.glsl
#version 330 core
out vec4 color;

void main(void)
{
    color = vec4(0.0, 1.0, 0.0, 1.0);
}


모든 쉐이더는 먼저 OpenGL 의 버전을 명시해주어야 합니다. 전 3.3버전이므로 330 이라고 적었습니다. 원글에서는 430 이라고 적었네요. 버전 다음에 나오는 core라는 단어는 해당 버전의 glsl 핵심 함수들을 사용할 것임을 나타냅니다. 


다음으로 main 함수로 넘어가봅시다. 3D 그래픽에서 삼각형을 그리기 위해서는 꼭짓점을 나타내는 세 개의 버텍스가 필요합니다. OpenGL의 버텍스 쉐이더에서는 이 버텍스들을 스크린상의 어디에 나타낼 것인지 계산을 한 후 gl_Position 변수에 담아 다음 쉐이더로 보내죠.



위 이미지처럼 스크린은  x, y 값의 범위가 모두 -1~1을 갖는 좌표계로 표현 됩니다. (0, 0)은 화면의 정중앙을 나타내죠. 그래서 200x400 해상도의 스크린을 예로 들면 소스코드의 삼각형은 스크린상에서 꼭지점 (125, 150), (75, 150), (125, 150) 으로 표현됩니다. 소스코드에서 사용된 이 좌표표현 방법을 NDC(Nomalized Device Coordinates) 라고 합니다.


실제로 NDC는 x, y, z, w 네가지 요소로 표현이 됩니다. 네 번째 원소인 w는 W를 표현하는데 위 소스의 경우 W가 1.0으로 NDC가 버텍스의 좌표를 다루고 있다는 것을 나타냅니다(W가 뭘 의미하는지는 잘 모르겠네요). NDC 변환이 끝난 후 Window Transformation 또는 Screen Transformation 변환을 합니다. 이 두 변환작업은 장면(scene)을 OpenGL의 viewport(윈도우창 안쪽의 OpenGL 로 그려질 부분)에 맞추기 위해 하는데, 그래픽카드에서 진행되므로 신경쓸 필요 없습니다. 여기까지 모든 변환을 마친 최종 좌표값들은 모든 모양들이 픽셀/프레그먼트로 전환되기 위해 raterization 과정을 거칩니다. 그러면 위 삼각형은 다음 이미지와 같은 형태를 갖게되겠죠.



glVertexID는 현재 처리되고 있는 버텍스의 ID를 나타냅니다. 

프래그먼트 쉐이더에서는 color 변수에 결과값을 내어야합니다. 


마지막으로 main 파일을 조금 손 보도록 하죠.


// main.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <vector>

#include "Core/Shader_Loader.h"

using namespace Core;

GLuint program;

void renderScene(void)
{
    glClearColor(1.0, 0.0, 0.0, 1.0);//clear red
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //use the created program
    glUseProgram(program);

    //draw 3 vertices as triangles
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glutSwapBuffers();
}

void Init()
{

    glEnable(GL_DEPTH_TEST);

    //load and compile shaders
    Core::Shader_Loader shaderLoader;
    program = shaderLoader.CreateProgram("Shaders\\Vertex_Shader.glsl",
        "Shaders\\Fragment_Shader.glsl");
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Drawing my first triangle");
    glewInit();

    Init();

    // register callbacks
    glutDisplayFunc(renderScene);
    glutMainLoop();
    glDeleteProgram(program);
    return 0;

}


수정한 main 파일에서는 CreateProgram 메서드를 호출하여 쉐이더 소스를 불러와 컴파일 하였습니다. 그리고 나서 glUseProgram 를 호출하여 렌더링 루프에서 사용할 프로그램을 지정하였고 glDrawArrays 를 호출하여 얼마나 많을 버텍스들을 사용하고 어떻게 그려낼 것인지 지정하였습니다. 마지막으로 새로 등장한 glDrawArrays 에 대해 알아보죠.


glDrawArrays(GLenum mode, GLint first, GLsizei count):

mode 는 드로잉 모드 또는 points, lines, triangles 등의 모형을 가리킵니다. first 는 버텍스 배열내에서, 그리기 시작할 버텍스의 인덱스를 나타내며 count 는 그릴 버텍스의 갯수를 나타냅니다.




후 여기까지 오다니... 힘드네요 힘들어 ㅋㅋ

다음은 이번 강의의 원문 링크와 현재까지의 프로젝트 폴더 구조입니다.


* 원문 링크

http://in2gpu.com/2014/10/20/building-blocks-vertex-pixel/

http://in2gpu.com/2014/10/29/shaders-basics/

http://in2gpu.com/2014/11/24/creating-a-triangle-in-opengl-shader/


* 프로젝트 폴더 구조




그럼 다음에 봅시다~~


아, 위 코드를 실행 했을 때 에러가 없는데도 제대로 동작하지 않는 경우가 있을 수 있습니다. 튜토리얼 작성자 말로는 쉐이더 코드가 일부 하드웨어와 맞지 않아 생기는 현상 같다고 하네요. 이번 강의는 쉐이더의 기본개념을 공부하기 위한 강의이고 실제 필드에선 저렇게 버텍스 쉐이더 안에서 버텍스를 정의하지 않으니 제대로 동작하지 않더라도 짜증내지말고 그냥 다음 강의로 넘어갑시다.ㅎㅎㅎ

Posted by nfyfamraa
,


저번시간에 이어 소스를 조금 추가해보죠!


#include <GL/glew.h>
#include <GL/freeglut.h>
#include <iostream>

void renderScene(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(1.0, 0.0, 0.0, 1.0); //clear red

    glutSwapBuffers();
}

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(50, 50);
    glutInitWindowSize(800, 600);
    glutCreateWindow("OpenGL First Window");

    glewInit();
    if (glewIsSupported("GL_VERSION_3_3"))
    {
        std::cout << " GLEW Version is 3.3\n ";
    }
    else
    {
        std::cout << "GLEW 3.3 not supported\n ";
    }

    glEnable(GL_DEPTH_TEST);

    // register callbacks
    glutDisplayFunc(renderScene);

    glutMainLoop();

    return 0;
}

크게 glut 디스플레이 루틴에 장면을 렌더링하는 콜백함수를 추가하였고 glew를 초기화하는 부분과 그래픽카드의 OpenGL 지원을 체크하는 조건문을 추가하였습니다.


위에서 등장한 함수들에 대한 설명입니다.


glutInit:

GLUT 라이브러리 초기화 함수 입니다.


glutInitDisplayMode:

OpenGL 의 디스플레이 모드를 지정합니다. 이 함수는 unsigned int 형을 인자로 받으며 여러 옵션을 지정하기 위해 bit OR 연산자로 묶을 수 있습니다.


GLUT_DEPTH 는 OpenGL 이 깊이 버퍼를 사용하도록 지시합니다. 깊이버퍼는 두 개 이상의 오브젝트가 겹쳤을 때 어떤 색을 출력할지를 결정하는데 사용됩니다.

GLUT_DOUBLE 은 OpenGL 이 더블버퍼링 시스템을 사용하도록 지정합니다.

GLUT_RGBA 는 32비트 Framebuffer 를 8비트의 RGBA 채널에 할당합니다.


glutInitWindowPosition:

윈도우 창의 위치를 픽셀 단위로 지정합니다.


glutInitWindowSize:

윈도우 창의 너비와 높이를 픽셀 단위로 지정합니다.


glutCreateWindow:

윈도우를 생성합니다. 인자로 윈도우 창의 이름을 지정하며 윈도우 ID 값을 반환합니다.


glewInit:

GLEW 초기화 함수입니다.


glewIsSupported:

인자로 전달한 OpenGL 버전이 GLEW 에서 지원할 수 있는 버전인지 확인합니다. 


glutMainLoop:

GLUT processing loop를 시작합니다.


glClear:

OpenGL이 인자로 전달된 버퍼를 비우도록 합니다. 위 코드에서는 GL_COLOR_BUFFER_BIT와 GL_DEPTH_BUFFER_BIT 를 지정하였습니다.


glClearColor:

색상버퍼를 인자값으로 초기화 합니다. 인자순서는 각각 R, G, B, A 입니다.


glutSwapBuffers:

더블 버퍼링 시스템에서 백버퍼와 프론트 버퍼를 바꿉니다.



간단하네요 :)


다음은 이번 강의 원문 링크입니다.

http://in2gpu.com/2014/10/17/creating-opengl-window/



======================================================

추가합니다.


코드를 실행시켜보면 바로 빨간 배경화면이 뜨는게 아니고 검은화면이 뜰겁니다. 그리고 마우스클릭이나 창을 움직이면 빨간배경화면으로 바뀌네요. 왜그런가 검색을 해보니 함수 호출 순서가 잘못되었네요. 먼저 glClearColor로 빨간색으로 배경색을 지정해주고 glClear로 색을 칠합니다. 다음 링크를 참조해 주세요.

http://stackoverflow.com/questions/2898503/background-colour-in-opengl

수정:
   glClearColor(1.0, 0.0, 0.0, 1.0); //clear red
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


'잡것 > OpenGL' 카테고리의 다른 글

[OpenGL 02] 셰이더를 적용해 봅시다.  (0) 2016.10.07
[OpenGL 00] OpenGL을 설치해봅시다.  (0) 2016.10.02
Posted by nfyfamraa
,

시작하기에 앞서 몇가지 알려드립니다

먼저 제 환경은 윈도우7 64bit 이며, IDE는 MS VisualStudio 2015 Community v.14 를 사용합니다.

또한 필자가 매.우. 게으르므로 이 시리즈는 연재따위가 아니며 다음 링크의 

http://in2gpu.com/2014/10/17/creating-opengl-window/

OpenGL 강의를 읽어보면서 정리한 글입니다.



자 시작해 봅시다.

OpenGL 라이브러리만 가지고 윈도우에서 OpenGL을 개발할 순 있습니다만 그러기 위해선 전반적인 winAPI 의 지식이 필요합니다.

그러므로 여기에선 그래픽 로직에만 집중할 수 있도록 FreeGLUT, GLEW 를 이용하여 예제를 작성합니다.

얘네들이 뭐하는건지는 아래 갓택오버플로우에 답변이 이미 올라와 있네요

http://stackoverflow.com/questions/19719055/what-are-the-differences-between-glu-glew-glut-qt-sdl-opengl-and-webgl


요약하자면 다음과 같습니다.

OpenGL: 2D 및 3D 그래픽을 렌더링하기 위한 크로스플랫폼 API 입니다.

GLUT: OpenGL Utility Toolkit 은 windows 환경에서 마우스와 키보드 컨트롤을 도와주는 라이브러리. win API 해보신분들은 아시겠지만 윈도우폼을 만들고 I/O 이벤트를 컨트롤 하는건 복잡하고 여간 귀찮은 일이죠.

GLEW: OpenGL Extension Wrangler Library 은 OpenGL의 확장기능들을 불러오고 사용할 수 있도록 도와주는 크로스플랫폼 라이브러리 입니다.



먼저 FreeGLUT를 설치해 봅시다. 아래링크에서 최신 안정 버전을 다운로드합니다.

http://freeglut.sourceforge.net/

GLUT와 FreeGLUT 라는 용어를 혼용하고 있어서 헷갈릴 수도 있는데 구분을 하자면 GLUT가 1998년을 마지막으로 개발자가 더 이상 업데이트를 하지않는데다 라이선스도 공개용이 아니라 사용에 문제가 있어서 X-Consortium 라이선스로 GLUT와 호환성을 유지하여 만든 것이 바로 FreeGLUT 입니다. 이하의 글에서 GLUT 라고 칭하는 것들은 모두 FreeGLUT 를 의미하는 것임을 유의해 주세요.


현재 3.0.0 버전이 최신 안정 버전이네요.

음.... 다운받아보니 뭔가 정체 모를 파일들이 많이도 있네요...

받은 압축파일은 소스코드이므로 컴파일을 해야 합니다

README 파일에 친절히 설명이 다 되어있네요.

순서대로 따라해봅시다

아래 링크에서 cmake를 다운받습니다.

http://www.cmake.org/cmake/resources/software.html


링크에 들어가보면 버전에 따라 설치버전과 포터블버전을 다운 할 수 있습니다

현재 cmake 최신 버전은 3.6.2 이며 저는 포터블버전인 cmake-3.6.2-win64-x64.zip 를 다운받았습니다.

압축을 풀고.... bin/cmake-gui.exe 를 실행 시킵니다.


Where is the source code 부분에는 freeglut 소스코드를 압축해제한 폴더를 설정해줍니다.

Where to build the binaries 에는 솔루션을 생성할 폴더를 지정합니다. 폴더이름은 freeglut로 해줍시다.

그리고 나서 왼쪽 아래 부분에 Configure을 누르고 현재 자신의 컴퓨터에 설치되어있는 VS 버전을 고릅니다. 저의 경우 Visual Studio 14 2015 이며, 이는 VisualStudio 를 키시고, 도움말 > Microsoft Visual Studio 정보 창에서 확인 할 수 있습니다.

Finish 를 누르면 configure 프로세스가 진행됩니다.



저는 DEMO 와 정적파일은 필요없으니 체크 해제하고 솔루션을 생성하였습니다.

아무래도 정적파일로 생성하면 예제프로그램들 빌드할 때 무거워질거 같아서...



Generate를 누르면 지정한 output 폴더에 VS 솔루션이 생성됩니다.

자 그럼 freeglut/freeglut.sln 을 엽니다.

빌드 단축키 F7을 눌러 빌드!


에러없이 빌드가 완료되면 freeglut/bin/Debug/freeglutd.dll 파일이, freeglut/lib/Debug/freeglutd.lib 파일이 각각 생성됩니다.

이 동적라이브러리 파일과 헤더파일을 프로젝트에 포함시켜 주어야 합니다.

VS의 기본 인클루드패스에 위 파일들을 복사해 넣거나 프로젝트 설정에서 패스를 추가로 설정해 주어야 합니다.


header include 경로에 압축해제한 폴더중 include 밑에 있는 GL 폴더를 폴더 째로 

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include 밑에 복사합니다.



빌드한 freeglutd.lib 파일은 

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib 폴더 밑에 넣고, 

freeglut.dll 파일은 

C:\Windows\SysWOW64 폴더와 

C:\Windows\System32 폴더에 각각 복사해 넣습니다.

참고로 어플리케이션의 DLL 검색 순서는 다음과 같습니다

https://msdn.microsoft.com/ko-kr/library/7d83bc18.aspx


후... 다음으로 glew를 설치해보도록 하죠

아래 링크에서 최신 릴리즈 소스파일을 다운받습니다. 현재 2.0.0가 최신이군요.

http://glew.sourceforge.net/


압축을 풀어보니 이것도 뭔가 이것저것 담겨 있군요.

build/vc12 폴더에 있는 VS 솔루션을 엽니다.

솔루션 버전과 현재 사용하고 있는 VS 버전이 다른데 프로젝트를 업그레이드할 것이냐고 물어보는데 OK를 눌러서 프로젝트를 업그레이드하고 F7 키를 눌러 빌드를 합니다.

빌드가 성공하면 bin/Debug 폴더와 lib/Debug 폴더 밑에 각각 dll 파일과 lib 파일이 생성되어 있을 겁니다.

이 두 파일과 함께 include/GL 내의 헤더파일들을 적당한 곳에 넣어줍니다.


C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\GL 에 헤더파일들을

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib 에 glew32d.lib 파일을

C:\Windows\SysWOW64 폴더와

C:\Windows\System32 폴더에 glew32d.dll 파일을 넣습니다.


후... 드디어 설치가 끝났군요. 뭐 사실 파일들을 저렇게 뿔뿔이 넣지 않고 한폴더에 모아서 예제프로젝트 내에서 패스 설정만 해줘도 되지만 전 이렇게 하고 싶었어요 데헷.


자 이제 예제 프로그램을 만들어 봅시다.

VS 에서 빈프로젝트를 만들고 Alt+F7 을 눌러 프로젝트 설정을 엽니다

링커 > 입력 > 추가 종속성 을 열고

opengl32.lib

glew32d.lib

freeglutd.lib

을 넣어줍시다



확인을 누르고 다음 코드를 복붙한 후 실행시켜 봅시다.


#include <GL/glew.h>
#include <GL/freeglut.h>
#include <iostream>

void renderScene(void) {}

int main(int argc, char **argv)
{

    glutInit(&argc, argv);
    glutInitWindowPosition(50, 50);//optional
    glutInitWindowSize(800, 600); //optional
    glutCreateWindow("OpenGL First Window");

    // register callbacks
    glutDisplayFunc(renderScene);
    glutMainLoop();

    return 0;
}


오... Nice boat...


빈창하나와 콘솔창 하나가 뜰텐데 에러가 없다면 여기까지 잘 따라오신겁니다.


지금까지 위에서 설명한 방법은 소스코드를 직접 다운받아서 컴파일하고 사용하는 방법인데 애초에 컴파일된 파일을 다운받거나 VS내의 NuGet 매니저를 사용하는 방법도 있습니다.

컴파일된 파일을 다운받아서 사용하는 것은 위에서 설명한 과정중 컴파일하는 과정만 빼고 같으니 생략하고 NuGet 매니저를 사용하는 방법을 간략하게 설명하겠습니다.

빈 프로젝트를 만드시고 프로젝트 > NuGet 패키지 관리 를 선택합니다.



nupengl.core 를 검색하고 설치합니다.

그리고 앞의 코드를 빌드 및 실행 ㅇㅅㅇ


NuGet 패키지 관리자를 사용하니까 정말 편하네요(직접 사용해보진 않았지만 말입니다 하하하하)



Posted by nfyfamraa
,




후... 드디어 마지막이네요.


1. VNC를 깝시다!

root@kali:~# apt-get install tightvncserver -y



흠.. 저는 이미 깔려있네요.  아까 업데이트 하면서 깔렸나 봅니다.

이제 tightvncserver 명령어를 실행해서 초기 설정해주면 되는데 저는 캡처를 못 떠서 이건 사진 없이 그냥 적을게요...

root@kali:~# tightvncserver

이 명령어를 실행시키면 vnc에 접속할 때 비번을 설정하라고 합니다.

vncserver 명령어로 서버를 엽니다.

root@kali:~# vncserver

이제 원격지에서 접속을 시도해봅시다.

저는 tiger VNC Viewer 를 사용했습니다.

현재 최신 안정 버전은 1.6.0 버전이며 다음 링크에서 다운받을 수 있습니다.

https://bintray.com/tigervnc/stable/tigervnc/1.6.0




안되네요.




2. VNC 화면을 마테로 설정하자!

회색화면이 뜨는 이유가 적절한 데스크탑 어플리케이션을 못찾기 때문이라는 것 같네요.

데스크탑을 마테로 설정해 줍시다.

먼저 마테를 설치합니다.

root@kali:~# apt-get install mate-core mate-desktop-environment-extra* mate-themes



마테 설치가 끝나면 계정의 홈 디렉토리의 .vnc/xstartup 파일을 vi로 열어서 수정해 줍시다.

root@kali:~# vi ~/.vnc/xstartup



이제 저장하고 기존에 열었던 서버는 닫고 다시 열어 봅시다.

root@kali:~# vncserver -kill :1
root@kali:~# vncserver


kill 옵션 뒤에 쌍따옴표와 세션 번호를 적게되는데 vnc 서버를 열 때마다 비어있는 순서대로 세션번호가 할당됩니다.

그리고 기본설정으로 각 각 세션에 590x 포트가 할당되죠.

x는 세션번호로 1번 세션에는 5901 포트가 2번 세션에는 5902 포트가 할당되는 식입니다.

어떤 세션이 열렸는지 확인하고 싶을 때에는 다음 명령어를 사용하면 됩니다.

root@kali:~# netstat --tcp -an | grep 590*


자 이제 정말로 접속해 봅시다!



위 사진처럼 서버 주소와 세션번호를 적어주면 됩니다.

포트번호로 지정해주고 싶을 땐 아래처럼 콜론 두개를 쓰면 됩니다.



이제 처음에 설정했던 vnc 접속 비밀번호를 입력해주면...



접.속.







귿귿 ㅎㅎ

Posted by nfyfamraa
,




후... 이제 VMware 를 설치해봅시다.

제가 VMware 를 설치하는 이유는 칼리 위에 여러개의 취약점 서버를 만들어 놓고 사용하기 위해서입니다. 매번 서버 바꾸려먼 설정하기 귀찮으니까 사용할 서버 몇개 동시에 올려 놓자는 거죠. 


* 설치하기 위해선 제일 먼저  루트 권한이 필요합니다

1. 자 VMware 홈페이지에 들어가서 최신버전을 다운받습니다.

http://www.vmware.com/products/player/playerpro-evaluation.html

현재 최신버전은 VMware-Player-12.1.1-3770994.x86_64.bundle 이네요




2. 설치받은 스크립트를 실행시킵니다

실행시기 전에 먼저 실행 권한을 주어야 합니다.

터미널을 키고 다음 명령어로 실행권한을 추가하고 ls -l 명령어로 실행권한이 잘 적용 되었는지 확인합니다. 그리고 나서 실행!

root@kali:~# chmod u+x 다운로드/VMware-Player-12.1.1-3770994.x86_64.bundle
root@kali:~# ls -l 다운로드/VMware-Player-12.1.1-3770994.x86_64.bundle
root@kali:~# 다운로드/VMware-Player-12.1.1-3770994.x86_64.bundle



다음과 같이 GUI 가 뜨면서 사용 약관에 동의 하냐고 묻습니다.

2번이 나오는데 두 번다 Yes.



실행시마다 업데이트 확인할 건지 묻는건데 저는 No



VMware software 에 사용자 정보를 제공할 것인지 묻는데 저는 No.



라이센스키를 묻는데 이건 빈공간으로 남겨놓고 Next를 눌러 패스합니다.



마지막으로 Install을 누르면 설치가 시작됩니다.



설치가 모두 끝나고 나선 Close를 눌러 닫아줍니다.




3. VMware 에 이미지를 돌려보자!

시작키를 누르고 또는 왼쪽 바로가기 패널에서 맨 아래에 있는 메뉴버튼을 누르고, vmware 라고 입력하면 설치된 프로그램중에서 vmware를 찾아줍니다. 



눌러서 실행해보면 다음과 같이 현재 실행중인 커널과 C 헤더 파일이 안 맞네 어쩌네 하면서 에러를 뱉네요.

다음 명령어로 커널 헤더파일을 설치합니다.

root@kali:~# apt-get install linux-headers-$(uname -r)

헤더파일을 설치하고 나서 다시 vmware 를 실행해보면 또 에러나네요...ㅎㅎ



4.4 버전 이 후로는 vmware를 설치하기 위해서 c코드를 조금 수정해야 한다는군요.

root@kali:~# tar -xvf /usr/lib/vmware/modules/source/vmmon.tar
root@kali:~# sed -i -e 's/get_user_pages/get_user_pages_remote/g' vmmon-only/linux/hostif.c
root@kali:~# tar -cvf vmmon.tar vmmon-only
root@kali:~# mv vmmon.tar /usr/lib/vmware/modules/source/vmmon.tar

먼저 vmmon.tar 파일을 현재 위치에 압축을 풀고 hostif.c 파일내의 get_user_pages를 get_user_pages_remote 로 치환해 줍니다. 

그후 다시 tar로 묶고 원래 파일과 바꿔줍니다.

이 과정을 vmnet.tar 의 userif.c 파일에 대해서 반복해 줍니다.

root@kali:~# tar -xvf /usr/lib/vmware/modules/source/vmnet.tar
root@kali:~# sed -i -e 's/get_user_pages/get_user_pages_remote/g' vmnet-only/linux/userif.c
root@kali:~# tar -cvf vmnet.tar vmnet-only
root@kali:~# mv vmnet.tar /usr/lib/vmware/modules/source/vmnet.tar


rm -rf 명령어로 임시로 작업했던 폴더를 지워줍시다.

root@kali:~# rm -rf vm*-only


자 이제 다시 vmware 를 시작해 봅시다.

이번에는 컴파일이 잘되었네요. 



이메일을 입력하라고 합니다. 입력해 줍시다.

후... 드디어 설치 했네요!





4. 마지막, 포트포워딩 설정 하기

이제 이미지를 돌려 봅시다. 저는 OWASP 이미지를 돌려보겠습니다.

그리고 ifconfig 명령어로 OWASP IP를 확인 봅니다.



192.168.219.128 이네요.

칼리의 터미널로 돌아와서 네트워크 에디터를 열어봅시다.

먼저 bin 폴더에 에디터를 링크시켜 놓고 vmware-netcfg 로 에디터를 실행합니다.

root@kali:~# ln -s /usr/lib/vmware/bin/vmware-netcfg /usr/bin/vmware-netcfg
root@kali:~# vmware-netcfg



vmnet8 을 선택하고 NAT Settings를 클릭합니다.



Port Forwading 의 Add 버튼을 클릭해서 포트포워딩을 지정해 줍니다.



Host port 가 외부에서 호스트 컴인 칼리로 접속할 포트고,

IP Adress 는 owasp 에 할당된 ip입니다. 아까 ifconfig로 확인 했었죠?

Virtual machine port 는 owasp 에 연결할 포트이고 

Description 에 대략적인 설명을 적습니다.

이렇게하면 칼리의 20080 포트로 오는 데이터들이 owasp 80 포트로 들어오게 되죠.

사진은 192.168.129.128로 적혀있는데 제가 잘못 입력하고 캡쳐뜬거라서 192.168.219.128 로 다시 입력했습니다.

Save -> Save 를 눌러주면 설정이 끝났습니다!

이제 같은 네트워크의 다른 시스템에서 접속해 보죠.



잘 되네요!



그럼 끗!

Posted by nfyfamraa
,




우분투는 자동으로 시행되게 설정된채로 배포되는데 칼리는 아닌가 보네요.

다음 명령을 이용해서 자동으로 시작하게 만들어 줍니다.

root@kali:~# update-rc.d ssh defaults



뭐라고 뜨네요. 자동시작에 등록된 서비스를 지우고 다시 설정해 줍니다.

root@kali:~# update-rc.d ssh remove
root@kali:~# update-rc.d ssh defautls



그리고 재시작 ㄱㄱ

재시작 후 다음 명령어로 서비스가 잘 실행되었는지 확인합니다.

root@kali:~# service ssh status

그럼 잘 실행될 수도 있고 위 사진처럼 inactive라도 뜰 수도 있습니다.


실행이 안된경우 vi로 /etc/rc.local 파일을 수정해 줍시다

root@kali:~# vi /etc/rc.local

아래 사진 처럼 exit 0 위에 다음을 입력해 줍니다.

/bin/sh -e /etc/init.d/ssh start


저장을 하고 재부팅을 시켜보면 정상적으로 실행된 것을 확인할 수 있습니다.


간단하게 설명하자면 리눅스는 부팅 레벨을 구분하고 레벨에 따라 부팅 방식이 달라지는데 level 3의 경우 콘솔로 부팅하게 되고 level 5 의 경우 GUI로 부팅하게 됩니다.

이 때 각 레벨에 따라 부팅시 실행될 서비스를 rcX.d/ (이 때 X는 런레벨입니다) 폴더에 관리하는데 폴더내용을 보면 실행될 서비스에 대해 심볼릭링크가 걸려있습니다.

여기서 링크를 직접 추가하지 않고 update-rc.d 명령어로 자동으로 추가하게끔 만들 수 있습니다.

하지만 칼리의 경우 update-rc.d 로 추가하고 rc5.d/ 폴더에 링크가 잘 담겨 있는것 까지 확인했는데도 자동으로 실행이 안되더라구요.

이 때 각 런레벨에 맞는 폴더에 담긴 링크들에 연결된 서비스들을 다 실행하고 마지막으로 공통으로 실행되는 스크립트가 rc.local입니다.

그래서 rc.local 스크립트 파일에 자동 실행되도록 직접 스크립트를 넣어준 겁니다.


끗!

Posted by nfyfamraa
,



얼마전에 침투 테스트 실습 환경 구축하기로 글을 올렸었는데 나중에 문제가 생겼는데 해결이 안되는것도 있고 제대로 정리되지가 않은 것 같아 이번엔 단계별로 나눠서 다시 올립니다.




1. 먼저 Kali linux 이미지를 받으세요

https://www.kali.org/downloads/

기서 현재 최신 버전을 확인할 수 있으며 저는 풀버전으로 받을거기 때문에 

"Kali Linux 64 bit" 이미지를 받았습니다.




2. 그리고 UUI를 다운받습니다.

http://www.pendrivelinux.com/universal-usb-installer-easy-as-1-2-3/

USB를 운영체제 부트 디스크로 만드는데 사용하는 툴인제 문서 작성 현재 Universal-USB-Installer-1.9.6.6.exe 버전이 최신이네요.

그때그때 최신으로 받아주시면 됩니다.




3. 이제 USB에 이미지를 굽습니다.

UUI를 실행시키면 먼저 약관동의 어쩌고 나오는데 Agree를 눌러줍니다.

그러면 설정 화면이 나오는데 배포판은 Kali Linux 이구요. 리스트박스에서 맨 아래에 찾아보면 있습니다.


다음으로 Step2. 의 Browse를 누르고 1번에 다운받은 이미지를 선택해 줍니다.

그리고 나서 Step3. 에서 USB를 선택하고 옆에 "We will format D:\ Drive as Fat32" 를 체크해줍니다.

이미 직접 포맷 하셨다면 체크 하지 않아도 됩니다.

다음 사진처럼 되죠?



마지막으로 Create를 눌러주면 아래 사진과 같은 모달박스가 또 뜨는데, 설정 확인하는 부분이니까 OK를 눌러주면 알아서 됩니다.





4. 이제 설치하려는 컴퓨터에 USB를 꽂고 부팅해 줍시다.

이 때 부팅 순서를 USB를 1번으로 해주시는거 잊지 마세요.

사진을 첨부하지 못해서 죄송하지만 글로 설명해 드리자면

부팅할 때 CMOS 창으로 들어가서 부팅 고급 옵션 탭에 들어가면 부팅 순서 바꾸기가 있습니다. 그거 들어가보면 보통 하드디스크나 CD-ROM이 1번인데 현재 꽂은 USB를 1번으로 올려주시면 됩니다. 그러고 나서 저장하고 다시 시작하기 하시면 됩니다.

그럼 용문양 배경에 인스톨 방식 선택하는 화면이 뜬느데 "Graphic Install" 을 선택해 줍니다.




5. 설치하기

다음은 순서대로 사진보고 따라하시면 됩니다.

먼저 언어를 설정하시고, '예' 를 눌러 줍니다.



다음으로 위치와 키보드를 설정합니다.



진행하다 보면 다음과 같이 설치 CD-ROM을 마운트 할 수 없다고 뜨는데



이 때 수동으로 마운트 시켜주어야 하므로 아니요를 눌러주고 다음과 같이 쉘을 실행해 줍니다.



쉘에 진입 하셔서 다음 사진과 같이 

~ # mount [usb위치] /cdrom

명령을 실행해 줍니다.

저는 ls 명령어로 실제 sdb1 가 잘 있는지 확인하였고 마운트 시킨후 다시 ls /cdrom 으로 마운트가 잘 되었는지 확인하였습니다.

컴퓨터에서 인식한 디스크 장치를 순서대로 sda, sdb, sdc 순으로 이름을 할당해 주기 때문에 usb 위치같은 경우 실제 환경마다 조금 씩 다를 수 있습니다.

저같은 경우 하드디스크 하나와 USB하나를 인식 시켰으므로 sda에는 하드디스크, sdb에는 usb가 잡혔습니다.

ls sdb*

명령어를 사용해 보시면  또 sdb sdb1 sdb2... 이렇게 넘버링이 되어있는걸 확인 할 수 있는데 이는 파티션 번호입니다.

숫자가 붙여있지 않은 sdb는 디스크 자체를 가리키며 sdb1은 첫번 째 파티션 sdb2는 두번 째 파티션 이런식이죠.

usb에는 따로 파티션을 나누지 않았기 때문에 sdb와 sdb1만 나타나는 것을 확인 할 수 있습니다.



exit 명령어로 쉘에서 나옵니다.



그리고 다시 "CD-ROM 찾기 및 마운트" 를 시도해 줍니다.

이번엔 인식이 잘되어서 설치가 정상적으로 진행되는데 간혹가다 안되는 때가 있습니다. 아니면 이 다음까진 정상적으로 잘 진행 되다가 GRUB가 설치되지 않을 때가 있는데 USB를 꽂고 뺄 때 데이터가 손상된 것으로 생각 되며 그 때는 USB에 설치디스크 굽는 과정부터 다시 시작해 주시면됩니다.

제건 USB가 오래된것이서 그런지 생각보다 오류가 자주 발생했네요.

저는 세 번이나 에러 났습니다 ㅠㅠ

다음으로 호스트 네임과 도메인을 설정하시면 되는데 전 도메인 이름까지 넣으면 괜히 불편해질거 같아서 사용하지 않았습니다.



다음으로 파티션하기 메뉴가 뜨네요. 저는 멀티부트를 선택할 것이므로 수동으로 파티션을 해주었습니다. 자동을 선택하면 아마 기존에 설치되어있던 윈도우가 날라가겠죠.. ㄷㄷ




파티션을 설정해주면 되는데 전 이미 나누어놨던게 있어서 4번을 포맷 하고 사용하였습니다.



파티션 삭제를 하면 아래 사진처럼 번호가 할당도지 않고 남은 공간으로 표시가 됩니다. 새로 파티션을 나누어야 하실 상황이라면 적당히 용량이 큰 파티션을 선택하셔서 나누시면 되는데, 윈도우가 깔려있는 C드라이브의 경우 (저는 1번 파티션이 C드라이브입니다.) 파티션을 나누게되면 윈도우가 부팅되지 않을 수 있으므로 D같이 운영체제가 설치되어 있지 않은 파티션을 나누길 권장합니다.



새파티션 만들기해서 주 파티션으로 설정하시고...



주 파티션으로 설정하게 되면 위와 같이 자동으로 EXT4 파일시스템으로 설정이되며 마운트 위치가 루트로 됩니다. 논리로 설정하시게되면 직접 선택해줘야 되요.

다음으로 준비를 마쳤습니다를 선택해주시고



파티션 나누기를 마치고 바뀐 사항을 디스크에 쓰기 를 선택합니다.



'아니요' 를 눌러줍니다.



여기선 '예' 를 눌러주면 파티셔닝이 진행되는데요 다 끝나면 루트 암호를 입력해 줍니다.


다음으로 네트워크 미러 사이트를 사용함으로 설정하시고



GRUB를 설치해 줍니다



하드디스크에서 부팅하는 거니까 sda로 선택해 줍니다.



설치 끝! USB를 뺴고 계속을 눌러 리부팅 해줍니다.

* 스크린 샷 찍어놓고 기억에 의존해서 순서 맞춰본거라 순서가 좀 틀릴 수 있습니다.



6. 한글이 깨지네요 한글 폰트를 다운받아 봅시다.

root 와 아까 입력했던 루트 비밀번호를 차례대로 입력해서 바탕화면으로 들어갑니다.

제일먼저 화면꺼짐 설정을 해줍시다.

한글 폰트 설치하는 동안 기다리게 되는데 자꾸 대기모드로 들어가서 불편합니다.

오른쪽 위의 삼각형을 클릭하고 잠자리(?) 아이콘을 클릭합니다



그러면 설정창이 뜨는데 거기서 우선 노란색 전기 모양 버튼을 클릭해서 전원옵션에 들어갑니다



(B) 가 있는 드롭박스를 클릭하고 마지막 아이템을 선택하고 나옵니다.



이번엔 가면 모양을 클릭하고 화면옵션에 들어갑니다


여기서 가장 첫번째 있는 메뉴를 클릭하고...



2개의 on/off 스위치가 있는데 첫번쨰꺼를 off 시켜줍니다



이렇게하면 화면이 안꺼집니다.

다음으로 왼쪽 바로가기 패널에서 터미널을 열고...


다음 명령어를 입력하여 패키지를 최신으로 업데이트 합니다

root@kali:~# apt-get update && apt-get dist-upgrade -y

그리고 나서 나눔 글꼴과 나비 한글 입력기를 설치해 줍니다.

root@kali:~# apt-get install fonts-nanum -y
root@kali:~# apt-get install nabi im-switch -y
root@kali:~# im-config -s nabi
root@kali:~# im-config -c

im-config -c 를 입력하게 되면 설정창이 뜨는데



여기서 엔터



왼쪽꺼로 맞추고 엔터



방향키로 움직여서 hangul로 맞추고 스페이스를 눌러 선택합니다

그리고 엔터


한글 깨짐 이슈는 다음 블로그를 참조했습니당 :)

http://lureout.tistory.com/573




여기까지 전부 마지셨다면 reboot 명령어로 재시작하세요!


끗!

Posted by nfyfamraa
,

곧 있으면 AWS free tier 기간이 끝나서 계정을 하나 새로 파기로 했다.

처음엔 그냥 기존 계정 지우고 나서 재생성하면 다시 1년짜리 프리티어를 받을 수 있을거라 생각했는데 역시나 약은 생각이었다.

계정관리 대쉬보드에 들어가보니 계정 탈퇴 또는 삭제하는 탭은 아예없고 대신에 계정 닫기 탭만 있더라. 

이렇게 계정을 지우지 않고 영구히 보존하는 시스템인가 보다.

그래서 어쩔수 없이 새로운 이멜로 계정을 새로 팠다.

그러고 나서 몇가지 기존 자료들을 새로운 인스턴스로 옮기고 기존의 계정은 인스턴스를 삭제하고 계정 닫기를 하였다.

이거 정말 중요한데 혹시나 나처럼 계정 옮길 예정이라면 기존에 사용하던 인스턴스는 꼭 닫아야 한다.

안그러면 무료기간 끝나고 얄짤없이 과금된다...


암튼 여차저차 새 인스턴스를 만들고 나서 서버 세팅을 하였는데 접속 할때마다 공개키인증을 하는게 넘 귀찮다. 

그냥 갠적으로 사용하는데 중요한 자료를 보관하는 것도 아니라서 공개키인증을 사용할정도로 보안수준을 높힐 필요성을 못 느껴서 간단히 패스워드 인증으로 하고 싶었다.

그래서 장장 몇시간동안 구글링을 통해 찾아본 결과 다음과 같이 SSH 세팅을 하면 되더라


일단 에디터로 /etc/ssh/sshd_config를 연다.

이 파일은 권한이 -rw-r--r-- 로 설정 되어있으므로 root계정또는 sudo로 열어야 한다.

그리고 PasswordAuthentication 를 찾는데, 이게 디폴트로 no일 것이다.

이걸 yes로 바꿔주면 된다.

그럼 세션 접속할 때 키파일 없이 해당 계정의 패스워드만 입력해서 접속하게 된다.


만약 특정 그룹 또는 계정에만 패스워드 인증을하고 나머지에겐 공개키인증을 하게 만들고 싶다면 PasswordAuthentication는 no로 두고 파일의 맨 끝에 다음을 추가한다.


Match ( user | group ) 계정 또는 그룹 이름

PasswordAuthentication yes



예를 들어 schtakadurr 라는 계정만 패스워드 인증을하고 나머진 공개키인증을 하고 싶다면

Match user schtakadurr

PasswordAuthentication yes

로 하면 된다.


여기서 중요한게 이 옵션을 파일의 맨 끝에 추가하지 않고 다른곳에 넣는다면 network error라는 메시지와 함께 접근이 거부된다.


여기까지 설정을 마쳤으면 저장을 하고 

sudo service ssh restart 로 ssh데몬을 재시작 하면 설정 끝


그럼 ㅅㄱ~

Posted by nfyfamraa
,