精华内容
下载资源
问答
  • 本文针对基于TP模型变换的并行分布式补偿(PDC)控制器进行了三方面的扩展:.1)现有基于TP模型变换的基于PDC变换的PDC控制器该方法通过统一采样方法和扩展信号扩展到基于TP模型变换的PDC跟踪控制器。.2)一种新的TP...
  • 目录前言绘制立方体模型变换矩阵齐次坐标通过矩阵进行变换平移变换矩阵旋转变换矩阵缩放变换矩阵变换的级联让立方体换个姿势构建模型变换矩阵向GPU传递模型变换矩阵在GPU中进行矩阵变换深度测试完整代码c++着色器小...

    前言

    上一篇回顾:OpenGL学习(二)渲染流水线与三角形绘制

    在上一篇博客中我们实现了二维平面上三角形的绘制,今天我们来绘制一个立方体,同时我们将会利用模型变换矩阵对立方体进行旋转,平移,缩放等操作,最后我们会通过阅读OFF格式的模型来读取更加复杂的三维模型。


    该部分的绘制代码基于上一篇博客:OpenGL学习(二)渲染流水线与三角形绘制
    博客内容因为篇幅关系,不会完整的列出所有的代码
    完整代码会放在文章末尾

    绘制立方体

    立方体的绘制,比起二维的三角形来说,要相对复杂。立方体有 6 个面,而我们的绘制是基于基本的三角形图元,这意味着我们需要用 2 个三角形来描述正方体的一个面。

    在这里插入图片描述

    此外,一个立方体有 6 个面,这意味着我们需要有 12 个三角面片,而每个三角面片有 3 个顶点,所以我们总共要生成 36 个顶点。

    你可能注意到哪里不对了,毕竟一个立方体才 8 个顶点啊,你搁这整 36 个,啥啊?

    注意到我们的绘制函数 glDrawArrays ,在该函数中如果第一个参数指定的绘制模式为 GL_TRIANGLES ,表示三个顶点一组绘制三角形,所以尽管会有一些点的冗余,但我们应该按照规则来给定顶点。下面以绘制一个正方形为例,解释为何立方体会有 36 个顶点:

    在这里插入图片描述

    可以看到,尽管一个正方形只有 4 个顶点,但是为了用两个三角形去绘制它,我们生成了 6 个顶点,将两个三角形拼凑为正方形。

    所以我们首先需要定义正方体的 8 个顶点,和其顶点的颜色。我个人更加倾向于把它定义为全局变量:

    // 正方形的8个顶点位置坐标
    std::vector<glm::vec3> vertexPosition = {
        glm::vec3(-0.5, -0.5, -0.5),
        glm::vec3(0.5, -0.5, -0.5),
        glm::vec3(-0.5,  0.5, -0.5),
        glm::vec3(0.5,  0.5, -0.5),
        glm::vec3(-0.5, -0.5,  0.5),
        glm::vec3(0.5, -0.5,  0.5),
        glm::vec3(-0.5,  0.5,  0.5),
        glm::vec3(0.5,  0.5,  0.5)
    };
    // 正方形的8个顶点的颜色
    std::vector<glm::vec3> vertexColor = {
        glm::vec3(1.0, 1.0, 1.0),	// White
        glm::vec3(1.0, 1.0, 0.0),	// Yellow
        glm::vec3(0.0, 1.0, 0.0),	// Green
        glm::vec3(0.0, 1.0, 1.0),	// Cyan
        glm::vec3(1.0, 0.0, 1.0),	// Magenta
        glm::vec3(1.0, 0.0, 0.0),	// Red
        glm::vec3(0.5, 0.5, 0.5),	// Gray
        glm::vec3(0.0, 0.0, 1.0)	// Blue
    };
    

    随后我们需要指定正方形的 6 个面,即 12 个三角面片。我们创建一个数组 faces,其中第 i 个元素 faces[i] 表示第 i 个三角面片的顶点下标:

    // 正方形由6个面 -- 12个三角形面片组成 faces存储顶点在vertexPosition中的下标
    std::vector<glm::vec3> faces = {
        glm::vec3(0,2,1),
        glm::vec3(1,2,3),
        glm::vec3(1,3,7),
        glm::vec3(7,5,1),
        glm::vec3(0,1,5),
        glm::vec3(5,4,0),
        glm::vec3(0,4,2),
        glm::vec3(4,6,2),
        glm::vec3(4,5,7),
        glm::vec3(7,6,4),
        glm::vec3(6,7,3),
        glm::vec3(3,2,6)
    };
    

    如图:通过 faces 数组指定三角面片的顶点:

    在这里插入图片描述

    知晓了 faces 中定义立方体三角面片的方式之后,我们就可以利用 faces 数组,生成顶点属性的索引:

    首先我们建立两个变量,分别表示顶点的位置坐标和顶点的颜色

    // 顶点坐标 / 颜色
    std::vector<glm::vec3> points, colors;
    

    然后我们在初始化(上一篇博客的 init 函数)中,通过 faces 数组,生成顶点属性:

    // 由面片信息生成三角形面片顶点
    for (int i = 0; i < faces.size(); i++)
    {
        // 取得第 i 个三角面片的三个顶点的下标
        int index1 = faces[i].x;
        int index2 = faces[i].y;
        int index3 = faces[i].z;
        // 生成顶点
        points.push_back(vertexPosition[index1]);
        points.push_back(vertexPosition[index2]);
        points.push_back(vertexPosition[index3]);
        // 生成顶点颜色
        colors.push_back(vertexColor[index1]);
        colors.push_back(vertexColor[index2]);
        colors.push_back(vertexColor[index3]);
    }
    

    剩下的步骤和我们在上一篇博客:OpenGL学习(二)渲染流水线与三角形绘制 中的操作一样,创建vao vbo,读取着色器,指定vao格式,传输数据…

    对了,别忘了改 display 中的绘制函数,我们绘制的顶点数不再是3个了:

    在这里插入图片描述

    重新运行程序,我们得到了一个。。。唔。。。正方形,而不是立方体?

    在这里插入图片描述

    结果是意料之中的,因为我们从立方体的正面看过去,那么就应该是一个正方形。如果我们想看到更多的面,我们就应该让正方体旋转起来!

    模型变换矩阵

    提到旋转,就不得不提一下模型变换。事实上,建模师在建立3D模型的时候,是以一个叫 模型坐标系 为参考建立的。比如立方体,如下的图展示了立方体的模型坐标。

    在这里插入图片描述

    但是事实上,在我们建立 3D 场景的时候,我们不同的三维模型具有不同的位置。我们不能强求建模师在建模时就确定模型的绝对位置,况且我们还会实时地对模型进行移动旋转等操作,这就意味着,对模型的平移旋转缩放必须是由程序完成的!

    于是我们引入 模型变换矩阵 这个概念。我们通过对模型的坐标进行变换,得到我们想要的效果。

    齐次坐标

    在开始构建我们的模型变换矩阵前,首先了解到齐次坐标的概念。通常情况下,我们都可以用三维向量来描述三维空间下的点,或者是一个方向:

    glm::vec3(0,2,1)
    glm::vec3(1,2,3)
    

    可是我们如何区分一个三维向量是坐标还是方向向量

    如果是坐标,那么我们平移这个向量,对应的坐标需要发生改变。如果是方向向量,那么我们平移这个向量,对应的坐标不能发生改变。

    在这里插入图片描述

    这就带来了难题。于是数学家们通过巧妙而猥琐的构造方式,想出了一个完美的解决方案:”即然没法区分,那就增加一个维度来保存向量的属性信息“,这就是齐次坐标。

    齐次坐标在三维坐标的基础上,拓展了一个维度,变为四维的向量。那么增加了一个维度就能够区分 坐标点方向向量 了吗?

    直接说结论:第四维度为0则为方向向量,第四维度为1则为坐标

    注:这其中涉及巧妙的构造,但是我们暂时记住结论。接下来我们会验证这种构造的正确性。

    通过矩阵进行变换

    平移旋转和缩放都是线性变换,我们观察矩阵乘法的定义式:

    在这里插入图片描述

    我们发现齐次坐标左乘一个矩阵,对于齐次坐标的四个维度而言,全都是 力士 线性变换!

    线性变换意味着我们可以通过将其次坐标和一个矩阵进行乘法,从而实现平移旋转和缩放等线性变换。

    平移变换矩阵

    平移变换是最简单的线性变换!我们只需要将一个坐标加上一定的偏移就可以实现。所以我们有

    v ′ = v + d v' = v + d v=v+d

    如果写成齐次坐标的矩阵乘法的形式,我们通过一个平移矩阵,对坐标进行变换(注意这里第四个维度为 1 表示这是一个点):

    [ 1 0 0 d x 0 1 0 d y 0 0 1 d z 0 0 0 1 ] ∗ ( x , y , z , 1 ) = ( x + d x , y + d y , z + d z , 1 ) \left[\begin{array}{cccc} 1 & 0 & 0 & d_{x} \\ 0 & 1 & 0 & d_{y} \\ 0 & 0 & 1 & d_{z} \\ 0 & 0 & 0 & 1 \end{array}\right] * \left(x,y,z, 1\right)=\left(x+d_{x},y+d_{y},z+d_{z}, 1\right) 100001000010dxdydz1(x,y,z,1)=(x+dx,y+dy,z+dz,1)

    我们再来看第四维度为 0 的情况:

    [ 1 0 0 d x 0 1 0 d y 0 0 1 d z 0 0 0 1 ] ∗ ( x , y , z , 0 ) = ( x , y , z , 0 ) \left[\begin{array}{cccc} 1 & 0 & 0 & d_{x} \\ 0 & 1 & 0 & d_{y} \\ 0 & 0 & 1 & d_{z} \\ 0 & 0 & 0 & 1 \end{array}\right] * \left(x,y,z, 0\right)=\left(x,y,z, 0\right) 100001000010dxdydz1(x,y,z,0)=(x,y,z,0)

    什么都没有发生。因为对于第四维度为 0 的情况,我们认为它是一个方向向量。对于方向向量的平移操作为无效,因为向量始终表示方向

    于是得到我们的平移变换矩阵:

    T ( d x , d y , d z ) = [ 1 0 0 d x 0 1 0 d y 0 0 1 d z 0 0 0 1 ] \boldsymbol{T}_{}(d_{x},d_{y},d_{z})=\left[\begin{array}{cccc} 1 & 0 & 0 & d_{x} \\ 0 & 1 & 0 & d_{y} \\ 0 & 0 & 1 & d_{z} \\ 0 & 0 & 0 & 1 \end{array}\right] T(dx,dy,dz)=100001000010dxdydz1

    旋转变换矩阵

    虽然短,但是能够旋转

    旋转也是一种线性变换。旋转分为三个部分:绕 xyz 轴旋转,所以我们理应有三个旋转变换矩阵。首先我们来看绕着 z 轴的旋转:

    在这里插入图片描述
    绕着 z 轴旋转相当于 z 不变。通过推理我们可以得到点的变换规律:

    x ′ = x cos ⁡ θ − y sin ⁡ θ y ′ = x sin ⁡ θ + y cos ⁡ θ z ′ = z \begin{array}{l} x^{\prime}=x \cos \theta-y \sin \theta \\ y^{\prime}=x \sin \theta+y \cos \theta \\ z^{\prime}=z \end{array} x=xcosθysinθy=xsinθ+ycosθz=z

    于是有绕着 z 轴的旋转矩阵:

    R z ( θ ) = [ cos ⁡ θ − sin ⁡ θ 0 0 sin ⁡ θ cos ⁡ θ 0 0 0 0 1 0 0 0 0 1 ] \boldsymbol{R}_{z}(\theta)=\left[\begin{array}{cccc} \cos \theta & -\sin \theta & 0 & 0 \\ \sin \theta & \cos \theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{array}\right] Rz(θ)=cosθsinθ00sinθcosθ0000100001

    同理我们可以得到绕 xy 轴的旋转矩阵:

    R x ( θ ) = [ 1 0 0 0 0 cos ⁡ θ − sin ⁡ θ 0 0 sin ⁡ θ cos ⁡ θ 0 0 0 0 1 ] \boldsymbol{R}_{x}(\theta)=\left[\begin{array}{cccc} 1 & 0 & 0 & 0 \\ 0 & \cos \theta & -\sin \theta & 0 \\ 0 & \sin \theta & \cos \theta & 0 \\ 0 & 0 & 0 & 1 \end{array}\right] Rx(θ)=10000cosθsinθ00sinθcosθ00001

    R y ( θ ) = [ cos ⁡ θ 0 sin ⁡ θ 0 0 1 0 0 − sin ⁡ θ 0 cos ⁡ θ 0 0 0 0 1 ] \boldsymbol{R}_{y}(\theta)=\left[\begin{array}{cccc} \cos \theta & 0 & \sin \theta & 0 \\ 0 & 1 & 0 & 0 \\ -\sin \theta & 0 & \cos \theta & 0 \\ 0 & 0 & 0 & 1 \end{array}\right] Ry(θ)=cosθ0sinθ00100sinθ0cosθ00001


    此处默认模型的中心点在原点,才能够实现绕 xyz 轴的旋转。如果中心不在原点,就会进行错误的变换

    缩放变换矩阵

    缩放的变换和平移类似,也是各个坐标乘一个系数(平移是加上一个系数)

    v ′ = v ∗ s v' = v * s v=vs

    于是我们能够很快的得出缩放变换的变换矩阵:

    S ( s x , s y , s z ) = [ s x 0 0 0 0 s y 0 0 0 0 s z 0 0 0 0 1 ] \mathbf{S}\left(s_{x}, s_{y}, s_{z}\right)=\left[\begin{array}{cccc} s_{x} & 0 & 0 & 0 \\ 0 & s_{y} & 0 & 0 \\ 0 & 0 & s_{z} & 0 \\ 0 & 0 & 0 & 1 \end{array}\right] S(sx,sy,sz)=sx0000sy0000sz00001


    此处默认模型的中心点在原点,才能够实现等比缩放。如果中心不在原点,就会进行错误的变换

    变换的级联

    对于上述的三种变换:平移 旋转 缩放,我们得到了 5 个变换矩阵,那么我们该怎么利用他们呢?

    我们当然可以逐个逐个的对顶点坐标进行变换:

    v = Rx * v	// x旋转
    v = Ry * v	// y旋转
    v = Rz * v	// z旋转
    v = S * v	// 缩放
    v = T * v	// 平移
    

    但是这一切值得吗?

    这里就再次体现出利用矩阵对齐次坐标的巧妙性:我们可以先将所有的变换矩阵都乘起来,形成最终的模型变换矩阵,再利用模型变换矩阵对顶点坐标进行变换

    model = T * R * S	// 变换的级联 -- 模型变换矩阵
    v = model * v
    

    我们首先进行缩放变换,然后是旋转,最后是平移。注意我们书写矩阵的顺序和实际的运算顺序相反。


    注意变换的先后顺序,因为旋转和缩放默认模型中心在原点
    我们必须先进行缩放旋转,之后再进行平移。否则会形成错误的变换!
    xyz轴的旋转级联过程并不是直接相乘,因为旋转会互相干扰,但是我们使用glm的API可以避免我们手动计算它。

    让立方体换个姿势

    我们已经知晓了如何构造我们的模型变换矩阵,现在开始让立方体换个姿势摆放。

    构建模型变换矩阵

    矩阵的构造是很辛苦的,好在 glm 库已经帮我们封装好了。我们首先引入如下的两个头文件,他们的作用分别是矩阵变换和矩阵寻址:

    #include <glm/gtc/matrix_transform.hpp>
    #include <glm/gtc/type_ptr.hpp>
    

    然后我们可以开始在初始化(init 函数)中,构造一个模型变换矩阵:

    // 构造模型变换矩阵
    glm::mat4 unit(    // 单位矩阵
        glm::vec4(1, 0, 0, 0),
        glm::vec4(0, 1, 0, 0),
        glm::vec4(0, 0, 1, 0),
        glm::vec4(0, 0, 0, 1)
    );
    
    glm::mat4 scale = glm::scale(unit, glm::vec3(0.6, 0.6, 0.6));   // xyz缩放0.6倍
    
    glm::mat4 translate = glm::translate(unit, glm::vec3(-0.3, 0.0, 0.0));  // 平移(-0.3, 0.0, 0.0)
    
    glm::mat4 rotate = unit;    // 旋转
    rotate = glm::rotate(rotate, glm::radians(-30.0f), glm::vec3(1, 0, 0)); // 绕x轴转-30度
    rotate = glm::rotate(rotate, glm::radians(20.0f), glm::vec3(0, 1, 0));  // 绕y轴转20度
    rotate = glm::rotate(rotate, glm::radians(0.0f), glm::vec3(0, 0, 1));   // 绕z轴转0度
    
    glm::mat4 model = translate * rotate * scale;   // 变换级联 -- 生成模型变换矩阵
    

    至此,模型矩阵到手,可是我们如何对顶点进行变换呢?一种简单的方式是写一个for循环,对所有的顶点都进行一次矩阵乘法,可是这么做相当于把工作交给cpu。

    一种更加流行的方式是我们将模型变换矩阵传递到GPU中,让GPU对顶点进行变换。

    向GPU传递模型变换矩阵

    因为我们要对所有的顶点都进行一次模型变换。这意味着对所有顶点来说,他们看到的模型变换矩阵必须是一致的

    为了解决这个问题,我们引入 uniform 变量。那么什么是 uniform 呢?

    在这里插入图片描述

    对不起放错图了。。。

    在这里插入图片描述

    唔。。。uniform 变量指的是一致变量。还记得上一篇博客中我我们提到的,对输入的每一个顶点,都会运行顶点着色器程序。在顶点着色器中,每个顶点的坐标是不同的,所有的顶点着色器程序看到的 uniform 变量是相同的,这就是一致变量的意思。

    彳亍! 我们开始向GPU传递 uniform 变量,在 init 函数中。和顶点数据的输入类似,我们要指定 model 矩阵在着色器中的位置索引(约定变量名称),然后向GPU传递数据:

    // 传递uniform变量
    GLuint mlocation = glGetUniformLocation(program, "model");    // 名为model的uniform变量的位置索引
    glUniformMatrix4fv(mlocation, 1, GL_FALSE, glm::value_ptr(model));   // 列优先矩阵
    

    其中我们必须通过 glGetUniformLocation 函数来获取uniform变量的位置。此外, glUniformMatrix4fv 函数 的第三个参数是表示该矩阵是列优先还是行优先。


    关于行列优先变量:
    我们使用的是 glm 库进行模型矩阵的构造,我们填 false。
    如果是自己手写矩阵构造,我们填 true。

    在GPU中进行矩阵变换

    我们修改顶点着色器的代码为如下的的代码:

    #version 330 core
    
    in vec3 vPosition;  // cpu传入的顶点坐标
    in vec3 vColor;     // cpu传入的顶点颜色
    
    out vec3 vColorOut; // 向片元着色器传递顶点颜色
    
    uniform mat4 model; // 模型变换矩阵
    
    void main()
    {
        gl_Position = model * vec4(vPosition, 1.0); // 指定ndc坐标
        vColorOut = vColor; // 这个颜色经过线性插值生成片元颜色
    }
    

    别紧张,改动的地方很少。我们只是将传过来的uniform变量,和我们的顶点坐标进行了一次矩阵乘法运算(相当于对顶点施加模型变换)。红色部分为我们增加的代码:

    在这里插入图片描述

    片元着色器不需要任何改动,嗯。

    深度测试

    如果你按部就班的完成了上面的代码,那么你会发现我们的程序结果是这样的:

    在这里插入图片描述

    我们的立方体好像被掏空一般,这是因为我们的测试策略。我们没有开启任何绘制测试策略,那么三角形就会按照三角面片的绘制顺序互相覆盖!我们的立方体正面显然被立方体的背面覆盖了。。。

    为了还原正确的覆盖方式,我们需要开启深度测试。我们只渲染离相机最近的像素点,其他像素点会被忽略。

    开启的方式也很简单。我们在 init 函数的最后加一句:

    glEnable(GL_DEPTH_TEST);  // 开启深度测试
    

    此外,我们在 main 函数创建窗体时(放在前后都可),启用深度模式:

    将:
    
    glutInitDisplayMode(GLUT_RGBA);
    
    改为:
    
    glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH);
    

    然后我们修改 display 函数中的代码。在每一次绘制之前,我们都需要清空两个缓冲:

    1. 颜色缓冲
    2. 深度缓冲(方便进行深度测试)

    改动如下:

    将:
    glClear(GL_COLOR_BUFFER_BIT);
    
    改为:
    
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    

    最后重新运行程序,可以看到绘制正常辣:

    在这里插入图片描述

    ohhhhhh

    完整代码

    c++

    #include <iostream>
    #include <string>
    #include <fstream>
    #include <vector>
    
    #include <GL/glew.h>
    #include <GL/freeglut.h>
    #include <glm/glm.hpp>
    #include <glm/gtc/matrix_transform.hpp>
    #include <glm/gtc/type_ptr.hpp>
    
    // 正方形的8个顶点位置坐标
    std::vector<glm::vec3> vertexPosition = {
        glm::vec3(-0.5, -0.5, -0.5),
        glm::vec3(0.5, -0.5, -0.5),
        glm::vec3(-0.5,  0.5, -0.5),
        glm::vec3(0.5,  0.5, -0.5),
        glm::vec3(-0.5, -0.5,  0.5),
        glm::vec3(0.5, -0.5,  0.5),
        glm::vec3(-0.5,  0.5,  0.5),
        glm::vec3(0.5,  0.5,  0.5)
    };
    // 正方形的8个顶点的颜色
    std::vector<glm::vec3> vertexColor = {
        glm::vec3(1.0, 1.0, 1.0),	// White
        glm::vec3(1.0, 1.0, 0.0),	// Yellow
        glm::vec3(0.0, 1.0, 0.0),	// Green
        glm::vec3(0.0, 1.0, 1.0),	// Cyan
        glm::vec3(1.0, 0.0, 1.0),	// Magenta
        glm::vec3(1.0, 0.0, 0.0),	// Red
        glm::vec3(0.5, 0.5, 0.5),	// Gray
        glm::vec3(0.0, 0.0, 1.0)	// Blue
    };
    // 正方形由6个面 -- 12个三角形面片组成 faces存储顶点在vertexPosition中的下标
    std::vector<glm::vec3> faces = {
        glm::vec3(0,2,1),
        glm::vec3(1,2,3),
        glm::vec3(1,3,7),
        glm::vec3(7,5,1),
        glm::vec3(0,1,5),
        glm::vec3(5,4,0),
        glm::vec3(0,4,2),
        glm::vec3(4,6,2),
        glm::vec3(4,5,7),
        glm::vec3(7,6,4),
        glm::vec3(6,7,3),
        glm::vec3(3,2,6)
    };
    // 顶点坐标 / 颜色
    std::vector<glm::vec3> points, colors;
    
    // 读取文件并且返回一个长字符串表示文件内容
    std::string readShaderFile(std::string filepath)
    {
        std::string res, line;
        std::ifstream fin(filepath);
        if (!fin.is_open())
        {
            std::cout << "文件 " << filepath << " 打开失败" << std::endl;
            exit(-1);
        }
        while (std::getline(fin, line))
        {
            res += line + '\n';
        }
        fin.close();
        return res;
    }
    
    // 获取着色器对象
    GLuint getShaderProgram(std::string fshader, std::string vshader)
    {
        // 读取shader源文件
        std::string vSource = readShaderFile(vshader);
        std::string fSource = readShaderFile(fshader);
        const char* vpointer = vSource.c_str();
        const char* fpointer = fSource.c_str();
    
        // 容错
        GLint success;
        GLchar infoLog[512];
    
        // 创建并编译顶点着色器
        GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, (const GLchar**)(&vpointer), NULL);
        glCompileShader(vertexShader);
        glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);   // 错误检测
        if (!success)
        {
            glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
            std::cout << "顶点着色器编译错误\n" << infoLog << std::endl;
            exit(-1);
        }
    
        // 创建并且编译片段着色器
        GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, (const GLchar**)(&fpointer), NULL);
        glCompileShader(fragmentShader);
        glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);   // 错误检测
        if (!success)
        {
            glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
            std::cout << "片段着色器编译错误\n" << infoLog << std::endl;
            exit(-1);
        }
    
        // 链接两个着色器到program对象
        GLuint shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);
    
        // 删除着色器对象
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);
    
        return shaderProgram;
    }
    
    // 初始化
    void init()
    {
        // 由面片信息生成三角形面片顶点
        for (int i = 0; i < faces.size(); i++)
        {
            // 取得第 i 个三角面片的三个顶点的下标
            int index1 = faces[i].x;
            int index2 = faces[i].y;
            int index3 = faces[i].z;
            // 生成顶点
            points.push_back(vertexPosition[index1]);
            points.push_back(vertexPosition[index2]);
            points.push_back(vertexPosition[index3]);
            // 生成顶点颜色
            colors.push_back(vertexColor[index1]);
            colors.push_back(vertexColor[index2]);
            colors.push_back(vertexColor[index3]);
        }
    
        // 生成vbo对象并且绑定vbo
        GLuint vbo;
        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        // 先确定vbo的总数据大小(画饼???) 传NULL指针表示我们暂时不传数据
        glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * (points.size() + colors.size()), NULL, GL_STATIC_DRAW);
    
        // 传送数据到vbo 分别传递 顶点位置 和 顶点颜色
        GLuint pointDataOffset = 0;
        GLuint colorDataOffset = sizeof(glm::vec3) * points.size();
        glBufferSubData(GL_ARRAY_BUFFER, pointDataOffset, sizeof(glm::vec3) * points.size(), &points[0]);
        glBufferSubData(GL_ARRAY_BUFFER, colorDataOffset, sizeof(glm::vec3) * colors.size(), &colors[0]);
    
        // 生成vao对象并且绑定vao
        GLuint vao;
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
    
        // 生成着色器程序对象
        std::string fshaderPath = "shaders/fshader.fsh";
        std::string vshaderPath = "shaders/vshader.vsh";
        GLuint program = getShaderProgram(fshaderPath, vshaderPath);
        glUseProgram(program);  // 使用着色器
    
        // 建立顶点变量vPosition在着色器中的索引 同时指定vPosition变量的数据解析格式
        GLuint vlocation = glGetAttribLocation(program, "vPosition");    // vPosition变量的位置索引
        glEnableVertexAttribArray(vlocation);
        glVertexAttribPointer(vlocation, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);  // vao指定vPosition变量的数据解析格式
    
        // 建立颜色变量vColor在着色器中的索引 同时指定vColor变量的数据解析格式
        GLuint clocation = glGetAttribLocation(program, "vColor");    // vColor变量的位置索引
        glEnableVertexAttribArray(clocation);
        glVertexAttribPointer(clocation, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(sizeof(glm::vec3) * points.size()));  // 注意指定offset参数
    
        // 构造模型变换矩阵
        glm::mat4 unit(    // 单位矩阵
            glm::vec4(1, 0, 0, 0),
            glm::vec4(0, 1, 0, 0),
            glm::vec4(0, 0, 1, 0),
            glm::vec4(0, 0, 0, 1)
        );
        glm::mat4 scale = glm::scale(unit, glm::vec3(0.6, 0.6, 0.6));   // xyz缩放0.6倍
        glm::mat4 translate = glm::translate(unit, glm::vec3(-0.3, 0.0, 0.0));  // 平移(-0.3, 0.0, 0.0)
        glm::mat4 rotate = unit;    // 旋转
        rotate = glm::rotate(rotate, glm::radians(-30.0f), glm::vec3(1, 0, 0)); // 绕x轴转-30度
        rotate = glm::rotate(rotate, glm::radians(20.0f), glm::vec3(0, 1, 0));  // 绕y轴转20度
        rotate = glm::rotate(rotate, glm::radians(0.0f), glm::vec3(0, 0, 1));   // 绕z轴转0度
        glm::mat4 model = translate * rotate * scale;   // 变换级联 -- 生成模型变换矩阵
        
        // 传递uniform变量
        GLuint mlocation = glGetUniformLocation(program, "model");    // 名为model的uniform变量的位置索引
        glUniformMatrix4fv(mlocation, 1, GL_FALSE, glm::value_ptr(model));   // 列优先矩阵
    
        glEnable(GL_DEPTH_TEST);  // 开启深度测试
    
        glClearColor(0.0, 0.0, 0.0, 1.0);   // 背景颜色 -- 黑
    }
    
    // 显示回调函数
    void display()
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);       // 清空窗口颜色缓存
        glDrawArrays(GL_TRIANGLES, 0, points.size());   // 绘制n个点
        glutSwapBuffers();                  // 交换缓冲区
    }
    
    int main(int argc, char** argv)
    {
        glutInit(&argc, argv);              // glut初始化
        glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH);
        glutInitWindowSize(512, 512);       // 窗口大小
        glutCreateWindow("2 - model"); // 创建OpenGL上下文
    
    #ifdef __APPLE__
    #else
        glewInit();
    #endif
    
        init();
    
        glutDisplayFunc(display);           // 设置显示回调函数 -- 每帧执行
        glutMainLoop();                     // 进入主循环
    
        return 0;
    }
    

    着色器

    顶点:

    #version 330 core
    
    in vec3 vPosition;  // cpu传入的顶点坐标
    in vec3 vColor;     // cpu传入的顶点颜色
    
    out vec3 vColorOut; // 向片元着色器传递顶点颜色
    
    uniform mat4 model; // 模型变换矩阵
    
    void main()
    {
        gl_Position = model * vec4(vPosition, 1.0); // 指定ndc坐标
        vColorOut = vColor; // 这个颜色经过线性插值生成片元颜色
    }
    

    片元:

    #version 330 core
    
    in vec3 vColorOut;  // 顶点着色器传递的颜色
    out vec4 fColor;    // 片元输出像素的颜色
    
    void main()
    {
        fColor = vec4(vColorOut, 1.0);
    }
    

    小结

    三维的绘制并没有什么大不了的,我们传递三维的顶点数据就是了。值得注意的是物体的模型变换矩阵。

    我们一定是通过模型变换矩阵来调整三维物体的姿态。注意现代的解决方案是传递模型变换矩阵到着色器中,让GPU帮我们进行运算。

    此外,在绘制三维物体的时候,我们不要忘记开启深度测试,这样才能够准确的描绘物体的覆盖关系。

    好了。。。这次的内容应该足够多了。我本来还想再加上键鼠控制和OFF文件的读取的,放到下一篇罢(摸了

    展开全文
  • opengl中模型变换,投影变换,视点变换,视口变换比较让人头晕,看个这个演示就会有清楚的认识啦 http://www.99pan.com/Invite?uid=171324
  • 立方体三维观察-模型变换

    千次阅读 2020-12-09 10:31:58
    /*模型变换*/ glMatrixMode(GL_MODELVIEW); winWidth = newWidth; winHeight = newHeight; } int main(int argc, char* argv[]) { GLubyte* pPixelData = (GLubyte*)malloc(800 * 400 * 3);//分配内存 GLint ...

    实验一:

    分析:

    三个图形的原型如下所示(所有的操作都必须先放到stack中,glPushMatrix () 和 glPopMatrix()):

    1.以中心绘制原点,设置颜色为红色,绘制单位立方体线框glutWireCube(1.0)
    2.设置颜色为绿色, 设置线宽2.0 glLineWidth(2.0),讲元立方体线框沿x轴正方向平移2.0这里用到了平移函数    glTranslatef(2.0f,0.0f,0.0f); 参数1 :x轴  参数2 :y轴 参数3 : z轴

    3.跟2 差不多就不写了

     

     

     

    // 提示:在合适的地方修改或添加代码
    #include <GL/freeglut.h>
    #include<stdio.h>
    
    // 评测代码所用头文件-开始
    #include<opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    // 评测代码所用头文件-结束
    
    GLint winWidth = 400, winHeight =400 ; 	      //设置初始化窗口大小
    
    /*观察坐标系参数设置*/
    GLfloat x0 = 0.0, yy = 0.0, z0 = 5.0;	   //设置观察坐标系原点 
    GLfloat xref = 0.0, yref = 0.0, zref = 0.0;	//设置观察坐标系参考点(视点) 
    GLfloat Vx = 0.0, Vy = 1.0, Vz = 0.0;	   //设置观察坐标系向上向量(y轴) 
    
    /*观察体参数设置 */
    GLfloat xwMin = -1.0, ywMin = -1.0, xwMax = 1.0, ywMax = 1.0;//设置裁剪窗口坐标范围
    GLfloat dnear = 1.5, dfar = 20.0;	      //设置远、近裁剪面深度范围
    
    void init(void)
    {
        glClearColor(0.0, 0.0, 0.0, 0.0);
    }
    void display(void)
    {
        glClear(GL_COLOR_BUFFER_BIT);
    
        glLoadIdentity();
        /*观察变换*/
        gluLookAt(x0, yy, z0, xref, yref, zref, Vx, Vy, Vz);        //指定三维观察参数
    
        // 请在此添加你的代码
        /********** Begin ********/
        glPushMatrix();
        glColor3f(1.0, 0.0, 0.0);
        glutWireCube(1.0);
        glPopMatrix();
    
        glPushMatrix();
        glColor3f(0.0, 1.0, 0.0);
        glLineWidth(2.0);
        glTranslatef(2.0f,0.0f,0.0f);
        glutWireCube(1.0);
        glPopMatrix();
    
        glPushMatrix();
        glColor3f(0.0, 0.0, 1.0);
        glLineWidth(2.0);
        glTranslatef(-2.0f,0.0f,0.0f);
        glutSolidCube(1.0);
        glPopMatrix();
    
        /********** End **********/
        glFlush();
    }
    
    void reshape(GLint newWidth, GLint newHeight)
    {
        /*视口变换*/
        glViewport(0, 0, newWidth, newHeight);	//定义视口大小
    
        /*投影变换*/
        glMatrixMode(GL_PROJECTION);
    
        glLoadIdentity();
    
        /*透视投影,设置透视观察体*/
    	glFrustum(xwMin, xwMax, ywMin, ywMax, dnear, dfar);
    
        /*模型变换*/
        glMatrixMode(GL_MODELVIEW);
        
        winWidth = newWidth;
        winHeight = newHeight;
    }
    int main(int argc, char* argv[])
    {
        GLubyte* pPixelData = (GLubyte*)malloc(800 * 400 * 3);//分配内存
        GLint viewport[4] = { 0 };
        glutInit(&argc, argv);
        glutInitWindowPosition(100, 100);
        glutInitWindowSize( 400 , 400 );        //设置初始化窗口大小
        glutCreateWindow("三维观察");
        init();
        glutDisplayFunc(display);
        glutReshapeFunc(reshape);
        glutMainLoopEvent();
    
    
    
    
    
        /*************以下为评测代码,与本次实验内容无关,请勿修改**************/
        glReadBuffer(GL_FRONT);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
        glGetIntegerv(GL_VIEWPORT, viewport);
        glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGB, GL_UNSIGNED_BYTE, pPixelData);
        cv::Mat img;
        std::vector<cv::Mat> imgPlanes;
        img.create(400, 400, CV_8UC3);
        cv::split(img, imgPlanes);
    
        for (int i = 0; i < 400; i++) {
            unsigned char* plane0Ptr = imgPlanes[0].ptr<unsigned char>(i);
            unsigned char* plane1Ptr = imgPlanes[1].ptr<unsigned char>(i);
            unsigned char* plane2Ptr = imgPlanes[2].ptr<unsigned char>(i);
            for (int j = 0; j < 400; j++) {
                int k = 3 * (i * 400 + j);
                plane2Ptr[j] = pPixelData[k];
                plane1Ptr[j] = pPixelData[k + 1];
                plane0Ptr[j] = pPixelData[k + 2];
            }
        }
        cv::merge(imgPlanes, img);
        cv::flip(img, img, 0);
        cv::namedWindow("openglGrab");
        cv::imshow("openglGrab", img);
        //cv::waitKey();
        cv::imwrite("../img_step1/test.jpg", img);
        return 0;
    }

     

    视点改为(1.0,1.5,8.0),观察中心改为在(0, 0 ,0),向上矢量改为(0, 1, 0);

     

    实验二:code;
     

    // 提示:在合适的地方修改或添加代码
    #include <GL/freeglut.h>
    #include<stdio.h>
    
    // 评测代码所用头文件-开始
    #include<opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    // 评测代码所用头文件-结束
    
    GLint winWidth = 400, winHeight =400 ; 	      //设置初始化窗口大小
    
    /*观察坐标系参数设置*/
    GLfloat x0 = 1.0, yy = 1.5, z0 = 8.0;	   //设置观察坐标系原点 
    GLfloat xref = 0.0, yref = 0.0, zref = 0.0;	//设置观察坐标系参考点(视点) 
    GLfloat Vx = 0.0, Vy = 1.0, Vz = 0.0;	   //设置观察坐标系向上向量(y轴) 
    
    /*观察体参数设置 */
    GLfloat xwMin = -1.0, ywMin = -1.0, xwMax = 1.0, ywMax = 1.0;//设置裁剪窗口坐标范围
    GLfloat dnear = 1.5, dfar = 20.0;	      //设置远、近裁剪面深度范围
    
    void init(void)
    {
        glClearColor(0.0, 0.0, 0.0, 0.0);
    }
    void display(void)
    {
        glClear(GL_COLOR_BUFFER_BIT);
    
        glLoadIdentity();
        /*观察变换*/
        gluLookAt(x0, yy, z0, xref, yref, zref, Vx, Vy, Vz);        //指定三维观察参数
    
        // 请在此添加你的代码
        /********** Begin ********/
    
        glPushMatrix();
        glColor3f(1.0, 0.0, 0.0);
        glutWireCube(1.0);
        glPopMatrix();
    
        glPushMatrix();
        glColor3f(0.0, 1.0, 0.0);
        glLineWidth(2.0);
        glTranslatef(2.0f,0.0f,0.0f);
        glutWireCube(1.0);
        glPopMatrix();
    
        glPushMatrix();
        glColor3f(0.0, 0.0, 1.0);
        glLineWidth(2.0);
        glTranslatef(-2.0f,0.0f,0.0f);
        glutSolidCube(1.0);
        glPopMatrix();
        /********** End **********/
        glFlush();
    }
    
    void reshape(GLint newWidth, GLint newHeight)
    {
        /*视口变换*/
        glViewport(0, 0, newWidth, newHeight);	//定义视口大小
    
        /*投影变换*/
        glMatrixMode(GL_PROJECTION);
    
        glLoadIdentity();
    
        /*透视投影,设置透视观察体*/
    	 gluPerspective( 45, 1, 1, 100);
    
        /*模型变换*/
        glMatrixMode(GL_MODELVIEW);
        
        winWidth = newWidth;
        winHeight = newHeight;
    }
    int main(int argc, char* argv[])
    {
        GLubyte* pPixelData = (GLubyte*)malloc(800 * 400 * 3);//分配内存
        GLint viewport[4] = { 0 };
        glutInit(&argc, argv);
        glutInitWindowPosition(100, 100);
        glutInitWindowSize( 400 , 400 );        //设置初始化窗口大小
        glutCreateWindow("三维观察");
        init();
        glutDisplayFunc(display);
        glutReshapeFunc(reshape);
        glutMainLoopEvent();
    
    
    
    
    
        /*************以下为评测代码,与本次实验内容无关,请勿修改**************/
        glReadBuffer(GL_FRONT);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
        glGetIntegerv(GL_VIEWPORT, viewport);
        glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGB, GL_UNSIGNED_BYTE, pPixelData);
        cv::Mat img;
        std::vector<cv::Mat> imgPlanes;
        img.create(400, 400, CV_8UC3);
        cv::split(img, imgPlanes);
    
        for (int i = 0; i < 400; i++) {
            unsigned char* plane0Ptr = imgPlanes[0].ptr<unsigned char>(i);
            unsigned char* plane1Ptr = imgPlanes[1].ptr<unsigned char>(i);
            unsigned char* plane2Ptr = imgPlanes[2].ptr<unsigned char>(i);
            for (int j = 0; j < 400; j++) {
                int k = 3 * (i * 400 + j);
                plane2Ptr[j] = pPixelData[k];
                plane1Ptr[j] = pPixelData[k + 1];
                plane0Ptr[j] = pPixelData[k + 2];
            }
        }
        cv::merge(imgPlanes, img);
        cv::flip(img, img, 0);
        cv::namedWindow("openglGrab");
        cv::imshow("openglGrab", img);
        //cv::waitKey();
        cv::imwrite("../img_step2/test.jpg", img);
        return 0;
    }

     

     

     

    实验三:

    code:

    // 提示:在合适的地方修改或添加代码
    #include <GL/freeglut.h>
    #include<stdio.h>
    
    // 评测代码所用头文件-开始
    #include<opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    // 评测代码所用头文件-结束
    
    GLint winWidth = 400, winHeight =400 ; 	      //设置初始化窗口大小
    
    /*观察坐标系参数设置*/
    GLfloat x0 = 0.0, yy = 0.0, z0 = 5.0;	   //设置观察坐标系原点 
    GLfloat xref = 0.0, yref = 0.0, zref = 0.0;	//设置观察坐标系参考点(视点) 
    GLfloat Vx = 0.0, Vy = 1.0, Vz = 0.0;	   //设置观察坐标系向上向量(y轴) 
    
    /*观察体参数设置 */
    GLfloat xwMin = -1.0, ywMin = -1.0, xwMax = 1.0, ywMax = 1.0;//设置裁剪窗口坐标范围
    GLfloat dnear = 1.5, dfar = 20.0;	      //设置远、近裁剪面深度范围
    
    void init(void)
    {
        glClearColor(0.0, 0.0, 0.0, 0.0);
    }
    void display(void)
    {
        glClear(GL_COLOR_BUFFER_BIT);
    
        glLoadIdentity();
        /*观察变换*/
        gluLookAt(x0, yy, z0, xref, yref, zref, Vx, Vy, Vz);        //指定三维观察参数
    
        // 请在此添加你的代码
        /********** Begin ********/
        glPushMatrix();
        glColor3f(1.0, 0.0, 0.0);
        glutWireCube(1.0);
        glPopMatrix();
    
        glPushMatrix();
        glColor3f(0.0, 1.0, 0.0);
        glLineWidth(2.0);
        glTranslatef(2.0f,0.0f,0.0f);
        glRotatef(-30.0f,1.0f,0.0f,0.0f);
        glutWireCube(1.0);
        
        glPopMatrix();
    
        glPushMatrix();
        glColor3f(0.0, 0.0, 1.0);
        // glLineWidth(2.0);
        glTranslatef(-2.0f,0.0f,0.0f);
        glutSolidCube(1.0);
        glPopMatrix();
    
        /********** End **********/
        glFlush();
    }
    
    void reshape(GLint newWidth, GLint newHeight)
    {
        /*视口变换*/
        glViewport(0, 0, newWidth, newHeight);	//定义视口大小
    
        /*投影变换*/
        glMatrixMode(GL_PROJECTION);
    
        glLoadIdentity();
    
        /*透视投影,设置透视观察体*/
    	glFrustum(xwMin, xwMax, ywMin, ywMax, dnear, dfar);
    
        /*模型变换*/
        glMatrixMode(GL_MODELVIEW);
        
        winWidth = newWidth;
        winHeight = newHeight;
    }
    int main(int argc, char* argv[])
    {
        GLubyte* pPixelData = (GLubyte*)malloc(800 * 400 * 3);//分配内存
        GLint viewport[4] = { 0 };
        glutInit(&argc, argv);
        glutInitWindowPosition(100, 100);
        glutInitWindowSize( 400 , 400 );        //设置初始化窗口大小
        glutCreateWindow("三维观察");
        init();
        glutDisplayFunc(display);
        glutReshapeFunc(reshape);
        glutMainLoopEvent();
    
    
    
    
    
        /*************以下为评测代码,与本次实验内容无关,请勿修改**************/
        glReadBuffer(GL_FRONT);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
        glGetIntegerv(GL_VIEWPORT, viewport);
        glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGB, GL_UNSIGNED_BYTE, pPixelData);
        cv::Mat img;
        std::vector<cv::Mat> imgPlanes;
        img.create(400, 400, CV_8UC3);
        cv::split(img, imgPlanes);
    
        for (int i = 0; i < 400; i++) {
            unsigned char* plane0Ptr = imgPlanes[0].ptr<unsigned char>(i);
            unsigned char* plane1Ptr = imgPlanes[1].ptr<unsigned char>(i);
            unsigned char* plane2Ptr = imgPlanes[2].ptr<unsigned char>(i);
            for (int j = 0; j < 400; j++) {
                int k = 3 * (i * 400 + j);
                plane2Ptr[j] = pPixelData[k];
                plane1Ptr[j] = pPixelData[k + 1];
                plane0Ptr[j] = pPixelData[k + 2];
            }
        }
        cv::merge(imgPlanes, img);
        cv::flip(img, img, 0);
        cv::namedWindow("openglGrab");
        cv::imshow("openglGrab", img);
        //cv::waitKey();
        cv::imwrite("../img_step4/test.jpg", img);
        return 0;
    }

     

     

     

    实验四:

    code:

    // 提示:在合适的地方修改或添加代码
    #include <GL/freeglut.h>
    #include<stdio.h>
    
    // 评测代码所用头文件-开始
    #include<opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    // 评测代码所用头文件-结束
    
    GLint winWidth = 400, winHeight =400 ; 	      //设置初始化窗口大小
    
    /*观察坐标系参数设置*/
    GLfloat x0 = 0.0, yy = 0.0, z0 = 5.0;	   //设置观察坐标系原点 
    GLfloat xref = 0.0, yref = 0.0, zref = 0.0;	//设置观察坐标系参考点(视点) 
    GLfloat Vx = 0.0, Vy = 1.0, Vz = 0.0;	   //设置观察坐标系向上向量(y轴) 
    
    /*观察体参数设置 */
    GLfloat xwMin = -1.0, ywMin = -1.0, xwMax = 1.0, ywMax = 1.0;//设置裁剪窗口坐标范围
    GLfloat dnear = 1.5, dfar = 20.0;	      //设置远、近裁剪面深度范围
    
    void init(void)
    {
        glClearColor(0.0, 0.0, 0.0, 0.0);
    }
    void display(void)
    {
        glClear(GL_COLOR_BUFFER_BIT);
    
        glLoadIdentity();
        /*观察变换*/
        gluLookAt(x0, yy, z0, xref, yref, zref, Vx, Vy, Vz);        //指定三维观察参数
    
        // 请在此添加你的代码
        /********** Begin ********/
    
          glPushMatrix();
        glColor3f(1.0, 0.0, 0.0);
        glutWireCube(1.0);
        glPopMatrix();
    
        glPushMatrix();
        glColor3f(0.0, 1.0, 0.0);
        glLineWidth(2.0);
        glTranslatef(2.0f,0.0f,0.0f);
        // glRotatef(-30.0f,1.0f,0.0f,0.0f);
        glutWireCube(1.0);
        
        glPopMatrix();
    
        glPushMatrix();
        glColor3f(0.0, 0.0, 1.0);
        // glLineWidth(2.0);
        glTranslatef(-2.0f,0.0f,0.0f);
        glutSolidCube(1.0);
        glPopMatrix();
    
        /********** End **********/
        glFlush();
    }
    
    void reshape(GLint newWidth, GLint newHeight)
    {
        /*视口变换*/
        glViewport(0, 0, newWidth, newHeight);	//定义视口大小
    
        /*投影变换*/
        glMatrixMode(GL_PROJECTION);
    
        glLoadIdentity();
    
        /*平行投影*/
        glOrtho(-3.0 ,3.0 ,-3.0 ,3.0 ,-100.0 ,100.0 );
    
        /*模型变换*/
        glMatrixMode(GL_MODELVIEW);
        
        winWidth = newWidth;
        winHeight = newHeight;
    }
    int main(int argc, char* argv[])
    {
        GLubyte* pPixelData = (GLubyte*)malloc(800 * 400 * 3);//分配内存
        GLint viewport[4] = { 0 };
        glutInit(&argc, argv);
        glutInitWindowPosition(100, 100);
        glutInitWindowSize( 400 , 400 );        //设置初始化窗口大小
        glutCreateWindow("三维观察");
        init();
        glutDisplayFunc(display);
        glutReshapeFunc(reshape);
        glutMainLoopEvent();
    
    
    
    
    
        /*************以下为评测代码,与本次实验内容无关,请勿修改**************/
        glReadBuffer(GL_FRONT);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
        glGetIntegerv(GL_VIEWPORT, viewport);
        glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGB, GL_UNSIGNED_BYTE, pPixelData);
        cv::Mat img;
        std::vector<cv::Mat> imgPlanes;
        img.create(400, 400, CV_8UC3);
        cv::split(img, imgPlanes);
    
        for (int i = 0; i < 400; i++) {
            unsigned char* plane0Ptr = imgPlanes[0].ptr<unsigned char>(i);
            unsigned char* plane1Ptr = imgPlanes[1].ptr<unsigned char>(i);
            unsigned char* plane2Ptr = imgPlanes[2].ptr<unsigned char>(i);
            for (int j = 0; j < 400; j++) {
                int k = 3 * (i * 400 + j);
                plane2Ptr[j] = pPixelData[k];
                plane1Ptr[j] = pPixelData[k + 1];
                plane0Ptr[j] = pPixelData[k + 2];
            }
        }
        cv::merge(imgPlanes, img);
        cv::flip(img, img, 0);
        cv::namedWindow("openglGrab");
        cv::imshow("openglGrab", img);
        //cv::waitKey();
        cv::imwrite("../img_step3/test.jpg", img);
        return 0;
    }

     

     

    实验五:

    code;
     

    // 提示:在合适的地方修改或添加代码
    #include <GL/freeglut.h>
    #include<stdio.h>
    
    // 评测代码所用头文件-开始
    #include<opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    // 评测代码所用头文件-结束
    
    GLint winWidth = 800, winHeight = 400; 	      //设置初始化窗口大小
    
    /*观察坐标系参数设置*/
    GLfloat x0 = 0.0, yy = 0.0, z0 = 5.0;	   //设置观察坐标系原点 
    GLfloat xref = 0.0, yref = 0.0, zref = 0.0;	//设置观察坐标系参考点(视点) 
    GLfloat Vx = 0.0, Vy = 1.0, Vz = 0.0;	   //设置观察坐标系向上向量(y轴) 
    
    /*观察体参数设置 */
    GLfloat xwMin = -1.0, ywMin = -1.0, xwMax = 1.0, ywMax = 1.0;//设置裁剪窗口坐标范围
    GLfloat dnear = 1.5, dfar = 20.0;	      //设置远、近裁剪面深度范围
    
    void init(void)
    {
        glClearColor(0.0, 0.0, 0.0, 0.0);
    }
    void display(void)
    {
        glClear(GL_COLOR_BUFFER_BIT);
    
        glLoadIdentity();
        /*观察变换*/
        gluLookAt(x0, yy, z0, xref, yref, zref, Vx, Vy, Vz);        //指定三维观察参数
    
        // 请在此添加你的代码
        /********** Begin ********/
    
        glPushMatrix();
        glColor3f(1.0, 0.0, 0.0);
        glutWireCube(1.0);
        glPopMatrix();
    
        glPushMatrix();
        glColor3f(0.0, 1.0, 0.0);
        glLineWidth(2.0);
        glTranslatef(2.0f,0.0f,0.0f);
        // glRotatef(-30.0f,1.0f,0.0f,0.0f);
        glutWireCube(1.0);
        
        glPopMatrix();
    
        glPushMatrix();
        glColor3f(0.0, 0.0, 1.0);
        // glLineWidth(2.0);
        glTranslatef(-2.0f,0.0f,0.0f);
        glutSolidCube(1.0);
        glPopMatrix();
    
    
    
    
    
        /********** End **********/
        glFlush();
    }
    
    void reshape(GLint newWidth, GLint newHeight)
    {
        /*视口变换*/
        glViewport(0, 0, newWidth, newHeight);	//定义视口大小
    
        /*投影变换*/
        glMatrixMode(GL_PROJECTION);
    
        glLoadIdentity();
    
        /*透视投影,设置透视观察体*/
        gluPerspective( 45, 2, 1, 100);
    
        /*模型变换*/
        glMatrixMode(GL_MODELVIEW);
        
        winWidth = newWidth;
        winHeight = newHeight;
    }
    int main(int argc, char* argv[])
    {
        GLubyte* pPixelData = (GLubyte*)malloc(800 * 400 * 3);//分配内存
        GLint viewport[4] = { 0 };
        glutInit(&argc, argv);
        glutInitWindowPosition(100, 100);
        glutInitWindowSize( winWidth , winHeight );        //设置初始化窗口大小
        glutCreateWindow("三维观察");
        init();
        glutDisplayFunc(display);
        glutReshapeFunc(reshape);
        glutMainLoopEvent();
    
    
    
    
    
        /*************以下为评测代码,与本次实验内容无关,请勿修改**************/
        glReadBuffer(GL_FRONT);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
        glGetIntegerv(GL_VIEWPORT, viewport);
        glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGB, GL_UNSIGNED_BYTE, pPixelData);
        cv::Mat img;
        std::vector<cv::Mat> imgPlanes;
        img.create(400, 800, CV_8UC3);
        cv::split(img, imgPlanes);
    
        for (int i = 0; i < 400; i++) {
            unsigned char* plane0Ptr = imgPlanes[0].ptr<unsigned char>(i);
            unsigned char* plane1Ptr = imgPlanes[1].ptr<unsigned char>(i);
            unsigned char* plane2Ptr = imgPlanes[2].ptr<unsigned char>(i);
            for (int j = 0; j < 800; j++) {
                int k = 3 * (i * 800 + j);
                plane2Ptr[j] = pPixelData[k];
                plane1Ptr[j] = pPixelData[k + 1];
                plane0Ptr[j] = pPixelData[k + 2];
            }
        }
        cv::merge(imgPlanes, img);
        cv::flip(img, img, 0);
        cv::namedWindow("openglGrab");
        cv::imshow("openglGrab", img);
        //cv::waitKey();
        cv::imwrite("../img_step5/test.jpg", img);
        return 0;
    }

     

     

     

    展开全文
  • 本文一些用于均参考《OpenGL编程指南(第8版)》,有兴趣的同学可以结合一起看。这篇算是整合补充。...(模型变换,Model Transform)将相机移动到准备拍摄的位置,将它对准某个方向。(视图变换,View Trans

    本文一些用于均参考《OpenGL编程指南(第8版)》,有兴趣的同学可以结合一起看。这篇算是整合补充。

      OpenGL采用的是相机模型,就是把视图变换操作类比为使用照相机拍摄照片的过程,具体步骤如下(这里和红宝书有一些改变):

    1. 将准备拍摄的对象移动到场景中指定位置。(模型变换,Model Transform)
    2. 将相机移动到准备拍摄的位置,将它对准某个方向。(视图变换,View Transform)
    3. 设置相机的焦距,或者调整缩放比例。(投影变换,Projection Transform)
    4. 拍摄照片并变换到需要的图片大小。(这个是视口变换,我们这里不做讨论)

      在这四个步骤中,前三个都是可以用变换矩阵实现的。看完本文之后基本就可以用自己写的矩阵来代替OpenGL里面的各种变换操作了。至于为什么要按照上述的顺序可以参考下面代码,有兴趣的读者可以调换一下位置试一试,其中投影变换矩阵一定要放在最左边。

    1
    2
    3
    4
    /*Project:投影变换矩阵
         View:视图变换矩阵
         Model:模型变换矩阵*/
    ModelViewProject = Project * View * Model;

     


      下面需要知道一些矩阵和齐次坐标的相关知识,网上有很多资料,如果又不知道的可以提前了解下。

      这里有必要深入讲解一下齐次坐标的意义。假设一3维向量x,我们对这个向量进行线性变换Tx,但无论T为任意3*3矩阵,都无法完成向量的平移操作(比如x=(0,0,0)Tx无论如何也不可能变换到(1,0,0))。这里我们就需要用到齐次坐标,使用齐次坐标的目的也就是通过增加额外的数值来完成向量的平移操作。熟悉OpenGL的已经知道了,将三维数据植入思维坐标空间当中就行了。就是将x=(0,0,0)转变成x=(0,0,0,1)。T也转变成4*4矩阵。

      我们再讨论一下齐次坐标的一个性质, 齐次坐标所表达的其实是方向而不是位置。比如x1=(1,2,3,1)x2=(2,4,6,2)其实在3维坐标系中表示的是同一个位置。此外,最后一个分量w越大,那么齐次坐标将处于更远的位置。当OpenGL准备显示集合体的时候,它会使用左后一个分量除以前三个分量,从而将其次坐标重新变换到3维坐标系中,因此w越大的物体也会显示的越小。w0.0的时候,由于坐标位于无限近的位置,以至于显示可能会呈现出无限大。这个概念非常重要,因为下面投影矩阵就是利用这个概念!

      我们接下来说明三个变换中具体包含哪些矩阵。对于一个顶点来说,我们对其先进行模型变换,在进行视图变换和投影变换。我们将这些变换细分一下就可以得到如下代码:

    1
    2
    3
    4
    5
    /*S:缩放 R:旋转 T:平移*/
    ModelViewProject = Project
              * ViewR * ViewT
              * ModelS * ModelR * ModelT
    gl_position = ModelViewProject * Vertex;

      


       下面我就按照顺序逐一介绍一下各个矩阵,先介绍一下模型变换矩阵,这部分我就不细说了,了解的人应该不少。

      (1)ModelT 平移矩阵

      这个矩阵算是最简单的矩阵,这里举个例子。假设我们要将v=(x,y,z)沿x轴正方向平移2.5,具体计算如下:

    图1

      (2)ModelR 旋转矩阵

      这个矩阵就比较多样了,我以前的博客关于这个矩阵有不少分析,这里也不多说了。下面列出三个欧拉角对应的旋转矩阵(也是最常用的):

    图2

      (3)ModelS 缩放矩阵

      这个矩阵也比较简单,只要把缩放因子factor放在矩阵的对角线上就行了。具体表达如下:

    图3

      这三个矩阵就组成了模型变换矩阵,根据具体需求可以随意组合平移旋转缩放矩阵,并不一定非要按照上面代码中的顺序。

     


      下面介绍视图矩阵,我们可以把这一步假想为设置相机或者人眼(View)的位置。设置相机一般只需要两步,首先把相机移动到固定的位置,然后把当前坐标系转化成相机坐标系。这里不像模型变换,可以多次旋转平移,这里只需要1个平移操作和1个旋转操作一般就可以了,而且还不需要缩放。

      (1)ViewT 平移矩阵

      这个矩阵和ModelT正好是相反的。我们可以这样理解,如果要在世界坐标系下的(x,y,z)位置设置相机,那么我们把相机再移回世界坐标系原点的位移就是(-x,-y,-z)。所以我们当以相机为坐标原点的时候,所有在原来坐标系下的物体都要加上这个负的平移分量。那么这个平移矩阵如下:

    图4

      (2)ViewR 旋转矩阵

      我们将原来的坐标系转变成相机坐标系,不仅需要平移到相机位置,还要旋转到相机的朝向。如图5所示,我们要将蓝色的坐标系通过旋转变换成红色的相机坐标系。由于坐标系的三个基向量都是单位化的,所以最简单的办法就是点乘。做法是点乘相机坐标系的三个基向量,具体看图6的公式。


    图5

    图6

     

      其中V指向相机坐标系的y轴,U指向相机坐标系的x轴,N指向相机坐标系的z轴。

      这两个矩阵就组成了视图变换矩阵,View乘以Model得到的矩阵,在OpenGL固定管线中称为模型视图矩阵,可以通过glMatrixMode(GL_MODELVIEW)进行设置。

    展开全文
  • 模型变换,是指变换被照物体的位置,角度。 这两个变换,都会影响最终图形中,物体的位置,角度。而这两个变换,可以达到相同的效果。比如,你想要一个倒着的水杯图形,可以把你自己倒立,这样看到的水杯就是倒立的...

    视图变换,是指变换照相机的位置,角度。 
    模型变换,是指变换被照物体的位置,角度。

    这两个变换,都会影响最终图形中,物体的位置,角度。而这两个变换,可以达到相同的效果。比如,你想要一个倒着的水杯图形,可以把你自己倒立,这样看到的水杯就是倒立的了。或者把水杯倒立,自己直立,也能看到倒立的水杯。 
    这里写图片描述 
    如图所示,这两种变换,可以看做达到目的的不同途径。甚至可以同时使用视图变换和模型变换,只要最终拿到了我们想要的图像就可以了。至于使用的是视图变换,还是模型变换,看我们理解问题的角度。

    3.2.1 对变换进行思考

    变换顺序,对最终的结果影响很大。 
    看下面的例子: 
    这里写图片描述 
    图中有两个操作,旋转和移动。一个是沿原点绕z轴逆时针旋转45度,另一个是沿x轴向下平移。左图中,是先旋转,再移动,物体最终在x轴上。右图中,是先移动,再旋转,物体最终在x=y轴上。变换顺序不同,导致物体最终位置不同,这就是变换顺序的影响。

    变换顺序,在OpenGL中的具体实现。 
    在OpenGL中,所有的变换,都是通过矩阵来实现的。一个矩阵,表示一个或多个变换。模型视图变换,是通过模型视图矩阵来实现的。由于这个矩阵经常变换,需要进行管理,OpenGL中是通过矩阵堆栈来对矩阵进行管理的。

    当前模型视图矩阵如果用C来表示,在当前模型视图基础上,进行一个变换,这个变换使用的矩阵为M。那么一个顶点v的变换之后的坐标为CMv。也就是说,M变换先作用于顶点v,然后再是当前模型视图矩阵C。

    看下面例子:

    glMatrixMode(GL_MODELVIEW);
    glLoadIndentity();
    glMultMatrixf(N);       //变换N
    glMultMatrixf(M);       //变换M
    glMultMatrixf(L);       //变换L
    glBegin(GL_POINTS);
    glVertex3f(v);
    glEnd();

    这段代码中,模型视图矩阵按顺序分别包含了I, N, NM,最后是NML,其中I表示单位矩阵。经过变换的顶点是NMLv。因此,顶点变换就是N(M(Lv)),也就是说,v首先与L相乘,Lv再与M相乘,MLv再与N相乘,而不是按它们指定的顺序出现的。

    全局固定坐标系

    图3-4中,左图中的先旋转,再平移,代码实现如下:

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMultMatrixf(T);   //平移
    glMultMatrixf(R);   //旋转
    draw_the_object();

    只要记住一点,物体的变换顺序,和矩阵的出现顺序,正好相反。

    局部移动坐标系统

    这个通常用于模型的关节控制。比如机器人手臂。比如画汽车轮子上的螺钉,这个螺钉的位置,相对于汽车轮子,这个轮子上建立的坐标系,叫局部移动坐标系。而轮子的位置,又是相对于汽车本身,最后,汽车本身,是在全局坐标系中指定。

    3.2.2 模型变换

    模型变换,主要涉及三个函数,移动、旋转、缩放。有了这三个函数的组合,我们可以进行任意变换。

    void glTranslate{fd}(TYPE x, TYPE y, TYPE z);
    void glRotate{fd}(TYPE angle, TYPE x, TYPE y, TYPE z);
    void glScale{fd}(TYPE x, TYPE y, TYPE z);

    这三个函数,之前接触过。glTranslate,进行平移,平移的偏移量,由(x, y, z)指定。 glRotate,旋转,以逆时针方向绕着从原点到点(x, y, z)的直线旋转角度angle。glScale,按照一定比列进行缩放,比例在x轴,y轴,z轴方向上的量是(x, y, z)。

    3.2.3 视图变换

    视图变换,相关的三个函数是glTranslate,glRotate和gluLookAt函数。

    这个glTranslate和glRotate在模型变换中,我们已经见过了。怎么模型变换和视图变换,使用的是同样的函数呢?因为变换是相对的。比如,让模型和照相机距离5个单位长度,假设模型和照相机放在一起。我们可以将物体向前移动5个单位长度,也可以将照相机向后移动5个单位长度。所以,视图变换,也是使用glTranslate和glRotate。只是这个参数的含义相反罢了。 
    比如

    glTranslatef(0.0, 0.0, -5.0);

    这个函数在场景中把物体沿z轴移动-5个单位,相当于把照相机沿z轴移动+5个单位。

    使用工具函数gluLookAt()

    void gluLookAt(
        GLdouble eyex, GLdouble eyey, GLdouble eyez,
        GLdouble centerx, GLdouble centery, GLdouble centerz,
        GLdouble upx, GLdouble upy, GLdouble upz);

    定义一个视图矩阵,并把它与当前矩阵进行右乘。目标观察点eyex, eyey, eyez。centerx, centery和centerz参数指定了视线上的任意一点。upx,upy和upz参数表示哪个方向是朝上的(也就是说,在视景体中自底向上的方向)。

    默认情况下,照相机位于原点,指向z轴的负方向,以y轴的正方向为朝上方向。相当于调用:

    gluLookAt(0.0, 0.0, 0.0, 0.0, 0.0, -100.0, 0.0, 1.0, 0.0);

    参考点的z值是-100.0, 但它也可以是任意的负值,因为它不会影响视线的方向。

    下面是使用gluLookAt的一个例子。

    gluLookAt(4.0, 2.0, 1.0, 2.0, 4.0, -3.0, 2.0, 2.0, -1.0);

    <img src="https://img-blog.csdn.net/20150315175335303" alt="这里写图片描述" title="" style="font-family: 'microsoft yahei'; line-height: 26px; border: none; max-width: 100%; background-color: rgb(255, 255, 255);" /><span style="font-family: 'microsoft yahei'; line-height: 26px; background-color: rgb(255, 255, 255);"> </span>

    这个函数,将摄像机移动到了(4.0, 2.0, 1.0)这个点,摄像机朝向(2.0, 4.0, -3.0)方向,摄像机向上的方向为(2.0, 2.0, -1.0)。

    强烈推荐,理解这几个函数(glTranslate, glRotate, gluLookAt),使用Nate Robin的教程。网上有下的。

    创建自定义的工具函数

    创建自定义的函数,其实就是在自己的函数中,调用glTranslate和glRotate这两个函数。 
    要创建自定义的工具函数,主要是弄清楚两个东西:一个是参数是相对于哪个坐标系的,第二个就是照相机的变换顺序,与glTranslate,glRotate出现的顺序相同。

    比如编写一个飞机模拟器,并且以飞机的驾驶员座位观察点显示飞机外面的景象。我们可以用一个圆点位于跑道上的坐标系统来描述整个场景,飞机相对于坐标(x, y, z)。然后,假设飞机还有倾侧角、螺旋角和航向改变角(这些都是飞机相对于它的重心的旋转角度)。下面这个函数可以作用视图变换函数使用。

    void pilotView(GLdouble planex, GLdouble planey, GLdouble planez, GLdouble roll, GLdouble pitch, GLdouble heading)
    {
        glRotated(roll, 0.0, 0.0, 1.0);
        glRotated(pitch, 0.0, 1.0, 0.0);
        glRotated(heading, 1.0, 0.0, 0.0);
        glTranslated(-planex, -planey, -planez);
    }

    这个其实很好理解,因为这个roll, pitch, heading,都是相对于飞机的。而飞机,就是我们的相机。所以,先将飞机旋转到一定角度,然后移动到点(planex, planey, planez)上面。因为这个planex, planey, planez是相对于机场跑道的坐标系,而我们移动的飞机相当于相机,所以都要取负号。

    **********************      投影变换    ********************************

    投影变换的目的是定义一个视景体。视景体定义了场景的可视范围,以及场景如何投射到屏幕上。投影有两种:透视投影 和 正交投影。相关的函数有4个: GL库中两个, GLU库中两个。 GL库中的两个分别为glFrustum和glOrtho,GLU库中的两个分别为gluPerspective和gluOrtho2D。

    3.3.1 透视投影

    透视投影的特点就是,物体离照相机越远,它在最终图像中看上去就越小。即所谓的近大远小。

    void glFrustum(GLdouble left, GLdouble right,

                                 GLdouble bottom, GLdouble top,

                                  GLdouble near, GLdouble far);

    创建一个透视视图平截头体的矩阵,并把它与当前矩阵相乘。平截头体的视景体是由这个函数的参数定义的:(left, bottom, -near)和(right, top, -near)分别指定了近侧裁剪平面左上角和右下角(x, y, z)坐标。near 和far分别表示从观察点到近侧和远侧裁剪平面的距离,它们的值应该是正的。 

    这里写图片描述

    由于glFrustum使用起来并不很直观。所以通常使用GLU库中的gluPerspective()函数。

    void gluPerspective(GLdouble fovy,  GLdouble aspect,  GLdouble near,  GLdouble far);

    创建一个表示对称透视视图平截头体的矩阵,并把它与当前矩阵相乘。fovy是yz平面上视野的角度,它的取值范围是【0.0, 180.0】。aspect是这个平截头体的纵横比,也就是它的宽度除以高度。near和far分别是观察点与近侧裁剪平面以及远裁剪平面的距离(沿z轴负方向),这两个值都应该是正的。

    OpenGL中的数值,并没有单位,唯一的规则是必须使用一致的测量单位。

    3.3.2 正投影

    正投影,与透视投影不同,物体与照相机的距离并不影响它看上去的大小。这种类型的投影,常用于建筑蓝图,或者计算机辅助设计的应用程序。

    正投影使用的函数,叫glOrtho。

    void glOrtho(GLdouble left,  Gldouble right,  GLdouble bottom,  GLdouble top,  GLdouble near,  GLdouble far);

    这个函数的参数,和glFrustum函数的参数完全一样。只是最终定义的视景体不一样。glOrtho定义的视景体如下图所示。 

    这里写图片描述 
    其中(left, bottom, -near)和(right, top, -near)是近侧裁剪平面上的点,分别映射到视口的左下角和右上角。(left, bottom, -far)和(right, top, -far)是远侧裁剪平面上的点,分别映射到视口的左下角和右上角。near和far可以是正值或负值,甚至可以设置为0。但是,near和far不应该取相同的值。

    3.3.3 视景体裁剪

    定义了视景体之后,场景中物体的顶点通过模型视图矩阵和投影矩阵进行变换之后,位于视景体之外的所有图元都将裁剪掉。6个裁剪平面就是定义视景体6个侧面的平面。还可以自定义裁剪平面,参照3.7节。


    ****************************     视口变换    ********************************

    视口变换,相当于选择照片大小。视口是一个矩形的窗口区域,图像就是在这个区域中绘制的。

    3.4.1 定义视口

    定义视口,使用函数glViewport。这个函数之前用过好多遍了。

    void glViewport(GLint x,  GLint y,  GLsizei width,  GLsizei  height);

    在窗口中定义一个像素矩形,最终的图像将映射到这个矩形中。(x, y)参数指定了视口的左下角,width和height表示这个视口矩形的宽度和高度。在默认情况下,视口的初始值是(0, 0, winWidth, winHeight),其中winWidth和winHeight指定了窗口的大小。

    这里注意,视口的纵横比一般和视景体的纵横比相同。如果这两个纵横比不同,当图像投影到视口时就会变形。如下图所示。 
    这里写图片描述

    为了避免变形可以修改投影的纵横比,使它与视口相匹配。

    int  width = 400;
    int  height = 200;
    double aspect = width / height;
    gluPerspective(fovy, aspect, near, far);
    glViewport(0, 0, width, height);

    3.4.2 变换深度坐标

    在OpenGL中,深度坐标(z坐标)总是被认为位于0.0~1.0的范围之间。当我们需要将坐标范围限制的更小的时候,就需要变换深度坐标。使用的函数叫glDepthRange。

    void glDepthRange(GLclampd near,  GLclampd  far);

    为z坐标定义了一种编码形式,它是在视口变换期间执行的。near和far值表示经过调整后可以存储在深度缓冲区中的最小值和最大值。在默认情况下,它们分别是0.0和1.0,适用于绝大多数应用程序。这两个参数的范围被限定在【0, 1】之间。也就是说,我们可以使用这个函数,限定比【0,1】更小的范围,比如【0.2, 0.5】。


    转自:http://blog.csdn.net/snail_hunan/article/details/44278949

    展开全文
  • OpenGL学习脚印: 模型变换(model transformation)

    万次阅读 多人点赞 2016-05-29 17:12:55
    OpenGL中的坐标处理过程包括模型变换、视变换、投影变换、视口变换等内容,这个主题的内容有些多,因此分节学习,主题将分为5节内容来学习。本节主要学习模型变换。本节示例代码均可在我的github处下载。通过本节...
  • 模型变换和视图变换有关函数的使用方法和说明。透视原理解释说明,源程序,例程,运行效果。
  • 1,OpenGL渲染3D物体到屏幕上的过程其实类似我们平时用照相机拍照的过程,这个步骤大致如下:一,把照相机固定在三脚架并让它对准场景(视图变换)二,把场景中的物体调整摆放好(模型变换)三,选择照相机的镜头,并...
  • 模型变换 Void glTranslate(TYPE x,TYPE y,TYPE z); Void glRotate{fd}(TYPE angle,TYPE x,TYPE y,TYPE z); 把当前矩阵与一个表示移动物体的矩阵相乘,以逆时针方向绕着原点到(x,y,z)的直线旋转。Angle是旋转的...
  • 2)对场景安排,使得各个物体在招片中的位置是我们所希望的(模型变换) 3)选择照相机的镜头,并调整放大的倍数(投影变换) 4)确定最终照片的大小,放大(视口变换) 完成这些步骤就可以进行场景的绘制了。 在这...
  • OpenGL视点变换,模型变换,投影变换,视口变换详解 作者:luck_net | 出处:博客园 | 2012/2/22 14:46:49 | 阅读112次 OpenGL通过相机模拟、可以实现计算机图形学中最基本的三维变换,即几何变换、
  • opengl读取.obj三维模型(可自己创建放入.obj文件夹中),arcball实现鼠标点击实现模型变换,及键盘控制放大缩小。
  • 详细讲解了OpenGL\WebGL关于绘制场景的图形变换过程,并推导了其图形变换矩阵。主要包括模型变换、视图变换以及投影变换。
  • 模型变换、视图变换、投影变换、视口变换介绍 opengl中存在四种变换,分别是模型变换,视图变换,投影变换,视口变换。这四种变换是图形渲染的基本操作,实质上这四种变换都是由矩阵乘法表示(这些操作都是由一个4*...
  • OpenGL中模型变换的指令主要有glTranslatef(),glRotatef()和glScale(),其中最常用的是前两个。程序初始化时,世界坐标系和模型坐标系重合,模型变换的目的是变换模型坐标系和世界坐标系的位置关系,使之满足我们的...
  • 实验6 OpenGL模型视图变换

    千次阅读 2020-10-21 15:44:10
    1.实验目的: 学习了解三维图形几何变换原理。 理解掌握OpenGL三维图形几何变换...(3)请分别调整观察变换矩阵、模型变换矩阵和投影变换矩阵的参数,观察变换结果; (4)掌握三维观察流程、观察坐标系的确定、世界坐标
  • 本文一些用于均参考《OpenGL编程指南(第8版)》,有兴趣的同学可以结合一起看。这篇算是整合补充。...(模型变换,Model Transform)将相机移动到准备拍摄的位置,将它对准某个方向。(视图变换,View Trans
  • Advanced3D模型 3D模型,转换和光线追踪 作者:Pavlov Dmitry,dmitrypavlov74 @ gmail.com
  • opengl模型变换

    2009-04-20 09:34:56
    opengl模型变换,有几种模型,对opengl的初学者特别有用。
  • 浙江大学CAD重点实验室的 3D模型变换的VC源程序 浙江大学CAD重点实验室的 3D模型变换的VC源程序 浙江大学CAD重点实验室的 3D模型变换的VC源程序 浙江大学CAD重点实验室的 3D模型变换的VC源程序
  • 实验5 OpenGL模型视图变换

    千次阅读 2017-04-17 10:33:04
    1.实验目的:理解掌握OpenGL程序的模型视图变换。2.实验内容:(1)阅读实验原理,运行示范实验代码,理解掌握OpenGL程序的模型视图变换;(2)根据示范代码,尝试完成实验作业;3.实验原理:我们生活在一个三维...
  • 1.模型变换(Model transformation)---- 视点不变,变物体 我们在OpenGL中通过定义一组顶点来定义一个模型,我们遇到的平移、缩放、旋转等变换都可以理解为模型变换,视点不变,主要对物体进行操作。 在OpenGL中,...
  • 模型变换和视图变换

    千次阅读 2012-02-16 22:26:28
    由于模型和视图的变换都通过矩阵运算来实现,在进行变换前,应先设置当前操作的矩阵为“模型视图矩阵”。设置的方法是以GL_MODELVIEW为参数调用glMatrixMode 函数,像这样:glMatrixMode(GL_MODELVIEW); 通常,我们...
  • 实验原理我们生活在一个三维的世界——如果要...(模型变换)3、如果把物体画下来,我们可以选择:是否需要一种“近大远小”的透视效果。另外,我们可能只希望看到物体的一部分,而不是全部(指定看的范围)。(投影...
  • 带隔离的DC-DC变换器 基本的DC-DC变换器输出与输入之间存在直接电联系 反激变换器通过变压器先将电网电压整流滤波得到初级直流电压,再通过斩波或逆变电路将直流电变换成高频的脉冲或交流电,在经过高频变压器将其...
  • 从logit变换到logistic模型

    千次阅读 2020-03-15 11:35:31
    前面我们知道对数函数和对数函数的一些基本性质,也许你会问,为什么要引入对数函数?而且还是一个基本初等函数?这就要从logit变换说起。
  • OpenGL的视图和模型变换

    千次阅读 2013-09-03 19:26:08
    几经周折,最后选择从视图和模型变换下手。 OpenGL的两个坐标系 世界坐标系: 以窗口的中心为坐标原点,X轴正方向为屏幕从左至右,Y轴正方向为屏幕从下至上,Z轴正方向为屏幕指向你。 无论如何进行变换,世界坐标...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 193,231
精华内容 77,292
关键字:

模型变换