GLFW 라이브러리를 사용하여 윈도우와 OpenGL context를 생성하고 GLEW 라이브러리를 사용하여 윈도우를 단색으로 채우는 간단한 예제를 설명합니다.
본 포스팅에 있는 예제 코드를 컴파일하기 위해 필요한 Visual Studio에서 프로젝트 생성 및 라이브러리 추가 방법은 다음 포스팅을 참고하세요.
Visual Studio 2023에 OpenGL 개발 환경 만들기 ( GLFW / GLEW )
https://webnautes.tistory.com/2085
처음 작성 : 2017. 1. 18
수정 : 2017. 6. 29
초안을 업데이트 하려는걸 미루다가 이제서야 포스팅합니다.
http://www.glfw.org/documentation.html 외에 여러 곳을 참고했는데 다 기록해놓지 못했습니다.
마지막 업데이트 2023. 7. 23.
Visual Studio 2022에서 테스트 진행
1. OpenGL과 GLFW, GLEW
1.1. GLFW
1.2. GLEW
2. GLFW와 GLEW를 사용한 OpenGL 예제
2.1. GLFW 헤더파일 포함시키기
2.2. GLFW 초기화
2.3. 윈도우와 Context 생성 힌트
2.4. window와 OpenGL context 생성
2.5. Current Context
2.6. 키보드 콜백함수 등록
2.7. GLEW 초기화
2.8. 원하는 OpenGL 버전 지원하는지 확인
2.9. VSync 활성화
2.10. 루프
2.11. 프레임 수 측정
2.12. 렌더링
2.13. 버퍼 스와핑(Buffer swapping)
2.14. 이벤트 처리
2.15. 프로그램 종료
3. 전체 소스 코드
1. OpenGL과 GLFW, GLEW
OpenGL은 실제 구현된 라이브러리가 아니라 API 스펙입니다.
OpenGL 라이브러리 구현시 플랫폼 종속성을 배제하기 위해서 윈도우 생성 및 관련 이벤트 처리(사용자 입력 이벤트, 윈도우 크기 조정 이벤트 등)와 OpenGL context, surface 생성을 위한 API는 스펙에 포함되어 있지 않습니다.
그래서 사용할 플랫폼을 지원되는 GUI API를 사용하여 관련 처리를 해줘야 합니다.
OpenGL 라이브러리를 초기화하기 위해서는 OpenGL context를 생성이 필수입니다.
OpenGL context는 state machine으로 렌더링을 하기 위해 필요한 모든 정보를 가지고 있습니다.
플랫폼 독립적인 라이브러리로는 GLFW, SFML, SDL, FreeGLUT 등이 있습니다.
SFML과 SDL은 다양한 기능을 포함한 종합적인 라이브러리인데 반해 GLFW, FreeGLUT는 OpenGL을 위해 필요한 최소한의 API만 제공합니다.
1.1. GLFW
본 포스팅에서는 위에서 언급한 윈도우와 OpenGL context를 생성등의 처리를 위해서도 플랫폼 독립적인 코드를 작성하기 위해서 GLFW 라이브러리를 사용했습니다.
GLFW는 데스크탑 환경에서 OpenGL, OpenGL ES, Vulkan을 사용하여 개발시 필요한 플랫폼 독립적인 API를 제공하는 오픈 소스 라이브러리입니다.
C언어로 작성되었으며, 윈도우, 맥OS, Linux, FreeBSD 등의 플랫폼을 지원합니다.
크로스 플랫폼 라이브러리는 지원하는 모든 플랫폼에서 똑같은 코드로 동작시키는게 목표지만 실제로는 특정 플랫폼에 맞게 일부 수정이 필요하기도 합니다.
1.2. GLEW
최근에 나온 윈도우 10에도 OpenGL 1.1을 구현한 라이브러리만 포함되어 있습니다.
그래픽 드라이버를 만드는 CPU 또는 그래픽카드 제조사에서 상위 OpenGL 버전의 스펙을 구현하기 위해 opengl32.dll을 수정할 수 없어서 Extension 메커니즘을 사용하여 해결합니다.
Extension은 사용 중인 OpenGL 버전의 OpenGL 코어에서 제공하지 않는 추가적이거나 새로운 기능을 의미합니다.
OpenGL 1.1 이후 마지막으로 나온 OpenGL 4.5까지 모든 버전의 기능들은 모두 Extension으로 구현되어 왔습니다.
Extension을 불러오는 과정이 플랫폼별로 다르고 간단하지 않기 때문에 보통 Extension loading library를 사용합니다.
본 포스팅에서 사용하는 GLEW(OpenGL Extension Wrangler Library)는 C/C++용 extension loading library입니다.
Windows, Linux, Mac OS X, FreeBSD, Irix, Solaris를 지원하는 크로스 플랫폼 라이브러리로 오픈소스입니다.
OpenGL 3.2 버전 이상의 API를 사용하려면 GLEW를 사용해야 합니다.
실시간으로 사용 가능한 API함수들이 결정되기 때문에 GLEW를 이용하여 OpenGL API 로드를 처리해야 합니다.
2. GLFW와 GLEW를 사용한 OpenGL 예제
2.1. GLFW 헤더파일 포함시키기
GLFW와 OpenGL 라이브러리의 API를 사용하기 위해 하나의 헤더파일만 추가하면 됩니다.
glfw3.h 파일에 사용중인 개발 플랫폼에 맞는 OpenGL 헤더파일을 추가하도록 구현되어 있기 때문입니다.
#include "include/GLFW/glfw3.h" |
윈도우의 경우에는 OpenGL 1.1을 구현한 라이브러리만 포함되어 있기 때문에 상위 버전의 OpenGL API를 사용하기 위해서는 그래픽 드라이버로부터 Extension을 로드해야 합니다.
Extension을 불러오는 과정이 플랫폼별로 다르고 간단하지 않기 때문에 보통 Extension loading library를 사용합니다.
Extension loading library를 사용하기 위해서는 반드시 GLFW 헤더파일 보다 먼저 인클루드해줘야 합니다.
GLFW 내부적으로 로드하는 OpenGL 헤더파일과 충돌하지 않기 위해서 입니다.
#include "include/GL/glew.h" #include "include/GLFW/glfw3.h" |
main 함수에서 가장 먼저 한 일은 GLFW 에러 콜백 함수를 등록해주는 것입니다.
GLFW 라이브러리의 함수를 호출시 발생한 에러 메시지를 출력해주는 역할을 합니다.
GLFW 초기화 함수 glfwInit()를 호출시 발생한 에러에 대해서도 처리하기 위해서 먼저 등록해줍니다.
glfwSetErrorCallback(errorCallback); |
GLFW 에러 콜백 함수는 다음과 같이 정의합니다.
발생한 errorCode에 대응하는 에러 문자열을 errorDescription를 통해 얻을 수 있습니다.
static void errorCallback(int errorCode, const char* errorDescription) { fprintf(stderr, "Error: %s\n", errorDescription); } |
2.2. GLFW 초기화
GLFW 라이브러리의 함수를 호출하기 전에 라이브러리에 대한 초기화가 필요합니다.
에러 발생시 위에서 등록한 errorCallback 에러 콜백 함수가 호출되어 에러 내용을 출력해줍니다.
if (!glfwInit()) { cerr << "Error: GLFW 초기화 실패" << endl; exit(EXIT_FAILURE); } |
GLFW 초기화에 성공했다면 프로그램 종료 전에 glfwTerminate() 함수를 호출해야 합니다.
초기화 실패시 glfwTerminate() 함수를 따로 호출해 줄 필요가 없습니다.
glfwInit() 함수가 GLFW_FALSE를 리턴하기 전에 glfwTerminate() 함수를 호출합니다.
2.3. 윈도우와 Context 생성 힌트
윈도우와 OpenGL context를 생성하기 전에 glfwWindowHint 함수를 호출하여 사용할 OpenGL 관련 설정 정보를 GLFW에게 알려줍니다.
▘사용할 OpenGL 버전 3.3을 위한 context가 필요하다는 것을 알립니다.
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); |
▘GLFW_OPENGL_PROFILE 옵션을 GLFW_OPENGL_CORE_PROFILE으로 지정합니다.
Core Profile를 지원하는 context가 필요하다는 것을 의미합니다.
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); |
OpenGL 3.2부터 Core Profile과 Compatibility Profile 개념이 도입되었습니다.
context를 생성할 때 하나를 선택해야 합니다.
Core Profile(GLFW_OPENGL_CORE_PROFILE)
이전 버전과의 호환성을 고려하지 않고 deprecated(=쓸모 없어지거나 대체되는 기능이 있어서 도태될 예정) 되지 않은 기능들만을 사용하여 코드 작성시 선택합니다.
Compatibility Profile(GLFW_OPENGL_COMPAT_PROFILE)
이전 버전과의 호환성을 유지하기 위하여 deprecated된 기능도 같이 사용해서 코드 작성이 필요할 때 선택합니다.
OpenGL 3.2 이하 버전 사용( GLFW_OPENGL_ANY_PROFILE)
▘GLFW_OPENGL_FORWARD_COMPAT 옵션을 GL_TRUE로 지정합니다.
지정한 OpenGL 버전에서 deprecated API는 제외시킵니다.
OpenGL 3.0 이상에서 사용해야 하는 옵션입니다.
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); |
▘GLFW_RESIZABLE 옵션을 GL_FALSE로 지정하면 사용자가 윈도우 크기를 조정할 수 없습니다.
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); |
▘GLFW_SAMPLES 옵션을 4로 지정합니다.
안티에일리어싱(Anti-Aliasing) 관련 옵션입니다. 참고 : https://stackoverflow.com/a/42858872
glfwWindowHint(GLFW_SAMPLES, 4); |
2.4. window와 OpenGL context 생성
윈도우와 OpenGL context를 생성하고 OpenGL context를 윈도우의 surface에 연결해줍니다.
OpenGL 라이브러리를 사용하려면 반드시 필요한 작업입니다.
예제에서는 800 x 600 크기의 윈도우를 생성합니다.
GLFWwindow* window = glfwCreateWindow( 800, // width 600, // height "OpenGL Example", // window title NULL, NULL); if (!window) { glfwTerminate(); exit(EXIT_FAILURE); } |
GLFW 라이브러리의 함수를 호출하다가 실패하여 프로그램을 종료해야 하는 경우에는 glfwTerminate() 함수를 호출해야 합니다.
GLFW에서 생성한 모든 윈도우를 종료하고 관련 자원을 해제합니다. 또한 GLFW에서 할당한 다른 자원들도 해제합니다.
개별 윈도우만 종료하지 않는한 glfwDestroyWindow(window)를 따로 호출해줄 필요는 없습니다.
프로그램 실행 중, 에러로 인한 프로그램 종료시에도 glfwTerminate() 함수를 호출해야 합니다.
GLFWwindow* window = glfwCreateWindow( 800, 600, "OpenGL Example", NULL, NULL); if (!window) { glfwTerminate(); exit(EXIT_FAILURE); } |
2.5. Current Context
modern OpenGL을 접근하기 위해 확장 로더 라이브러리(GLEW)를 사용한다면 glfwMakeContextCurrent를 호출한 다음에 해야 합니다.
Current Context를 필요로 하기 때문입니다.
glfwMakeContextCurrent(window); |
OpenGL은 state machine입니다. OpenGL context에 렌더링을 위해 필요한 모든 상태를 저장하고 있습니다.
current context를 새로 설정하면, 예전에 사용했던 context의 상태는 무시되고 새로 설정한 context로 모든 상태가 변경됩니다.
예를 들어 두개의 윈도우 및 context를 생성하고
GLFWwindow* window = glfwCreateWindow( 800, 600, "OpenGL Example", NULL, NULL); GLFWwindow* window2 = glfwCreateWindow( 800, 600, "OpenGL Example", NULL, NULL); |
window2를 current로 설정한 상태에서 컬러 버퍼 지울 때 색을 빨간색으로 지정합니다.
glfwMakeContextCurrent(window2); glClearColor(1, 0, 0, 0); //Red |
window를 current로 설정한 상태에서 컬러 버퍼 지울 때 색을 파란색으로 지정합니다.
glfwMakeContextCurrent(window); glClearColor(0, 0, 1, 0); //Blue |
다시 window2를 current로 설정한 상태에서 glClear를 호출하면 windows2 윈도우는 빨간색이 됩니다.
중간에 window 에서 지정한 값이 영향을 주지 못합니다.
모든 그리기는 current context를 기반으로 이루어지기 때문입니다.
glfwMakeContextCurrent(window2); glClear(GL_COLOR_BUFFER_BIT); |
2.6. 키보드 콜백함수 등록
렌더링 중에 사용자의 키보드 입력에 반응하기 위해서 콜백 함수를 등록해줍니다.
윈도우당 하나씩 지정해줘야 합니다.
glfwSetKeyCallback(window, keyCallback); |
키보드 입력이 있을 때 호출되는 함수입니다.
키를 눌렀을 때(GLFW_PRESS)와 키에서 손을 떼었을 때(GLFW_RELEASE)를 구분합니다.
void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GLFW_TRUE); } |
2.7. GLEW 초기화
OpenGL context 생성이 선행돼야 GLEW 초기화가 가능합니다.
우선 glewExperimental 변수를 GL_TRUE로 설정하여 GLEW에서 이용가능한 모든 OpenGL extension에 대한 정보를 가져올 수 있도록 설정합니다.
그리고나서 glewInit 함수를 호출하여 GLEW를 초기화합니다.
glewExperimental = GL_TRUE; GLenum errorCode = glewInit(); if (GLEW_OK != errorCode) { cerr << "Error: GLEW 초기화 실패 - " << glewGetErrorString(errorCode) << endl; glfwTerminate(); exit(EXIT_FAILURE); } |
GLEW 초기화에 성공하면 이제부터 OpenGL 라이브러리의 함수 호출이 가능합니다.
2.8. 원하는 OpenGL 버전 지원하는지 확인
그래픽 드라이버에서 원하는 OpenGL 버전을 지원하는지 체크합니다.
if (!GLEW_VERSION_3_3) { cerr << "OpenGL 3.3 API is not available." << endl; glfwTerminate(); exit(EXIT_FAILURE); } |
현재 설정한 OpenGL 버전 정보와 그래픽 드라이버 정보를 출력합니다.
cout << "OpenGL version: " << glGetString(GL_VERSION) << endl; //8 cout << "GLSL version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << endl; cout << "Vendor: " << glGetString(GL_VENDOR) << endl; cout << "Renderer: " << glGetString(GL_RENDERER) << endl; |
2.9. VSync 활성화
스왑 간격(VSync)은 버퍼 스와핑이 일어나기 전까지 기다려야하는 프레임 수(디스플레이의 수직 동기화 타이밍)를 의미합니다.
GLFW에서 디폴트로 스왑 간격은 0입니다.
즉 백버퍼가 준비되면 프론트 버퍼가 화면에 출력이 완료되지 않았더라도 스와핑이 바로 이루어집니다.
보통 초당 60~75개의 프레임이 화면에 업데이트되기 때문에 CPU와 GPU가 빠른 경우 낭비가 발생합니다. 화면에서 보여줄 수 있는 것 이상으로 화면 업데이트가 발생합니다.
이런 상황에서 화면 업데이트 하는 도중에 스왑이 발생하게 되면, 이전 프론트 버퍼에서 화면에 출력했던 영상과 지금 프론트 버퍼가 출력하는 영상이 화면에 같이 보이게 됩니다. 이때 화면이 찢어져 보이는 현상인 스크린 테어링(tearing)이 발생합니다.
이 문제를 해결하기 위해서 스왑 간격(vsync)를 1로 설정합니다.
한 프레임에 대한 출력이 완료되어야 다음 버퍼 스와핑이 발생합니다.
더 높은 값을 설정할 수 있지만 입력 대기 시작때문에 권장하지 않습니다.
아규먼트를 1로 하면 모니터의 refresh rate인 60Hz에 맞추어 60 fps로 렌더링하게 됩니다.
다시말하면 1초에 60개의 프레임을 화면에 출력하게 됩니다.
glfwSwapInterval(1); |
디폴트가 VSync 비활성화라서 glfwSwapInterval 함수를 호출하지 않거나 아규먼트를 0으로 지정하면 VSync를 비활성화 합니다.
예를 들어 glfwSwapInterval함수의 아규먼트를 1로 지정하면 60 fps가 되지만
16.393443 ms/frame 61 fps
16.666667 ms/frame 60 fps
16.666667 ms/frame 60 fps
16.666667 ms/frame 60 fps
16.666667 ms/frame 60 fps
16.666667 ms/frame 60 fps
16.666667 ms/frame 60 fps
16.666667 ms/frame 60 fps
16.666667 ms/frame 60 fps
16.949153 ms/frame 59 fps
16.666667 ms/frame 60 fps
16.393443 ms/frame 61 fps
16.666667 ms/frame 60 fps
glfwSwapInterval 함수의 아규먼트를 0로 지정하거나 glfwSwapInterval 함수를 호출하지 않으면 760 fps 정도가 됩니다.
이 값은 컴퓨터의 그래픽 성능에 따라 달라집니다.
1.308901 ms/frame 764 fps
1.321004 ms/frame 757 fps
1.314060 ms/frame 761 fps
1.317523 ms/frame 759 fps
1.317523 ms/frame 759 fps
1.317523 ms/frame 759 fps
1.324503 ms/frame 755 fps
1.297017 ms/frame 771 fps
1.342282 ms/frame 745 fps
1.295337 ms/frame 772 fps
본 포스팅의 코드에서는 빨간색과 파란색을 번갈아가며 지정하여 윈도우의 컬러 버퍼를 지워줍니다.
VSync 활성화 여부에 따른 결과 차이를 보여주기 위해 캡처한 영상입니다.
스크린 테어링 여부를 확인하기는 어렵지만 VSync 활성화 해서 60 fps로 보여주는 영상이 더 자연스럽게 보입니다.
2.10. 루프
사용자가 Alt + F4를 누르거나 타이틀바에서 close를 눌러 윈도우를 닫아서 glfwWindowShouldClose함수가 GL_TRUE를 리턴하기 직전까지 반복하게 됩니다.
화면에 렌더링 결과를 일정시간마다 업데이트하면서
키보드나 마우스 등으로부터의 이벤트를 받아 처리하기 위해 이벤트 루프를 사용합니다.
while (!glfwWindowShouldClose(window)) { . . . . . . . . . . . . . . . . . . } |
2.11. 프레임 수 측정
루프를 시작하기 전에 시간을 측정하여 lastTime에 저장하고
double lastTime = glfwGetTime(); int numOfFrames = 0; |
루프에서 계속 새로 시간을 측정하여 currentTime에 저장합니다.
lastTime과 currentTime이 1초 차이나면, 현재까지 카운트한 프레임 수를 화면에 출력합니다.
이제 lastTime은 현재 값이 되고 루프를 돌며 새로 시간을 측정하여 다시 1초 차이 날때 까지 반복합니다.
while (!glfwWindowShouldClose(window)) { double currentTime = glfwGetTime(); numOfFrames++; if (currentTime - lastTime >= 1.0) { printf("%f ms/frame %d fps \n", 1000.0 / double(numOfFrames), numOfFrames); numOfFrames = 0; lastTime = currentTime; } . . . . . . . . . . . . . . . . . . } |
2.12. 렌더링
이벤트 루프 안에서 OpenGL 라이브러리를 사용하여 화면에 그리게 됩니다.
본 예제에서는 단순히 윈도우를 특정 색으로 지우는 것만 예로 들고 있습니다.
glClearColor 함수를 사용하여 컬러 버퍼를 지울 때, 사용할 색을 지정합니다.
red, green, blue, alpha의 값 범위는 [0,1]입니다.
void glClearColor( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); |
glClear 함수의 아규먼트로 GL_COLOR_BUFFER_BIT를 사용하여 호출하면 실제로 컬러 버퍼가 지워집니다.
렌더링 결과 해당 값으로 윈도우가 채워지는 것처럼 보입니다.
void glClear(GL_COLOR_BUFFER_BIT); |
예제에서는 빨간색과 파란색을 번갈아가며 지정하여 윈도우의 컬러 버퍼를 지워주고 있습니다.
glfwSwapInterval(1); . . . . . . . . . . . . . . . . . . int count = 0; while (!glfwWindowShouldClose(window)) { . . . . . . . . . . . . . . . . . . if (count % 2 == 0) glClearColor(0, 0, 1, 0); else glClearColor(1, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT); count++; glfwSwapBuffers(window); glfwPollEvents(); } |
2.13. 버퍼 스와핑(Buffer swapping)
이벤트 루프 내에서 화면에 원하는 것을 그리고, glfwSwapBuffers()함수를 호출하면 화면이 업데이트 됩니다.
GLFW에서는 기본적으로 더블 버퍼링을 사용합니다.
즉 각 윈도우는 2 개의 렌더링 버퍼를 갖습니다. - 프론트 버퍼와 백 버퍼
화면 전환을 자연스럽게 하기위해 백 버퍼에다가 다음에 화면에 보여줄 영상(렌더링 결과물)을 준비해놓습니다.
백 버퍼에서 준비가 완료되면 현재 프론트 버퍼와 역활을 바꿔서 백 버퍼의 내용을 화면에 출력해줍니다.
glfwSwapInterval(1); . . . . . . . . . . . . . . . . . . int count = 0; while (!glfwWindowShouldClose(window)) { . . . . . . . . . . . . . . . . . . if (count % 2 == 0) glClearColor(0, 0, 1, 0); else glClearColor(1, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT); count++; glfwSwapBuffers(window); glfwPollEvents(); } |
2.14. 이벤트 처리
정기적으로 glfwPollEvents() 함수를 호출하여 아직 처리되지 않은 대기중인 이벤트를 체크합니다.
보통 매 프레임마다 버퍼 스와핑이 이루어진 다음에 처리합니다.
이벤트 타입별로 해당되는 콜백함수를 호출하여 이벤트를 처리하도록 합니다.
- 키보드 입력
- 마우스 입력
- 윈도우 크기 조정
while (!glfwWindowShouldClose(window)) { . . . . . . . . . . . . . . . . . . glfwPollEvents(); } |
GLFW에는 이벤트(pending events)를 처리하는 2가지 방법이 있습니다.
폴링(polling)은 아직 처리되지 않은 이벤트가 있는지 검사하기 위해 정기적으로 glfwPollEvents() 함수를 호출합니다.
렌더링이 일정 시간마다 계속 되어야 하는 경우 최적입니다.
사용자의 입력이 있을 때에만 렌더링 처리를 해야 한다면 glfwWaitEvents()를 사용하는게 좋습니다.
2.15. 프로그램 종료
프로그램 종료 전에 GLFW를 종료해주어야 합니다.
glfwTerminate() 함수는 GLFW를 사용해 생성한 모든 윈도우와 context를 해제합니다.
또한 GLFW에 의해 할당된 리소스도 해제합니다.
glfwTerminate(); return 0; |
지정한 윈도우와 context만 destroy하려면 glfwDestroyWindow()함수를 사용합니다.
윈도우와 컨텍스트를 더이상 사용하지 않는다면 destroy 해야 합니다.
이 함수가 호출되면, 더 이상 어떤 이벤트도 윈도우로 전달되지 못합니다.
void glfwDestroyWindow( GLFWwindow * window ) |
3. 전체 소스 코드
포스팅에서 사용한 전체 소스 코드입니다.
#include "include/GL/glew.h" #include "include/GLFW/glfw3.h" #include <iostream> #pragma comment(lib, "OpenGL32.lib") #pragma comment(lib, "lib/glew32.lib") #pragma comment(lib, "lib/glfw3.lib") using namespace std; void errorCallback(int errorCode, const char* errorDescription) { fprintf(stderr, "Error: %s\n", errorDescription); } void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GLFW_TRUE); } int main() { glfwSetErrorCallback(errorCallback); //#2 if (!glfwInit()) { cerr << "Error: GLFW 초기화 실패" << endl; exit(EXIT_FAILURE); } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //#3 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); glfwWindowHint(GLFW_SAMPLES, 4); GLFWwindow* window = glfwCreateWindow( //#4 800, // width 600, // height "OpenGL Example", // title NULL, NULL); if (!window) exit(EXIT_FAILURE); //#5 glfwMakeContextCurrent(window); glfwSetKeyCallback(window, keyCallback); //#6 glewExperimental = GL_TRUE; GLenum errorCode = glewInit(); //#7 if (GLEW_OK != errorCode) { cerr << "Error: GLEW 초기화 실패 - " << glewGetErrorString(errorCode) << endl; glfwTerminate(); exit(EXIT_FAILURE); } //#8 if (!GLEW_VERSION_3_3) { cerr << "OpenGL 3.3 API is not available." << endl; glfwTerminate(); exit(EXIT_FAILURE); } cout << "OpenGL version: " << glGetString(GL_VERSION) << endl; cout << "GLSL version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << endl; cout << "Vendor: " << glGetString(GL_VENDOR) << endl; cout << "Renderer: " << glGetString(GL_RENDERER) << endl; //#9 glfwSwapInterval(1); double lastTime = glfwGetTime(); //#11 int numOfFrames = 0; int count = 0; while (!glfwWindowShouldClose(window)) { //#10 double currentTime = glfwGetTime(); numOfFrames++; if (currentTime - lastTime >= 1.0) { printf("%f ms/frame %d fps \n", 1000.0 / double(numOfFrames), numOfFrames); numOfFrames = 0; lastTime = currentTime; } //#12 if (count % 2 == 0) glClearColor(0, 0, 1, 0); else glClearColor(1, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT); count++; glfwSwapBuffers(window); //#13 glfwPollEvents(); //#14 } glfwTerminate(); //#15 exit(EXIT_SUCCESS); } |
'OpenGL' 카테고리의 다른 글
Modern OpenGL 강좌 - 사각형 그리기(렌더링, Element Buffer Object) (0) | 2023.10.19 |
---|---|
Modern OpenGL 강좌 - 삼각형 그리기( 렌더링, Vertex Array Object, Vertex Buffer Object) (0) | 2023.10.19 |
Visual Studio 2023에 OpenGL 개발 환경 만들기 ( GLFW / GLEW ) (0) | 2023.10.19 |
Windows 환경에서 그래픽 드라이버가 지원하는 OpenGL 버전 확인하기 (0) | 2023.10.19 |
Python으로 배우는 Modern OpenGL - 3. 사각형 그리기( Element Buffer Object) (0) | 2018.12.16 |
시간날때마다 틈틈이 이것저것 해보며 블로그에 글을 남깁니다.
블로그의 문서는 종종 최신 버전으로 업데이트됩니다.
여유 시간이 날때 진행하는 거라 언제 진행될지는 알 수 없습니다.
영화,책, 생각등을 올리는 블로그도 운영하고 있습니다.
https://freewriting2024.tistory.com
제가 쓴 책도 한번 검토해보세요 ^^
그렇게 천천히 걸으면서도 그렇게 빨리 앞으로 나갈 수 있다는 건.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!