OpenGL 学习笔记(二)使用OpenGL 绘制三角形

绘制三角形的本质是光栅化的过程,那什么是光栅化呢? 简单来说,光栅化就是将三维图形转变成二维图形,在屏幕上显示出来的过程。(小时候看西游记的时候,一直在幻想自己可以钻进电视里哈哈哈),刚开始做3D相关软件开发的时候,心里就一直有个疑问,图形是怎么在电脑里的软件上显示出来的,后来百度的时候了解到是光栅化,当时对这个概念也是似懂非懂。言归正传,下面我们看一下这个张图,整个图形渲染管线包括以下几个阶段,而光栅化就是其中的一个阶段。总体而言,图形渲染管线包括应用程序阶段、几何阶段、光栅化阶段。

应用程序阶段所做的工作是由程序员决定的,是可控的,程序员可以使用加速算法,几何处理,这一阶段只有cpu参与。这一阶段的主要任务是将需要显示在屏幕上的图元(点,线,三角形)等发送给gpu的显存中。

几何阶段:主要包括mvp矩阵变换,顶点的着色,以及裁剪,视口变换。

这里重点说一下顶点着色,我们说 确定材质上的光照效果 的操作被称为着色。着色通常需要考虑三角形的每个顶点的数据,三角形的顶点可以存储材质,颜色或者深度等值,而三角形内部的这些值就通过插值来实现。

光栅化阶段:给定经过mvp变换之后的顶点,颜色以及纹理,给每个像素配正确的颜色,从而挥着整个图像的过程,叫做光栅化。首先我们可以用包围盒来确定三角形的位置,然后通过采样包围盒里的像素,通过三角形检测判断哪些像素是在三角形内,哪些像素是在三角形外,然后确定需要被着色的像素,通过z-buffer的方法,来判断深度值,确保屏幕上能显示的像素的值是最小的。

对于一整个图形而言,我们需要对一个大的图形进行三角剖分,对每一个三角形都进行着色。下一届会讲到矩形的绘制。整个光栅化过程可以分为:

1.三角形的设定

2.三角形遍历

3.像素着色

4.融合阶段(深度测试)

有了图形学图形渲染管线的一些知识,我们下面看一下opengl是怎样绘制三角形的。

//GLSL  opengl shading language
//vertex  triangles(shape assembly,geometry shader) rasterize fragment test blending
//图元类型  GL_POINTS  GL_TRIANGLES  GL_LINE_STRIP

#include <iostream>
#define GLEW_STATIC
#include<GL/glew.h>
#include<GLFW/glfw3.h>

void key_callback(GLFWwindow* window, int key, int sancode, int action, int mode);

void key_callback(GLFWwindow* window, int key, int sancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, GL_TRUE);
    }
}
// Window dimensions
const GLuint WIDTH = 800, HEIGHT = 600;
// Shaders
//顶点着色器源码
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\0";

//片段颜色着色器源码
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
"color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n";

// The MAIN function, from here we start the application and run the game loop
int main()
{
   
    std::cout << "Starting GLFW context, OpenGL 3.3" << std::endl;
    // Init GLFW
    //实例化glfw窗口并配置
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
    //创建窗口对象
    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "ILoveLearnOpenGL", nullptr, nullptr);
    if (window == nullptr)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    //将窗口上下文设置为当前主线程的上下文
    glfwMakeContextCurrent(window);

    //设置键盘输入的回调函数
    glfwSetKeyCallback(window, key_callback);
    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
   //使用glew库来管理OpenGL的指针,GL_TRUE表示使用现代化的技术
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    if (glewInit() != GLEW_OK)
    {
        std::cout << "Failed to initialize GLEW" << std::endl;
        return -1;
    }

    // Define the viewport dimensions
    //根据屏幕来获取屏幕的w,h
    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    glViewport(0, 0, width, height);



  //glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
   
 #pragma region 设置顶点属性

    //创建shader
    GLuint vertexShader;
    vertexShader = glCreateShader(GL_VERTEX_SHADER);

    //将着色器的源码附着在着色器上 然后编译
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);

    //判断是否编译成功
    GLint success;
    GLchar infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    //不成功输出日志
    if (!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
#pragma  endregion

    //片段着色器
    GLuint fragmentShader;
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    //Shader Program Object
    GLuint shaderProgram;
    shaderProgram = glCreateProgram();

    //将着色器附加到着色器程序上,然后链接
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    //检测链接是否成功
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
    }
   
    //-------至此,我们已经将cpu中存储的顶点数据发送给了gpu显存,并指示gpu
    // 如何在渲染管线中的顶点shader以及fragment shader中去如何处理它
        
  
    //glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
#if 1

    //定义三角形三个顶点
    GLfloat vertices[] = {
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f,
     0.0f,  0.5f, 0.0f
    };
#endif

   
    
    //VBO vertex buffer object,分配一个ID  1
    GLuint VBO;
    glGenBuffers(1, &VBO);

    //VAO  vertex Array Object  顶点数组对象
    GLuint VAO;
    glGenVertexArrays(1, &VAO);

    //绑定
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    //复制定义数据到缓冲内存,将cpu中数据发送到gpu 显存中

    // 1. 绑定VAO
    glBindVertexArray(VAO);
    // 2. 把顶点数组复制到缓冲中供OpenGL使用
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    // 3. 设置顶点属性指针
      //接下来,我们告诉OpenGL该如何解释这些顶点
    /* para1 顶点位置
    * para2  顶点属性大小vec3 (3个值)
    * para3  指定数据的类型
    * para4  是否会被标准化  到[-1,1]
    * para5  步长 stride ,连续顶点之间的间隔 设置为0  自动设置
    * para6  偏移量
    */
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    //4. 解绑VAO
    glBindVertexArray(0);

    // Game loop 游戏循环
    while (!glfwWindowShouldClose(window))
    {
        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        //键盘鼠标响应事件
        glfwPollEvents();

        //填充成橙色
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        //激活 着色器程序对象
        glUseProgram(shaderProgram);
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        //解绑
        glBindVertexArray(0);
        // Swap the screen buffers  交换颜色缓冲
        //前缓冲保存着最终输出的图像,它会在屏幕上显示;
        // 而所有的的渲染指令都会在后缓冲上绘制。当所有的渲染指令执行完毕后
        // 我们交换(Swap)前缓冲和后缓冲
        //这样图像就立即呈显出来,之前提到的不真实感就消除了。
        glfwSwapBuffers(window);
    }
    //删除顶点着色器对象和片段着色器对象
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

具体的代码的含义可以参考:OpenGL - LearnOpenGL-CN

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值