몇년에 걸쳐서 막혔던게 이렇게 쉽게 몇번의 피드백으로 주어지다니..
공부가 의미없는 시대인가.. (현타)
curl -O https://raw.githubusercontent.com/nothings/stb/master/stb_image.h wget https://m.health.chosun.com/site/data/img_dir/2025/04/08/2025040803041_0.jpg mv 2025040803041_0.jpg dog.jpg |
#include <GL/glut.h>
#include <math.h>
#include <stdio.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
// 창 크기
int width = 800;
int height = 600;
// 카메라 변수
float cam_pos_x = 5.0f, cam_pos_y = 4.0f, cam_pos_z = 5.0f;
float cam_yaw = -135.0f; // Y축 기준 회전 (좌/우)
float cam_pitch = -30.0f; // X축 기준 회전 (상/하)
float fov = 45.0f; // 시야각 (줌인/아웃용)
// 마우스 상태 변수
int last_mouse_x, last_mouse_y;
int is_panning = 0; // 휠 드래그(패닝) 상태
int is_rotating = 0; // 좌클릭 드래그(회전) 상태
// 객체 회전 변수
float obj_rot_x = 0.0f;
float obj_rot_y = 0.0f;
// 클리핑 평면 변수
float near_plane = 1.0f;
float far_plane = 100.0f;
// 텍스처 변수
GLuint texture_id;
int texture_enabled = 1; // 텍스처 활성화 상태
// 카메라 방향 벡터 계산
void update_camera_vectors(float* front_x, float* front_z, float* up_x, float* up_y, float* up_z, float* right_x, float* right_z) {
float yaw_rad = cam_yaw * M_PI / 180.0f;
float pitch_rad = cam_pitch * M_PI / 180.0f;
// 시점 방향 벡터
*front_x = cos(yaw_rad) * cos(pitch_rad);
*front_z = sin(yaw_rad) * cos(pitch_rad);
// 오른쪽 벡터 (패닝에 사용)
*right_x = cos(yaw_rad - M_PI / 2.0f);
*right_z = sin(yaw_rad - M_PI / 2.0f);
// 위쪽 벡터 (패닝에 사용)
// 간단한 구현을 위해 Y축 고정
*up_x = 0.0f;
*up_y = 1.0f;
*up_z = 0.0f;
}
void draw_scene() {
glPushMatrix();
glRotatef(obj_rot_x, 1.0f, 0.0f, 0.0f); // X축 회전
glRotatef(obj_rot_y, 0.0f, 1.0f, 0.0f); // Y축 회전
// 뚫린 상자 그리기 (6개의 면을 각각 그림)
float thickness = 0.1f;
// 앞면 (빨강)
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glTranslatef(0, 0, 1.5f);
glScalef(3.0f, 3.0f, thickness);
glutSolidCube(1.0);
glPopMatrix();
// 뒷면 (초록)
glPushMatrix();
glColor3f(0.0, 1.0, 0.0);
glRotatef(180, 0, 1, 0);
glTranslatef(0, 0, 1.5f);
glScalef(3.0f, 3.0f, thickness);
glutSolidCube(1.0);
glPopMatrix();
// 왼쪽 (파랑)
glPushMatrix();
glColor3f(0.0, 0.0, 1.0);
glRotatef(-90, 0, 1, 0);
glTranslatef(0, 0, 1.5f);
glScalef(3.0f, 3.0f, thickness);
glutSolidCube(1.0);
glPopMatrix();
// 오른쪽 (노랑)
glPushMatrix();
glColor3f(1.0, 1.0, 0.0);
glRotatef(90, 0, 1, 0);
glTranslatef(0, 0, 1.5f);
glScalef(3.0f, 3.0f, thickness);
glutSolidCube(1.0);
glPopMatrix();
// 윗면 (마젠타)
glPushMatrix();
glColor3f(1.0, 0.0, 1.0);
glRotatef(90, 1, 0, 0);
glTranslatef(0, 0, 1.5f);
glScalef(3.0f, thickness, 3.0f - thickness*2);
glutSolidCube(1.0);
glPopMatrix();
// 아랫면 (시안)
glPushMatrix();
glColor3f(0.0, 1.0, 1.0);
glRotatef(-90, 1, 0, 0);
glTranslatef(0, 0, 1.5f);
glScalef(3.0f, thickness, 3.0f - thickness*2);
glutSolidCube(1.0);
glPopMatrix();
// 텍스처가 입혀진 원통 그리기
if (texture_enabled) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture_id);
}
glColor3f(1.0, 1.0, 1.0); // 텍스처와 곱해질 색상
GLUquadric* quad = gluNewQuadric();
gluQuadricTexture(quad, GL_TRUE);
gluCylinder(quad, 0.5, 0.5, 2.0, 32, 32);
gluDeleteQuadric(quad);
if (texture_enabled) {
glDisable(GL_TEXTURE_2D);
}
glPopMatrix();
}
// 텍스트 렌더링 함수
void render_text(int x, int y, const char* string) {
glRasterPos2f(x, y);
for (const char* c = string; *c != '\0'; c++) {
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *c);
}
}
// 행렬 정보 출력 함수
void draw_matrix_info() {
GLfloat modelview[16];
GLfloat projection[16];
GLfloat rotation[16];
char buffer[128];
glGetFloatv(GL_MODELVIEW_MATRIX, modelview);
glGetFloatv(GL_PROJECTION_MATRIX, projection);
// 회전 행렬 계산
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glRotatef(obj_rot_x, 1.0f, 0.0f, 0.0f);
glRotatef(obj_rot_y, 0.0f, 1.0f, 0.0f);
glGetFloatv(GL_MODELVIEW_MATRIX, rotation);
glPopMatrix();
glColor3f(1.0, 1.0, 1.0); // 흰색으로 설정
// 2D 렌더링을 위해 투영 행렬 변경
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, width, 0, height);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
// Model-View Matrix 정보 출력
sprintf(buffer, "Model-View Matrix:");
render_text(10, height - 20, buffer);
for (int i = 0; i < 4; i++) {
sprintf(buffer, "%.2f %.2f %.2f %.2f", modelview[i*4], modelview[i*4+1], modelview[i*4+2], modelview[i*4+3]);
render_text(10, height - 35 - (i * 15), buffer);
}
// Projection Matrix 정보 출력
sprintf(buffer, "Projection Matrix:");
render_text(10, height - 100, buffer);
for (int i = 0; i < 4; i++) {
sprintf(buffer, "%.2f %.2f %.2f %.2f", projection[i*4], projection[i*4+1], projection[i*4+2], projection[i*4+3]);
render_text(10, height - 115 - (i * 15), buffer);
}
// Rotation Matrix 정보 출력
sprintf(buffer, "Rotation Matrix:");
render_text(10, height - 180, buffer);
for (int i = 0; i < 4; i++) {
sprintf(buffer, "%.2f %.2f %.2f %.2f", rotation[i*4], rotation[i*4+1], rotation[i*4+2], rotation[i*4+3]);
render_text(10, height - 195 - (i * 15), buffer);
}
// Near/Far 정보 출력
sprintf(buffer, "Near: %.2f, Far: %.2f", near_plane, far_plane);
render_text(10, height - 260, buffer);
// 원래의 투영, 모델뷰 행렬로 복원
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
void display() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
float yaw_rad = cam_yaw * M_PI / 180.0f;
float pitch_rad = cam_pitch * M_PI / 180.0f;
float eye_separation = 0.5f; // 눈 사이 간격
// 오른쪽 벡터 계산
float right_x = cos(yaw_rad - M_PI / 2.0f);
float right_z = sin(yaw_rad - M_PI / 2.0f);
// 왼쪽 눈 렌더링
glViewport(0, 0, width / 2, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fov, (double)(width/2) / (double)height, near_plane, far_plane);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
float left_cam_x = cam_pos_x - right_x * eye_separation / 2.0f;
float left_cam_z = cam_pos_z - right_z * eye_separation / 2.0f;
float look_at_x = left_cam_x + cos(yaw_rad) * cos(pitch_rad);
float look_at_y = cam_pos_y + sin(pitch_rad);
float look_at_z = left_cam_z + sin(yaw_rad) * cos(pitch_rad);
gluLookAt(left_cam_x, cam_pos_y, left_cam_z, look_at_x, look_at_y, look_at_z, 0.0, 1.0, 0.0);
draw_scene();
draw_matrix_info();
// 오른쪽 눈 렌더링
glViewport(width / 2, 0, width / 2, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fov, (double)(width/2) / (double)height, near_plane, far_plane);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
float right_cam_x = cam_pos_x + right_x * eye_separation / 2.0f;
float right_cam_z = cam_pos_z + right_z * eye_separation / 2.0f;
look_at_x = right_cam_x + cos(yaw_rad) * cos(pitch_rad);
look_at_y = cam_pos_y + sin(pitch_rad);
look_at_z = right_cam_z + sin(yaw_rad) * cos(pitch_rad);
gluLookAt(right_cam_x, cam_pos_y, right_cam_z, look_at_x, look_at_y, look_at_z, 0.0, 1.0, 0.0);
draw_scene();
draw_matrix_info();
glutSwapBuffers();
}
void reshape(int w, int h) {
width = w;
h = h > 0 ? h : 1;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fov, (double)width / (double)height, 1.0, 100.0);
}
void keyboard(unsigned char key, int x, int y) {
float speed = 0.1f;
float yaw_rad = cam_yaw * M_PI / 180.0f;
switch (key) {
case 'w':
cam_pos_x += cos(yaw_rad) * speed;
cam_pos_z += sin(yaw_rad) * speed;
break;
case 's':
cam_pos_x -= cos(yaw_rad) * speed;
cam_pos_z -= sin(yaw_rad) * speed;
break;
case 'a':
cam_pos_x += cos(yaw_rad - M_PI / 2.0f) * speed;
cam_pos_z += sin(yaw_rad - M_PI / 2.0f) * speed;
break;
case 'd':
cam_pos_x -= cos(yaw_rad - M_PI / 2.0f) * speed;
cam_pos_z -= sin(yaw_rad - M_PI / 2.0f) * speed;
break;
case 'r': // 리셋 기능
cam_pos_x = 5.0f; cam_pos_y = 4.0f; cam_pos_z = 5.0f;
cam_yaw = -135.0f;
cam_pitch = -30.0f;
fov = 45.0f;
obj_rot_x = 0.0f;
obj_rot_y = 0.0f;
near_plane = 1.0f;
far_plane = 100.0f;
reshape(width, height);
break;
case 'i': // near 증가
near_plane += 0.1f;
break;
case 'k': // near 감소
near_plane -= 0.1f;
if (near_plane < 0.1f) near_plane = 0.1f; // 0보다 작아지지 않도록
break;
case 'o': // far 증가
far_plane += 0.1f;
break;
case 'l': // far 감소
far_plane -= 0.1f;
if (far_plane < near_plane) far_plane = near_plane + 0.1f; // near보다 작아지지 않도록
break;
case 't': // 텍스처 토글
texture_enabled = !texture_enabled;
break;
}
glutPostRedisplay();
}
void specialKeys(int key, int x, int y) {
float rot_speed = 5.0f;
switch (key) {
case GLUT_KEY_UP:
obj_rot_x -= rot_speed;
break;
case GLUT_KEY_DOWN:
obj_rot_x += rot_speed;
break;
case GLUT_KEY_LEFT:
obj_rot_y -= rot_speed;
break;
case GLUT_KEY_RIGHT:
obj_rot_y += rot_speed;
break;
}
glutPostRedisplay();
}
void mouse(int button, int state, int x, int y) {
// 휠 줌 인/아웃
if (button == 3) { // 휠 업
fov -= 1.0f;
if (fov < 1.0f) fov = 1.0f;
reshape(width, height); // 투영 행렬 업데이트
} else if (button == 4) { // 휠 다운
fov += 1.0f;
if (fov > 90.0f) fov = 90.0f;
reshape(width, height); // 투영 행렬 업데이트
}
// 휠 클릭 (패닝)
if (button == GLUT_MIDDLE_BUTTON) {
if (state == GLUT_DOWN) {
is_panning = 1;
last_mouse_x = x;
last_mouse_y = y;
} else {
is_panning = 0;
}
}
// 좌클릭 (회전)
if (button == GLUT_LEFT_BUTTON) {
if (state == GLUT_DOWN) {
is_rotating = 1;
last_mouse_x = x;
last_mouse_y = y;
} else {
is_rotating = 0;
}
}
glutPostRedisplay();
}
void motion(int x, int y) {
int dx = x - last_mouse_x;
int dy = y - last_mouse_y;
last_mouse_x = x;
last_mouse_y = y;
if (is_panning) {
float pan_speed = 0.01f;
float front_x, front_z, up_x, up_y, up_z, right_x, right_z;
update_camera_vectors(&front_x, &front_z, &up_x, &up_y, &up_z, &right_x, &right_z);
cam_pos_x -= right_x * dx * pan_speed;
cam_pos_z -= right_z * dx * pan_speed;
cam_pos_y += dy * pan_speed; // Y축 방향으로 이동
} else if (is_rotating) {
float rot_speed = 0.5f;
obj_rot_y += dx * rot_speed;
obj_rot_x += dy * rot_speed;
}
glutPostRedisplay();
}
void init() {
glClearColor(0.1, 0.1, 0.1, 1.0);
glEnable(GL_DEPTH_TEST);
}
// 텍스처 로드 함수
void load_texture(const char* filename) {
int width, height, nrChannels;
unsigned char *data = stbi_load(filename, &width, &height, &nrChannels, 0);
if (data) {
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLenum format = (nrChannels == 4) ? GL_RGBA : GL_RGB;
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
} else {
printf("Failed to load texture: %s\n", filename);
stbi_image_free(data);
}
}
void print_help() {
printf("====== Key Controls ======\n");
printf(" w, a, s, d : Move Camera\n");
printf(" Arrow Keys : Rotate Object\n");
printf(" i, k : Adjust Near Plane\n");
printf(" o, l : Adjust Far Plane\n");
printf(" r : Reset Camera and Object\n");
printf(" t : Toggle Texture\n");
printf("--------------------------\n");
printf("====== Mouse Controls ======\n");
printf(" Left Drag : Rotate Object\n");
printf(" Wheel Drag : Pan Camera\n");
printf(" Wheel Scroll: Zoom (Adjust FOV)\n");
printf("==========================\n");
}
int main(int argc, char** argv) {
print_help();
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(width, height);
glutCreateWindow("OpenGL Camera Control Demo");
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutSpecialFunc(specialKeys);
glutMouseFunc(mouse);
glutMotionFunc(motion); // 마우스 버튼이 눌린 상태에서의 움직임
init();
load_texture("dog.jpg"); // 텍스처 로드
glutMainLoop();
return 0;
}
'Programming > openGL' 카테고리의 다른 글
blender in openGL (0) | 2025.04.28 |
---|---|
opengl glortho gluperspective (0) | 2023.08.28 |
glReadPixels() 와 glUseProgram() (0) | 2022.11.17 |
openCV + openGL (0) | 2022.02.08 |
glMatrixMode() (0) | 2020.04.14 |