Qt OpenGL:学习现代3D图形编程之一,画一个三角形


1.显示到屏幕

这里先简单介绍一下缓冲区,缓冲区存在于图形卡的显存中,OpenGL在绘制图元时,先是在一个缓冲区中完成渲染,然后再把渲染结果交换到屏幕上。

void display()
{
    //指定OpenGL清理屏幕时将要使用的颜色,这里为黑色。
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    //开始清理屏幕,GL_COLOR_BUFFER_BIT表示清理将影响颜色缓冲区,清理时使用上面指定的颜色。
    glClear(GL_COLOR_BUFFER_BIT);
    //告知OpenGL渲染的时候需要调用应用程序对象。
    glUseProgram(theProgram);
    //下面三行设置三角形的坐标,它们告知OpenGL三角形在缓冲区中的位置
    //获取已经初始化的缓冲区对象。
    glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
    //启用缓冲区对象中的数据,参数指定要修改的顶点属性的索引值
    glEnableVertexAttribArray(0);
    //尽管这个函数包含“Pointer”,但是它不处理指针,它用来对缓冲区对象中的顶点属性进行设定。
    //参数1指定要修改的顶点属性的索引值;参数2指定每个顶点向量的维数;参数3指定每个顶点向量的数据类型;
    //参数4指定是否归一化;参数5指定各顶点向量间是否有空隙,0表示紧密排列;参数6指定顶点数组起始位置与缓冲区
    //对象起始位置的偏移量,0表示无偏移。后三个参数通常取默认值。
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
    //渲染函数,从顶点数组索引0开始,读取3个顶点,然后将它们连接成一个三角形。
    glDrawArrays(GL_TRIANGLES, 0, 3);
    //下面两行是清理工作,释放为了实现渲染所做的一些设置。
    glDisableVertexAttribArray(0);
    glUseProgram(0);
}

2.顶点传递
以下是我们希望传递的数据,一个顶点数组。
const float vertexPositions[] = {
    0.75f, 0.75f, 0.0f, 1.0f,
    0.75f, -0.75f, 0.0f, 1.0f,
    -0.75f, -0.75f, 0.0f, 1.0f,
};
每行表示一个顶点的四维向量,向量的前三个数字表示顶点的x、y、z坐标,第四个顶点w是缩放因子,通常为1,当w为1是,可以忽略它。尽管我们定义了这些数据,但是OpenGL不能直接使用。所以我们需要分配一些OpenGL可见的缓冲区,然后将我们的数据添加到这些缓冲区。这可以通过缓冲区对象(buffer object)来实现。缓冲区对象可以看成GPU内存中的线性数组,GPU可以快速访问这些数组。缓冲区对象在初始化时创建,代码如下所示。

void InitializeVertexBuffer()
{   
    //创建一个缓冲区对象,并将positionBufferObject指向该缓存区对象。
    //此时缓存区对象已经创建,但并未给它分配存储空间。
    glGenBuffers(1, &positionBufferObject);   
    //将缓冲区对象绑定到绑定目标GL_ARRAY_GUFFER,OpenGL中的所有对象在进行操作前都需要绑定到上下文,缓冲区对象也不例外。
    glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
    //一旦绑定了一个缓冲区对象,就需要给该对象分配足够的GPU内存来存储顶点数据,分配的大小为 sizeof(vertexPositions)。     
    //然后该函数将我们定义的数据拷贝到缓冲区对象,第四个参数是数据的使用方式。
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
    //简单的清理。将0绑定到绑定目标GL_ARRAY_BUFFER后,之前的绑定自动解除。这里的0类似NULL指针。
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

3.顶点着色器

#version 330
layout(location = 0) in vec4 position;
void main()
{
    gl_Position = position;
}
第一行是着色器语言的版本声明,这里对应的是OpenGL 3.3版本。
着色器程序以main()函数开头,这个程序很简单,只是将输入的位置信息position拷贝到了变量gl_Position。以"gl_"开头的变量是着色器内置的变量,自定义的变量不要以"gl_"开头。gl_Position在内部的定义为out vec4 gl_Position。着色器包含输入和输出,就像函数包含输入的参数和输出的返回值。上面代码中 in 表示输入,输入到顶点着色器的是顶点属性,而顶点属性在缓冲区对象有一个索引位置(location)叫做属性索引,属性索引值必须大于或等于0。在代码中,当需要使用属性时,通常使用的是属性索引,函数glEnableVertexAttribArray,glDisableVertexAttribArray和glVertexAttribPointer都将第一个参数设置为属性索引。在第二行中,我们将属性索引值0指定给属性position,这样当调用glVertexAttribPointer等函数时,将第一个参数设置为0,可以访问属性position。下图是顶点着色器的数据流图。


4.片段着色器

#version 330
out vec4 outputColor;
void main()
{
   outputColor = vec4(1.0f, 1.0f, 1.0f, 1.0f);
}
片段做色器用于计算片段的输出颜色。
第二行定义了段着色器的输出变量outputColor。
main()函数简单的将输出变量赋值为一个四维向量,该向量表示白色。当片段着色器执行时,白色将被输出到图像中。需要注意的是在顶点着色器中,我们通过layout(location = #)语法来建立顶点着色器输入和顶点属性索引之间的联系,但是在片段着色器却没有采用类似的做法,将片段着色器输出和屏幕关联起来。这是因为片段着色器的输出目的地只有一个—当前要渲染的图像,即屏幕。因此当仅定义一个输出变量时,这个变量会被自动写入到当前图像。当然也可以定义多个片段着色器输出变量,分别输出了不同的目的图像,这样做会复杂一些,这里不做讨论。

5.开始着色

void InitializeProgram()
{
    //创建着色器对象列表,这些着色器对象将通过链接生成应用程序对象
    std::vector<GLuint> shaderList;
    //下面两行分别编译顶点着色器和片段着色器,着色器的编译和链接与c语言类似,也是先编译后链接。更重要的是,编译还引入了出错检查。
    shaderList.push_back(CreateShader(GL_VERTEX_SHADER, strVertexShader));
    shaderList.push_back(CreateShader(GL_FRAGMENT_SHADER, strFragmentShader));
    theProgram = CreateProgram(shaderList);
    //删除着色器对象
    std::vector<GLuint>::iterator it;
    for(it=shaderList.begin();it!=shaderList.end();it++)
        glDeleteShader(*it);
}

GLuint CreateShader(GLenum eShaderType, const std::string &strShaderFile)
{
    //创建着色器对象
    GLuint shader = glCreateShader(eShaderType);
    const char *strFileData = strShaderFile.c_str();
    //将着色器对象与着色器程序文件关联,参数2为着色器程序文件数量,当需要将多个文件编译到一个着色器对象,可以多个文件组成数组。
    //最后一个参数指定文件的长度,这里用NULL,告知OpenGL假设该文件中不含null字符,除非需要在文件中使用null字 符,否则该参数
    //都可以设置为 NULL
    glShaderSource(shader, 1, &strFileData, NULL);
    //编译着色器对象
    glCompileShader(shader);  
    GLint status;
    //获取编译状态、
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
    //如果编译失败,打印日志信息
    if (status == GL_FALSE)
    {
        GLint infoLogLength;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);  
        GLchar *strInfoLog = new GLchar[infoLogLength + 1];
        glGetShaderInfoLog(shader, infoLogLength, NULL, strInfoLog);    
        const char *strShaderType = NULL;
        switch(eShaderType)
        {
            case GL_VERTEX_SHADER: strShaderType = "vertex"; break;
            case GL_GEOMETRY_SHADER: strShaderType = "geometry"; break;
            case GL_FRAGMENT_SHADER: strShaderType = "fragment"; break;
        }     
        fprintf(stderr, "Compile failure in %s shader:\n%s\n", strShaderType, strInfoLog);
        delete[] strInfoLog;
    }
   return shader;
}

 接着可以把编译的着色器对象传给 CreateProgram函数 
GLuint CreateProgram(const std::vector<GLuint> &shaderList)
{
    //创建应用程序对象
    GLuint program = glCreateProgram();
    //将着色器对象附加到应用程序对象
    for(size_t iLoop = 0; iLoop < shaderList.size(); iLoop++)
    glAttachShader(program, shaderList[iLoop]);
    //链接应用程序对象
    glLinkProgram(program);  
    GLint status;
    //获取链接状态
    glGetProgramiv (program, GL_LINK_STATUS, &status);
    //如果链接失败,打印日志信息
    if (status == GL_FALSE)
    {
        GLint infoLogLength;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);     
        GLchar *strInfoLog = new GLchar[infoLogLength + 1];
        glGetProgramInfoLog(program, infoLogLength, NULL, strInfoLog);
        fprintf(stderr, "Linker failure: %s\n", strInfoLog);
        delete[] strInfoLog;
    }
    //取消着色器对象的附加
    for(size_t iLoop = 0; iLoop < shaderList.size(); iLoop++)
        glDetachShader(program, shaderList[iLoop]);
    return program;
}

参考链接:http://alfonse.bitbucket.org/oldtut/Basics/Tut01%20Dissecting%20Display.html

源码需要用Qt5.6.0编译

源码链接:http://download.csdn.net/detail/caoshangpa/9471466

  • 18
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

草上爬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值