OpenGL & GLSL训练(六)

目录

前言

一、在练习5的基础上,实现放大镜功能,放大场景中的任意部分

1.简单修改FOV,放大场景

2.读取鼠标周围的像素数据,并作为贴图贴在一个圆上

3.使用Render To Texture

总结


Exercise 6:放大镜功能


前言

第六次作业感觉是目前为止最难的了,可能也是九次作业中最难的(如果后面有更难的当我没说)


 

一、在练习5的基础上,实现放大镜功能,放大场景中的任意部分

对于如何实现放大镜效果,我从开始到解决一共使用了三种方法,显然前两种并不正确,或并不是最好的方法,但我也在这里一并记录下来

1.简单修改FOV,放大场景

修改FOV,将FOV减小,理论上就和将广角镜头改为长焦镜头一样,可以让视角更窄,看得更远,这种方法用来作为望远镜效果或许不错,但并不能做到放大场景中的任意部分一点

2.读取鼠标周围的像素数据,并作为贴图贴在一个圆上

读取像素数据的代码如下

void ScreenShot()
{
	magnifier.pView[0] = magnifier.mouseX -magnifier.scale;
	magnifier.pView[1] = Height-magnifier.mouseY - magnifier.scale;
	magnifier.pView[2] = magnifier.scale*2;
	magnifier.pView[3] = magnifier.scale*2;
	GLsizei numComponet = 3;
	GLsizei bufferSize = magnifier.pView[2] * magnifier.pView[3] * sizeof(GLubyte)*numComponet;

	magnifier.scaleData = new GLubyte[bufferSize];
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);//设置4字节对齐

	glReadPixels(magnifier.pView[0], magnifier.pView[1], magnifier.pView[2], magnifier.pView[3], GL_RGB, GL_UNSIGNED_BYTE, magnifier.scaleData);
}

 

以及获取鼠标在世界空间对应位置的代码

void GetWorldPos()
{
	GLint viewport[4];
	GLdouble modelview[16];
	GLdouble projection[16];
	GLfloat winX, winY, winZ;
	GLdouble object_x, object_y, object_z;
	glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
	glGetDoublev(GL_PROJECTION_MATRIX, projection);
	glGetIntegerv(GL_VIEWPORT, viewport);

	winX = (float)magnifier.mouseX;
	winY = (float)viewport[3] - (float)magnifier.mouseY;
	glReadPixels(magnifier.mouseX, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
	winZ = 0.96;            //这里我并没有搞明白,但不将winZ的值修改,得不到正确的鼠标对应位置
	gluUnProject((GLdouble)magnifier.mouseX, (GLdouble)winY, (GLdouble)winZ, modelview, projection, viewport, &object_x, &object_y, &object_z);
	magnifier.worldX = object_x;
	magnifier.worldY = object_y;
}

最终实现了可以在任意位置放大场景,但弊端是在图片放大一定大小后就会糊掉,并不是实现场景放大的最优解

3.使用Render To Texture

这里是采用了Render To Texture的方法,在另一个FrameBuffer上再次渲染场景,并修改对应的FOV和方向,并存储为纹理,以得到更好的放大镜效果

#include<gl/glew.h>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<GL/freeglut.h>

int mainWindow;
int subWindow;
int imageheight;
int imagewidth;
int pixellength;
GLubyte*  pixeldata;
GLint Height = 600;
GLint Width = 800;
GLfloat LightPosition0[4] = { 0,0,0,-1 };

int oldX,oldY;
float rotateX, rotateY;
float fovy = 45;
bool mouseScale = false;
bool rotate = false;
struct Magnifier
{
	int mouseX, mouseY;
	float worldX, worldY;
	float radius = 2;
	float scale = 35;
};
struct camer {
	float eyePos[3] = { 0,0,20 };
	float lookAt[3] = { 0,0,0 };
	float upDir[3] = { 0,1,0 };
};
Magnifier magnifier;
camer ca;
GLfloat speed = 0;
float aspect;
void Lighting();
GLuint framebuffer;
GLuint textureColorbuffer;
GLuint rbo;
float offsetz;
void GetWorldPos();
void init()
{
	glClearColor(0.5, 0.5, 0.5, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST);			//开启深度测试
	Lighting();
}
void initFrameBuffer()
{
	glGenRenderbuffers(1, &rbo);
	glGenFramebuffers(1, &framebuffer);
	glGenTextures(1, &textureColorbuffer);
	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
	init();
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void DrawScene(bool scale)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(ca.eyePos[0], ca.eyePos[1], ca.eyePos[2], ca.lookAt[0], ca.lookAt[1], ca.lookAt[2], ca.upDir[0], ca.upDir[1], ca.upDir[2]);
	glViewport(0, 0, Width, Height);
	//进行投影变换前调用下面两个函数,接下来的变换函数将影响投影矩阵
	//在窗口改变函数reshape中先用glMatrixMode(GL_PROJECTION)定义视锥,再用glMatrixMode(GL_MODELVIEW)改为模型变换
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(fovy, aspect, 1.0f, 100.0f);//
	glViewport(0, 0, Width, Height);

	glMatrixMode(GL_MODELVIEW);
	if(scale)
	{

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		//修改fov
		gluPerspective(fovy-magnifier.scale, aspect, 1.0f, 100.0f);

		glMatrixMode(GL_MODELVIEW);
		//修改观测图像的位置,以得到正确的放大结果
		glTranslatef(-magnifier.worldX, -magnifier.worldY, 0);

	}
	else if(!scale&&mouseScale)
	{
		GetWorldPos();
	}
	glEnable(GL_LIGHTING);
	glLightfv(GL_LIGHT0, GL_POSITION, LightPosition0);
	glPushMatrix();
	glRotatef(rotateX, 0, 1, 0);
	glRotatef(rotateY, 1, 0, 0);
	//glTranslated(0, 0, 3);
	glPushMatrix();
	{
		GLfloat ambient[] = { 0,0.52,0,1 };
		glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
		GLfloat diffuse[] = { 0.86,0.54,0,1 };
		glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
		glTranslated(0, 0, -13);
		glutSolidTeapot(4.5);
	}
	glPopMatrix();
	glPushMatrix();
	{
		GLfloat ambient[] = { 0.12,0,0,1 };
		glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
		GLfloat diffuse[] = { 0.81,0,0,1 };
		glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
		glTranslated(0, 0, -6);
		glutSolidTeapot(3);
	}
	glPopMatrix();
	glPushMatrix();
	{
		GLfloat ambient[] = { 0.56,0.23,0,1 };
		glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
		GLfloat diffuse[] = { 0.54,0.13,0.32,0.5 };
		glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
		glEnable(GL_BLEND);
		glDepthMask(GL_FALSE);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glTranslated(0, 0, -1);
		glutSolidTeapot(2);
		glDepthMask(GL_TRUE);
		glDisable(GL_BLEND);
	}
	glPopMatrix();
	glPushMatrix();
	{
		GLfloat ambient[] = { 0.2,0.63,0.5,1 };
		glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
		GLfloat diffuse[] = { 0.57,0.83,0.62,0.2 };
		glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
		glEnable(GL_BLEND);
		glDepthMask(GL_FALSE);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glTranslated(0, 0, 2);
		glutSolidTeapot(1);
		glDepthMask(GL_TRUE);
		glDisable(GL_BLEND);
	}
	glPopMatrix();
	glPushMatrix();
	{
		GLfloat ambient[] = { 0.2,0.3,0.2,1 };
		glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
		GLfloat diffuse[] = { 1,1,1,0.35 };
		glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
		glEnable(GL_BLEND);
		glDepthMask(GL_FALSE);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glTranslated(0, 0, 0.5);
		glutSolidTeapot(1.2);
		glDepthMask(GL_TRUE);
		glDisable(GL_BLEND);
	}
	glPopMatrix();
	glPopMatrix();
}

void display()
{
	DrawScene(false);
	if (mouseScale)
	{
		glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
		// 创建一个颜色附加纹理
		glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, Width, Height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0);
		// 为深度和模板附件创建一个renderbuffer对象
		glBindRenderbuffer(GL_RENDERBUFFER, rbo);
		glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, Width, Height); // 深度和模板缓冲区使用一个renderbuffer对象。
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);

		
		DrawScene(true);

		glBindFramebuffer(GL_FRAMEBUFFER, 0);
		DrawScene(false);
		glDisable(GL_LIGHTING);
		glDisable(GL_DEPTH_TEST);
		glDepthMask(GL_FALSE);

		glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
		glEnable(GL_TEXTURE_2D);
		glBegin(GL_TRIANGLE_FAN);
		glTexCoord2f(0.5, 0.5);
		glVertex2f(magnifier.worldX, magnifier.worldY);
		for (double i = 0; i <= 50; i++)
		{
			glTexCoord2f(0.5 + 0.5*cos(i*2 / 50.0 * 3.1415926), 0.5 + 0.5*sin(i*2 / 50.0 * 3.1415926));
			glVertex2f(magnifier.worldX + magnifier.radius*cos(i*2 / 50.0 * 3.1415926), magnifier.worldY+ magnifier.radius*sin(i*2 / 50.0 * 3.1415926));
		}
		glEnd();

		glDepthMask(GL_TRUE);
		glEnable(GL_DEPTH_TEST);
		glDisable(GL_TEXTURE_2D);
		glEnable(GL_LIGHTING);
	}

	glutSwapBuffers();
}

void GetWorldPos()
{
	GLint viewport[4];
	GLdouble modelview[16];
	GLdouble projection[16];
	GLfloat winX, winY, winZ;
	GLdouble object_x, object_y, object_z;
	glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
	glGetDoublev(GL_PROJECTION_MATRIX, projection);
	glGetIntegerv(GL_VIEWPORT, viewport);

	winX = (float)magnifier.mouseX;
	winY = (float)viewport[3] - (float)magnifier.mouseY;
	glReadPixels(magnifier.mouseX, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
	winZ = 0.96;
	gluUnProject((GLdouble)magnifier.mouseX, (GLdouble)winY, (GLdouble)winZ, modelview, projection, viewport, &object_x, &object_y, &object_z);
	magnifier.worldX = object_x;
	magnifier.worldY = object_y;
}

void Lighting()
{
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	GLfloat AmbientLight0[4] = { 1.0, 1.0, 1.0, 1.0 };
	glLightfv(GL_LIGHT0, GL_AMBIENT, AmbientLight0);
	GLfloat DiffuseLight0[4] = { 1.0, 1.0, 1.0, 1.0 };
	glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight0);
	GLfloat SpecularLight0[4] = { 1.0,1.0,1.0,1.0 };
	glLightfv(GL_LIGHT0, GL_SPECULAR, SpecularLight0);

	//glEnable(GL_LIGHT1);
	GLfloat AmbientLight1[4] = { 0.0, 1.0, 0.0, 1.0 };
	glLightfv(GL_LIGHT1, GL_AMBIENT, AmbientLight1);
	GLfloat DiffuseLight1[4] = { 0.0, 1.0, 0.0, 1.0 };
	glLightfv(GL_LIGHT1, GL_DIFFUSE, DiffuseLight1);
	GLfloat SpecularLight1[4] = { 0.0,1.0,0.0,1.0 };
	glLightfv(GL_LIGHT1, GL_SPECULAR, SpecularLight1);


	//glEnable(GL_LIGHT2);
	GLfloat AmbientLight2[4] = { 1.0, 0.0, 0.0, 1.0 };
	glLightfv(GL_LIGHT2, GL_AMBIENT, AmbientLight2);
	GLfloat DiffuseLight2[4] = { 1.0, 0.0, 0.0, 1.0 };
	glLightfv(GL_LIGHT2, GL_DIFFUSE, DiffuseLight2);
	GLfloat SpecularLight2[4] = { 1.0,0.0,0.0,1.0 };
	glLightfv(GL_LIGHT2, GL_SPECULAR, SpecularLight2);

}
void Idle()
{
	glutPostRedisplay();
}
void reshape(int w, int h)
{
	aspect = (float)w / ((h) ? h : 1);//平截头体的纵横比,也就是宽度除以高度,(h)?h:1意思是若h=0,则h=1
	Height = h;
	Width = w;
	glViewport(0, 0, w, h);
	//进行投影变换前调用下面两个函数,接下来的变换函数将影响投影矩阵
	//在窗口改变函数reshape中先用glMatrixMode(GL_PROJECTION)定义视锥,再用glMatrixMode(GL_MODELVIEW)改为模型变换
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(fovy, aspect, 1.0f, 100.0f);//
	glViewport(0, 0, w, h);

	glMatrixMode(GL_MODELVIEW);
}
void MouseFunc(int button,int state,int x,int y)
{
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{
		rotate = true;
		oldX = x;
		oldY = y;
	}
	else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
	{
		rotate = false;
	}
	else if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
	{
		magnifier.mouseX = x;
		magnifier.mouseY = y;
		mouseScale = !mouseScale;
	}
}
void MouseWheel(int button, int dir, int x, int y)
{
	if (dir > 0&&magnifier.scale>10)
	{
		magnifier.scale -=2;
	}
	else if (dir < 0&&magnifier.scale<40)
	{
		magnifier.scale += 2;
	}
}
void MouseMove(int x, int y)
{
	if (rotate)
	{
		rotateX += x - oldX;
		rotateY += y - oldY;
		oldX = x;
		oldY = y;
	}
	if (mouseScale)
	{
		magnifier.mouseX = x;
		magnifier.mouseY = y;
	}
}
void MousePassiveMove(int x, int y)
{
	
	if (mouseScale)
	{
		magnifier.mouseX = x;
		magnifier.mouseY = y;
	}
}
void KeyBoard(unsigned char button, int x,int y)
{

}
int main(int argc, char** argv)
{


	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(Width, Height);
	mainWindow = glutCreateWindow("Test");
	GLenum err = glewInit(); //如果使用FBO的话,此句不能缺少
	init();
	initFrameBuffer();
	glutDisplayFunc(display);            //窗口绘制
	glutIdleFunc(Idle);
	glutReshapeFunc(reshape);
	glutMouseFunc(MouseFunc);
	glutMouseWheelFunc(MouseWheel);
	glutMotionFunc(MouseMove);
	glutKeyboardFunc(KeyBoard);
	glutPassiveMotionFunc(MousePassiveMove);

	glutMainLoop();

	return 0;
}

在我总结了以下几点遇到的问题和我自己的理解:

1.OpenGL的默认渲染Framebuffer编号为0,且只有0号Framebuffer可以渲染在屏幕上,如果调用glBindFramebuffer()修改渲染的Framebuffer时渲染效果是不会显示在屏幕上的,即离屏渲染

2.OpenGL支持生成的纹理总数是有限的,约为2400个,若生成的纹理总数超出,则后续贴图将失效(我不小心放在display函数里踩到的坑)

3.在另一个Framebuffer里渲染场景时,仅靠FOV和物体位移是不够的,必须还要有旋转去调整观察的方向,才能得到指哪看哪的效果,如果不进行旋转的修正,则得到的效果非常奇怪,如下

需补充加入如下的旋转修正

	glRotated(atan(-magnifier.worldY / ca.eyePos[2]) * 180 / 3.14159, 1, 0, 0);
	glRotated(atan(magnifier.worldX/ ca.eyePos[2])*180/3.14159, 0, 1, 0);

总结

这次作业得到的效果如下图,有非常好的放大镜效果

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值