OpenGL基础变换

这篇博客介绍了OpenGL中的基础变换,包括向量和矩阵知识、视图变换、模型变换、投影变换和视口变换。重点讲解了模型视图矩阵、投影矩阵在3D图形中的作用,以及如何使用矩阵堆栈进行状态管理和照相机、角色的移动。同时提到了光源在场景渲染中的应用。
摘要由CSDN通过智能技术生成

主要讲如何在坐标系中移动图形和对象,OpenGL里面的变化都是矩阵运算,我们不需要去进行矩阵操作,但是我们得知道相关的API是什么,该如何去运用它们。

一些基本的向量、矩阵知识

(X, Y, Z)代表一个向量,表明了方向和数值。
长度为1的向量为单位向量,向量不是单位向量,将其转化为单位向量,这个过程为标准化。
math3d库里面有两个数据类型,能够表示一个三维和四维的向量:M3DVector3f可以表示为一个三维向量,M3DVector4f可以表示为一个四维向量,将它们定义为数组:
typedef float M3DVector3f[3]; //由此可见,M3DVector3f为一个含有3个float类型数组的别名
typedef float M3DVector4f[4];

声明一个三分量向量:
M3DVector3f vVector;

类似的,申明一个4分量向量:
M3DVector4f vVector = {xxx, xxx, xxx, xxx,};

申明一个3分量顶点数组,例如为了生成一个三角形:
M3DVector3f vVects[] = {xxx, xxx, xxx, xxx, xxx, xxx, xxx, xxx, xxx};

点乘函数:求得向量的夹角(余弦值)或者求得弧度
叉乘函数:与两个向量定义的平面垂直的向量

理解变换

3D图形其实是2D的,只是将3D的数据压扁成2D的数据的处理过程叫做投影,在我们想要描述投影中的变换的(正交变换或者透视变换)的类型的时候,我们都会涉及到投影。
变换的类型:视图变换、模型变换和投影变换。
变换 应用
视图 指定观察者或者照相机的位置
模型 在场景中移动物体
模型视图 描述模型和视图变换的二元性
投影 改变视镜体的大小或重新设计它的形状
视口 这是一种伪变化,只是对窗口上的最终输出进行缩放

视觉坐标

视觉坐标系:x轴的正方向指向右边,y轴的正方向指向上方,z轴的正方向垂直于屏幕指向自己,
原点是屏幕的中心。

视图变换

在默认情况下,透视投影中的观察点位于原点(0,0,0),并沿着z轴的负方向进行观察(向显示器内部看进去)。观察点相对于视觉坐标系移动,来提供特定的有利位置。当观察点位于原点时,就像透视投影一样,绘制在z坐标为正的位置的对象则位于观察者背后。

然而在正投影中,观察者被认为是在z轴正方向无穷远的位置,能够看见视景体的中的任何东西。视图变换 允许我们将观察点放在任何位置,指定任何方向来观察场景,glulookat函数应该就是实现了该功能

模型变换

模型变换用于操作模型和其中特定的对象。这些变换将对象移动到需要的位置,然后再对它们进行旋转和缩放。
最普遍的模型变换为平移,旋转和缩放,注意旋转会使得坐标系发生相应的变化,所以先平移后旋转与先旋转后平移是不同的。

视图变换和模型变换的二元性

视图变化和模型变换对场景的最终外观来说是一样的,例如:观察者后移和坐标系前进其实是一样的。
一般情况下:场景中的对象先进行视图变换再进行模型变换

投影变换

投影变换指定一个完成的场景(所有模型变换已经完成)是如何投影到屏幕上的最终图像,主要有两种投影:正投影和透视投影
正投影(平行投影):所有多边形都是按照指定的相对大小来在屏幕上绘制的。线和多边形使用平行线直接映射到2D屏幕上,这就意味着,无论一个物体有多远, 它都会按照同样的大小来绘制。这种投影一般用在二维图像上:文本、蓝图、屏幕菜单等
透视投影:透视缩短,使远处的物体会看起来比近处的小。适用于3D。

视口变换

当进行了投影变换之后,得到了一个场景的二维投影,它会被映射到屏幕上某处的窗口上。这种映射就是最后一步,称为视口变换。

模型视图矩阵

模型视图矩阵是一个4*4的矩阵,它表示一个变换后的坐标系,我们可以用来放置对象和确定对象的方向。

模型视图矩阵
第4列是变换后的坐标系原点的x,y和z的值。
第4行是缩放因子,很少去改动它。
无论是平移缩放还是旋转都是将这些变换转换为这个矩阵,然后矩阵之间相乘,得到最终的代表变换的矩阵。

投影矩阵

正投影和透视投影

变换管线

变换管线
从图中可以看出一个初始的顶点如何变化为一个最终的窗口坐标

使用矩阵堆栈

GLMatrixStack类来建立矩阵堆栈,来方便用户来构造和管理矩阵。
有相应的API可以在该堆栈中顶部载入这个单位矩阵或者在堆栈的顶部载入任何的矩阵。
也有相应的API可以进行矩阵相乘然后将结果存入堆栈顶部,也可以获得堆栈顶部的矩阵。

压栈和出栈

真正的作用在于通过压栈操作来存储一个状态,出栈操作来恢复一个状态。
本质在于栈顶的元素代表的是当前的状态。
堆栈也有平移旋转和缩放操作,操作之后的结果矩阵放入了堆栈的顶部。
运用压栈和出栈的地方:看下面这段代码:

    // 保存刚开始的视图矩阵(单位矩阵)
    modelViewMatrix.PushMatrix();     
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    modelViewMatrix.PushMatrix(mCamera);
    // Transform the light position into eye coordinates
    M3DVector4f vLightPos = { 0.0f, 10.0f, 5.0f, 1.0f };
    M3DVector4f vLightEyePos;
    m3dTransformVector4(vLightEyePos, vLightPos, mCamera);      
    // Draw the ground(floor)
    shaderManager.UseStockShader(GLT_SHADER_FLAT,
                                 transformPipeline.GetModelViewProjectionMatrix(),
                                 vFloorColor);  
    floorBatch.Draw();    
    for(int i = 0; i < NUM_SPHERES; i++) {
        modelViewMatrix.PushMatrix();
        modelViewMatrix.MultMatrix(spheres[i]);
        shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(), 
                                transformPipeline.GetProjectionMatrix(), vLightEyePos, vSphereColor);
        sphereBatch.Draw();
        modelViewMatrix.PopMatrix();
        }
    // 对矩阵的第一次平移操作,此时堆栈的栈顶是该平移之后的矩阵
    modelViewMatrix.Translate(0.0f, 0.0f, -2.5f); 
    // 将该矩阵压入栈顶
    modelViewMatrix.PushMatrix();
    // 对该栈顶矩阵进行旋转操作
    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(), 
                                     transformPipeline.GetProjectionMatrix(), vLightEyePos, vTorusColor);
        torusBatch.Draw();
    modelViewMatrix.PopMatrix(); // 消除之前的旋转操作,现在堆栈的栈顶还是平移之后的矩阵
    // 对该平移的矩阵进行旋转,平移,然后由得到的结果矩阵绘制sphere
    // yRot is bigger which means the sphere will rotate faster
    modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);
    modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(), 
                                transformPipeline.GetProjectionMatrix(), vLightEyePos, vSphereColor);
    sphereBatch.Draw();
    // 恢复到之前的单位矩阵
    //第一次调用恢复到平移的时候的矩阵
    modelViewMatrix.PopMatrix();
    //第二次调用恢复到单位矩阵时候
    modelViewMatrix.PopMatrix();    
    // Do the buffer Swap
    glutSwapBuffers();        
    // Tell GLUT to do it again
    glutPostRedisplay();
    }

使用照相机和角色进行移动

对于地面来说通常不变换,但是对于地面上的物体来说,这些个角色是需要移动的。这些角色有自己的变换,常常不仅与全局坐标系(视觉坐标系)有关,也与其他角色有关。每个角色都有自己的参考帧或者说本地坐标系,而你们会发现本地坐标系和全局坐标系的转换是非常有用的。

角色帧

GLFrame这个参考帧就是一个类,里面有该参考帧的空间中的位置,有一个指向y轴的向量,一个指向z轴的向量,用叉乘可以求得x轴的向量,由此我们可以得到一个4*4的矩阵,来表示一个对象的位置和方向。
同时之前那些对堆栈的API都是可以适用于该GLFrame类的。

照相机

使用照相机的角色变换,并对它进行反转,这样向后移动照相机也就相当于向前移动整个场景,向左相当于把整个场景向右旋转。常用的渲染流程如下所示:
渲染流程

光源

光源的位置也需要转换到视觉坐标系,整个世界实际上是相对于照相机运动的,其中也包含光源。
光源可以用来渲染角色的颜色 ,如下:

    // 先将光源坐标转化为相对于视觉坐标系的坐标
    M3DVector4f vLightPos = { 0.0f, 10.0f, 5.0f, 1.0f };
    M3DVector4f vLightEyePos;
    m3dTransformVector4(vLightEyePos, vLightPos, mCamera);
    //使用正确的着色器并传递uniform值来渲染一个sphere
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(), 
                                transformPipeline.GetProjectionMatrix(), vLightEyePos, vSphereColor);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值