小白学opengl 第六课:纹理映射



  • 终于我们可以把图片贴到我们的图形上了

    0_1526631569328_QQ截图20180518161627.png

    纹理坐标

    当我们把图片贴到我们的正方形上,需要指定四个角的对应关系,这就是纹理坐标,通常,我们希望图片左下角对应正方形左下角,图片右上角对应正方形右上角,如果对应错了,图片就会上下颠倒,左右颠倒

    0_1526631721822_cube1.png

    把原来顶点颜色数组改为,顶点纹理坐标数组

    //VAO数据,顶点与颜色
        VertexData1 vcs[] = {
            //正面
            {QVector3D(-0.5f, 0.0f, 0.5f), QVector2D(0.0f, 0.0f)},     //1
            {QVector3D(0.5f, 0.0f, 0.5f), QVector2D(1.0f, 0.0f)},      //2
            {QVector3D(0.5f, 1.0f, 0.5f), QVector2D(1.0f, 1.0f)},      //3
            {QVector3D(-0.5f, 1.0f, 0.5f), QVector2D(0.0f, 1.0f)},     //4
    }
    

    索引

    //索引
        GLuint indices[] = { // 起始于0!
            0, 1, 2, 3,// face 1
    }
    

    常用步骤

    //1 使用glGenBuffers函数生成一个缓冲ID    
        glGenVertexArrays(1, &VAO);
        glGenBuffers(1, &VBO);
        glGenBuffers(1, &EBO);
        //2 绑定vao
        glBindVertexArray(VAO);
        //3 使用glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER缓冲类型上
        glBindBuffer(GL_ARRAY_BUFFER, VBO); //(绑定和解绑的顺序很重要,勿更改)
        //4 把用户定的义数据复制到当前绑定缓冲的函数
        glBufferData(GL_ARRAY_BUFFER, sizeof(vcs), vcs, GL_STATIC_DRAW);
    
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    
        //5 链接顶点属性
        //indx: 属性名
        //size: 顶点大小
        //type: 数据类型
        //normalized:数据被标准化
        //stride: 步长
        //ptr: 数据在缓冲中起始位置的偏移量
        glVertexAttribPointer(m_posAttr, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData1), (GLvoid*)0);
        glVertexAttribPointer(m_texcoordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData1), (GLvoid*)(sizeof(QVector3D)));
    //(GLvoid*)(sizeof(QVector3D)
        //6 解绑缓存着色器(绑定和解绑的顺序很重要,勿更改)
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    
        //7 解绑VAO
        glBindVertexArray(0);
    

    创建纹理

    用一张图片生成纹理,我们使用QOpenGLTexture来很方便的实现

    //使用图片的垂直镜像来创建纹理
        m_texture = new QOpenGLTexture(QImage(":/cube1.png").mirrored());
    
        //设置纹理过滤器的滤波方式
        //当图片缩小的比原始纹理小的时候 滤波方式为 mip层之间使用线性插值和使用线性过滤
        m_texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
        //当图片放大的比原始纹理大的时候 滤波方式为 mip基层上使用线性过滤
        m_texture->setMagnificationFilter(QOpenGLTexture::Linear);
    
        //设置图片格式
        m_texture->setFormat(QOpenGLTexture::RGBFormat);
        //纹理绑定
        m_texture->bind();
    
        //使用纹理单元
        m_program->setUniformValue("texture", 0);
    

    绘制

    //纹理绑定
        m_texture->bind();
    
        //1 绑定vao
        glBindVertexArray(VAO);
    
        //2 开启顶点属性
        glEnableVertexAttribArray(0);
        //颜色值
        glEnableVertexAttribArray(2);
    
        //3 绘制四边形
        //24个索引值
    //    glDrawArrays(GL_QUADS, 0, 24);
        glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, (GLvoid*)0);
    
        //4 停用对应的顶点属性数组
        glDisableVertexAttribArray(2);
        glDisableVertexAttribArray(0);
    
        //5 解绑VAO
        glBindVertexArray(0);
    
        //纹理释放
        m_texture->release();
    

    注意

    • 启用2D纹理映射,否则显示白色
    //启用2D纹理映射
        glEnable(GL_TEXTURE_2D);
    
    • 启用纹理对应的属性,否则显示白色
    glEnableVertexAttribArray(2);
    

    六面纹理-单一

    思考了一下,每个面对应一个图片,如果用索引,只有8个点,死活对应不上,只好放弃索引,重新使用24个点

    struct VertexData
    {
        QVector3D position;
        QVector3D color;
        QVector3D texture;
    };
    VertexData vc[] = {
            //正面
            {QVector3D(-0.5f, 0.0f, 0.5f), QVector3D(1.0f, 0.0f, 0.0f), QVector2D(0.0f, 0.0f)},     //1
            {QVector3D(0.5f, 0.0f, 0.5f),  QVector3D(0.0f, 1.0f, 0.0f), QVector2D(1.0f, 0.0f)},      //2
            {QVector3D(0.5f, 1.0f, 0.5f),  QVector3D(0.0f, 0.0f, 1.0f), QVector2D(1.0f, 1.0f)},      //3
            {QVector3D(-0.5f, 1.0f, 0.5f), QVector3D(1.0f, 1.0f, 1.0f), QVector2D(0.0f, 1.0f)},     //4
    
            //右面
            {QVector3D(0.5f, 0.0f, 0.5f),  QVector3D(0.0f, 1.0f, 0.0f), QVector2D(0.0f, 0.0f)},     //2
            {QVector3D(0.5f, 0.0f, -0.5f), QVector3D(1.0f, 1.0f, 0.0f), QVector2D(1.0f, 0.0f)},     //5
            {QVector3D(0.5f, 1.0f, -0.5f), QVector3D(0.0f, 1.0f, 1.0f), QVector2D(1.0f, 1.0f)},     //6
            {QVector3D(0.5f, 1.0f, 0.5f),  QVector3D(0.0f, 0.0f, 1.0f), QVector2D(0.0f, 1.0f)},     //3
    
            //左面
            {QVector3D(-0.5f, 0.0f, -0.5f),QVector3D(1.0f, 0.0f, 1.0f), QVector2D(0.0f, 0.0f)},     //8
            {QVector3D(-0.5f, 0.0f, 0.5f), QVector3D(1.0f, 0.0f, 0.0f), QVector2D(1.0f, 0.0f)},     //1
            {QVector3D(-0.5f, 1.0f, 0.5f), QVector3D(1.0f, 1.0f, 1.0f), QVector2D(1.0f, 1.0f)},     //4
            {QVector3D(-0.5f, 1.0f, -0.5f),QVector3D(1.0f, 0.6f, 0.0f), QVector2D(0.0f, 1.0f)},     //7
    
            //背面
            {QVector3D(0.5f, 0.0f, -0.5f),   QVector3D(1.0f, 1.0f, 0.0f), QVector2D(0.0f, 0.0f)},   //5
            {QVector3D(-0.5f, 0.0f, -0.5f),  QVector3D(1.0f, 0.0f, 1.0f), QVector2D(1.0f, 0.0f)},   //8
            {QVector3D(-0.5f, 1.0f, -0.5f),  QVector3D(1.0f, 0.6f, 0.0f), QVector2D(1.0f, 1.0f)},   //7
            {QVector3D(0.5f, 1.0f, -0.5f),   QVector3D(0.0f, 1.0f, 1.0f), QVector2D(0.0f, 1.0f)},   //6
    
            //顶面
            {QVector3D(-0.5f, 1.0f, 0.5f),   QVector3D(1.0f, 1.0f, 1.0f), QVector2D(0.0f, 0.0f)},   //4
            {QVector3D(0.5f, 1.0f, 0.5f),    QVector3D(0.0f, 0.0f, 1.0f), QVector2D(1.0f, 0.0f)},   //3
            {QVector3D(0.5f, 1.0f, -0.5f),   QVector3D(0.0f, 1.0f, 1.0f), QVector2D(1.0f, 1.0f)},   //6
            {QVector3D(-0.5f, 1.0f, -0.5f),  QVector3D(1.0f, 0.6f, 0.0f), QVector2D(0.0f, 1.0f)},   //7
    
            //底面        
            {QVector3D(-0.5f, 0.0f, -0.5f),  QVector3D(1.0f, 0.0f, 1.0f), QVector2D(0.0f, 0.0f)},   //8
            {QVector3D(0.5f, 0.0f, -0.5f),   QVector3D(1.0f, 1.0f, 0.0f), QVector2D(1.0f, 0.0f)},   //5
            {QVector3D(0.5f, 0.0f, 0.5f),    QVector3D(0.0f, 1.0f, 0.0f), QVector2D(1.0f, 1.0f)},   //2
            {QVector3D(-0.5f, 0.0f, 0.5f),   QVector3D(1.0f, 0.0f, 0.0f), QVector2D(0.0f, 1.0f)},   //1
        };
    

    纹理坐标链接时候注意要跳过两个QVector3D大小

    //5 链接顶点属性
        //indx: 属性名
        //size: 顶点大小
        //type: 数据类型
        //normalized:数据被标准化
        //stride: 步长
        //ptr: 数据在缓冲中起始位置的偏移量
        glVertexAttribPointer(m_posAttr, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (GLvoid*)0);    //(GLvoid*)0
        glVertexAttribPointer(m_texcoordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (GLvoid*)(sizeof(QVector3D)*2));
    

    使用 glDrawArrays(GL_QUADS, 0, 24);绘制

    0_1526634110381_240b60a8-fed1-4579-9a2c-058fd780e40a-image.png

    下次我们做个骰子出来

    先说一个坑

    同样的代码拿回家就不好用,研究发现,绘制时候没启用颜色顶点,所以一直是黑屏

    //颜色值
        glEnableVertexAttribArray(1);
    

    骰子的六面

    要贴6次纹理,所以用指针数组, 管理全部纹理

    声明数组

    //纹理对象 数组
        QVector<QOpenGLTexture *> m_vTexture;
    

    创建数组

    for(int i=0; i<6; i++)
        {
            //使用图片的垂直镜像来创建纹理
            QString path = QString(":/cube%1.png").arg(QString::number(i+1));
            QOpenGLTexture *_texture = new QOpenGLTexture(QImage(path).mirrored());
            //设置纹理过滤器的滤波方式
            //当图片缩小的比原始纹理小的时候 滤波方式为 mip层之间使用线性插值和使用线性过滤
            _texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
            //当图片放大的比原始纹理大的时候 滤波方式为 mip基层上使用线性过滤
            _texture->setMagnificationFilter(QOpenGLTexture::Linear);
    
            m_vTexture.append(_texture);
        }
    

    绘制注意问题

    开启顶点属性

    //2 开启顶点属性
        glEnableVertexAttribArray(0);
        //颜色值
        glEnableVertexAttribArray(1);
        //纹理
        glEnableVertexAttribArray(2);
    

    6个面要依次绘制一遍,绘制的起点也要依次往后移动

    //纹理绑定
        for(int i=0; i<6; i++)
        {
            m_vTexture.at(i)->bind();
            //3 绘制四边形
            //24个索引值
            glDrawArrays(GL_QUADS, i*4, 4);
            //    glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, (GLvoid*)0);
    
            m_vTexture.at(i)->release();
        }
    

    停用顶点属性

    //4 停用对应的顶点属性数组
        glDisableVertexAttribArray(2);
        glDisableVertexAttribArray(1);
        glDisableVertexAttribArray(0);
    

    0_1526684517642_fcb07104-1ebe-4306-bcf9-3c21aa8793cb-image.png

    同时显示颜色和纹理

    gl_FragColor = col*texture2D(texture, v_texcoord);
    0_1526684652591_a7df691b-cf8d-42df-a6eb-2cfbaf93e898-image.png

    源代码

    https://gitee.com/chen227/opengl_OpenGLFunctions6



  • 我跑起来这个代码有点问题



  • 0_1526738252238_深度录屏_opengl_OpenGLFunctions_20180519215653.gif



  • @大黄老鼠 画面裁剪的感觉



  • This post is deleted!


  • @大黄老鼠 要启用深度测试:

    glEnable( GL_DEPTH_TEST );
    

Log in to reply
 

走马观花

最近的回复

  • 113.jpg
    1、什么是lambda表达式,什么是闭包?
    lambda表达式即lambda函数,也就是匿名函数。

    lambda表达式在C++中包含了
    []表示捕获
    ()是函数的参数,需要指定类型
    ->type是返回的类型,可以省略,如果编译器无法推出类型的话可以强制编写
    {}是函数体。

    lambda可以被声明为mutable的,作用是将捕获的内容进行改变。
    闭包是函数的定义以及定义函数时提供的环境,总称为闭包。lambda函数也是一种闭包。
    lambda本身是匿名函数,而捕获语句则是提供了定义函数时提供的环境。

    2、什么是右值引用?
    右值引用相对与左值引用而言的。左值即=运算符左边的变量,右值是=运算符右边的常量或变量。由此可以看出,
    右值引用指的是对常量或变量的引用。它的用途包含了移动语义和完美转发。
    移动语义就是弥补了C++历史在处理变量传递时丢失的一种语义。它和值传递、引用传递一样,是变量传递的方式之一。
    如果没有移动语义,为了将一个类的实例传递给另外一个实例,就需要额外地进行构造、赋值、销毁的操作。
    对于一些比较复杂的变量,的确是非常耗时并且消耗大的操作。(浪费指令时间、浪费内存)

    对于这样的函数返回:
    vector<string> str_split(const string& s) {
    vector<string> v;
    // ...
    return v; // v是左值,但优先移动,不支持移动时仍可复制。
    }

    标准要求先调用移动构造函数,如果不符合那么再调用拷贝构造函数。所以可以轻松地写出这种写法而不必担心效率问题。
    同时,现代编译器都会对返回值进行优化,成为RVO以及NRVO。所以不用太担心会多调用构造析构函数。

    对于完美转发,C++对于引用的转发有规则。传统的C++是无法对引用进行再引用的。但是现代的C++放宽了它的使用范围。
    只有右引用右值的时候,才会产生右引用。这也称为引用折叠。

    3、auto关键字的作用是什么?
    auto关键字为的是能够让编译器自动推导类型。自C++98之后,编译器对类型的推导变得越来越智能了。
    而我们在编写复杂代码的时候,冗长的类型不仅容易出错,有时也不容易人工推导出类型。
    因此auto可以简化我们的任务量,让类型的推导交给编译器完成。
    除了auto外,我们还可以使用decltype()来让编译器推导类型。

    read more
  • 我感觉比起《Physically Based Rendering Technique》,还是《Ray Tracing in a Weekend》更容易上手,因为慢慢地能够做出一个渲染效果,这个是有成就感的。🎓

    read more
  • G

    北京名律免费法律咨询,解决您的法律困扰,如果您眼下没有遇到法律上的问题,也可以留一位大律师的联系方式以备不时之需!ccfd91efc3a6b2ed0e79054d2248eed.jpg bf6015b383483f1fe83cdcfd130fc6b.jpg 2f347879129add1096bf3424edbe517.jpg

    read more

关注我们

微博
QQ群