#include <GL/glew.h> #include <GLFW/glfw3.h> #include <iostream> #include <FreeImage.h>
using namespace std;
int framebufferWidth, framebufferHeight; GLuint triangleVertexArrayObject; GLuint triangleShaderProgramID; GLuint trianglePositionVertexBufferObjectID, triangleColorVertexBufferObjectID; GLuint triangleTextureCoordinateBufferObjectID;
//CreateTexture 함수의 원본 코드 출처 //https://r3dux.org/2014/10/how-to-load-an-opengl-texture-using-the-freeimage-library-or-freeimageplus-technically/ // // Method to load an image into a texture using the freeimageplus library. Returns the texture ID or dies trying. GLuint CreateTexture(char const* filename) { // Determine the format of the image. // Note: The second paramter ('size') is currently unused, and we should use 0 for it. FREE_IMAGE_FORMAT format = FreeImage_GetFileType(filename, 0);
// Image not found? Abort! Without this section we get a 0 by 0 image with 0 bits-per-pixel but we don't abort, which // you might find preferable to dumping the user back to the desktop. if (format == -1) { cout << "Could not find image: " << filename << " - Aborting." << endl; exit(-1); }
// Found image, but couldn't determine the file format? Try again... if (format == FIF_UNKNOWN) { cout << "Couldn't determine file format - attempting to get from file extension..." << endl;
// ...by getting the filetype from the filename extension (i.e. .PNG, .GIF etc.) // Note: This is slower and more error-prone that getting it from the file itself, // also, we can't use the 'U' (unicode) variant of this method as that's Windows only. format = FreeImage_GetFIFFromFilename(filename);
// Check that the plugin has reading capabilities for this format (if it's FIF_UNKNOWN, // for example, then it won't have) - if we can't read the file, then we bail out =( if (!FreeImage_FIFSupportsReading(format)) { cout << "Detected image format cannot be read!" << endl; exit(-1); } }
// If we're here we have a known image format, so load the image into a bitap FIBITMAP* bitmap = FreeImage_Load(format, filename);
// How many bits-per-pixel is the source image? int bitsPerPixel = FreeImage_GetBPP(bitmap);
// Convert our image up to 32 bits (8 bits per channel, Red/Green/Blue/Alpha) - // but only if the image is not already 32 bits (i.e. 8 bits per channel). // Note: ConvertTo32Bits returns a CLONE of the image data - so if we // allocate this back to itself without using our bitmap32 intermediate // we will LEAK the original bitmap data, and valgrind will show things like this: // // LEAK SUMMARY: // definitely lost: 24 bytes in 2 blocks // indirectly lost: 1,024,874 bytes in 14 blocks <--- Ouch. // // Using our intermediate and cleaning up the initial bitmap data we get: // // LEAK SUMMARY: // definitely lost: 16 bytes in 1 blocks // indirectly lost: 176 bytes in 4 blocks // // All above leaks (192 bytes) are caused by XGetDefault (in /usr/lib/libX11.so.6.3.0) - we have no control over this. // FIBITMAP* bitmap32; if (bitsPerPixel == 32) { cout << "Source image has " << bitsPerPixel << " bits per pixel. Skipping conversion." << endl; bitmap32 = bitmap; } else { cout << "Source image has " << bitsPerPixel << " bits per pixel. Converting to 32-bit colour." << endl; bitmap32 = FreeImage_ConvertTo32Bits(bitmap); }
// Some basic image info - strip it out if you don't care int imageWidth = FreeImage_GetWidth(bitmap32); int imageHeight = FreeImage_GetHeight(bitmap32); cout << "Image: " << filename << " is size: " << imageWidth << "x" << imageHeight << "." << endl;
// Get a pointer to the texture data as an array of unsigned bytes. // Note: At this point bitmap32 ALWAYS holds a 32-bit colour version of our image - so we get our data from that. // Also, we don't need to delete or delete[] this textureData because it's not on the heap (so attempting to do // so will cause a crash) - just let it go out of scope and the memory will be returned to the stack. GLubyte* textureData = FreeImage_GetBits(bitmap32);
// Generate a texture ID and bind to it GLuint tempTextureID; glGenTextures(1, &tempTextureID); glBindTexture(GL_TEXTURE_2D, tempTextureID);
// Construct the texture. // Note: The 'Data format' is the format of the image data as provided by the image library. FreeImage decodes images into // BGR/BGRA format, but we want to work with it in the more common RGBA format, so we specify the 'Internal format' as such. glTexImage2D(GL_TEXTURE_2D, // Type of texture 0, // Mipmap level (0 being the top level i.e. full size) GL_RGBA, // Internal format imageWidth, // Width of the texture imageHeight, // Height of the texture, 0, // Border in pixels GL_BGRA, // Data format GL_UNSIGNED_BYTE, // Type of texture data textureData); // The image data to use for this texture
// Specify our minification and magnification filters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, 0);
// Check for OpenGL texture creation errors GLenum glError = glGetError(); if (glError) { cout << "There was an error loading the texture: " << filename << endl;
switch (glError) { case GL_INVALID_ENUM: cout << "Invalid enum." << endl; break;
case GL_INVALID_VALUE: cout << "Invalid value." << endl; break;
case GL_INVALID_OPERATION: cout << "Invalid operation." << endl;
default: cout << "Unrecognised GLenum." << endl; break; }
cout << "See https://www.opengl.org/sdk/docs/man/html/glTexImage2D.xhtml for further details." << endl; }
// Unload the 32-bit colour bitmap FreeImage_Unload(bitmap32);
// If we had to do a conversion to 32-bit colour, then unload the original // non-32-bit-colour version of the image data too. Otherwise, bitmap32 and // bitmap point at the same data, and that data's already been free'd, so // don't attempt to free it again! (or we'll crash). if (bitsPerPixel != 32) { FreeImage_Unload(bitmap); }
// Finally, return the texture ID return tempTextureID; }
bool initShaderProgram() {
//#3 const GLchar* vertexShaderSource = "#version 330 core\n" "in vec3 positionAttribute;" //"in vec3 colorAttribute;" "in vec2 textureCoordinateAttribute;" //"out vec3 passColorAttribute;" "out vec2 passTextureCoordinateAttribute;" "void main()" "{" "gl_Position = vec4(positionAttribute, 1.0);" //"passColorAttribute = colorAttribute;" "passTextureCoordinateAttribute = textureCoordinateAttribute;" "}";
//#4 const GLchar* fragmentShaderSource = "#version 330 core\n" //"in vec3 passColorAttribute;" "in vec2 passTextureCoordinateAttribute;" "out vec4 fragmentColor;" "uniform sampler2D tex;" "void main()" "{" //컬러만 출력 //"fragmentColor = vec4(passColorAttribute, 1.0);" //텍스처만 출력 "fragmentColor = texture(tex, passTextureCoordinateAttribute);" //텍스처와 컬러 같이 출력 //"fragmentColor = texture(tex, passTextureCoordinateAttribute)*vec4(passColorAttribute, 1.0); " "}";
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader);
GLint result; GLchar errorLog[512]; glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &result); if (!result) { glGetShaderInfoLog(vertexShader, 512, NULL, errorLog); cerr << "ERROR: vertex shader 컴파일 실패\n" << errorLog << endl; glDeleteShader(vertexShader); return false; }
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &result); if (!result) { glGetShaderInfoLog(fragmentShader, 512, NULL, errorLog); cerr << "ERROR: fragment shader 컴파일 실패\n" << errorLog << endl;
return false; }
//#5 triangleShaderProgramID = glCreateProgram();
glAttachShader(triangleShaderProgramID, vertexShader); glAttachShader(triangleShaderProgramID, fragmentShader);
glLinkProgram(triangleShaderProgramID);
glDeleteShader(vertexShader); glDeleteShader(fragmentShader);
glGetProgramiv(triangleShaderProgramID, GL_LINK_STATUS, &result); if (!result) { glGetProgramInfoLog(triangleShaderProgramID, 512, NULL, errorLog); cerr << "ERROR: shader program 연결 실패\n" << errorLog << endl; return false; }
return true; }
bool defineVertexArrayObject() {
//#1 //삼각형을 구성하는 vertex 데이터 - position과 color float position[] = { 0.0f, 0.5f, 0.0f, //vertex 1 위 중앙 0.5f, -0.5f, 0.0f, //vertex 2 오른쪽 아래 -0.5f, -0.5f, 0.0f //vertex 3 왼쪽 아래 };
float color[] = { 1.0f, 0.0f, 0.0f, //vertex 1 : RED (1,0,0) 0.0f, 1.0f, 0.0f, //vertex 2 : GREEN (0,1,0) 0.0f, 0.0f, 1.0f //vertex 3 : BLUE (0,0,1) };
float textureCoordinate[] = { 0.5f, 1.0f, //vertex 1 1.0f, 0.0f, //vertex 2 0.0f, 0.0f //vertex 3 };
//#2 //Vertex Buffer Object(VBO)를 생성하여 vertex 데이터를 복사한다. glGenBuffers(1, &trianglePositionVertexBufferObjectID); glBindBuffer(GL_ARRAY_BUFFER, trianglePositionVertexBufferObjectID); glBufferData(GL_ARRAY_BUFFER, sizeof(position), position, GL_STATIC_DRAW);
glGenBuffers(1, &triangleColorVertexBufferObjectID); glBindBuffer(GL_ARRAY_BUFFER, triangleColorVertexBufferObjectID); glBufferData(GL_ARRAY_BUFFER, sizeof(color), color, GL_STATIC_DRAW);
glGenBuffers(1, &triangleTextureCoordinateBufferObjectID); glBindBuffer(GL_ARRAY_BUFFER, triangleTextureCoordinateBufferObjectID); glBufferData(GL_ARRAY_BUFFER, sizeof(textureCoordinate), textureCoordinate, GL_STATIC_DRAW);
//#6 glGenVertexArrays(1, &triangleVertexArrayObject); glBindVertexArray(triangleVertexArrayObject);
GLint positionAttribute = glGetAttribLocation(triangleShaderProgramID, "positionAttribute"); if (positionAttribute == -1) { cerr << "position 속성 설정 실패" << endl; return false; } glBindBuffer(GL_ARRAY_BUFFER, trianglePositionVertexBufferObjectID); glVertexAttribPointer(positionAttribute, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(positionAttribute);
/* GLint colorAttribute = glGetAttribLocation(triangleShaderProgramID, "colorAttribute"); if (colorAttribute == -1) { cerr << "color 속성 설정 실패" << endl; return false; } glBindBuffer(GL_ARRAY_BUFFER, triangleColorVertexBufferObjectID); glVertexAttribPointer(colorAttribute, 3, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(colorAttribute); */
GLint textureCoordinateAttribute = glGetAttribLocation(triangleShaderProgramID, "textureCoordinateAttribute"); if (textureCoordinateAttribute == -1) { cerr << "Texture Coordinate 속성 설정 실패" << endl; return false; } glBindBuffer(GL_ARRAY_BUFFER, triangleTextureCoordinateBufferObjectID); glVertexAttribPointer(textureCoordinateAttribute, 2, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(textureCoordinateAttribute);
glBindVertexArray(0);
return true; }
void framebufferSizeCallback(GLFWwindow* window, int width, int height) { //처음 2개의 파라미터는 viewport rectangle의 왼쪽 아래 좌표 //다음 2개의 파라미터는 viewport의 너비와 높이이다. //framebuffer의 width와 height를 가져와 glViewport에서 사용한다. glViewport(0, 0, width, height);
framebufferWidth = width; framebufferHeight = height; }
void errorCallback(int errorCode, const char* errorDescription) { cerr << "Error: " << errorDescription << endl; }
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);
if (!glfwInit()) {
cerr << "Error: GLFW 초기화 실패" << endl; std::exit(EXIT_FAILURE); }
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 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( 800, 600, "OpenGL Example", NULL, NULL); if (!window) {
glfwTerminate(); std::exit(EXIT_FAILURE); }
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, keyCallback); glfwSetFramebufferSizeCallback(window, framebufferSizeCallback);
glewExperimental = GL_TRUE; GLenum errorCode = glewInit(); if (GLEW_OK != errorCode) {
cerr << "Error: GLEW 초기화 실패 - " << glewGetErrorString(errorCode) << endl;
glfwTerminate(); std::exit(EXIT_FAILURE); }
if (!GLEW_VERSION_3_3) {
cerr << "Error: OpenGL 3.3 API is not available." << endl;
glfwTerminate(); std::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;
if (!initShaderProgram()) {
cerr << "Error: Shader Program 생성 실패" << endl;
glfwTerminate(); std::exit(EXIT_FAILURE); }
if (!defineVertexArrayObject()) {
cerr << "Error: Shader Program 생성 실패" << endl;
glfwTerminate(); std::exit(EXIT_FAILURE); }
glfwSwapInterval(1);
double lastTime = glfwGetTime(); int numOfFrames = 0; int count = 0;
glUseProgram(triangleShaderProgramID); glBindVertexArray(triangleVertexArrayObject);
GLuint texureId = CreateTexture("number-board.jpg");
glUniform1i(glGetUniformLocation(triangleShaderProgramID, "tex"), 0); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texureId);
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; }
glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);
count++;
glfwSwapBuffers(window); glfwPollEvents();
}
glUseProgram(0); glBindVertexArray(0);
glDeleteProgram(triangleShaderProgramID); glDeleteBuffers(1, &trianglePositionVertexBufferObjectID); glDeleteBuffers(1, &triangleColorVertexBufferObjectID); glDeleteBuffers(1, &triangleTextureCoordinateBufferObjectID); glDeleteVertexArrays(1, &triangleVertexArrayObject); glfwTerminate();
std::exit(EXIT_SUCCESS); } |