Programming/openGL2025. 7. 16. 16:54

몇년에 걸쳐서 막혔던게 이렇게 쉽게 몇번의 피드백으로 주어지다니..

공부가 의미없는 시대인가.. (현타)

 

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
Posted by 구차니