OpenGL蓝宝书第五章代码勘误以及惯性坐标系去解释模型变换:Pyramid.cpp

OpenGL蓝宝书第五章代码勘误以及惯性坐标系去解释模型变换:Pyramid.cpp

大家好,又见面了,我是全栈君。

假设你也发现依照教程代码完毕贴图时,你会底面的坐标和寻常顶点坐标正负相反,比方-1.0f, -1.0f, -1.0f这个顶点相应的却是世界坐标中1.0f,-1.0f,1.0f

问题到底出如今哪里?

原来是:objectFrame.GetCameraMatrix(mObjectFrame); //原书中的代码为GetMatrix。获取了objectFrame的朝向。导致顶点和纹理的对象关系出现了相反内容

objectFrame中的朝向和OpenGL的默认朝向相反,getMatrix的操作会导致modelViewMatrix在无形中运行了一次180°旋转。鉴于此,我们应该仅仅须要获得其camera矩阵就可以。这个camera矩阵记录了当前模型的旋转、缩放和位移。

modelViewMatrix乘上这个矩阵的结果就是世界原点到模型各顶点的作用矩阵。

这里引出一个我差点忘记的概念:惯性坐标。

世界坐标系的原点通过变换矩阵,可定位了模型的位移、旋转(包括朝向和定义惯性正方向)、缩放状态(这个事实上对世界顶点变换影响不大。仅仅是模型对于自身的惯性相对点影响比較大)。

实际渲染绘制流程是:世界原点,移动画刷(这个概念非常重要),旋转画刷,依据缩放绘制模型各顶点。模型各顶点的位置是相对于画刷的。画刷事实上在很多其它情况下命名为模型的惯性坐标系。从世界坐标系到画刷(模型惯性坐标系)则是模型变换矩阵。模型顶点的绘制则由模型编辑器或绘制算法定义。

摄像机则影响了终于可视区域。

(有些情况下取用了相机的变换矩阵,模型惯性坐标系的原点是摄像机的当前所在位置)。

假设代码中使用了

//modelViewMatrix.Translate(0.0f, -1.0f, -7.0f);

这样类似的操作。而不是从camera中获取相对位移矩阵。那么这个模型的惯性坐标系的原点和世界坐标空间的原点,只是是运行了移动是以于自身的为原点。关注点之中的一个:惯性坐标系(画刷)的默认朝向正方向是和OpenGL的世界坐标系一样使用的是右手坐标系。

所以,-7也是向屏幕里面移动。

由此,纹理贴图的st相应关系围绕就清楚的和2D像素点操作明显差别开来。

OpenGL的st从左下角为原点,而内存中的像素操作是从左上角为原点。

此外还遇到一个问题,使用了release的模式库,导致无法正常调试。比方代码优化导致无法断点和查看到正确的数据。仅仅能切换回debug模式。

M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.MultMatrix(mCamera);//仅仅是获取cameraFrame中记录的位移。没有获得朝向和旋转

#include <GLTools.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#include <GLFrustum.h>
#include <GLFrame.h>
#include <GLShaderManager.h>
#include <iostream>

#include <math.h>

#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

GLMatrixStack mvpMatrix;
GLMatrixStack projectionMatrix;
GLMatrixStack modelViewMatrix;

GLFrustum viewFrustum;
GLGeometryTransform transformPipeLine;

GLFrame cameraFrame;
GLFrame objectFrame;

GLBatch pyramidBatch;
GLuint textureID;
GLShaderManager shaderManager;

void ChangeSize(int w, int h)
{
	if (h <= 0)
	{
		h = 1;
	}

	glViewport(0, 0, w, h);
	viewFrustum.SetPerspective(35, float(w) / float(h), 1.0f, 1000.0f);
	projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
	transformPipeLine.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}

// Load a TGA as a 2D Texture. Completely initialize the state
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
	GLbyte *pBits;
	int nWidth, nHeight, nComponents;
	GLenum eFormat;

	// Read the texture bits
	pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
	if (pBits == NULL)
		return false;

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);

	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0,
		eFormat, GL_UNSIGNED_BYTE, pBits);

	free(pBits);

	if (minFilter == GL_LINEAR_MIPMAP_LINEAR ||
		minFilter == GL_LINEAR_MIPMAP_NEAREST ||
		minFilter == GL_NEAREST_MIPMAP_LINEAR ||
		minFilter == GL_NEAREST_MIPMAP_NEAREST)
		glGenerateMipmap(GL_TEXTURE_2D);

	return true;
}

void MakePyramid(GLBatch & pyramidBatch)
{
	//每三个点画一个三角形
	pyramidBatch.Begin(GL_TRIANGLES, 18, 1);

	//原点在金字塔的几何中心(总之就是非重点。比中心高的的位置。高度的1/2)

	//底部,初始法线是向下,纹理映射的顶点须要查看原始纹理对比一下?? //todo
	//逆着法线看的话。以下三个点的围绕是逆时针的
	pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
	pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
	pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);

	pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
	pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
	pyramidBatch.Vertex3f(1.0f, -1.0f, -1.0f);

	pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
	pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
	pyramidBatch.Vertex3f(1.0f, -1.0f, 1.0f);

	//逆着法线看的话,以下三个点的围绕是逆时针的
	pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
	pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
	pyramidBatch.Vertex3f(-1.0f, -1.0f, 1.0f);

	pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
	pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
	pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);

	pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
	pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
	pyramidBatch.Vertex3f(1.0f, -1.0f, 1.0f);

	
	//原先作者代码中的front和back的方向不是相对于观察者所得到的视野
	//“前”这个面居然是金字塔的背对着我们的面,即第一视角看不到这个面
	//感觉这坐标居然是

	M3DVector3f vApex = { 0.0f, 1.0f, 0.0f };
	M3DVector3f vFrontLeft = { -1.0f, -1.0f, 1.0f };
	M3DVector3f vFrontRight = { 1.0f, -1.0f, 1.0f };
	M3DVector3f vBackLeft = { -1.0f, -1.0f, -1.0f };
	M3DVector3f vBackRight = { 1.0f, -1.0f, -1.0f };


	M3DVector3f n;

	//求法线,前
	m3dFindNormal(n, vApex, vFrontLeft, vFrontRight);
	pyramidBatch.Normal3fv(n);
	pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
	pyramidBatch.Vertex3fv(vApex);

	pyramidBatch.Normal3fv(n);
	pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
	pyramidBatch.Vertex3fv(vFrontLeft);

	pyramidBatch.Normal3fv(n);
	pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
	pyramidBatch.Vertex3fv(vFrontRight);


	////求法线。左
	//m3dFindNormal(n, vApex, vBackLeft, vFrontLeft);
	//pyramidBatch.Normal3fv(n);
	//pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
	//pyramidBatch.Vertex3fv(vApex);

	//pyramidBatch.Normal3fv(n);
	//pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
	//pyramidBatch.Vertex3fv(vBackLeft);

	//pyramidBatch.Normal3fv(n);
	//pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
	//pyramidBatch.Vertex3fv(vFrontLeft);


	////求法线,右
	//m3dFindNormal(n, vApex, vFrontRight, vBackRight);
	//pyramidBatch.Normal3fv(n);
	//pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
	//pyramidBatch.Vertex3fv(vApex);

	//pyramidBatch.Normal3fv(n);
	//pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
	//pyramidBatch.Vertex3fv(vFrontRight);

	//pyramidBatch.Normal3fv(n);
	//pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
	//pyramidBatch.Vertex3fv(vBackRight);


	////求法线。后
	//m3dFindNormal(n, vApex, vBackRight, vBackLeft);
	//pyramidBatch.Normal3fv(n);
	//pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
	//pyramidBatch.Vertex3fv(vApex);

	//pyramidBatch.Normal3fv(n);
	//pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
	//pyramidBatch.Vertex3fv(vBackRight);

	//pyramidBatch.Normal3fv(n);
	//pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
	//pyramidBatch.Vertex3fv(vBackLeft);

	pyramidBatch.End();
}

void SetupRC(void)
{
	glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
	
	shaderManager.InitializeStockShaders();

	glEnable(GL_DEPTH_TEST);

	glGenTextures(1, &textureID);
	glBindTexture(GL_TEXTURE_2D, textureID);
	LoadTGATexture("stone.tga", GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
	MakePyramid(pyramidBatch);

	cameraFrame.MoveForward(-7.0f);//??什么用意??
}

void RenderScene(void)
{
	static GLfloat vLightPos[] = { 1.0f, 1.0f, 0.f };
	static GLfloat vWhite[] = { 1.0f, 1.0f, 1.0f, 1.0f };

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	modelViewMatrix.PushMatrix();
	//{
		//处理画笔位置相关,即模型相对于摄像机的位置

		M3DMatrix44f mCamera;
		cameraFrame.GetCameraMatrix(mCamera);
		modelViewMatrix.MultMatrix(mCamera);//仅仅是获取cameraFrame中记录的位移,没有获得朝向和旋转
		//modelViewMatrix.Translate(0.0f, -1.0f, -7.0f);


		//处理模型自身的旋转或缩放
		M3DMatrix44f mObjectFrame;
		objectFrame.GetCameraMatrix(mObjectFrame); //原书中的代码为GetMatrix,获取了objectFrame的朝向,导致顶点和纹理的对象关系出现了相反内容
		modelViewMatrix.MultMatrix(mObjectFrame);

		glBindTexture(GL_TEXTURE_2D, textureID);
		shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
			transformPipeLine.GetModelViewMatrix(),
			transformPipeLine.GetProjectionMatrix(),
			vLightPos, vWhite, 0);
		pyramidBatch.Draw();
	//}
	modelViewMatrix.PopMatrix();

	glutSwapBuffers();
}

void ShutdownRC(void)
{
	glDeleteTextures(1, &textureID);
}

void SpecialKeys(int key, int x, int y)
{
	if (key == GLUT_KEY_UP)
	{
		objectFrame.RotateWorld(m3dDegToRad(5.0f),1.0f,0.0f,0.0f);//模型和摄像机的操作是相反的,模型向上旋转5.0f
	}

	if (key == GLUT_KEY_DOWN)
	{
		objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
	}

	if (key == GLUT_KEY_LEFT)
	{
		objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
	}

	if (key == GLUT_KEY_RIGHT)
	{
		objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
	}

	glutPostRedisplay();
}

int main(int argc, char * argv[])
{
	gltSetWorkingDirectory(argv[0]);

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
	glutInitWindowSize(800, 600);
	glutCreateWindow("Pyramid");
	glutReshapeFunc(ChangeSize);
	glutSpecialFunc(SpecialKeys);
	glutDisplayFunc(RenderScene);

	GLenum err = glewInit();
	if (GLEW_OK != err) {
		fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
		return 1;
	}


	SetupRC();

	glutMainLoop();

	ShutdownRC();

	return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/115641.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 帝国时代之国家崛起_崛起于帝国时代

    帝国时代之国家崛起_崛起于帝国时代01关系数据库帝国现在是公元2009年,关系帝国已经统治了我们30多年,实在是太久了。1970年,科德提出关系模型,1974年张伯伦和博伊斯制造出了SQL,帝国迅速建立起了统治。从北美到…

    2022年9月24日
    2
  • Python for循环的使用

    Python for循环的使用Pythonfor循环的使用(一)for循环的使用场景1.如果我们想要某件事情重复执行具体次数的时候可以使用for循环。2.for循环主要用来遍历、循环、序列、集合、字典,文件、甚至是自定义类或函数。(二)for循环操作列表实例演示使用for循环对列表进行遍历元素、修改元素、删除元素、统计列表中元素的个数。1.for循环用来遍历整个列表#for循环主

    2022年8月12日
    9
  • Pytorch实现基于卷积神经网络的面部表情识别(详细步骤)「建议收藏」

    Pytorch实现基于卷积神经网络的面部表情识别(详细步骤)「建议收藏」文章目录一、项目背景二、数据处理1、标签与特征分离2、数据可视化3、训练集和测试集三、模型搭建四、模型训练特征五、完整代码一、项目背景数据集cnn_train.csv包含人类面部表情的图片的label和feature。在这里,面部表情识别相当于一个分类问题,共有7个类别。其中label包括7种类型表情:一共有28709个label,说明包含了28709张表情包嘿嘿。每一行就是一张表情包4848=2304个像素,相当于4848个灰度值(intensity)(0为黑,255为白)二、数据处理

    2022年5月16日
    70
  • 做了6年的Java,java简历包装项目经验[通俗易懂]

    高频问题1.上一家公司,你为什么会离职?公司很好,但是公司调整了业务,接下来的发展路线和自己的目标不一致,所以要换工作工作太清闲,学不到知识,我不怕累,就是想多锻炼自己,想找具有挑战力工作公司的管理制度不也是很完善,没有晋升机会,我比较想进步,找一个更好的平台我想去优秀的公司,让自己变得更好2.为什么来我们这里?对原单位充满感恩,这是我人生中非常重要的经历,我认同原单位领导和文化两份工作的本质是一致的,行业和工作性质都有紧密联系选择一份新的工作不代表背叛过去,发扬原公司魅力,同时为了

    2022年4月11日
    342
  • java拦截器放行_java拦截器放行某些请求

    java拦截器放行_java拦截器放行某些请求在java开发中,拦截器使用是很普遍的,最常用的就是登陆拦截了,然后并不是所有的请求我们都需要拦截,比如index页面的请求我们是不拦截的.通常情况下我们有两种方式:先贴出来springboot使用拦截器的case:1.自定义拦截器,实现HandlerInterceptor,也可以采用继承的方式(HandlerInterceptorAdapter),内容不重要,看过程publicclassL…

    2022年6月7日
    158
  • Linux文件锁

    Linux文件锁翻阅参考资料,你会发现文件锁可以进行很多的分类,最常见的主要有读锁与写锁,前者也叫共享锁,后者也叫排斥锁,值得注意的是,多个读锁之间是不会相互干扰的,多个进程可以在同一时刻对同一个文件加读锁;但是,如果已经有一个进程对该文件加了写锁,那么其他进程则不能对该文件加读锁或者写锁,直到这个进程将写锁释放,因此可以总结为:对于同一个文件而言,它可以同时拥有多个读者,但是在某一时刻,他只能拥有一个写者。

    2022年6月28日
    43

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号