참고 : http://blog.naver.com/soulcriboh/150168261078
포토샵을 보면 색상 변화를 주기 위해 사용하는 기능으로 Curves가 있죠.
이 기능 말입니다 !
이 기능을 구현해 보았습니다.
Curves의 기본 내용은
그래프의 x 축 input 값이 들어가면 사용자가 지정한 그래프 에 따라 y축 값이 달라지는데
이 y축 값을 output 값으로 색상을 변경해 출력하는 겁니다.
Curves의 알고리즘을 알았으니 어떻게 할까 고민을 하다가
선배의 도움을 받아 그래프를 수정할 좌표 점을 2개 이상 (시작점, 끝점 포함) 입력하면
입력된 좌표를 모두 지나는 그래프 식을 구하는 방법을 사용하기로 했습니다.
그 그래프로 256*1 크기의 texture map을 생성하고 그 texture map을 이용하여 색상 값을
변경해서 출력해줍니다 !
1) 받은 좌표를 이용해 다항식을 구해야 하는데 방법으로는 라그랑지(lagrange) 보간법을
사용하기로 함
자세한 라그랑지 보간법 설명과 코드의 설명은 위에 적은 블로그 에서 확인해 주세요 !!
(여담이지만... 코드를 이용하긴 했는데 이해를 완벽하게 하지는 못했어요 ㅠㅠ
역시 수학은 잘하고 봐야함.......)
사용한 블로그에서의 코드는 구한 다항식을 출력만 해주는데 전 그 수식을 이용한
값을 사용해야 해서 약간 수정해서 사용했습니다.
적용 라그랑지 코드 접기
int i, j, k; // for문용
int index=-1, c=0;
unsigned __int64 lcm=1, g ; //최소 공배수 값이 입력 좌표에 따라 값의 크기가 천차만별
float fTotal[256], son, mom;
bool bLcm = FALSE;
int fDenominator[256], fCloneDen[256], fNumerator[256][256], fInputPosX[256];
byte* pTexData = NULL;
pTexData = N3_MALLOC(N3_DEF(pN3), sizeof(n3byte) * 256 * 1);
for(i=0;i<256;i++){
fTotal[i] = 0;
pTexData[i] = 0;
}
//lagrange
for(i=0;i<nPosNum; i++){ // PosNum 입력 받은 좌표 갯수
for(j=0; j<nPosNum; j++){ //사용할 배열 초기화
if(j==0)
fNumerator[i][j] = fCurvesPos[i][1]; //입력 받은 y좌표 저장
else
fNumerator[i][j] = 0;
}
fInputPosX[i] = fCurvesPos[i][0]; //입력받은 x좌표 저장
}
for(i=0; i<nPointNum; i++){
fInputPosX[i]*=-1;
for(j=0; j<nPointNum; j++){ //분자계산
if(i==j){
++index;
continue;
}
for(k=index; k>=0; k--)
fNumerator[j][k+1]+=fNumerator[j][k]*fInputPosX[i];
}
fDenominator[i]=1;
for(j=0; j<i; j++){ //분모계산
fDenominator[i]*=fInputPosX[j]-fInputPosX[i];
fDenominator[j]*=fInputPosX[i]-fInputPosX[j];
}
}
for(i=0; i<nPointNum; i++) fCloneDen[i]=fDenominator[i];
i=2;
do{
for(j=0; j<nPointNum; j++){
if((int)fDenominator[j]%i==0){
if(!bLcm){
bLcm=TRUE;
lcm*=i;
}
fDenominator[j]/=i;
}
}
if(bLcm) bLcm=FALSE;
else ++i;
while(fDenominator[c]==1 || fDenominator[c]==-1){
++c;
if(c>=nPointNum) break;
}
}while(c<nPointNum);
for(i=0; i<nPointNum; i++){
son=0; mom=0;
for(j=0; j<nPointNum; j++)
son+=(float)fNumerator[j][i]*(float)lcm/(float)fCloneDen[j];
if(son>0) index=son;
else if(son<0) index=-son;
if(son==0) continue;
g=GCD(lcm, index); //분자 분모 약분
son/=g;
mom=(float)lcm/g;
for(k=0;k<256;k++){
fTotal[k] += son/mom*(float)pow(k,nPointNum-i-1);
if(fTotal[k] > 256)
fTotal[k] = 255;
else if(fTotal[k] < 0)
fTotal[k] = 0;
}
}
for(i=0;i<256;i++)
pTexData[i] = (int)fTotal[i];
접기
2) 생성한 texture map과 해당 이미지를 쉐이더로 넘겨서 curves를 적용해 줍니다.
curves frag shader 접기
precision mediump float;
uniform sampler2D uTexID0; //원본 이미지
uniform sampler2D uTexID1; //texture map
varying vec2 vTexCoord;
void main(void)
{
vec4 inColor = texture2D(uTexID0, vTexCoord); // 원본 이미지로 input 값 구함
// input 값을 그래프에 적용하여 output을 구함
gl_FragColor = vec4(texture2D(uTexID1,vec2(inColor.r,0.0)).r,
texture2D(uTexID1,vec2(inColor.g,0.0)).g,
texture2D(uTexID1,vec2(inColor.b,0.0)).b, 1.0);
}
접기
적용 결과
입력한 좌표
(0,0) , (0.325, 0.25), (0.625,0.75), (1,1)
원본
결과
참고로 texture map을 만드는 lagrange 보간법에서 소수점은 정확한 계산을 하지 못합니다.
그래서 전 좌표 입력은 보기 편하게 0 - 1사이값으로 받지만
받자마자 0 - 255 값으로 변경한 후에 계산해 줍니다 !
포토샵이랑 비교했을 때 정확히 일치하는 결과는 아니지만 비슷하게 출력됩니다 !