=======================
=======================
=======================
I'm working on a game for the iPhone that has a drawing/paint mechanic involved and I'm having problems trying to create a tool that would erase things already painted.
The main problem is that the background being painted on is not a solid color but a static image or animation. I've tried using different blending options and logical operations in drawing but nothing seemed to work. I'm new to OpenGL so I must be missing something.
Any tips?
EDIT: To give a little more information, I'm using textures for my brushes and using glVertexPointer() and glDrawArrays() to render them. For example:
glBindTexture(GL_TEXTURE_2D, circleBrush);
glVertexPointer(3, GL_FLOAT, 0, verts);
glTexCoordPointer(2, GL_FLOAT, 0, coords);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EDIT 2: Unfortunately, stencil buffers are not available on the iPhone. : (
EDIT 3: Framebuffer objects are available on the iPhone and that is the road I took. I haven't fully implemented it yet, but so far it looks like it works the way I wanted it to. Thanks everyone!
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Draw a full-screen textured quad over your scene. When the user draws a brush stroke, use glTexSubImage2D to update the your texture.
glReadPixels/glDrawPixels is slow.
Using FrameBufferObjects is even better, but I doubt this extention is available on the iPhone (then again, I don't know for sure, so maybe try it). FBO's allow you to draw directly into a texture as if it were another rendering context.
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Stencil Buffer is the best way to approach this for sure...You'll save time, cpu, and potential problems..,They are available on Iphone, you just have to created an OpenGlES 2.0 surface (not gles 1.0 or 1.1).
// Turn off writing to the Color Buffer and Depth Buffer We want to draw to
// the Stencil Buffer only
glColorMask(false, false, false, false);
glDepthMask(false);
// Enable the Stencil Buffer
glEnable(GL_STENCIL_TEST);
// Set 1 into the stencil buffer
glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
// CALL YOUR DRAWING METHOD HERE
// Turn on Color Buffer and Depth Buffer
glColorMask(true, true, true, true);
glDepthMask(true);
// Only write to the Stencil Buffer where 1 is set
glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
// Keep the content of the Stencil Buffer
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// CALL OUR DRAWING METHOD AGAIN
Lookup this example :http://www.opengl.org/resources/code/samples/glut_examples/examples/stenciltst.c
Here's the visual result :http://www.opengl.org/resources/code/samples/glut_examples/examples/stenciltst.jpg
I've implented the same kind of eraser tool with this, on both android and iPhone and it works like a charm !
Good luck !
Cheers !
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
You don't give much info, but I assume that your storing whatever they "paint" into a buffer and then drawing it on screen like this:
glWindowPos2i(X, Y); glDrawPixels(drawing->Width, drawing->Height, drawing->Format, GL_UNSIGNED_BYTE, drawing->ImageData);
With drawing->ImageData being the buffer. What you could do is have a separate background buffer and draw it first. Then the erase tool will simply white-out the the drawing buffer(turning all the values, including alpha, all the way up).
For this solution to work you'd have to turn on blending and turn off depth testing.
glEnable(GL_BLEND); glDisable(GL_DEPTH_TEST); glWindowPos2i(X, Y); //background never changes glDrawPixels(background->Width, background->Height, background->Format, GL_UNSIGNED_BYTE, background->ImageData); glWindowPos2i(X, Y); glDrawPixels(drawing->Width, drawing->Height, drawing->Format, GL_UNSIGNED_BYTE, drawing->ImageData); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND);
Is that what you're looking for?
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
You could use the stencil buffer for this operation. The stencil buffer is a special buffer which holds information for every pixel, similiar to the depth buffer. Unlike the depth buffer, you decide how the stencil buffer is altered while drawing and how it influences the decision to draw into the color buffer or not. To do so, you can set the specific states before any drawing operation. Here is what you do:
- At the time the users erases, draw the "erased" region into the stencil buffer (using StencilOp, see below). You can use any GL drawing function for that.
- In every rendering pass, first draw the background (if you want, you can use a negative stencil test here to only draw the "erased" part of the background - may increase performance).
- Then, enable the stencil test for all other drawing operations. OpenGL will then only draw to the "non-erased" regions.
The function to set how the stencil buffer should influence drawing (or not): glStencilFunc()
The function to set how the stencil buffer itself is influenced by drawing: glStencilOp()
Further explanation, also with the arguments for these:http://www.opengl.org/resources/code/samples/sig99/advanced99/notes/node117.html
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
Using glBlendFunc(GL_ONE, GL_ZERO) mode can earse.
=======================
=======================
=======================
출처ㅣ http://blog.naver.com/PostView.nhn?blogId=perlhy&logNo=120107860010
OpenGL 기본 도형 그리기
* OpenGL 상태 기계 ( OpenGL state machine ) 는 렌더링의 여러 측면들에 영향을 주는 수 백개의 설정들로 구성된다. 상태기계는 OpenGL 의 모든 측면들을 관장한다.
상태들을 조회하거나 설정하는 함수들
glGet() 함수들 : 상태기계의 현재 상태를 조회
void glGetBooleanv( GLenum pname, GLboolean *params );
void glGetDoublev( GLenum pname, GLdouble *params );
void glGetFloatv( GLenum pname, GLfloat *params );
void glGetIntegerv( GLenum pname, GLint *params );
이 함수들을 호출하면 조회하고자 하는 상태에 관련된 값이 param 인자에 배정된다.
pname 은 조회하고자 하는 상태를 지정한다.
glIsEnabled() 함수 : 어떤 특정한 OpenGL 기능성의 활성화 여부를 알아낼때 사용한다. ( 참 거짓 만을 알아내는 경우 )
GLboolean glIsEnabled( GLenum cap );
이 함수는 인자가 가리키는 기능성이 활성화되어 있으면 GL_TRUE, 그렇지 않으면 GL_FALSE 를 돌려준다.
< 기본 도형 그리기 >
OpenGL 에서 기본 도형( primitive )은 가장 기본적인 기하학적인 객체들, 즉 점과 선, 삼각형 등을 가리킨다.
도형을 그리기 위해 가장많이 사용되는 함수는 glBegin(), glEnd()
glBegin() 함수 : 기본도형을 그리기 위한 선언
void glBegin( GLenum mode );
glBegin()의 인자에 사용할 수 있는 값들
상수 설명 GL_POINTS 개별적인 점들 GL_LINES 연결되지 않는 선분들 GL_LINE_STRIP 연결된 일련의 선분들 GL_LINE_LOOP 연결된 선분들 - 처음과 끝이 연결되어 하나의 닫힌 선분들이 된다 GL_TRIANGLES 개별적인 삼각형들 GL_TRIANGLE_STRIP 연결된 일련의 삼각형들 GL_TRIANGLE_FAN 하나의 꼭지점을 공유하는 일련의 삼각형들 GL_QUADS 개별적인 사각형들 GL_QUAD_STRIP 연결된 일련의 사각형들 GL_POLYGON 임의의 개수의 꼭지점들을 가진 다각형
glBegin() 을 호출해서 기본도형을 시작했으면 반드시 glEnd()로 끝내야 한다.
void glEnd();
여기서 명심해야 할것은, glBegin() / glEnd() 쌍이 중첩되어서는 안된다. 이 블럭안에서 또 다른 glBegin() / glEnd() 를 호출해서는 안된다.
glBegin() / glEnd() 쌍안에서 사용할수 있는 OpenGL 함수들 :
glVertex(), glColor(), glIndex(), glNormal(), glTexcoord(), glEvalCoord(). glEvalPoint(), glMaterial(), glEdgeFlag(), glCallList(), glCallLists() 의 변형함수들
- 점찍기
glBegin( GL_POINTS );
glVertex3f( 0.0, 0.0, 0.0 );
glEnd();
점크기 변경 : void glPointSize( GLfloat size );
현재 점의 크기 확인 : glGet( GL_POINT_SIZE );
점의 안티얼라이어싱( antialiasing ) : 변을 부드럽게 표현하는 하나의 수단. 활성화
활성화 : glEnable( GL_POINT_SMOOTH );
비활성화 : glDisable( GL_POINT_SMOOTH );
현재 활성화여부 확인 : glGet( GL_POINT_SMOOTH ); or glIsEnabled( GL_POINT_SMOOTH );
안티얼라이어싱 지원크기 확인 :
GLfloat sizes[ 2 ];
GLfloat granularity;
glGetFloatv( GL_POINT_SIZE_RANGE, sizes );
GLfloat minPointSize = sizes[ 0 ];
GLfloat maxPointSize = sizes[ 1 ];
glGetFloatv( GL_POINT_SIZE_GRANULARITY, &granularity ); - 선 긋기
glBegin( GL_LINES );
glVertex3f( -2.0, -1.0, 0.0 );
glVertex3f( 3.0, 1.0, 0.0 );
glEnd();
선굵기변경 : 기본굵기는 1.0 void glLineWidth( GLfloat width );
현재의 선굵기 확인 : glGet( GL_LINE_WIDTH );
선의 안티얼라이어싱 : 점의 안티얼라이어싱과 동일 ( GL_POINT_SIZE_RANGE --> GL_LINE_SIZE_RANGE,
GL_POINT_SIZE_GRANULARITY --> GL_LINE_WIDTH_GRANULARITY,
GL_POINT_SMOOTH --> GL_LINE_SMOOTH,
minLineWidth, maxLineWidth ) - 다각형 그리기
OpenGL 은 다각형의 전면( front face - 카메라를 바라보는 면 )과 후면( back face - 카메라 반대쪽을 바라보는 면 )을 개별적으로 취급
void glPolygonMode( GLenum face, GLenum mode );
face 인자 : GL_FRONT, GL_BACK, GL_FRONT_AND_BACK
mode 인자 : GL_POINT, GL_LINE, GL_FILL
예) glPolygonMode( GL_FRONT, GL_FILL ); 전면 색칠
glPolygonMode( GL_BACK, GL_LINE ); 후면 와이어 프레임( 테두리만 )
현재 그리기 모드 확인 : glGet( GL_POLYGON_MODE );
다각형의 면제거( face culling ) :
glEnable( GL_CULL_FACE );
glCullFace( GL_BACK );
OpenGL은 다각형을 감는 방법( polygon winding )에 기반해서 전면과 후면 판단.
반시계 방향으로 감긴 다각형 : 전면
시계 방향으로 감긴 다각형 : 후면
형식 변경 : void glFrontFace( GLenum mode );
mode 인자 : GL_CCW ( 반시계방향을 전면으로 간주 ), GL_CW ( 시계 방향을 전면으로 간주 )
다각형 변숨기기 : void glEdgeFlag( GLboolean isEdge ); or void glEdgeFlag( const GLboolean *isEdge );
isEdge : GL_TRUE ( 기본 설정 ), GL_FALSE ( 그리지 않는다 )
다각형의 안티얼라이어싱 : 켜기 ( glEnable( GL_POLYGON_SMOOTH ); ), 끄기 ( glDisable( GL_POLYGON_SMOOTH ); ) - 삼각형 그리기
glBegin( GL_TRIANGLES );
glVertex3f( 0.0, 0.0, 1.0 );
glVertex3f( 0.0, 0.0, 1.0 );
glVertex3f( 0.0, 0.0, 1.0 );
glEnd();
* void glFlush( void );
앞에서 요청한 OpenGL 커멘트들을 강제로 실행시켜서 요청된 커맨트들이 일정한 시간내에 완전히 종료한다는것을 보장
이것으로 부족할 경우 glFinish() 사용[출처] OpenGL 기본 도형 그리기|작성자 헨리스탈
=======================
=======================
=======================
출처: http://wonjayk.tistory.com/34
화면에 그려진 그림은 색상버퍼라는 메모리에 저장됩니다.
그림이 이미 그려진 상태에서 같은 위치에 다른 그림을 그리면 새 그림을 새 메모리에 기록하기 때문에, 이전에 있던 그림에 덮어져서 그려지게 됩니다.
이런 당연한 현상도 블렌딩 모드를 변경하면 달라질 수 있습니다.
블렌딩은 색상버퍼에 이미 기록되어있는 값과 새로 그려지는 값의 논리연산방법을 지정해 줍니다.
블렌딩 기능을 사용하려면 다음 명령으로 요고를 적용시켜야 합니다.
glEnable(GL_BLEND);
블렌딩은 색상버퍼에 이미 기록된 값 D와 새로 기록되는 값 S와의 연산을 정의합니다.
연산방법은 다음 두 함수로 정의됩니다.
void glBlendFunc(GLenum sfactor, GLenum dfactor);
void glBlendEquation(GLenum mode);
sfactor와 dfactor는 S색상과 D색상에 각각 적용할 연산식을 정의하며, mode는 두 연산결과를 합칠 방법을 정의합니다.
모드에 따른 연산식은 아래와 같습니다, 디폴트는 GL_FUNC_ADD
SF, DF는 sfactor, dfactor
모드 연산식
GL_FUNC_ADD S*SF + D*DF
GL_FUNC_SUBTRACT S*SF - D*DF
GL_FUNC_REVERSE_SUBTRACT D*DF - S*SF
GL_MIN S*SF, D*DF중 작은 값
GL_MAX S*SF, D*DF중 큰 값
사실 뭔소린지 잘 모르겠어요, 예제를 해봅시다.
S와 D에 적용되는 연산식의 종류는 아래와 같습니다, 잘 안쓰는 항은 생략한거니까 자세한건 레퍼런스를 참고하세염
연산식 색상(FR, FG, FB, FA)
GL_ZERO (0,0,0,0)
GL_ONE (1,1,1,1)
GL_SRC_COLOR (RS, GS, BS, AS)
GL_ONE_MINUS_SRC_COLOR (1-RS, 1-GS, 1-BS, 1-AS)
GL_DST_COLOR (RD, GD, BD, AD)
GL_ONE_MINUS_DST_COLOR (1-RD, 1-GD, 1-BD, 1-AD)
GL_SRC_ALPHA (AS, AS, AS, AS)
GL_ONE_MINUS_SRC_COLOR (1-AS, 1-AS, 1-AS, 1-AS)
GL_DST_ALPHA (AD, AD, AD, AD)
GL_ONE_MINUS_DST_COLOR (1-AD, 1-AD, 1-AD, 1-AD)
GL_CONSTANT_COLOR (RC, GC, BC, AC)
GL_ONE_MINUS_CONSTANT_COLOR (1-RC, 1-GC, 1-BC, 1-AC)
GL_CONSTANT_ALPHA (AC, AC, AC, AC)
GL_ONE_MINUS_CONSTANT_ALPHA (1-AC, 1-AC, 1-AC, 1-AC)
GL_SRC_ALPHA_SATURATE (i,i,i,1) i = min(AS, 1-AD)
연산식에 의해 R, G, B, A 색상 요소에 곱해지는 FR, FG, FB, FA 함수가 정의되고 이 함수가 각 색상요소에 적용됨으로써, 중간식이 생성되며 두 중간식을 연산하여 최종 색상을 도출합니다.
색상이 아닌 상수와도 연산이 가능한데 그 때는 아래의 함수로 지정합니다.
void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
디폴트는 (0,0,0,0) 검정색이다.
다음 함수는 좀 더 상세한 연산식을 정의한다.
void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha);
지정 가능한 연산식의 종류는 동일하다.
계산식이 좀 더 복잡한 대신 다양한 기교를 부릴 수 있다.
#include <gl/glut.h>
void DoDisplay();
void KeyDown(unsigned char key, int x, int y);
void DoMenu(int value);
GLfloat Alpha = 0.5 f;
GLenum Src = GL_SRC_ALPHA;
GLenum Dest = GL_ONE_MINUS_SRC_ALPHA;
int main()
{
glutCreateWindow("OpenGL"); // 윈도우 만들기.
glutDisplayFunc(DoDisplay); // 화면표시함수.
glutKeyboardFunc(KeyDown); // 키보드 입력 함수.
glutCreateMenu(DoMenu); // 메뉴 생성 함수
glutAddMenuEntry("Opaque", 1); // 메뉴 엔트리 1
glutAddMenuEntry("Traslucent", 2); // 메뉴 엔트리 2
glutAttachMenu(GLUT_RIGHT_BUTTON); // 오른쪽 버튼을 누르면 메뉴생성
glutMainLoop(); // 메인루프
return 0;
}
void KeyDown(unsigned char key, int x, int y) // q, a를 눌렀을 때 알파값 변경
{
switch (key) {
case 'q':
Alpha += 0.1;
break;
case 'a':
Alpha -= 0.1;
break;
}
glutPostRedisplay(); // 화면 재생성
}
void DoMenu(int value)
{
switch (value) {
case 1:
Src = GL_ONE;
Dest = GL_ZERO;
break;
case 2:
Src = GL_SRC_ALPHA;
Dest = GL_ONE_MINUS_SRC_ALPHA;
break;
}
glutPostRedisplay();
}
void DoDisplay()
{
glClearColor(1, 1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(Src, Dest);
glColor3f(0, 0, 1);
glRectf(-0.5, 0.8, 0.5, 0.0);
glColor4f(1, 0, 0, Alpha);
glBegin(GL_TRIANGLES);
glVertex2f(0.0, 0.5);
glVertex2f(-0.5, -0.5);
glVertex2f(0.5, -0.5);
glEnd();
glFlush();
}
삼각형이 q를 누르면 진해지고 a를 누르면 연해집니다
오른쪽 버튼을 눌러 메뉴를 호출하고 Opaque를 누르면 알파값 1, Traslucent를 누르면 알파값이 적용됩니다.
이 예제에서의 반투명 출력에 사용한 블렌드 연산식은 다음과 같습니다.
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
삼각형의 알파값이 0.4라고 했을 때, GL_SRC_ALPHA의 블렌딩 함수는 (AS, AS, AS, AS)이다. 그래서 S의 각 색상요소에 0.4가 곱해집니다.
GL_ONE_MINUS_SRC_ALPHA의 블렌딩 함수는 모두 1-AS이므로, D의 각 색상요소에 0,6이 곱해진다. 두 연산결과를 연산하는 모드는 디폴트인 GL_FUNC_ADD이므로, 두 값을 더해 최종 색상을 결정합니다.
(0.4 * 1 + 0.6 * 0, 0.4 * 0 + 0.6 * 0, 0.4 * 0 + 0.6 * 0, 0.4 * 0.4 + 0.6 * 0.6) // RGBA
(0.4 * (1, 0, 0, 0.4) + 0.6 * (0, 0, 1, 0.6)) // 빨간색삼각형 + 파란색삼각형
0.4 * 빨간색 + 0.6 * 파란색
빨간거 40%(S색), 파란거 60%(D색) 섞어서 기록하는겁니다.
불투명 모드일 때는
glBlendFunc(GL_ONE, GL_ZERO);
앞이 S고 뒤가 D입니다.
S의 색상은 그대로고, D의 색상은 없애니까 불투명해지는거겠지요?
ㅇㅋㅇㅋ
출처: http://wonjayk.tistory.com/34 [배고파서 까먹고 만든 블로그]
=======================
=======================
=======================
출처: http://wonjayk.tistory.com/33
모든 디지털 그래픽의 기본은 점입니다.
선이나, 면도 아시다시피 모두 점의 집합입니다.
하지만, 3차원 그래픽의 가장 원시적인 요소는 정점(Vertex)입니다.
정점 : 위치정보만 갖는다.
점 : 위치 + 색깔 + 크기 등
정점은 다음 두 블록 사이에 정의됩니다.
void glBegin(GLenum mode);
void glEnd(void);
아시죠잉?
정점을 찍어내는 함수는
glVertex[2,3,4][s,i,f,d][v](x,y,z,w);
블록내의 정점들로 무엇을 어떻게 그려낼까는 glBegin으로 전달되는 모드값에 따라 달라집니다.
모드 설명
GL_POINTS 독립적인 점
GL_LINE_STRIP 연결된 선분
GL_LINE_LOOP 시작점과 끝점을 이은 선분
GL_LINES 두개의 정점들을 이은 선분
GL_TRIANGLES 세개씩 연결한 삼각형
GL_TRIANGLE_STRIP 연결된 삼각형
GL_TRIANGLE_FAN 중심을 공유하는 삼각형
GL_QUADS 정점 4개씩을 연결하여 사각형을 그린다
GL_QUAD_STRIP 연결된 사각형
GL_POLYGON 연결된 블록 다각형
void DoDisplay() {
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1, 0, 0); // 정점의 색은 빨간색
glBegin(GL_POINTS); // 점만 찍어낸다.
glVertex2f(0.0, 0.5);
glVertex2f(-0.5, -0.5);
glVertex2f(0.5, -0.5);
glEnd();
glFlush();
}
결과화면을 스샷하려다가 점세개만 딸랑 나오는걸 보고 스샷해도 안보이겠다싶어서 화면은 스킵했습니다.
다른부분은 첫번째 삼각형 그릴때의 부분과 같기 때문에, 디스플레이부분만 실어 보았습니다.
그래서 이 작은 점을 확대하는 함수가 있습니다.
void glPointSize(GLFloat size);
void DoDisplay() {
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1, 0, 0); // 정점의 색은 빨간색
glPointSize(10.0); // 점의 크기는 10
glBegin(GL_POINTS); // 점만 찍어낸다.
glVertex2f(0.0, 0.5);
glVertex2f(-0.5, -0.5);
glVertex2f(0.5, -0.5);
glEnd();
glFlush();
}
GL_POINTS
점의 크기가 10으로 커져서 눈에 보이게 됐습니다.
이 점의 크기는 1.0 ~ 63.375까지의 범위를 가지며, 범위를 넘어가더라도 가장 가까운 값이 선택되면서 에러는 나지 않습니다. 똑똑하죠?
안티엘리어싱을 하지 않아서 점이 사각형으로 나온 것인데, 이건 나중에..
GL_LINE_STRIP
GL_LINE_LOOP
GL_LINES : 정점을 두개씩 한 조로 선을 그어준다. 홀수면 마지막 정점은 무시됩니다.
선을 더 굵게 그리고싶다구요?
void glLineWidth(GLFloat width);
width에 설정된 숫자대로 선의 굵기가 변형됩니다.
void DoDisplay() {
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1, 0, 0);
glLineWidth(10.0); // 너비 10짜리 선
glBegin(GL_LINE_LOOP); // 삼각형
glVertex2f(0.0, 0.5);
glVertex2f(-0.5, -0.5);
glVertex2f(0.5, -0.5);
glEnd();
glFlush();
}
실선이 아닌 선을 그리고 싶을 땐 어쩔까요?
점선같은거
void glLineStipple(GLint factor, GLushort pattern);
그닥 중요하지 않으니까, 예제로 만들지는 않고 이론으로 짤막하게 설명드리겠습니다.
요 기능을 사용하려면 일단
glEnable(GL_LINE_STIPPLE);
로 켜줘야하고, glDisable하면 됩니다.
pattern은 2진수로 표현된 점의 모양입니다.
대응되는 비트가 1이면 점이 찍히고 0이면 찍히지 않습니다.
이런겁니다.
0x33ff를 패턴 자리에 넣으면 선 모양은 맨 아래처럼 나오게 됩니다.
이제 삼각형을 그려보겠습니다.
void DoDisplay() {
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1, 0, 0);
glBegin(GL_TRIANGLES);
GLfloat x = -0.8;
GLfloat y = 0.4;
for (int i = 0; i < 6; i ++) {
glVertex2f(x, y);
x += 0.3;
y *= -1; // +1과 -1의 반복 (곱하기니까)
}
glEnd();
glFlush();
}
GL_TRIANGLES : 정점을 세개씩 짝지어 삼각형을 그린다, 3의 배수에서 남는 점은 무시됩니다.
void DoDisplay() {
glClear(GL_COLOR_BUFFER_BIT);
glShadeModel(GL_FLAT); // 단색으로 채색, 나중에 자세하게 하겠습니다^^
glBegin(GL_TRIANGLE_STRIP);
GLfloat x = -0.8;
GLfloat y = 0.4;
for (int i = 0; i < 6; i ++) {
if (i % 2 == 0) {
glColor3f(1.0, 0.0, 0.0); // 삼각형을 구별하기위해 색상처리만 다르게
} else {
glColor3f(0.0, 1.0, 0.0);
}
glVertex2f(x, y);
x += 0.3;
y *= -1;
}
glEnd();
glFlush();
}
GL_TRIANGLE_STRIP : 삼각형을 이어서 그립니다.
void DoDisplay() {
glClear(GL_COLOR_BUFFER_BIT);
glShadeModel(GL_FLAT);
glBegin(GL_TRIANGLE_FAN);
glColor3f(1.0, 0.0, 0.0);
glVertex2f(0.0, 0.0);
glVertex2f(0.0, 0.5);
glVertex2f(-0.35, 0.35);
glColor3f(0.0, 1.0, 0.0);
glVertex2f(-0.5, 0.0);
glColor3f(1.0, 0.0, 0.0);
glVertex2f(-0.35, -0.35);
glColor3f(0.0, 1.0, 0.0);
glVertex2f(0.0, -0.5);
glEnd();
glFlush();
}
GL_TRIANGLE_FAN : 첫번째 정점을 기준으로 부채꼴모양으로 삼각형을 그려줍니다,
이걸 아주 작은 삼각형을 만들게되면 원모양이 됩니다.
이제 사각형을 그려보겠습니다.
사각형은 완전 독립된 함수이기 때문에, glBegin ~ glEnd사이에 들어가 있지 않더라도 작성 가능합니다.
void DoDisplay() {
glClear(GL_COLOR_BUFFER_BIT);
glRectf(-0.8, 0.8, 0.8, -0.8); // 사각형그려! (왼쪽 위, 오른쪽 아래)
glFlush();
}
심플허니 아름답지요?
왼쪽 위와, 오른쪽 아래를 꼭지점으로 하는 사각형이 만들어집니다.
대각선의 두 점을 지정하는 방식이라 각 변이 수직인 직사각형만 그릴 수 있습니다.
평행사변형이나 사다리꼴 처럼 직각이 아닌 사각형은 그릴 수 없습니다.
불규칙한 사각형은 정점을 직접 지정하여 다각형으로 그려야 합니다.
void DoDisplay() {
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_QUADS);
glVertex2f(0.0, 0.5);
glVertex2f(-0.5, 0.0);
glVertex2f(0.0, -0.5);
glVertex2f(0.5, 0.0);
glEnd();
glFlush();
}
GL_QUADS : 정점을 4개씩 묶어서 사각형을 그립니다.
마찬가지로 네개가 넘어가는 나머지 정점은 무시됩니다.
GL_QUAD_STRIP은 GL_TRIANGLE_STRIP처럼 사각형을 이어서 그립니다.
6개의 정점만으로 두개의 사각형을 그릴 수 있습니다.
그래서 따로 예제를 만들지는 않았고요.
자, 이제 마지막으로 다각형을 그려보겠습니다.
GL_POLYGON으로 그리는데, 모든 정점을 연결하여 다각형을 그려냅니다.
그런데 다각형이라고 다 그려지는게 아닙니다.
좀 까다롭습니다.
* 정점의 선이 교차해서는 안된다.
* 다각형은 볼록해야 한다.
* 모든 정점은 같은 평면 내에 있어야 한다.
왼쪽이 볼록한 다각형, 오른쪽이 오목하거나 선분이 교차하는 다각형입니다.
왼쪽 같은 경우만 가능합니다,
모든 정점이 같은 평면 내에 있다는 조건이 애매하게 들릴 지 모르겠습니다.
왼쪽 그림은 종이를 책상위에 놓은것이고,
오른쪽 그림은 책상 위에 둔 종이의 오른쪽 아래 귀퉁이를 살짝 들어 올린 형태입니다.
이렇게 되면, 왼쪽은 책상이라는 같은 평면에 있게된 사각형인데,
오른쪽은 오른쪽 아래 귀퉁이를 위로 올려버리는 바람에 책상이라는 평면에 있지 않게 되었습니다.
이게 바로 모든 정점이 같은 평면에 있어야 한다는 조건입니다. 따라서 오른쪽의 다각형은 그려낼 수 없습니다.
- 물론, 같은 평면 내에서 정점의 위치를 다르게 잡아서 하는 경우엔 가능합니다. 보이기에는 오른쪽 도형과 같을 지 모르겠지만, 같은 평면에서 정점 위치만 달라진 것이기 때문에 같은 평면에 있는 조건을 만족시켜서 그려지는 것입니다.
void DoDisplay() {
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_POLYGON); // 다각형을 그려내기 위해 변경
glVertex2f(0.0, 0.5); // 가장 꼭대기 정점
glVertex2f(-0.5, 0.0); // 왼쪽 정점
glVertex2f(0.5, 0.0); // 마름모 예제와 3, 4번 정점의 위치를 바꾸었다.
glVertex2f(0.0, -0.5); // 아래 정점
glEnd();
glFlush();
}
GL_POLYGON : 다각형
OpenGL이 인정하지 않는 오목한 다각형이 나왔습니다. 어쨋든..
OpenGL ES에서는 사각형 함수가 없다는 것 잊지말자.
- 삼각형 두개로 사각형을 그려내야합니다.
출처: http://wonjayk.tistory.com/33 [배고파서 까먹고 만든 블로그]
=======================
=======================
=======================
출처: https://skyfe79.gitbooks.io/opengl-tutorial/content/chapter4.html
선 그리기
- GL_LINES
- GL_LINE_STRIP
- GL_LINE_LOOP
- glLineWidth()
- glLineStipple()
앞에서는 점을 그려보았다. 선을 그리려면 선의 길이만큼 점을 그려줘야하는데 일일이 그것을 해준다는 것은 생각만해도 질릴 일이다. OpenGL 에서 그 일을 쉽게 해주는 것이 glBegin() 함수에 GL_LINES 나 GL_LINE_STRIP , GL_LINE_LOOP 를 설정하는 것이다. 이렇게 함으로써 선을 쉽게 그릴 수 있다. 선을 그리기 위해서는 두 개의 정점이 필요한데, 하나는 선의 시작 위치점이고 나머지 하나는 선이 끝나는 위치 점이다. OpenGL 에서는 가는 선만 그릴 수 있는 것이 아니라 glLineWidth() 함수로 선의 굵기를 조절할 수 있다 또, 직선만 그릴 수 있는 것이 아니라 glLineStipple() 함수를 이용해 패턴이 있는 선을 그릴 수 있다. 예를 들면 점선과 같이 패턴이 있는 선을 쉽게 그릴 수 있는 것이다.
이 예제는 앞에서의 점찍기와 아주 비슷한 예제다. 단 틀린 점은 마우스 오른쪽 버튼을 누르면 선의 굵기가 점점 굵어 지도록 해 놓은 것 뿐이다.
#define EGL_USE_STL
#include "lib\egl.h"
struct Point3D {
GLfloat x,
y,
z;
Point3D() {
x = y = z = 0.0 f;
}
Point3D(GLfloat ax, GLfloat ay, GLfloat az) {
x = ax;
y = ay;
z = az;
}
};
struct Line {
Point3D startPoint;
Point3D endPoint;
Line(Point3D aStartPoint, Point3D aEndPoint) {
startPoint = aStartPoint;
endPoint = aEndPoint;
}
};
typedef vector < Line > line_list;
typedef vector < Line >:: iterator liter;
class eglSubWindow: public eglWindow {
private : line_list mLineList;
GLfloat Range;
GLsizei ClientWidth;
GLsizei ClientHeight;
GLfloat LineWidth;
public : virtual void RenderGLScene(void);
virtual void OnSize(WPARAM wParam, LPARAM lParam);
virtual void OnCreate(WPARAM wParam, LPARAM lParam);
virtual void OnLButtonDown(WPARAM wParam, LPARAM lParam);
virtual void OnLButtonUp(WPARAM wParam, LPARAM lParam);
virtual void OnRButtonUp(WPARAM wParam, LPARAM lParam);
};
void eglSubWindow::OnCreate(WPARAM wParam, LPARAM lParam) {
ClientWidth = ClientHeight = 0.0 f;
Range = 5.0 f;
LineWidth = 1.0 f;
}
void eglSubWindow::OnSize(WPARAM wParam, LPARAM lParam) {
GLsizei width = LOWORD(lParam);
GLsizei height = HIWORD(lParam);
if (height == 0)
height = 1;
ClientWidth = width;
ClientHeight = height;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-Range, Range, -Range, Range, 1.0 f, 100.0 f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void eglSubWindow::OnLButtonDown(WPARAM wParam, LPARAM lParam) {
GLfloat xPos = (GLfloat)(LOWORD(lParam));
GLfloat yPos = (GLfloat)(HIWORD(lParam));
GLfloat glX = ((xPos * (2 * Range)) / ClientWidth) - Range;
GLfloat glY = ((yPos * (2 * Range)) / ClientHeight) - Range;
mLineList.push_back(Line(Point3D(glX, glY, 0.0 f), Point3D(glX, glY, 0.0 f)));
SetCapture(GetHWND());
}
void eglSubWindow::OnLButtonUp(WPARAM wParam, LPARAM lParam) {
GLfloat xPos = (GLfloat)(LOWORD(lParam));
GLfloat yPos = (GLfloat)(HIWORD(lParam));
GLfloat glX = ((xPos * (2 * Range)) / ClientWidth) - Range;
GLfloat glY = ((yPos * (2 * Range)) / ClientHeight) - Range;
if (!mLineList.empty()) {
mLineList[mLineList.size() - 1].endPoint.x = glX;
mLineList[mLineList.size() - 1].endPoint.y = glY;
mLineList[mLineList.size() - 1].endPoint.z = 0.0 f;
}
ReleaseCapture();
}
void eglSubWindow::OnRButtonUp(WPARAM wParam, LPARAM lParam) {
LineWidth += 1.0 f;
if (LineWidth > 10.0 f) {
LineWidth = 10.0 f;
}
}
void eglSubWindow::RenderGLScene(void) {
eglWindow::RenderGLScene();
glTranslatef(0.0 f, 0.0 f, -10.0 f);
glLineWidth(LineWidth);
glBegin(GL_LINES);
for (liter i = mLineList.begin(); i != mLineList.end(); ++i) {
glVertex3f(( * i).startPoint.x, -( * i).startPoint.y, ( * i).startPoint.z);
glVertex3f(( * i).endPoint.x, -( * i).endPoint.y, ( * i).endPoint.z);
}
glEnd();
}
int APIENTRY WinMain(
HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd
) {
eglSubWindow app;
app.Create(FALSE, "EDin's OpenGL glLine");
return app.Run();
}
GL_LINES 와 GL_LINE_STRIP 그리고 GL_LINE_LOOP 의 차이점을 알기 위해서, 위의 코드 중에서 RenderGLScene() 함수를 다음과 같이 수정하고 실행해 보자. 그러면 차이점을 금방 알게 될 것이다.
void eglSubWindow::RenderGLScene(void)
{
eglWindow::RenderGLScene();
glTranslatef(0.0f, 0.0f, -10.0f);
glLineWidth(LineWidth);
glBegin(GL_LINE_STRIP);
for(liter i=mLineList.begin(); i!=mLineList.end(); ++i)
{
glVertex3f((*i).startPoint.x, -(*i).startPoint.y, (*i).startPoint.z);
glVertex3f((*i).endPoint.x, -(*i).endPoint.y, (*i).endPoint.z);
}
glEnd();
}
void eglSubWindow::RenderGLScene(void)
{
eglWindow::RenderGLScene();
glTranslatef(0.0f, 0.0f, -10.0f);
glLineWidth(LineWidth);
glBegin(GL_LINE_LOOP);
for(liter i=mLineList.begin(); i!=mLineList.end(); ++i)
{
glVertex3f((*i).startPoint.x, -(*i).startPoint.y, (*i).startPoint.z);
glVertex3f((*i).endPoint.x, -(*i).endPoint.y, (*i).endPoint.z);
}
glEnd();
}
glLineStipple() 을 이용해서 패턴이 있는 선을 그려보자. glLineStipple() 함수의 원형은 다음과 같다.
void glLineStipple ( GLint factor, GLushort pattern )
factor 는 pattern 에 정의한 선의 패턴이 몇 픽셀에 나타나게 할 것인지를 말한다. 예를 들어 0xAAAA 라는 패턴이 있으면 이를 2진수로 표현하면 1010101010101010 이다. 여기서 factor 를 1로 설정하면 앞의 패턴이 1 픽셀 안에서 나타나는 것이고 2이면 2픽셀 안에서 4이면 4픽셀 안에서 패턴이 나타난다. 따라서 factor 의 값이 작을 수록 촘촘한 패턴이 나타나게 된다. 또 한가지 중요한 점은 위의 패턴이 거꾸로 적용된다는 것이다. 즉 1010101010101010 패턴이 적용될 때에는 0101010101010101 이 된다. 우리가 패턴을 사용할 경우에 위의 함수에 패턴을 정의하면 모든 선에 패턴이 적용될 것이다. 하지만 그렇게 되면 패턴이 적용되지 않는 선은 그릴 수가 없다. 그래서 OpenGL 에서는 여러 상태들 중 LineStipple 의 사용 가능 여부의 상태를 설정하는 glEnable( GL_LINE_STIPPLE ) 과 glDisable( GL_LINE_STIPPLE ) 을 제공한다. 여기서 glEnable() 과 glDisable() 의 함수는 OpenGL 의 여러가지 상태들을 ON / OFF 하는 함수다.
위의 예제는 좌표계의 각 축을 그려본 것이다. 점선은 각축의 음의 부분을 나타내고 실선은 양의 부분을 나타낸다. 빨간색은 X 축 녹색은 Y 축 파란색은 Z 축이다.
#include "lib\egl.h"
class eglSubWindow : public eglWindow
{
private:
GLushort pattern;
POINT oldPoint;
GLfloat xrot, yrot;
public:
virtual void RenderGLScene(void);
virtual void OnSize(WPARAM wParam, LPARAM lParam);
virtual void OnCreate(WPARAM wParam, LPARAM lParam);
virtual void OnLButtonDown(WPARAM wParam, LPARAM lParam);
virtual void OnLButtonUp(WPARAM wParam, LPARAM lParam);
virtual void OnMouseMove(WPARAM wParam, LPARAM lParam);
};
void eglSubWindow::OnCreate(WPARAM wParam, LPARAM lParam)
{
pattern = 0xAAAA;
xrot = yrot = 0.0f;
}
void eglSubWindow::OnSize(WPARAM wParam, LPARAM lParam)
{
GLsizei width = LOWORD(lParam);
GLsizei height = HIWORD(lParam);
if (height == 0)
height = 1;
glViewport( 0, 0, width, height );
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-5, 5, -5, 5, 1.0f, 100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void eglSubWindow::OnLButtonDown(WPARAM wParam, LPARAM lParam)
{
oldPoint.x = LOWORD(lParam);
oldPoint.y = HIWORD(lParam);
SetCapture(GetHWND());
}
void eglSubWindow::OnLButtonUp(WPARAM wParam, LPARAM lParam)
{
oldPoint.x = 0;
oldPoint.y = 0;
ReleaseCapture();
}
void eglSubWindow::OnMouseMove(WPARAM wParam, LPARAM lParam)
{
if(GetCapture()==GetHWND())
{
yrot = LOWORD(lParam) - oldPoint.y/3.6;
xrot = HIWORD(lParam) - oldPoint.x/3.6;
}
}
void eglSubWindow::RenderGLScene(void)
{
eglWindow::RenderGLScene();
glTranslatef(0.0f, 0.0f, -10.0f);
glRotatef(xrot, 1.0f, 0.0f, 0.0f);
glRotatef(yrot, 0.0f, 1.0f, 0.0f);
glLineStipple(4, pattern);
glEnable(GL_LINE_STIPPLE);
glBegin(GL_LINES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(-4.0f, 0.0f, 0.0f);
glVertex3f( 0.0f, 0.0f, 0.0f);
glEnd();
glDisable(GL_LINE_STIPPLE);
glBegin(GL_LINES);
glVertex3f( 0.0f, 0.0f, 0.0f);
glVertex3f( 4.0f, 0.0f, 0.0f);
glEnd();
glEnable(GL_LINE_STIPPLE);
glBegin(GL_LINES);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 4.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glEnd();
glDisable(GL_LINE_STIPPLE);
glBegin(GL_LINES);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f,-4.0f, 0.0f);
glEnd();
glEnable(GL_LINE_STIPPLE);
glBegin(GL_LINES);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, -4.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glEnd();
glDisable(GL_LINE_STIPPLE);
glBegin(GL_LINES);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 4.0f);
glEnd();
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
eglSubWindow app;
app.Create(FALSE, "EDin's OpenGL glLineStipple");
return app.Run();
}
=======================
=======================
=======================
출처: http://naakjii.tistory.com/5
OpenGL 점,선 그리기
그래픽의 가장 원시적요소가 Vertex(정점) 이고 이를 바탕으로 선과 면이 구성된다.
점을 화면상에 표시해주기 위해서는 다음과 같은 코드가 필요하다
void glBegin(GLenum mode);
void glEnd();
위 두 코드 사이에 정점을 그려주면되는데 이때 Begin에 입력하는 mode에 따라 정점이 어떻게 그려지냐가 결정된다.
모드 설명
GL_POINTS 독립적인 점
GL_LINE_STRIP 연결된 선분
GL_LINE_LOOP 시작점과 끝점을 이은 선분
GL_LINES 두개의 정점들을 이은 선분
GL_TRIANGLES 세개씩 연결한 삼각형
GL_TRIANGLE_STRIP 연결된 삼각형
GL_TRIANGLE_FAN 중심을 공유하는 삼각형
GL_QUADS 정점 4개씩을 연결하여 사각형을 그린다
GL_QUAD_STRIP 연결된 사각형
GL_POLYGON 연결된 블록 다각형
다음 소스코드를 통해 mode별 결과를 확인하자.
#include <gl\glut.h>
void display()
{
glPointSize(5.0f); // 정점의 size 조절
glBegin(GL_POINTS); // mode 선택
glVertex2f(-0.5f, 0.5f);
glVertex2f(0.5f, 0.5f);
glVertex2f(-0.5f, -0.5f);
glVertex2f(0.5f, -0.5f);
glEnd();
glFlush();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA);
glutInitWindowSize(200, 200);
glutCreateWindow("opengl project");
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
똑같이 4개의 정점을 그려도 mode에 따라 이렇게 결과가 달라진다.
glPointSize를 설정하지 않고 정점을 그릴경우 눈에 잘 안보일것이다. 꼭 설정하길 바란다.
=======================
=======================
=======================
출처: http://blog.amaorche.com/24
OpenGL 점, 선, 삼각형, 사각형 그리기
가장 간단한 도형 그리기
Visual Studio에 OpenGL 환경 설치 글 ( http://blog.amaorche.com/20 )의 예제소스코드를 다시 보자.
#include "gl/glut.h"
void display() {
glBegin(GL_POLYGON);
glVertex2f(-0.5 f, -0.5 f);
glVertex2f(0.5 f, -0.5 f);
glVertex2f(0.5 f, 0.5 f);
glVertex2f(-0.5 f, 0.5 f);
glEnd();
glFinish();
}
int main(int argc, char ** argv) {
glutInit(& argc, argv);
glutCreateWindow("OpenGL");
glutDisplayFunc(display); // 여기서 그릴 함수를 넣어준다.
glutMainLoop();
return 0;
}
코드는 display 함수와 main 함수로 이루어져 있다.
먼저 main 함수를 보면, glut을 접두사로 붙이는 함수들이 있다.
먼저 초기화 함수인 glutInit()을 해주고,
새 창을 만드는 glutCreateWindow()를 해준다. 함수 전달인자에 있는 "OpenGL"은 새로 만들어지는 창 이름이다.
그 다음, 실제로 그리는 함수인 glutDisplayFunc()에서 우리가 필요한 그림을 그릴 수 있다. 함수 전달인자로, 그림을 그리는 함수를 넣어준다. 소스코드에서는 main 함수 위의 display 함수를 전달인자로 넘겨준다.
자, 그럼 다시 display 함수를 보자. 모든 함수 앞에는 gl이라는 접두사가 붙는다.
void display() {
glBegin(GL_POLYGON);
glVertex2f(-0.5f, -0.5f);
glVertex2f(0.5f, -0.5f);
glVertex2f(0.5f, 0.5f);
glVertex2f(-0.5f, 0.5f);
glEnd();
glFinish();
}
출처: http://blog.amaorche.com/24 [팬더노트]
간단하게 순차적으로 설명하자면, 이미 그려져 있는 것이 있다면 Clear로 깨끗하게 지운다. 그다음 Begin으로 시작하여 End까지, 그리고자 하는 도형 정보를 넣는다. 위 예제에서는 다각형을 그리는데,
(-0.5,-0.5)
(0.5,-0.5)
(0.5,0.5)
(-0.5,0.5)
를 꼭지점으로 가지는 다각형(사각형)을 그린다.
여기서 다른 모양을 그리고 싶으면, Begin의 전달인자에 그리고자 하는 도형을 정하고 각 꼭지점 위치정보를 glVertex(x좌표,y좌표) 형태로 추가한다. 이때, openGL 창 정중앙이 (0,0)이다. 기본 도형의 glBegin 전달인자와 꼭지점 개수 조건은 다음과 같다.
점 : GL_POINTS , 꼭지점 개수조건 없음
선 : GL_LINES, 꼭지점 은 짝수개 있어야 함. 연결되지 않은 점은 그리지 않음
삼각형 : GL_TRIANGLES, 꼭지점은 3배수로 있어야 함. 3배수가 안되는 나머지 1~2개의 점은 그리지 않음
사각형 : GL_QUADS, 꼭지점은 4배수로 있어야 함, 4배수가 안되는 나머지 1~3개의 점은 그리지 않음
다각형 : GL_POLYGON 꼭지점은 2개 이상 (최소 삼각형을 그릴 수 있어야 함)
점 그리기
점은 Begin ~ End 사이에 있는 Vertex 수 만큼 그릴 수 있다. 점 크기를 조절하려면 glPointSize 함수를 사용하면 된다. 전달인자에 점 지름을 float로 넣을 수 있다.
glPointSize(10.0);
glBegin(GL_POINTS);
glVertex2f(-0.5 f, -0.5 f);
glVertex2f(0.5 f, -0.5 f);
glVertex2f(0.5 f, 0.5 f);
glVertex2f(-0.5 f, 0.5 f);
glEnd();
glFinish();
출처: http://blog.amaorche.com/24 [팬더노트]
선 그리기
선은 GL_LINES 으로 그릴 수 있다. 꼭지점은 꼭 2배수로 있어야 한다. 홀수이면, 마지막 꼭지점은 무시된다. 선 두께를 조절하려면 glPointSize 함수를 사용하면 된다. 전달인자에 두께 픽셀 크기를 float로 넣을 수 있다.
glLineWidth(10.0);
glBegin(GL_LINES);
glVertex2f(-0.5 f, -0.5 f);
glVertex2f(0.5 f, -0.5 f);
glVertex2f(0.5 f, 0.5 f);
glVertex2f(-0.5 f, 0.5 f);
glEnd();
glFinish();
출처: http://blog.amaorche.com/24 [팬더노트]
삼각형 그리기
삼각형은 GL_TRIANGLES 로 그릴 수 있다. 꼭지점은 3개가 꼭 있어야 한다. 순차적으로 3개씩 짝지어 내려갔을 때 남는 1개 또는 2개의 꼭지점은 그리지 않는다.
glBegin(GL_TRIANGLES);
glVertex2f(-0.5 f, -0.5 f);
glVertex2f(0.5 f, -0.5 f);
glVertex2f(0.5 f, 0.5 f);
glEnd();
출처: http://blog.amaorche.com/24 [팬더노트]
사각형 그리기
사각형은 GL_QUADS로 그릴 수 있다. 꼭지점은 4개가 있어야 하고, 마찬가지로 마지막 3개 이하의 꼭지점은 무시된다. 또한, 점의 방향 (반시계 또는 시계)을 유지해야 한다.
glBegin(GL_QUADS);
glVertex2f(-0.5 f, -0.5 f);
glVertex2f(0.5 f, -0.5 f);
glVertex2f(0.5 f, 0.5 f);
glVertex2f(-0.5 f, 0.5 f);
glEnd();
glFinish();
출처: http://blog.amaorche.com/24 [팬더노트]
다각형 그리기
기타 다각형은 GL_POLYGON으로 그릴 수 있다. 꼭지점은 최소 3개 이상이 있어야 한다.
glBegin(GL_POLYGON);
glVertex2f(-0.5 f, -0.5 f);
glVertex2f(0.5 f, -0.5 f);
glVertex2f(0.5 f, 0.0 f);
glVertex2f(0.0 f, 0.5 f);
glVertex2f(-0.5 f, 0.0 f);
glEnd();
glFinish();
출처: http://blog.amaorche.com/24 [팬더노트]
출처: http://blog.amaorche.com/24 [팬더노트]
=======================
=======================
=======================
출처: http://blog.naver.com/PostView.nhn?blogId=inocent_xx&logNo=80176409360
이번 포스팅에서는
OpenGL을 이용한 선그리기를 해보겠습니다.
진짜 간단합니다
일단 저번에 포스팅한 프로젝트를 실행합니다.
http://blog.naver.com/inocent_xx/80176250589
(여기서 다운받으세요^^)
당분간은 OpenGLView.cpp 파일에서
모든 작업을 하시면 됩니다.^^
만약 프로젝트 이름을 다르게 하셨다면
[프로젝트이름]View.cpp 파일입니다.
파일을 여셨으면
DrawGLScene 함수 부분으로 갑니다
밑에 파란 네모를 보시면 저번시간에 작성한 코드가 있습니다.
간단히 보고 넘어가면
glColor3f (1.f, 0.f, 0.f);
앞으로 그릴 객체들의
색상을 RGB 값을 이용하여 지정해주는 함수 입니다.
인자 3개가 순서대로 R, G, B 값입니다.
실수 형태이므로 뒤에 f를 붙였구요^^
R값만 1을 넣고 나머지는 0을 넣었으니
빨간색으로 그려지게 됩니다.
glBegin(GL_TRIANGLES);
glVertex3f( 0.5f, 0.f, 0.f );
glVertex3f( 0.f, 0.5f, 0.f );
glVertex3f( -0.5f, 0.f, 0.f );
glEnd();
삼각형을 그려주는 부분입니다.
glBegin()으로 그리기 시작을 알려주고
glEnd()로 그리기를 끝냅니다.
glBegin() 안에 GL_TRIANGLES은 삼각형을 그리겠다는 의미이고,
glVertex3f( 0.5f, 0.f, 0.f );로
공간상의 삼각형의 3점의 좌표를 지정합니다.
이렇게 해서 삼각형이 간단하게 그려집니다. ^^
오늘은 선을 그려 볼 것이므로 위의 코드부분을 삭제 합니다.
그리고 다음과 같은 코드를 추가합니다.
glColor3f( 1.f, 0.f, 0.f ); // 빨간색 지정
glBegin(GL_LINE_LOOP); // 시작과 끝 점을 포함한 모든 점을 연결
glVertex3f(1.0, 0.0, 0.0); // 시작 점
glVertex3f(-1.0, 0.0, 0.0); // 끝 점
glEnd();
이렇게요!
쉬운 코드라서 주석 보시면 이해 되실거고
보고 넘어가실 부분은
glBegin(GL_LINE_LOOP); 이 부분입니다.
GL_TRIANGLES 대신에 GL_LINE_LOOP를 넣고
두 점을 지정해 주시면 연결해서 선을 그리게 됩니다.
나중에 기회가 되면 자세히 하겠지만
시점에 따라 보는 방향이 달라서
공간상에 물체를 그리기가 좀 어려울 때가 있습니다.
그래서 저는 x축, y축, z축을 그려놓고 코딩하곤 합니다.
다시 draw 부분을 지우고
다음 코드를 삽입해 보겠습니다.
// draw
glColor3f(1.f,0.f,0.f); // 빨간색 지정
glBegin(GL_LINE_LOOP); // x축 그리기
glVertex3f(1.0, 0.0, 0.0); // 시작 점
glVertex3f(-1.0, 0.0, 0.0); // 끝 점
glEnd();
glColor3f(0.f,1.f,0.f); // 초록색 지정
glBegin(GL_LINE_LOOP); // y축 그리기
glVertex3f(0.0, 1.0, 0.0);
glVertex3f(0.0, -1.0, 0.0);
glEnd();
glColor3f(0.f,0.f,1.f); // 파란색 지정
glBegin(GL_LINE_LOOP); // z축 그리기
glVertex3f(0.0, 0.0, 1.0);
glVertex3f(0.0, 0.0, -1.0);
glEnd();
부연 설명은 필요 없을 것 같고,
실행 화면입니다.
분명 축을 3개 그렸는데 2개밖에 안나오네요
이유는 시점에 있습니다.
xy평면 바로 위에서 내려다 봐서 이런 모양으로 출력이 됬네요
z축은 여러분 모니터를 뚫고 나오는 방향이구요
제대로 확인을 하려면 위에
// camera view configuration
gluLookAt( 0.f, 0.f, 3.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f );
부분을 수정해야 합니다.
자세한 내용은 다음에 카메라 부분에서 다루도록 하기로 하고
오늘은 그냥 다음과 같이 수정해 주세요
// camera view configuration
gluLookAt(3.f,3.f,3.f, 0.f,0.f,0.f, 0.f,0.f,1.f);
그리고 실행을 하면
짠!
저희가 고등학교 벡터시간에 배웠던 모양으로
축이 생성되네요
이번 포스팅도 소스파일 첨부해 놓도록 하겠습니다!
따라하다가 잘 안되신 분들은 다운받아서 비교해보세요
그럼 OpenGL로 선그리기
[출처] OpenGL 선그리기|작성자 이피스
=======================
=======================
=======================
#include<GL/glut.h>
#include<GL/gl.h>
#include<GL/glu.h>
#define TRUE 1
#define FALSE 0
GLint TopLeftX, TopLeftY, BottomRightX, BottomRightY; //마우스의 좌표값을 저장하는 변수
GLfloat R = 0.0, G = 0.0, B = 0.0; //선의 색상 정할 때 이용되는 변수
GLboolean Line = TRUE; //TRUE로 설정을 해서 처음 실행하면 직선을 그린다.
bool identify = TRUE; //마우스를 누르고있으면 false, 안누르면 true. false일 때 자유곡선을 그린다.
bool click = TRUE; // 화면 초기화 안되게 막아주는 변수
void MyDisplay()
{
if(click == TRUE) // click이 True 일 때에 화면을 초기화해준다.
//그러나 Mousemove에서 click = false로 하고 true로 바꿔주는게 없으므로 초기화 될 일이 없다. //직선으로 바꾸면 click = TRUE가 됨
glClear(GL_COLOR_BUFFER_BIT); // if문을 안넣어주면 계속 반복하면서 선을 두개만 그려주므로 이건 반드시 필요하다.
glViewport(0, 0, 300, 300);
glColor3f(1.0, 0.0, 0.0); //RED color box
glBegin(GL_POLYGON);
glVertex3f(0.9, 1.0, 0.0);
glVertex3f(1.0, 1.0, 0.0);
glVertex3f(1.0, 0.95, 0.0);
glVertex3f(0.9, 0.95, 0.0);
glEnd();
glColor3f(0.0, 1.0, 0.0); //Green color box
glBegin(GL_POLYGON);
glVertex3f(0.9, 0.95, 0.0);
glVertex3f(1.0, 0.95, 0.0);
glVertex3f(1.0, 0.9, 0.0);
glVertex3f(0.9, 0.9, 0.0);
glEnd();
glColor3f(0.0, 0.0, 1.0); //Blue color box
glBegin(GL_POLYGON);
glVertex3f(0.9, 0.9, 0.0);
glVertex3f(1.0, 0.9, 0.0);
glVertex3f(1.0, 0.85, 0.0);
glVertex3f(0.9, 0.85, 0.0);
glEnd();
glColor3f(0.0, 0.0, 0.0); //Black color box
glBegin(GL_POLYGON);
glVertex3f(0.9, 0.85, 0.0);
glVertex3f(1.0, 0.85, 0.0);
glVertex3f(1.0, 0.8, 0.0);
glVertex3f(0.9, 0.8, 0.0);
glEnd();
glColor3f(R, G, B); //선의 색상.
if(Line) //직선그리기일 경우.
{
glBegin(GL_LINES);
glVertex3f(TopLeftX/300.0, (300-TopLeftY)/300.0, 0.0);
glVertex3f(BottomRightX/300.0, (300-BottomRightY)/300.0, 0.0);
glEnd();
}
glFlush();
}
void MyMainMenu(int entryID)
{
if(entryID==1)
Line = TRUE; //직선그리기
else if(entryID==2)
Line = FALSE; //자유곡선그리기
else
exit(0);
//glutPostRedisplay();
}
void MyMouseClick(GLint Button, GLint State, GLint X, GLint Y)
{
GLfloat SelectX = X/300.0;
GLfloat SelectY = (300-Y)/300.0;
if(SelectX >= 0.9 && SelectX <= 1.0 && SelectY >= 0.8 && SelectY <= 1.0)
{
/* RGB box에 마우스를 클릭했는지 확인하는 부분*/
if(Button == GLUT_LEFT_BUTTON && State == GLUT_DOWN)
{
if(SelectY >= 0.95 && SelectY <= 1.0) //Red box
{
R = 1.0;
G = 0.0;
B = 0.0;
}
else if(SelectY >= 0.9 && SelectY <= 0.95) //Green box
{
R = 0.0;
G = 1.0;
B = 0.0;
}
else if(SelectY >= 0.85 && SelectY <= 0.9) //Blue box
{
R = 0.0;
G = 0.0;
B = 1.0;
}
else if(SelectY >= 0.8 && SelectY <= 0.85) //Black box
{
R = 0.0;
G = 0.0;
B = 0.0;
}
}
}
if(Line)
if(Button == GLUT_LEFT_BUTTON && State == GLUT_DOWN)
{
TopLeftX = X;
TopLeftY = Y;
}
if(Button == GLUT_LEFT_BUTTON && State == GLUT_UP) //마우스를 떼고 다시 다른곳에 누를때
{ //선을 연결 안시키려고 identify = TRUE로 바꿔준다.
identify = TRUE;
}
}
void MyMouseMove(GLint X, GLint Y)
{
if(Line)
{
click = TRUE; //화면 초기화
BottomRightX = X;
BottomRightY = Y;
glutPostRedisplay();
}
else
{
BottomRightX = X;
BottomRightY = Y; //마우스의 현 좌표를 bottom x, y에 저장
if(identify == TRUE)
{
TopLeftX = BottomRightX; //처음 마우스를 눌렀을 때 top x, y에 현 좌표를 저장.
TopLeftY = BottomRightY;
glutPostRedisplay(); // 여기 첫선
}
identify = FALSE; //마우스를 누르고 있는 시점
click = FALSE;//click이 false로 변한 시점부터 화면이 초기화되지 않는다.
//즉, 선을 그릴때 좌표 두개만 쓸 수 있었는데 이제 무한으로 쓸 수 있는것.
/*마우스클릭하여 점을 찍고 이동하는 순간부터 top과 bottom을 계속 연결하여 자유곡선을 그린다*/
glBegin(GL_LINES);
glVertex3f(TopLeftX/300.0, (300-TopLeftY)/300.0, 0.0);
glVertex3f(BottomRightX/300.0, (300-BottomRightY)/300.0, 0.0);
glEnd();
glFlush();
glutPostRedisplay(); // R, G, B box를 반복적으로 표시하고 rgb박스를 누를시 선의 색을 바꾸기 위함.
// 그래서 이게 MouseMove의 어디에 들어가든 상관없다.(if(Line)부분 빼고)
TopLeftX = BottomRightX; //마지막으로 마우스를 뗐던 부분을 선의 시작좌표로 바꾼다.
TopLeftY = BottomRightY; //시작좌표로 바꾸고 선을 그리고. 또 시작좌표로 바꿔서 선을 그리고. 이렇게 반복하면서 선 을 그림.
}
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB);
glutInitWindowSize(300, 300);
glutInitWindowPosition(0, 0);
glutCreateWindow("아무 이름이나 지어보세요.");
glClearColor(1.0, 1.0, 1.0, 1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
GLint MyMainMenuID = glutCreateMenu(MyMainMenu);
glutAddMenuEntry("자유곡선그리기", 2);
glutAddMenuEntry("직선그리기", 1);
glutAddMenuEntry("종료", 3);
glutAttachMenu(GLUT_RIGHT_BUTTON);
glutDisplayFunc(MyDisplay);
glutMouseFunc(MyMouseClick);
glutMotionFunc(MyMouseMove);
glutMainLoop();
return 0;
}
혹시 조대생 분들이라면 그대로 복사 붙여넣기 하지 마세요.
교수님께 제출한 레포트인지라 그대로 복붙했다간 바로 걸립니다.
이 코드를 보고 이해를 확실히 한 다음 자기만의 코드로 바꿔주셨으면 좋겠네요.
출처: http://shayete.tistory.com/entry/OpenGL-그래픽스-자유곡선-그리기 [샤의 공간]
=======================
=======================
=======================
출처: http://egloos.zum.com/nom3203/v/2628791
OpenGL - 코드 3-6 / 다양한 두께로 선 그리기
SetupRC() 함수에서 배경색과 전경색을 지정했고,
RenderScene() 햠수를 통해 각 라인을 그려주었는데, 반복문 내의 코드를 살펴보면
// Set the line width
glLineWidth(fCurrSize);
// Draw the line
glBegin(GL_LINES);
glVertex2f(-80.0f, y);
glVertex2f(80.0f, y);
glEnd();
glLineWidth() 함수를 통해 선 두께를 지정하였고, GL_LINES 인자를 통해 두 버텍스 사이에 선분을 그려주었다.
이 외에도 방향키에 대한 처리부분과 창의 크기가 변했을때의 처리부분도 포함되어 있다.
해당 내용은 이전 내용과 반복되므로 생략한다.
// Linesw.c
// OpenGL SuperBible, Chapter 4
// Demonstrates primative GL_LINES with line widths
// Program by Richard S. Wright Jr.
#include "../../Common/OpenGLSB.h" // System and OpenGL Stuff
#include <math.h>
// Define a constant for the value of PI
#define GL_PI 3.1415f
// Rotation amounts
static GLfloat xRot = 0.0f;
static GLfloat yRot = 0.0f;
// Called to draw scene
void RenderScene(void)
{
GLfloat y; // Storeage for varying Y coordinate
GLfloat fSizes[2]; // Line width range metrics
GLfloat fCurrSize; // Save current size
// Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT);
// Save matrix state and do the rotation
glPushMatrix();
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
// Get line size metrics and save the smallest value
glGetFloatv(GL_LINE_WIDTH_RANGE,fSizes);
fCurrSize = fSizes[0];
// Step up Y axis 20 units at a time
for(y = -90.0f; y < 90.0f; y += 20.0f)
{
// Set the line width
glLineWidth(fCurrSize);
// Draw the line
glBegin(GL_LINES);
glVertex2f(-80.0f, y);
glVertex2f(80.0f, y);
glEnd();
// Increase the line width
fCurrSize += 1.0f;
}
// Restore transformations
glPopMatrix();
// Flush drawing commands
glutSwapBuffers();
}
// This function does any needed initialization on the rendering
// context.
void SetupRC()
{
// Black background
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
// Set drawing color to green
glColor3f(0.0f, 1.0f, 0.0f);
}
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP)
xRot-= 5.0f;
if(key == GLUT_KEY_DOWN)
xRot += 5.0f;
if(key == GLUT_KEY_LEFT)
yRot -= 5.0f;
if(key == GLUT_KEY_RIGHT)
yRot += 5.0f;
if(key > 356.0f)
xRot = 0.0f;
if(key < -1.0f)
xRot = 355.0f;
if(key > 356.0f)
yRot = 0.0f;
if(key < -1.0f)
yRot = 355.0f;
// Refresh the Window
glutPostRedisplay();
}
void ChangeSize(int w, int h)
{
GLfloat nRange = 100.0f;
// Prevent a divide by zero
if(h == 0)
h = 1;
// Set Viewport to window dimensions
glViewport(0, 0, w, h);
// Reset coordinate system
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Establish clipping volume (left, right, bottom, top, near, far)
if (w <= h)
glOrtho (-nRange, nRange, -nRange*h/w, nRange*h/w, -nRange, nRange);
else
glOrtho (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutCreateWindow("Line Width Example");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
SetupRC();
glutMainLoop();
return 0;
}
=======================
=======================
=======================
출처ㅣ http://www.linuxfocus.org/Korean/March1998/article28.html
선 그리기
지난 시간에 우리는 OpenGL에서 어떻게 다각형을 이루는 기본 요소들을 살펴보았다. 실제로 OpenGL은 점,직선,다각형, 조그만 삼각형 내지 사각형들의 집합으로 이루어진 면등 지원하는 기본 형상객체가 몇개되지 않는다.
이런 OpenGL의 단순함에 내포된 의미는 결국 이러한 간단한 객체들을 이용하여 복잡한 기하학적 모델을 만드는 것은 개발자에 달려있다는 것이다. OpenGL에는 점,선,다각형들을 자세히 다룰수 있는 많은 명령어들이 존재한다.
만약 점의 크기를 정해주고 싶다면 glPointSize명령을 이용하면 된다.:
void glPointSize(GLfloat size)
점의 크기는 기본적으로 1.0으로 주어져 있으며, 위 함수에서 size는 반드시 0보다 커야 한다. 점의 크기를 실수형으로 설정한다는 점을 기억하자. 점과 선의 크기를 분수꼴로 나타내는 것 역시 가능하다. OpenGL은 렌더링 컨텍스트에 따라 분수형으로 주어진 점의 크기를 해석해서 실행한다. 만약 안티-앨리어싱(Anti-aliasing)모드를 켜두었다면 OpenGL은 분수형 폭을 나타내기위하여 약간의 수정이 필요한 선이라 의심되는 부분을 찾아 인근의 픽셀을 보고 수정을 한다.안티앨리어싱은 저해상도의 모니터에서 직선을 보여줄때 발생하는 끊긴 계단모양을 제거해 주기 위하여 사용되어지기도 하는 기법이다. 만약 안티앨리어싱모드가 꺼져있다면 glPointSize에서 준 점/선의 크기(size)는 반올림되어 가까운 정수로 바뀐다.
점의 실제 크기는 장치에 따라 다르다. 따라서 낮은 해상도의 모니터에서 보면 점이 더 커보이게된다. 그러나 플로터와 같이 매우 높은 해상도를 가진 장치에서 기본값 1로 설정된 선을 그린다면 거의 볼 수 없을정도의 두께를 가진 선이 그려진다. 실제로 그려지는 선의 두깨를 알고싶다면 여러분은 먼저 출력장치의 정확한 사양과 해상도를 알고 있어야 한다.
선의 굵기는 glLineWidth함수에서 설정할 수 있는데, 이 함수는 선을 그리는 부분이 들어있는 glBegin() - glEnd() 이전에 호출되어야 새로운 굵기가 적용되어 그려진다. 사용법은 이렇다.:
void glLineWidth(GLfloat width)
OpenGL의 실행에서 안티앨리어싱되지 않는 선의 두께로 정할 수 있는 범위는 최대 안티앨리어싱선의 두께까지로 제한되어 있기때문에 반올림되어 비슷한 정수값이 사용된다. 선의 두께는 직선의 수직방향폭을 나타내는 것이 아니다. 만약 기울기가 1보다 작다면 y축방향이 크기가 되며 반대로 1보다 크다면 x방향의 기울기의 절대값이 선의 크기가 된다는 것을 명심하자.
이번달에는 간단하지만 유용한 2차원 애니메이션프로그램을 작성해보자. 이 프로그램은 여러분이 작성하는 OpenGL프로그램에서 다양한 두께를 가진 선을 어떻게 사용하는지 이해하는데 도움이 될 것이다. (example2.c, Makefile). 나는 먼저 양자물리학책에서 예제를 찾았다. 물리학책을 보면 "a quantum particle trapped in a double well potential."라는 말이 나온다. 왜 그렇게 되냐면...음 나도 잊어버렸다. 어쨌든 물리학을 전공하는 학생들에게 슈뢰딩거 방정식에서 시간이 증가할때마타 어떻게 값이 변하는가를 보여주는데 유용한 그림을 그릴 수 있다. 이분야를 전공하지않는 사람들은 그냥 쉽게이해되지 않는 양자역학의 속성을 살펴보면 된다. 양자역학에서 입자는 위치와 속도로 표현되는 것이 아니라 양자파라는 파형으로 나타낸다.우리가 작성한 애니메이션에서 보라색 실선으로 나타낸 부분이 양자파를 나타낸다. 이것은 주어진위치(하얀 점선)에서 입자를 관측할 수 있는 확률값의 제곱값을 의미한다.:
그림 1. 양자 시뮬레이션 스냅샷
상미분방정식을 풀때 이루어지는 몇몇 과정들은 파동방정식이 고속 퓨리에변환 스플릿연산자 방법을 사용하여 적분된다. 이 방법은 다른 무한미분법에 비해 빠르고 정확한 장점이 있다. 이 방법은 비선형 파동 확산에도 이용할 수 있다;시간증가를 초단위로 하도록 하고(또는 더 높은 단위로 할 수도 있다) 위치나 모멘텀(주파수)에만 의존하게 한다면 파동함수는 위치와 모멘텀(주파수)공간사이를 왔다갔다하면서 바뀌는 이 연산자를 적용하여 시간에 연속적인 것을 볼 수 있을 것이다.
소스코드의 주요부분은 다른 많은 응용프로그램에서도 사용될 수 있다. 여러분은 필자가 작성한 시간의존함수에 따라 이루어지는 양자시뮬레이션을 지워버리면 더 나은 애니메이션을 여러분의 컴퓨터에서 볼 수 있을 것이다. 또한 그래프를 그리는 함수나 데이터파일을 읽어서 그래프를 그려주는 간단한 OpenGL기반의 gnuplot도 작성할 수 있을 것이다.
여러분이 계속 GLUT와 OpenGL에 관한 연재기사를 읽어왔다면 이 소스코드는 매우 간단하고 이해하기 쉬울것이다.(물론 양자역학부분은 제외한다.) 소스에 특별한 함수나 특이사항은 눈에 띄지 않는다.main()에서 우리는 이중버퍼모드로 단일윈도우를 연 다음 파동함수를 그리고 각각의 파동방정식을 통합하는 작업을 담당하는 display()와 idle()함수를 전달한다. 다시한번 idel()함수에 어떤 기능이 들어갈지 생각해보자. 이 글의 내용을 이해하기위해 다른 모든것(양자역학적 지식등등)을 알고있어야 하는 것은 아니다. 정말 새로운 OpenGL의 기능은 display 콜백함수이다:
void
display (void)
{
static char label[100];
float xtmp;
/* 그려질 부분을 지운다. */
glClear (GL_COLOR_BUFFER_BIT);
/* 각주를 단다.*/
glColor3f (0.0F, 1.0F, 1.0F);
sprintf (label, "(c)Miguel Angel Sepulveda 1998");
glRasterPos2f (-1.1, -1.1);
drawString (label);
/* 미세한 격자를 그린다. */
glLineWidth (0.5);
glColor3f (0.5F, 0.5F, 0.5F);
glBegin (GL_LINES);
for (xtmp = -1.0F; xtmp < 1.0F; xtmp += 0.05)
{
glVertex2f (xtmp, -1.0);
glVertex2f (xtmp, 1.0);
glVertex2f (-1.0, xtmp);
glVertex2f (1.0, xtmp);
};
glEnd ();
/* 외각의 네모부분을 그린다.*/
glColor3f (0.1F, 0.80F, 0.1F);
glLineWidth (3);
glBegin (GL_LINE_LOOP);
glVertex2f (-1.0F, -1.0F);
glVertex2f (1.0F, -1.0F);
glVertex2f (1.0F, 1.0F);
glVertex2f (-1.0F, 1.0F);
glEnd ();
/* 격자(그리드)를 그린다.*/
glLineWidth (1);
glColor3f (1.0F, 1.0F, 1.0F);
glBegin (GL_LINES);
for (xtmp = -0.5; xtmp < 1.0; xtmp += 0.50)
{
glVertex2f (xtmp, -1.0);
glVertex2f (xtmp, 1.0);
glVertex2f (-1.0, xtmp);
glVertex2f (1.0, xtmp);
};
glEnd ();
/* 좌표축을 그린다.*/
glLineWidth (2);
glBegin (GL_LINES);
glVertex2f (-1.0, 0.0);
glVertex2f (1.0, 0.0);
glVertex2f (0.0, -1.0);
glVertex2f (0.0, 1.0);
glEnd ();
/* 축에 글을 적는다.*/
glColor3f (1.0F, 1.0F, 1.0F);
sprintf (label, "Position");
glRasterPos2f (0.80F, 0.025F);
drawString (label);
glColor3f (1.0F, 0.0F, 1.0F);
sprintf (label, " Quantum Probability ");
glRasterPos2f (0.025F, 0.90F);
drawString (label);
glColor3f (1.0F, 1.0F, 1.0F);
sprintf (label, " Real(Psi) ");
glRasterPos2f (0.025F, 0.85F);
drawString (label);
/* 파동함수를 그린다. */
psiDraw (NR_POINTS, psi, x);
/* 포텐셜함수를 그린다.*/
potentialDraw (NR_POINTS, potential, x);
glutSwapBuffers ();
};
맨처음 한 작업은 컬러버퍼비트를 지우는 작업이다. 이렇게 하면 깨끗한(검은) 그림판을 우리는 얻게 된다. 그 다음 glRasterPos와 glutBitmapCharacter함수를 이용하여 각주를 단다. (drawstring은 glut유틸리티를 위한 포장지(wrapper)에 불과하다) 다음 강좌에서 다시 자세히 살펴볼 glRasterPos함수는 텍스쳐 렌더링을 위한 부가함수이다. OpenGL이나 GLUT는 그래픽 윈도우에 글자를 렌더링하는 간단하면서 강력한 방법을 제공해 주지 않는다. The glutBitmapCharacter함수는 글꼴 비트맵에서 컬러 버퍼로 기본적인 래스팅작업을 수행한다.
주석을 달고난 다음 외각사각형, 배경화면 격자, 좌표축등 많은 줄을 그리는 부분이 온다. 물론 현재 곡선은 psiDraw 와 potentialDraw에서 그려진다. 직선을 그리기 전에 그리고자 하는 선의 두께를 픽셀단위로 정해주는 glLineWidth함수를 사용하였다. 그림 1은 리눅스알파머신의 X윈도우 시스템에서 출력된 화면이다. 이 프로그램을 윈도우 95에서 컴파일해서 실행시켰었는데 많은 시간이 걸렸다. 왜 그런지 필자가 도무지 알아낼수 없었다. 아마도 SGI OpenGL드라이버에서 안티앨리어싱기능을 지원해주지 않기때문이 아닐까싶다. 원리적으로 서로 다른 폭으로 선을 구분하는 것은 매우 힘들다. 그리고 배경의 격자선은 같이 그려져야 한다. 이런 결함들은 높은 해상도에서 그릴때 나타나는데 낮은 해상도를 가지는 모니터때문에 생긴것은 아니다. 어쨌든 윈95/NT에서 보다 리눅스의 X윈도우시스템에서 성능이 더 뛰어났었다는 점을 다시 한번 말해둔다.
display() 함수에서 선을 그리는 두가지 방식이 있다. GL_LINES모드는 선을 이루는 두 정점을 계속 입력하는 방식이며 GL_LINE_LOOP모드는 마지막에 폐루프을 형성한다.
안티앨리어싱기능
reshape() 콜백함수에 선을 그릴때 안티앨리어스모드도 가능하도록 하였다.
void reshape(int w, int h) {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-1.2, 1.2, -1.2, 1.2);
glEnable(GL_LINE_SMOOTH); /* 안티앨리어싱 된 직선을 그릴수 있게 한다.*/
glEnable(GL_LINE_STIPPLE);
};
GL_LINE_STIPPLE은 무엇인가? OpenGL은 직선의 두께뿐만 아니라 모양까지도 결정할 수 있게 해준다. GL_LINE_STIPPLE 을 가능하게 하여 우리는 점선이나 다른 모양의 직선을 그릴 수 있다. 애니메이션에는 psiDraw()함수에서만 점선을 사용하였다.:
glLineWidth(1);
glPushAttrib(GL_LINE_BIT);
glLineStipple(3, 0xAAAA);
glBegin(GL_LINE_STRIP);
for(i = 0; i < nx; i++) {
xs = ratio1 * (x[i] - XMIN) - 1.0;
ys = ratio2 * (psi[2 * i] - YMIN) - 1.0;
glVertex2d(xs, ys);
};
glEnd();
glPopAttrib();
선 모양바꾸기
glLineStipple은 점선모양을 결정해주는 함수인데, 이 글에 사용된 예제에서는 0xAAAA패턴을 사용하였다. 이 값을 이진수로 변환하면 0000100010001000이 되는데 OpenGL은 이를 3비트는 꺼져있고, 한비트 켜지고, 3비트 꺼져있고, 1비트켜지고, 3비트 꺼지고 1비트켜지고, 4비트꺼진 식의 점선을 그린다. 그렇다. 이 선모양은 뒤에서부터 읽는다. 뒤에서 부터 읽는 이유는 낮은차의 비트를 먼저 사용하기때문이다. 이제 glLineStipple은 두개의 파라메터가 설정되었다. 하나는 모양을 알려주는 16진수값이고 하나는 이러한 패턴을 그려줄때 사용되는 비율을 알려주는 정수가 주어진다. 즉 예제에서는 3이라 주었으므로 패턴이 다시 변화되어 9칸꺼지고, 3칸 켜지고, 9칸 꺼지고, 3칸 꺼지고, 9칸 꺼지고, 3칸 켜지고 마지막에 12칸 꺼지는 그런 점선을 그리게 된다. 이 값과 이진수로 표현되는 패턴을 가지고 여러분은 모든종류의 점선을 그릴 수 있을 것이다.
조금더 자세히 말해둘것이 있다.: 필자는 점선렌더링을 푸시(push)와 팝(pop)속성행사이에 두었다. 여러분은 이 글을 처음 연재할때 OpenGL은 상태머신이라는 말을 본 기억이 있는가? 아마 다음호에 우리는 이 푸시와 팝조작에 관하여 더 자세히 살펴볼 것이다. 그러나 간단하게 말하자면 처음에 glPushAttrib (GL_LINE_BIT)에서 하는 작업은 GL_LINE_BIT의 현재값(이 값이 점선모양을 결정한다)을 스택에 쌓는다. 그리고 나서 glLineStipple 함수를 이용하여 GL_LINE_BIT의 값을 바꾼다. 그 다음 우리가 실제 하고자 하는 작업을 수행한 다음 glPopAttrib 함수를 실행히켜 이전의 GL_LINE_BIT변수값을 다시 가져온다. 이러한 방식은 OpenGL머신의 상태변수값을 부분적으로 바꾸고자 할때 효율적이게 된다. 만약 이렇게 하지 않는다면 선을 그리고 나서 다시 이전의 선모양으로 바꾸어주기 위하여 매번 이전과 동일한 선모양으로 glLineStipple 함수를 호출해주어야 한다. 푸시와 팝기능은 이런 번거로운 작업을 덜어준다.
다음번에는....
OpenGL은 환상적인 3차원 API인터페이스로 알려져있다. 지금까지 우리는 OpenGL을 가지고 2차원 렌더링의 기본적인 기능을 살펴보았다. 다음번에는 3차원 OpenGL프로그램을 만들어 보면서 관조법은 어떻게 하고 시스템의 좌표와 클리핑영역과 투사행렬을 설정하는지 살펴보기로 하겠다.
다음연재까지 OpenGL을 가지고 재미있게 놀기 바란다.......
=======================
=======================
=======================
출처: http://diehard98.tistory.com/entry/OpenGL-%EB%B8%94%EB%A0%8C%EB%94%A9
OpenGL 블렌딩
OpenGL에서 블렌딩은 예상외로 조금 복잡하다. 일단 함수의 인자부터 헷갈린다. glBlendFunc() 함수가 입력으로 들어오는 화소와 프레임 버퍼에 이미 그려져 있는 화소의 블렌딩 비율을 지정하는 놈인데 이 놈의 인자들을 살펴보면, 첫번째 인자가 source이고 두번째가 destination 이다.
이거 마치 source가 원래 그려져 있던 화소 같이 들리지만 그 반대다. source인 첫번째 인자가 들어오는 화소고 두번째 인자인 destination이 목적지인 그려질 위치에 있던 화소를 말한다.
아래에 두 가지 대표적인 블렌딩의 예를 그림으로 표현했는데 우선 첫번째 인자가 GL_SRC_ALPHA이고 두번째 인자가 GL_ONE_MINUS_SRC_ALPHA 이면 입력으로 들어오는 화소의 알파값이 입력 화소의 블렌딩 비율이되고 1에서 입력으로 들어오는 화소의 알파값을 뺀 나머지 값이 원래 버퍼에 들어있던 화소의 블렌딩 비율이 되게 하라는 말이다.
고로 소스로 들어온 사각형의 알파값이 아래 그림 첫번째 처럼 50% 였다면, 1 - 0.5를 뺀 값인 0.5가 원래 있던 화소들인 빨강색 삼각형에 적용되어 둘이 합쳐져 오른쪽의 그림처럼 스크린에 나타난다.
만약 입력으로 들어오는 사각형의 알파값이 20%라면 삼각형은 80% (100 - 20)로 블렌딩 되어 그림 내부의 아래 그림 처럼 블렌딩 된다.
이것을 코드로 표현하자면 아래와 같다. 우선 블렌딩을 활성화하고 사각형을 그리는데 블렌딩 옵션을 입력으로 들어오는 화소만 그려지라는 의미의 GL_ONE, GL_ZERO 로 주고 삼각형을 그린다. 그리고나서 블렌딩 옵션을 바꿔서 사각형을 그리면 된다.
glEnable(GL_BLEND); // 블렌딩활성화
glBlendFunc(GL_ONE, GL_ZERO); // 소스(Incoming) 만그리기
glBegin(GL_TRIANGLES); // Drawing Using Triangles
glColor4f(1.0 f, 0.0 f, 0.0 f, 1.0 f);
glVertex4f(0.0 f, 1.2 f, 0.0 f, 1.0 f);
glVertex4f(-1.2 f, -1.0 f, 0.0 f, 1.0 f);
glVertex4f(1.2 f, -1.0 f, 0.0 f, 1.0 f);
glEnd();
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBegin(GL_QUADS); // Draw A Quad
glColor4f(0.0 f, 0.0 f, 1.0 f, 0.5 f);
glVertex4f(-1.0 f, 1.0 f, 0.0 f, 1.0 f);
glVertex4f(1.0 f, 1.0 f, 0.0 f, 1.0 f);
glVertex4f(1.0 f, -1.0 f, 0.0 f, 1.0 f);
glVertex4f(-1.0 f, -1.0 f, 0.0 f, 1.0 f);
glEnd();
또 다른 예로는 3개의 물체를 균일한 값으로 블렌딩 하는 것인데 여기서는 3개의 물체 (원, 사각형, 삼각형)가 나온다. 이것도 위와 유사한 방법이지만 glBlendFunc() 함수의 인자를 GL_SRC_ALPHA와 GL_ONE으로 준다. 소스로 들어오는 화소의 블렌딩은 들어오는 알파값을 그대로 사용하되 먼저 그려져 있던 화소는 GL_ONE을 줌으로써 가지고 있던 알파값을 그대로 유지하게 한다. 결국 이 의미는 '중첩'시키라는 의미다. 고로 33%씩 세번 중첩 하면 100% 하얀 부분이 3개의 물체가 겹치는 부분에 나타나야 한다. 근데 아래 그림은 좀 약하다... 파워 포인트로 작업한지라 제대로 중첩이 안되고 있다.. -_-
그래서 프로그램을 돌려 나온 결과를 스크린 캡쳐하여 첨부하였다. 아래 그림을 보면 아... 세개의 도형이 겹치는 부분은 100% 흰색이구나 하는 느낌이 올 것이다.
소스 코드는 다음과 같다.
glEnable(GL_BLEND); // 블렌딩활성화
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glBegin(GL_TRIANGLES); // Drawing Using Triangles
glColor4f(1.0 f, 1.0 f, 1.0 f, 0.33333 f);
glVertex4f(0.0 f, 1.2 f, 0.0 f, 1.0 f);
glVertex4f(-1.2 f, -1.0 f, 0.0 f, 1.0 f);
glVertex4f(1.2 f, -1.0 f, 0.0 f, 1.0 f);
glEnd();
glBegin(GL_QUADS); // Draw A Quad
glColor4f(1.0 f, 1.0 f, 1.0 f, 0.33333 f);
glVertex4f(-1.0 f, 1.0 f, 0.0 f, 1.0 f);
glVertex4f(1.0 f, 1.0 f, 0.0 f, 1.0 f);
glVertex4f(1.0 f, -1.0 f, 0.0 f, 1.0 f);
glVertex4f(-1.0 f, -1.0 f, 0.0 f, 1.0 f);
glEnd();
glColor4f(1.0 f, 1.0 f, 1.0 f, 0.33333 f);
glutSolidSphere(1.0 f, 36, 36);
출처: http://diehard98.tistory.com/entry/OpenGL-블렌딩 [Unavailable]
=======================
=======================
=======================
#include <GL/glut.h>
#include <GL/GL.h>
#include <GL/GLU.h>
GLint TopLeftX, TopLeftY, BottomRightX, BottomRightY;
GLfloat r = 0.5, g = 0.5, b = 0.5; // 선색 초기화
void MyDisplay() {
glViewport(0, 0, 300, 300);
glBegin(GL_POLYGON); // 빨간색 박스
glColor3f(1.0, 0.0, 0.0);
glVertex3f(0.9, 0.9, 0.0);
glVertex3f(1.0, 0.9, 0.0);
glVertex3f(1.0, 1.0, 0.0);
glVertex3f(0.9, 1.0, 0.0);
glEnd();
glBegin(GL_POLYGON); // 녹색 박스
glColor3f(0.0, 1.0, 0.0);
glVertex3f(0.9, 0.8, 0.0);
glVertex3f(1.0, 0.8, 0.0);
glVertex3f(1.0, 0.9, 0.0);
glVertex3f(0.9, 0.9, 0.0);
glEnd();
glBegin(GL_POLYGON); // 파란색 박스
glColor3f(0.0, 0.0, 1.0);
glVertex3f(0.9, 0.7, 0.0);
glVertex3f(1.0, 0.7, 0.0);
glVertex3f(1.0, 0.8, 0.0);
glVertex3f(0.9, 0.8, 0.0);
glEnd();
glFlush();
}
void MyMouseClick(GLint Button, GLint State, GLint X, GLint Y) {
// 마우스 왼쪽버튼을 눌렀을때 실행
if (Button == GLUT_LEFT_BUTTON && State == GLUT_DOWN) {
TopLeftX = X; // 클릭좌표를 반환
TopLeftY = Y;
if (TopLeftX / 300.0 > 0.9 && TopLeftX / 300.0 < 1.0)
{
//빨간박스 클릭시 선색 변경
if ((300 - TopLeftY) / 300.0 > 0.9 && (300 - TopLeftY) / 300.0 < 1.0) {
r = 1.0;
g = 0.0;
b = 0.0;
// 녹색박스 클릭시 선색 변경
} else if ((300 - TopLeftY) / 300.0 > 0.8 && (300 - TopLeftY) / 300.0 < 0.9) {
r = 0.0;
g = 1.0;
b = 0.0;
// 파란색박스 클릭시 선색 변경
} else if ((300 - TopLeftY) / 300.0 > 0.7 && (300 - TopLeftY) / 300.0 < 0.8) {
r = 0.0;
g = 0.0;
b = 1.0;
}
}
}
}
void MyMouseMove(GLint X, GLint Y) {
BottomRightX = X; // 마우스를 움직일때 좌표 반환
BottomRightY = Y;
glBegin(GL_LINES); // 점과 점을 연결
glColor3f(r, g, b);
// 이전 점의 위치
glVertex3f(TopLeftX / 300.0, (300 - TopLeftY) / 300.0, 0.0);
// 마우스를 움직일때 점의 위치
glVertex3f(BottomRightX / 300.0, (300 - BottomRightY) / 300.0, 0.0);
glEnd();
TopLeftX = BottomRightX; // 현재 점의 위치를 이전 위치에 저장
TopLeftY = BottomRightY;
glutPostRedisplay(); // 디스플레이 함수 재실행
}
int main(int argc, char ** argv) {
glutInit( & argc, argv); // GLUT 라이브러리 초기화 및 세션 연결
glutInitDisplayMode(GLUT_RGB); // 기본 컬러 모드 RGB
glutInitWindowSize(300, 300); // 윈도우 사이즈 조절
glutInitWindowPosition(200, 200); // 윈도우 생성좌표
glutCreateWindow("OpenGL Drawing Example"); // 윈도우 생성
glClearColor(1.0, 1.0, 1.0, 1.0); // 배경색 초기화
glClear(GL_COLOR_BUFFER_BIT); // 프레임 버퍼 초기화(배경색 사용)
glMatrixMode(GL_PROJECTION); // 행렬모드를 프로젝션 행렬로 설정
glLoadIdentity(); // 항등행렬을 실어라
glOrtho(0.0, 1.0, 0.0, 1.0, 1.0, -1.0); // 투상 방식 설정
glutDisplayFunc(MyDisplay); // 디스플레이 함수 등록
glutMouseFunc(MyMouseClick); // 마우스클릭 함수 등록
glutMotionFunc(MyMouseMove); // 마우스이동 함수 등록
glutMainLoop(); // 이벤트 루프로 진입
return 0;
}
===================
프로그램 실행후, 윈도우 위에서 마우스 왼쪽버튼이 눌린 상태라면 현재 마우스의 좌표를 TopLeftX, TopLeftY에 저장하고 마우스를 누른상태에서 이동했을때 이동한 좌표를 BottomRightX, BottomRightY에 저장해서 두좌표를 직선으로 잇는다.
BottomRightX, BottomRightY의 좌표를 TopLeftX, TopLeftY에 저장하면 Motion 이벤트가 실행될때마다 점과 점이 연결된 직선을 그리고 glutPostRedisplay()를 실행하여 마치 연결된 곡선인것처럼 보이게 된다.그리고 우측위의 R, G, B 색상 박스 안쪽의 좌표를 클릭하면 이벤트에 의해서 좌표를 TopLeftX, TopLeftY에 저장하고 좌표를 비교하여 r,g,b 변수의 값이 변경되면서 다음 그리는 선의 색이 해당되는 색으로 변경된다.
===================
출처: http://moldd.tistory.com/entry/openGL-자유곡선-그리기 [몰래먹는떡]
=======================
=======================
=======================
출처: https://stackoverflow.com/questions/292071/opengl-how-to-implement-an-eraser-tool
up vote4down votefavorite 8 |
I'm working on a game for the iPhone that has a drawing/paint mechanic involved and I'm having problems trying to create a tool that would erase things already painted. The main problem is that the background being painted on is not a solid color but a static image or animation. I've tried using different blending options and logical operations in drawing but nothing seemed to work. I'm new to OpenGL so I must be missing something. Any tips? EDIT: To give a little more information, I'm using textures for my brushes and using glVertexPointer() and glDrawArrays() to render them. For example: EDIT 2: Unfortunately, stencil buffers are not available on the iPhone. : ( EDIT 3: Framebuffer objects are available on the iPhone and that is the road I took. I haven't fully implemented it yet, but so far it looks like it works the way I wanted it to. Thanks everyone! iphone opengl-es framebuffer |
add a comment |
5 Answers
up vote5down voteaccepted | Draw a full-screen textured quad over your scene. When the user draws a brush stroke, use glTexSubImage2D to update the your texture. glReadPixels/glDrawPixels is slow. Using FrameBufferObjects is even better, but I doubt this extention is available on the iPhone (then again, I don't know for sure, so maybe try it). FBO's allow you to draw directly into a texture as if it were another rendering context. |
add a comment |
up vote4down vote | Stencil Buffer is the best way to approach this for sure...You'll save time, cpu, and potential problems..,They are available on Iphone, you just have to created an OpenGlES 2.0 surface (not gles 1.0 or 1.1). Lookup this example :http://www.opengl.org/resources/code/samples/glut_examples/examples/stenciltst.c Here's the visual result :http://www.opengl.org/resources/code/samples/glut_examples/examples/stenciltst.jpg I've implented the same kind of eraser tool with this, on both android and iPhone and it works like a charm ! Good luck ! Cheers ! |
add a comment |
up vote2down vote | You don't give much info, but I assume that your storing whatever they "paint" into a buffer and then drawing it on screen like this: With drawing->ImageData being the buffer. What you could do is have a separate background buffer and draw it first. Then the erase tool will simply white-out the the drawing buffer(turning all the values, including alpha, all the way up). For this solution to work you'd have to turn on blending and turn off depth testing. Is that what you're looking for? |
add a comment |
up vote2down vote | You could use the stencil buffer for this operation. The stencil buffer is a special buffer which holds information for every pixel, similiar to the depth buffer. Unlike the depth buffer, you decide how the stencil buffer is altered while drawing and how it influences the decision to draw into the color buffer or not. To do so, you can set the specific states before any drawing operation. Here is what you do:
The function to set how the stencil buffer itself is influenced by drawing: glStencilOp() Further explanation, also with the arguments for these:http://www.opengl.org/resources/code/samples/sig99/advanced99/notes/node117.html |
add a comment |
up vote0down vote | Using glBlendFunc(GL_ONE, GL_ZERO) mode can earse. |
=======================
=======================
=======================
출처: http://blog.naver.com/kzh8055/140043022533
[OpenGL] Fragment TEST - 1.스텐실 테스트(Stencil TEST)
흠...
이번에 살펴볼 내용은 Stencil Buffer(혹은 Stencil Test)에 대해서다.
*정점 변환 하다 말고 왜 갑자기 Rendering PipeLine 의 막바지인 Fragment Test로
건너 띄게 된 이유는 따지지마라.
Direct Draw 때와 마찬가지로 순전히 본인 X리는데로 적는것 뿐이니.
Stencil TEST
우선 Stencil Test 가 뭔 짓거리를 하는 건지 책에서 읽었던 기억을 더듬어
아주 대략 설명해 보겄다.
문을 새로이 도색하고자 한다. 보통 페인트 칠에 앞서 문꼬리에 신문지와 같이 무언가를
덧 씌워놓는데 이것은 문꼬리까지 도색하지 않기 위해서일것이다.
Stencil Buffer 용도가 바로 이런것이다.
즉,문을 칠하는데 있어 자신이 칠하기 싶은 않은곳에 Mask를 씌어 그곳만은
페인트 칠의 영향을 받지 않게 하는것이다.
뭐 여기까지야 Stencil Buffer 를 거론 하는 대부분의 지문들에 나와있는 기본적인 내용들이고
이해도 그리 어렵지 않을것이다.
다만, 본인이 Stencil TEST를 이해 하는데 가장 어려웠던 부분인
'대체 언제 Stencil Buffer에 쓰는거여?'
즉,그리지 말아야 할 부분의 형태(MASK)를 만드는 부분에 대해선 관련 내용을 찾기 힘들었다.
하여간 지금 부터 Stencil Test 사용법과 예제를 통해 위에 대한 질문에 답해 보겄다.
1.Stencil Buffer 요청
처음으로 해야할일은 Stencil Buffer를 요청하는 일이다.
이 작업은 GLUT 를 사용해 OpenGL Programming을 할경우 아주 간단하게
Display Mode 초기화 함수 호출시 인자에 GLUT_STENCIL (상수)를 추가해 주면 된다.
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_STENCIL )
2.Stencil Test 활성화
glutEnable(GL_STENCIL_TEST)
3. Stencil Test 설정
glStencilFunc(GLenum 함수,GLint 비교값, GLint Mask)
위 함수는 Stencil buffer내에 있는 내용과 비교값에 지정된 값을 어떠한 기준으로
비교할것인지 를 지정하는 용도로 사용된다.
함수, 즉 비교방법에 올 수있는 enum 값은
GL_NEVER,GL_ALWAYS,GL_LESS,GL_LEQUAL,GL_EQUAL,GL_NOTEQUAL
등이 있고 가령 GL_NEVER의 경우 아무것도 TEST 를 통과 하지 못하게 하고
GL_ALWAYS 는 모두 통과 GL_NOTEQUAL은 Stencil Buffer내의 값과 비교 값이 같지 않을
경우 통과 된다.
4. Stencil Pattern 생성
glStencilOp(GLenum 실패, GLenum z_실패, GLenum z_성공)
이 부분이 바로 Stencil Buffer에 접근해 Buffer내의 값을 변경할 조건을 설정하는것이다.
첫번째 인자는 Stencil Test가 실패 했을시 Stencil Buffer 어떻게 변경할것인가를 나타낸다.
여기엔 GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR, GL_DECR 등이 올수 있다.
한가지 예를 들면 GL_KEEP 은 Stencil Buffer 내의 값을 그대로 유지한다.
두 번째 인자와 세 번째 인자는 Stencil Test가 성공했다는 전제 하에 Depth(Z) Test 의 성공
여부에 따라 적용될 Stencil Buffer내의 값 변경 방식을 나타낸다.
흠...이론은 이정도로 됐고 예제 하나를 훑어보며 Stencil Test 개념을 잡아보자.
*위의 내용과 지금부터 등장할 예제는 OpenGL SuperBible을 거의 그대로 옮긴것이다.
void RenderScence(void) {
GLdouble dRadius = 0.1;
GLdouble dAngle;
glClearColor(0.0 f, 0.0 f, 1.0 f, 0.0 f);
glClearStencil(0.0 f);
glEnable(GL_STENCIL_TEST);
glClear(GL_COLOR_BUFFER | GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_NEVER, 0x0, 0x0);
glStencilOp(GL_INCR, GL_INCR, GL_INCR);
glColor3f(1.0 f, 1.0 f, 1.0 f);
glBegin(GL_LINE_STRIP);
for (dAngle = 0; dAngle < 400; dAngle += 0.1) {
glVertex2d(dRadius * cos(dAngle), dRadius * sin(dAngle));
dRadius *= 1.002;
}
glEnd();
glStencilFunc(GL_NOTEQUAL, 0x1, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glColor3f(1.0 f, 0.0 f, 0.0 f);
glRectf(x, y, x + rsize, y - rsize);
glSwapBuffer();
}
위 예제는 Stencil Test 를 이용해 특정 부분을 제외한 채 Rendering하는 예제다.
하여간 차근차근 살펴보자.
glClearStencil(0.0f) 함수를 통해 Clear 할 Stencil Buffer의 초기값을 지정해주고
glClear(GL_COLOR_BUFFER | GL_STENCIL_BUFFER_BIT) 함수를 호출함으로써 실제로
위에서 지정한 값으로 Stencil Buffer를 채운다.
그리고 나서 Stencil TEST를 활성화 시킨다. -> glEnable(GL_STENCIL_TEST)
다음은 Stencil Test 방법을 지정하는것인데
여기서 알아야 할 중요한 부분은
Stencil Test를 활성화 시킨 후 부터는 Frame Buffer 에 쓰는 작업이
Stencil Buffer에도 그대로 반영된다는 것이다.
일단 그렇게 알고 계속 가보자.
glStencilFunc(GL_NEVER,0x0,0x0)
glStencilOp(GL_INCR , GL_INCR , GL_INCR);
glStencilFunc(...)함수는 Stencil Buffer 내의 값과 비교값(두번째 인자)를 비교해
조건(첫번째 인자)과 맞다면 Stencil Test를 통과하게끔 하기 위해 조건과 비교값을 지정한다.
그러나 조건 방식인 GL_NEVER 이므로 어떠한 Fragment(pixel)도 Test를 통과 하지 못한다.
그런데 아래 함수 glStencilOp의 첫번째 인자, 즉 Stencil Test를 통과하지 못했을때
Stencil Buffer에 적용되는 변경 방법은 GL_INCR, Buffer내의 해당 값을 증가 시키는 것이다.
이게 뭔 말이냐면 일반적인 Rendering 즉 Frame Buffer에 뭔가를 그릴려 시도한다해도
현 상태에서 무조건 Stencil TEST가 실패로 간주되므로 Frame Buffer에 아무 것도 그려지지
않고 그 Frame Buffer의 대응되는 동일 위치의 Stencil Buffer내의 값이 증감된다는 말이다.
glBegin(GL_LINE_STRIP) 과 glEnd() 사이의 Loop 는 서로 지름이 다른 동심원을 그린다.
이때 위의 조건에 의해 Frame Buffer에는 어떤 Pixel(Fragment)도 써지지 못하지만
Stencil Buffer에는 아마도 Frame Buffer에 쓰려던 형태(값은 다르다. 0x1 )가 그대로 써질 것이다.
바로 여기까지가 Stencil Pattern 즉, Mask를 만든것이다.
<Frame Buffer> <Stencil Buffer>
<A. glClear() 함수를 통해 초기화된 상태>
<B. 동심원을 Rendering 한 후 >
<C. 사각형을 Rendring 한 후 >
여기서 다시 Stencil TEST 방식이 변경됐는데 이번엔
참조 방식이 GL_NOTEQUAL 이고 비교 값이 0x1 이됐다.
풀어서 얘기하자면 Frame Buffer에 무언가를 그릴시에
Stencil Buffer내에 0x1이 아닌 위치에 Pixel들만 Test가 통과 되게 설정했다는것이다.
이런 상태에서 Rectf(...) 함수를 이용해 사각형을 그리면 Stencil Buffer에 그려진
동심원의 선 부분을 제외한 곳이 그려질것이다.
반대로 Stencil Buffer에 정의된 부분만 Frame Buffer에 그리고자 한다면
마지막 glStencilFunc(...)의 첫번째 인자를 GL_EQUAL 로 바꾸면 될것이다.
하여간 정리하자면
Stencil Buffer를 이용해 특정 부분만 Rendering 하고자 하는 방법은 다음과 같다.
1. Stencil TEST 를 활성화 시킨다.
2. 일단 모든 Stencil TEST 실패하게 한상태에서 Mask 형태 Rendering 한다.
->결국, 이것은 Frame Buffer가 아닌 Stencil Buffer에 그려진다.
3. Stencil Test Option을 변경한뒤 객체를 Rendering 한다.
=======================
=======================
=======================
출처: https://stackoverflow.com/questions/19615003/continuous-line-on-mouse-drag
I am making a basic OpenGl program which is my weekend project.
Description: If I drag the mouse on the window screen then after I release the button(left) a line should appear that shows the path of dragging the mouse and a circle should traverse that line from start to end.
My Code Works Very Well but the problem is that the line drawn is not continuous[rather its dashed] and it may be because of the mapping of OS screen to OpenGL screen. SO is there any way to make a continous line path or correct me If I am doing something wrong
Here is my code:
#include <iostream>
#include <glut.h>
#include <string.h>
#define WIDTH 600
#define HEIGHT 600
using namespace std;
double arr[5000][4];
int z = 0;
int flag = 0;
float radius = 0.03;
int ptr = 0;
int faltu_bit = 1;
float color[3][3] = {
{
1.0,
1.0,
1.0
},
{
1.0,
1.0,
0.0
},
{
0.0,
1.0,
0.0
}
};
void drawText(char * str, float x, float y, int id) {
int i;
int len = strlen(str);
//glLoadIdentity();
glColor3f(color[id][0], color[id][1], color[id][2]);
glRasterPos2f(x, y);
for (i = 0; i < len; i++)
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, str[i]);
}
void init() {
glClearColor(0.0, 0.0, 0.0, 1.0);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0.0, WIDTH, 0.0, HEIGHT);
memset(arr, 0, 5000);
glPointSize(20.0);
}
void resetAll() {
memset(arr, 0, 5000);
z = 0;
}
///OPENGL MAPPING///
float getOpenGLX(int x) {
double ox = x / (double) WIDTH * (WIDTH);
return ox;
}
float getOpenGLY(int y) {
double oy = (1 - y / (double) HEIGHT) * HEIGHT;
return oy;
}
void drawPoints() {
glBegin(GL_POINTS);
glColor3f(0.0, 1.0, 0.0);
for (int i = 0; i < z; i++) {
glVertex2f(arr[i][0], arr[i][1]);
}
glEnd();
}
void drawBall(float x, float y) {
glBegin(GL_POINTS);
glColor3f(1.0, 1.0, 0.0);
glVertex2f(x, y);
glEnd();
}
void drawLines() {
glBegin(GL_LINES);
glColor3f(1.0, 0.0, 0.0);
for (int i = 0; i < z; i++) {
glVertex2f(arr[i][0], arr[i][1]);
}
glEnd();
}
void addValue(int x, int y) {
arr[z][0] = getOpenGLX(x);
arr[z++][1] = getOpenGLY(y);
}
void trackBall() {
drawPoints();
}
void myDisplay() {
glClear(GL_COLOR_BUFFER_BIT);
if (!flag) {
drawLines();
if (!faltu_bit)
drawBall(arr[ptr][0], arr[ptr][1]);
}
if (faltu_bit) {
drawText("Project by: Adil Ansar [10 CSS-32]", 50.0, 500.0, 0);
drawText("Welcome", 250.0, 300.0, 1);
drawText("Drag the Mouse Any Where in the Window to see the Path", 10.0, 200.0, 2);
}
glutSwapBuffers();
glutPostRedisplay();
glFlush();
}
void myMouseStat(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
if (!flag) {
if (faltu_bit) {
faltu_bit = 0;
}
resetAll();
flag = 1;
}
} else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
if (flag) {
ptr = 0;
flag = 0;
}
}
}
void myPressedMove(int x, int y) {
if (flag) {
addValue(x, y);
}
}
void myTimer(int t) {
if (ptr != z) {
ptr++;
} else {
ptr = 0;
}
glutTimerFunc(100, myTimer, 0);
}
int main(int argc, char ** argv) {
glutInit( & argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowPosition(100, 100);
glutInitWindowSize(WIDTH, HEIGHT);
glutCreateWindow("Testing");
init();
glutDisplayFunc(myDisplay);
glutMouseFunc(myMouseStat);
glutMotionFunc(myPressedMove);
glutTimerFunc(100, myTimer, 0);
glutMainLoop();
return 0;
}
This Is What I get:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
In this code
void drawLines() { glBegin(GL_LINES); glColor3f(1.0,0.0,0.0); for(int i=0;i<z;i++) { glVertex2f(arr[i][0],arr[i][1]); } glEnd(); }
GL_LINES is a drawing mode where two consecutive vertices make a line segment. Then the next two and so so. What you're drawing is a line strip; replacing GL_LINES with GL_LINE_STRIP will give you your expected result.
On a side note: You should abandon using immediate mode (glBegin, glVertex, glEnd). It's slow, it's cumbersome to work with and in the long term can become a major PITA. Use at least vertex arrays; they've been the recommended way of supplying geometry data for well over 15 years. Plus it makes your code much simpler. Your code above can be replaced with this:
/* vertex data comes from an array */ glEnableClientState(GL_VERTEX_ARRAY); /* we want to use a common color for all vertices */ glDisableClientState(GL_COLOR_ARRAY); glColor3f(1.0, 0.0, 0.0); /* where to get the data from */ glVertexPointer(2, GL_DOUBLE, sizeof(double)*4, arr); /* draw the whole thing */ glDrawArrays(GL_LINE_STRIP, 0, z); /* cleanup */ glDisableClientState(GL_VERTEX_ARRAY);
As you can see it's shorter, more concise, easier to read and it avoids doing z+3 function calls, each of which taking some time to execute.
Important that OpenGL can digest that multidimensional array arr is due to the fact, that statically allocated storage of multidimensional arrays is always contiguous. It would not work if you'd allocate an array of pointers to arrays (the naive way to allocate multidimensional arrays dynamically).
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Thanks For the Answer and a very good info about using vertex arrays.. This works like charm – adil Oct 27 '13 at 13:13 | |
I Have one more question. when I Used the exe of above code on my friends pc then instead of a circular point there was a squared box traversing the path. What could be the reason for this? – adil Oct 27 '13 at 19:44 | |
@adil: Usually you get square points of point smoothing is disabled and round points with point smoothing enables. I don't see your program enabling point smoothing. However implementations have some leeway how they interpret points (I recommend reading the specification on the topic) and your friend may have antialiasing enabled in the driver, which makes the point appear round. – datenwolf Oct 27 '13 at 21:10 | |
I Have started learning openGL few weeks ago...so thanks for guiding me properly – adil Oct 28 '13 at 1:06 |
=======================
=======================
=======================
출처: http://wecansoar.tistory.com/103
문을 페인트칠 하려고 할때
문고리까지 도색하지 않기 위해 마스킹테이프 같은걸로 덧씌워놓는데
이 마스킹테이프 같은 용도로 사용하는게 Stencil Test (=Stencil Buffer)이다.
특정 부분을 제외한 채 Rendering 하는 예제
void RenderScene(void) {
GLdouble dRadius = 0.1; // 원의 반지름 값 설정
GLdouble dAngle;
// 화면을 파란색으로 채움
glClearColor(0.0 f, 0.0 f, 1.0 f, 0.0 f);
// 스텐실 버퍼의 초기값 지정
glClearStencil(0);
// 위에서 지정한 값으로 Stencil Buffer를 채움 (스텐실 버퍼를 0으로 채움)
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// 스텐실 테스트 기능 활성화
glEnable(GL_STENCIL_TEST);
/* ---- 스텐실 테스트를 활성화 시킨 후부터는 Frame Buffer 에 쓰는 작업이 Stencil Buffer 에도 그대로 반영된다
---- */
// 스텐실 버퍼내의 값과 두번째 인자와 세번째 인자를 And 연산한 값을 비교값을 비교해 첫번째 인자인 조건에 맞춰 스텐실 테스트를
// 통과시킬지 지정
glStencilFunc(GL_NEVER, 0, 0);
/* GL_NEVER : 어떠한 Fragment(pixel)도 Test를 통과하지 못함. 두번째 세번째 매개변수 값이 뭐가 되던지
아무것도 그려지지 않게 됨
GL_ALWAYS : 모든 값 통과. 즉 두번째 세번째 매개변수와 상관없이 화면에 그리고자 하는 그림이 그대로 보임.
GL_LESS
GL_LEQUAL
GL_EQUAL
GL_NOTEQUAL : 스텐실 버퍼내의 값과 비교값이 같지 않을 경우 통과. 여기서는 (GL_NOTEQUAL, 0, 0) 도 같은 의미가 된다. 초기값으로 스텐실 버퍼를 모두 0으로 채웠기 때문에 0이 아닌 경우가 없어 모든 스텐실 테스팅이 실패하기 때문이다.
*/
// 스텐실 버퍼에 접근해 버퍼내의 값을 변경할 조건을 설정하는 부분
glStencilOp(GL_INCR, GL_INCR, GL_INCR);
/* 첫번째 인자는 스텐실 테스트가 실패 => 스텐실 버퍼의 다음 동작 정의
두번째 인자는 스텐실 테스트 성공, depth 테스트 실패 => 스텐실 버퍼의 다음 동작 정의
세번째 인자는 스텐실 테스트, depth 테스트 모두 성공 => 스텐실 버퍼의 다음 동작 정의
GL_KEEP : 스텐실 버퍼 내의 값을 그대로 유지
GL_ZERO
GL_REPLACE
GL_INCR : 스텐실 버퍼에 있는 값을 하나 증가시킨다. 여기서는 GL_NEVER,0,0 으로 했기 때문에 모든 스텐실 테스트는
실패하므로 첫번째 인자인 GL_INCR이 동작하고, 화면에 보이지는 않지만 해당 위치의 버퍼 내용만 0에서 1로 바뀌게 된다.
(종이에 구멍이 뚫린다는 개념??)
GL_DECR */
// 스텐실 패턴을 생성. 서로 지름이 다른 흰색 선 나선형 패턴을 그림
glColor3f(1.0 f, 1.0 f, 1.0 f);
glBegin(GL_LINE_STRIP);
for (dAngle = 0; dAngle < 400.0; dAngle += 0.1) {
glVertex2d(dRadius * cos(dAngle), dRadius * sin(dAngle));
dRadius *= 1.002;
}
glEnd();
/* ---- 여기까지... 나선원은 화면에 보이지는 않지만 나선원이 그려지는 부분에 있는 스텐실 버퍼의 값이 1로 변경되어 있음 ----
*/
// 스텐실 테스팅 방식이 변경됨. 스텐실 버퍼내에 1이 아닌 위치에 픽셀들만 Test 통과되게 설정
// 나선원을 그린 후에 나선원 부분의 스텐실 버퍼 값은 1로 변경됐는데 1과 같지 않은 것만 통과시키겠다는 뜻이므로 나선원 부분만
// 제외하고 그려질 것이다.
// 만약 (GL_NOTEQUAL, 2, 1); 이렇게 하면 2 & 1 = 0 이므로 0이 아닌것만 통과시키겠다는 뜻이므로 나선원 부분만
// 그려지게 된다.
glStencilFunc(GL_NOTEQUAL, 1, 1);
// 나선원 그려진 부분의 스텐실 버퍼값이 1이고 나머지 부분은 0이기 때문에 나선원이 있는 부분은 실패. 나머지는 성공.
// 나선원이 있는 부분은 첫번째 인자 GL_KEEP 실행. 나머지 부분은 두번째 인자 GL_KEEP 실행.
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// 빨간색 사각형 그림
glColor3f(1.0 f, 0.0 f, 0.0 f);
glRectf(x, y, x + rsize, y - rsize);
/* ---- 여기까지... 나선원 부분만 제외하고 사각형이 그려짐 ------ */
glSwapBuffers();
}
출처: http://wecansoar.tistory.com/103 [비상, 飛上 , soar]
=======================
=======================
=======================
기타링크들:
=======================
=======================
=======================
'프로그래밍 관련 > 3D,2D DRAW 관련' 카테고리의 다른 글
OpenGL에서 텍스쳐 로딩, 스레드 thread 에서의 텍스처 로딩 관련 (0) | 2020.09.15 |
---|---|
비쥬얼스튜디오 Visual Studio C, C++ MFC와 OpenGL 오픈지엘 연동 관련 (0) | 2020.09.15 |
FreeImage Library 셋팅 관련 (0) | 2020.09.15 |
OpenGL 오픈지엘 텍스쳐, 화질 개선 밉맵 필터링 Texture Filtering and Mipmapping 관련 (0) | 2020.09.15 |
OpenGL 이동,회전,확대축소 관련 (0) | 2020.09.10 |
댓글 영역