• OpenGL入门(三)之着色器Shader


    本系列文章为Learn OpenGL个人学习总结!
    OpenGL入门(一)之认识OpenGL和创建Window
    OpenGL入门(二)之渲染管线pipeline,VAO、VBO和EBO
    OpenGL入门(三)之着色器Shader
    OpenGL入门(四)之纹理Texture
    OpenGL入门(五)之Matrix矩阵操作和坐标系统
    OpenGL进阶(一)之帧缓冲FrameBuffer
    OpenGL进阶(二)之像素缓冲PixelBuffer

    GLSL

    GLSL(OpenGL Shder Language):OpenGL着色器语言,用于写Shader的语言。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。

    一个典型的着色器

    #version version_number   // #version 330 core
    in type in_variable_name;  // in vec3 positon
    in type in_variable_name;  // in vec4 color
    
    out type out_variable_name;  // out vec4 outColor
    
    uniform type uniform_name;  //uniform vec4 codeColor
    
    int main()
    {
      // 处理输入并进行一些图形操作
      ...
      // 输出处理过的结果到输出变量
      out_variable_name = weird_stuff_we_processed;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    特别的对于顶点着色器来说,每个输入变量也叫顶点属性(Vertex Attribute)!我们可以查询当前支持的属性个数!一般来说都是16个!

        //获取支持的顶点属性的个数
        int nrAttributes;
        glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
        std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;
    
    • 1
    • 2
    • 3
    • 4

    数据类型

    GLSL中包含C等其它语言大部分的默认基础数据类型:intfloatdoubleuintbool。GLSL也有两种容器类型,它们会在这个教程中使用很多,分别是向量(Vector)和矩阵(Matrix)

    向量

    GLSL中的向量是一个可以包含有2、3或者4个分量的容器,分量的类型可以是前面默认基础类型的任意一个。
    默认vecn是包含n个float类型的vector!
    一个向量的分量可以通过vec.x这种方式获取,这里x是指这个向量的第一个分量。你可以分别使用.x、.y、.z和.w来获取它们的第1、2、3、4个分量。GLSL也允许你对颜色使用rgba,或是对纹理坐标使用stpq访问相同的分量。

    输入和输出

    每个着色器都有输入和输出,这样才能进行数据交流和传递。GLSL定义了inout关键字专门来实现这个目的。每个着色器使用这两个关键字设定输入和输出,只要一个输出变量与下一个着色器阶段的输入匹配,它就会传递下去

    注意:对于顶点着色器,它的输入稍微有些特殊,它从顶点数据中直接接收输入。顶点着色器需要为它的输入提供一个额外的layout标识,这样我们才能把它链接到顶点数据。
    在前边已经见到了:

    layout (location = 0) in vec3 aPos;  
    
    • 1

    从顶点着色器中为片段着色器颜色赋值

    顶点着色器:

    const char *vertexShaderSource = "#version 330 core\n"
        "layout (location = 0) in vec3 aPos;\n"
        "out vec4 vertexColor;\n" //为片段着色器指定一个颜色
        "void main()\n"
        "{\n"
        "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
        "   vertexColor = vec4(0.5, 0.0, 0.0, 1.0);\n" // 把输出变量设置为暗红色
        "}\0";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    片段着色器:

    const char *fragmentShaderSource = "#version 330 core\n"
        "in vec4 vertexColor;\n" //接收顶点着色器中的输入变量(名称、类型相同) 
        "out vec4 color;\n"
        "void main()\n"
        "{\n"
        "   color = vertexColor;\n"
        "}\0";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    只需保证变量的名称和类型相同,即把顶点着色器中设置的颜色传递到片段着色器中,运行即可得到一个暗红色的三角形!

    使用Uniform从代码中给片段着色器颜色赋值

    Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。

    修改片段着色器:

    const char *fragmentShaderSource = "#version 330 core\n"
        "out vec4 color;\n"
        "uniform vec4 colorInCode;\n" //接收代码中的颜色
        "void main()\n"
        "{\n"
        "   color = colorInCode;\n"
        "}\0";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用也比较简单:

     		//对fragment shader中的uniform赋值
            float timeValue = glfwGetTime();
            float greenValue = (sin(timeValue) / 2.0f) + 0.5f;
            //查询uniform位置值,返回-1则表示没有找到
            int vertexColorLocation = glGetUniformLocation(shaderProgram, "colorInCode");
            glUseProgram(shaderProgram); //赋值更新前,必须先使用program
            //赋值,这里需要4个float,所以使用glUniform4f()
            glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    一个综合的例子

    将color也放在顶点数据里,给每个顶点都设置一个不同的颜色:

        //绘制三角形+颜色
        float vertices[] = {
        // 位置              // 颜色
         0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // 右下
        -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // 左下
         0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // 顶部
        };
    
        const char *vertexShaderSource = "#version 330 core\n"
        "layout (location = 0) in vec3 aPos;\n"
        "layout (location = 1) in vec3 aColor;\n"
        
        "out vec3 vertexColor;\n" //为片段着色器指定一个颜色
        "void main()\n"
        "{\n"
        "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
        "   vertexColor = aColor;\n" //将color设置成从顶点数据中获取的颜色
        "}\0";
    
        const char *fragmentShaderSource = "#version 330 core\n"
        "in vec3 vertexColor;\n" //接收顶点着色器中的输入变量(名称、类型相同) 
        "out vec4 color;\n"
        // "uniform vec4 colorInCode;\n" //接收代码中的颜色
        "void main()\n"
        "{\n"
        "   color = vec4(vertexColor, 1.0);\n"
        "}\0";
    
    
        //属性0  location
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);//启用
        //属性1  color
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
        glEnableVertexAttribArray(1);//启用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    绘制会得到一个类似调色板的三角形!
    这是在片段着色器中进行的所谓片段插值(Fragment Interpolation)的结果。当渲染一个三角形时,光栅化(Rasterization)阶段通常会造成比原指定顶点更多的片段。光栅会根据每个片段在三角形形状上所处相对位置决定这些片段的位置。

    好了,这里就是GLSL中比较核心的内容了!

  • 相关阅读:
    【Java集合类】之Map集合的特点及使用
    全球名校AI课程库(30)| MIT麻省理工 · 深度学习与无人驾驶课程『Deep Learning for Self-Driving Cars』
    121.(前端)商品管理增加静态参数显示——获取上一级tabs内容发送请求得到下一级tabs内容
    日常 - UX 响应 Nginx 502 Bad Gateway
    EvaluatorFilter简介说明
    inveta PLSB 点线面体 示例工程
    LC15.三数之和、LC22括号生成
    上机实验四 图的最小生成树算法设计 西安石油大学数据结构
    21组Midjourney绘画关键词,专为游戏设计的奇异生物的盛宴
    AcWing 801. 二进制中1的个数——算法基础课题解
  • 原文地址:https://blog.csdn.net/aiynmimi/article/details/126648176