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

时间:2022-09-10 17:11:44

如果你也发现按照教程代码完成贴图时,你会底面的坐标和平常顶点坐标正负相反,比如-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;
}