zoukankan      html  css  js  c++  java
  • 3D Computer Grapihcs Using OpenGL

    从这里就接触到了可编程图形渲染管线。 

    下面介绍使用Vertex Shader (顶点着色器)和 Fragment Shader(像素着色器)的方法。

    我们的目标是使用这两个着色器给三角形填充绿色。

    添加了一个cpp文件存放Shader文件,MyShaderCode.cpp:

     1 const char* vertexShaderCode =
     2 "    #version 430                            
    "
     3 "                                            
    "
     4 "    in layout(location=0) vec2 position;    
    "
     5 "                                            
    "
     6 "                                            
    "
     7 "                                            
    "
     8 "    void main()                             
    "
     9 "    {                                       
    "
    10 "      gl_Position= vec4(position,0.0,1.0);  
    "
    11 "    }                                       
    "
    12 "                                            
    "
    13 "                                            
    ";
    14 
    15 const char* fragmentShaderCode =
    16 "    #version 430                            
    "
    17 "                                            
    "
    18 "    out vec4 finalColor;                    
    "
    19 "                                            
    "
    20 "                                            
    "
    21 "    void main()                             
    "
    22 "    {                                       
    "
    23 "      finalColor = vec4(0.0,1.0,0.0,1.0);   
    "
    24 "    }                                       
    "
    25 "                                            
    "
    26 "                                            
    ";

    书写Shader使用的是GLSL语言,和C语言语法非常相似。

    Vertex Shader

    #version 430 表示opengl的版本号,要使用显卡支持的版本,否则会编译出错

    vertex Shader第四行的 in 关键字表示这里定义的是一个输入。layout(location=0)表示这里接收的是位置信息。vec2表示这里是一个2维向量。position是自己定义的接受数据的变量名称。

    gl_Position是GLSL定义的关键字,使用在Vertex shader中就表示它将接收一个四维向量来表示顶点位置信息。

    vec4(position,0.0,1.0)是GLSL的一种特殊语法,用于方便的构造一个四维向量,因为position是二维的,后面再添加两个数字就表示了四维向量。最后一位是1.0,表示没有缩放。

    Fragment Shader

    第18行使用out关键字,表示这里是一个输出。

    而Fragment Shader在渲染管线中只有一个输出,就是每个Fragment(暂时可以理解为像素,实际上不等同于像素)的颜色。

    在main中直接将所有的像素颜色都设置成绿色。

    MyGlWindow.cpp

     1 #include <glglew.h>
     2 #include "MyGlWindow.h"
     3 
     4 extern const char* vertexShaderCode;
     5 extern const char* fragmentShaderCode;
     6 
     7 GLuint programID;
     8 
     9 void MyGlWindow::sendDataToOpenGL()
    10 {
    11     GLfloat verts[] =
    12     {
    13         +0.0f, +0.0f, //0
    14         +1.0f, +1.0f, //1
    15         -1.0f, +1.0f, //2
    16         -1.0f, -1.0f, //3
    17         +1.0f, -1.0f, //4
    18     };
    19 
    20     GLuint vertexBufferID;
    21     glGenBuffers(1, &vertexBufferID);
    22     glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
    23     glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
    24 
    25     GLushort indices[] =
    26     {
    27         0,1,2,
    28         0,3,4,
    29     };
    30     GLuint indexBufferID;
    31     glGenBuffers(1, &indexBufferID);
    32     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
    33     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    34 
    35     glEnableVertexAttribArray(0);
    36     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
    37 }
    38 
    39 void MyGlWindow::installShaders()
    40 {
    41     GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
    42     GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
    43 
    44     glShaderSource(vertexShaderID, 1, &vertexShaderCode, 0);
    45     glShaderSource(fragmentShaderID, 1, &fragmentShaderCode, 0);
    46 
    47     glCompileShader(vertexShaderID);
    48     glCompileShader(fragmentShaderID);
    49 
    50     programID = glCreateProgram();
    51     glAttachShader(programID, vertexShaderID);
    52     glAttachShader(programID, fragmentShaderID);
    53 
    54     glLinkProgram(programID);
    55 
    56     glDeleteShader(vertexShaderID);
    57     glDeleteShader(fragmentShaderID);
    58     
    59     glUseProgram(programID);
    60 }
    61 
    62 void MyGlWindow::initializeGL()
    63 {
    64     glewInit();
    65     sendDataToOpenGL();
    66     installShaders();
    67 }
    68 
    69 void MyGlWindow::paintGL()
    70 {
    71     glViewport(0, 0, width(), height());
    72     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
    73 }
    74 
    75 MyGlWindow::~MyGlWindow()
    76 {
    77     glUseProgram(0);
    78     glDeleteProgram(programID);
    79 }

    MyGlWindow.h

     1 #pragma once
     2 #include <QtOpenGLqgl.h>
     3 class MyGlWindow :public QGLWidget
     4 {
     5 protected:
     6     void sendDataToOpenGL();
     7     void installShaders();
     8     void initializeGL();
     9     void paintGL();
    10 
    11 public:
    12     ~MyGlWindow();
    13 };

    对MyGlWindow.cpp文件进行了一个重构,把上节initializeGL()中的内容除了第一句glewInit()之外全部封装进了sendDataToOpenGL()函数,因为这些代码都是向OpenGL函数发送数据的。

    为了使用Shader,我们新增加了一个installShaders()函数,所有使用Shader的功能都写在其中。

    在initializeGL函数中先调用sendDataToOpengGL(),然后调用installShaders()。

    使用Shader的步骤清单

    1. 创建Shader并分配ID,glCreateShader
    2. 设置Shader内容, glSourceShader
    3. 编译Shader, glCompileShader
    4. 创建Program,glCreateProgram
    5. 附加Shader,glAttachShader
    6. 链接Shader, glLinkProgram
    7. 删除Shader,glDeleteShader
    8. 使用Program, glUseProgram
    9. 删除Program,glDeleteProgram

    下面就按照这六个步骤逐步解析代码。

    1. 创建Shader并分配ID

    39 - 40 行使用glCreateShader分别创建了vertex shader和 fragment shader,并分别将ID保存在GLuint对象里(和前面使用GLuint存储Buffer对象类似)

    函数的参数是固定的宏

    2. 设置Shader内容

    42 - 43 行使用glShaderSource函数给Shader提供具体内容。

    我们额外增加了一个文件MyShaderCode用来存放Shader的代码,代码具体内容放在两个较长的字符串里。

    然后在MyGlWindow.cpp的开头使用extern 声明了vertexShaderCode和fragmentShaderCode字符串变量。

    glShaderSource的

    • 第一个参数表示Shader的ID
    • 第二个参数表示Shader内容的字符串有几个数组,这里只有1个
    • 第三个参数表示字符串数组本身,但是需要注意参数的类型是const char **,所以还要在前面增加一个取地址符号
    • 第四个参数使用0表示由OpenGL自己计算字符串的长度

    3. 编译Shader

    45 - 46 行使用glCompileShader分别对两个Shader进行编译

    4.创建Program

    进行后续操作之前需要创建一个Program,这里使用glCreateProgram()创建了一个Porgram,并将其ID保存在GLuint对象里。

    5.附加Shader

    glAttachShader有两个参数,第一个参数是Program的ID,第二个参数是Shader的ID

    6.链接Shader

    虽然通常称为“链接SHader”, 但其实是链接Program, 函数glLinkProgram的参数就是Program的ID

    7.删除Shader

    Shader使用完成后一定要删除Shader,第56和57行进行了这步操作。在useProgram之前进行也没问题,只要编译和链接了Shader就行。

    8.使用Program

    glUseProgram的参数是Program的ID

    9. 删除Program

    使用完成Program之后要删除Program,这里在析构函数里进行是比较合适的,分两步,如77和78行所示。

    完成后编译,我们将得到两个绿色的三角形。

  • 相关阅读:
    WebService的使用
    Nginx的安装与部署
    Linux下安装mysql5.7
    SpringBoot之旅 -- 定时任务两种(Spring Schedule 与 Quartz 整合 )实现
    ELK 日志分析系统
    解决SpringBoot jar包太大的问题
    IntelliJ Idea 授权服务器使用
    Mybatis Generator最完整配置详解
    PyCharm2016.2专业版注册码
    HttpClient实现HTTP文件通用下载类
  • 原文地址:https://www.cnblogs.com/AnKen/p/8337475.html
Copyright © 2011-2022 走看看