OpenGL学习记录二(着色器)

github源码

着色器 -> 运行在gpu上的程序
顶点缓冲 ->只是一个内存缓冲区 字节数组缓冲区
顶点缓冲区和c++中字符数组缓冲区不一样,顶点缓冲区实际上实在gpu上(显卡vram)

上下文会有自己的流,缓冲区会有自己的流
Opengl中生成的所有东西都会被分配一个唯一的标识符(整数)  (比如着色器id等)

一个完整的顶点(vertex):不止包含坐标,还包含颜色,纹理其他东西
顶点着色器(Vertex Shader)是几个可编程着色器中的一个。如果我们打算做渲染的话,现代OpenGL需要我们至少设置一个顶点和一个片段着色器。
顶点着色器允许我们指定任何以顶点属性为形式的输入。

stride--每个顶点之间的字节数量--偏移,顶点开始到顶点结束的跨度长度
pointer---数组,顶点里,位置、纹理、法线的偏移量(字节长度)

着色器:shader
在计算机图形中颜色被表示为有4个元素的数组:红色、绿色、蓝色和alpha(透明度)分量,通常缩写为RGBA。
当在OpenGL或GLSL中定义一个颜色的时候,我们把颜色每个分量的强度设置在0.0到1.0之间。
比如说我们设置红为1.0f,绿为1.0f,我们会得到两个颜色的混合色,即黄色。
这三种颜色分量的不同调配可以生成超过1600万种不同的颜色
顶点着色器:指定顶点的位置(需要能够给他们提供转换),每一个顶点调用一次顶点着色器
片段(像素)着色器:基本目的是决定像素的颜色是什么,会为每个像素运行一次去光栅化(画在屏幕上) --损耗大
glShaderSource() --length-> nullptr,以null结尾----每个字符都认定以null结尾

截图 learnopengl

创建着色器

/**
* 创建着色器
*/
static int CompileShader(unsigned int type, const std::string& source)
{
    unsigned id = glCreateShader(type);
    const char* src = source.c_str();
    /* 指定着色器 */
    glShaderSource(id, 1, &src, nullptr);
    glCompileShader(id);

    //TODO Eroor Handling
    int result;
    glGetShaderiv(id, GL_COMPILE_STATUS, &result);
    if (result == GL_FALSE)
    {
        int length;
        glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        char* message = (char*)alloca(length * sizeof(char));
        glGetShaderInfoLog(id, length, &length, message); 
        std::cout << "Failed to compile " << 
            (type == GL_VERTEX_SHADER ? "vertex" : "fragment") << "shader!" << std::endl;
        std::cout << message << std::endl;


        glDeleteShader(id);
        return 0;
    }

    return id;
}


static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{
    unsigned int program = glCreateProgram();
    unsigned int vs = CompileShader(GL_VERTEX_SHADER,vertexShader);
    unsigned int fs = CompileShader(GL_FRAGMENT_SHADER,fragmentShader);

    /* 链接着色器 */
    glAttachShader(program, vs);
    glAttachShader(program, fs);
    /* 链接程序 */
    glLinkProgram(program);
    /* 检索 */
    glValidateProgram(program);

    /* 已经链接到程序,可以删除shader,但不是很必要,不利于调试 */
    //glDeleteShader(vs);
    //glDeleteShader(fs);

    return program;
}
/**
    * Opengl中生成的所有东西都会被分配一个唯一的标识符(整数) 
    */
    //3个顶点坐标
    float positions[6] = {
        -0.5f, -0.5f,
         0.0f,  0.5f,
         0.5f, -0.5f
    };

    /* 创建缓冲区 */
    unsigned int buffer;
    glGenBuffers(1, &buffer);
    /* 选择缓冲区 */
    glBindBuffer(GL_ARRAY_BUFFER,buffer);//数组,缓冲区
    /*
        STREAM : The data store contents will be modified once and used at most a few times.(数据存储内容将被修改一次,最多使用几次)
        STATIC : The data store contents will be modified once and used many times.(数据存储内容将被修改一次并使用多次)
        DYNAMIC : The data store contents will be modified repeatedly and used many times.(数据存储内容将被重复修改和多次使用)
        GL_STATIC_DRAW :数据不会或几乎不会改变。
        GL_DYNAMIC_DRAW:数据会被改变很多。
        GL_STREAM_DRAW :数据每次绘制时都会改变。
    */
    /* 指定数据 :大小以字节为单位 , 数据可以传入null*/
    glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(float), positions, GL_STATIC_DRAW);



    /* 启用顶点属性 */
    glEnableVertexAttribArray(0);
    /* 链接顶点属性 */
    /* (TERPROCGLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) */
    /* (TERPROCGLuint 索引,GLint 大小,GLenum 类型,GLboolean 归一化,GLsizei stride, const void *指针) */
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0);



    std::string vertexShader =
        "#version 330 core\n"
        "\n"
        "layout(location = 0) in vec4 position;\n"
        "\n"
        "void main()\n"
        "{\n"
        "   gl_Position = position;\n"
        "}\n";

    std::string fragmentShader =
        "#version 330 core\n"
        "\n"
        "layout(location = 0) out vec4 color;\n"
        "\n"
        "void main()\n"
        "{\n"
        "   color = vec4(1.0, 0.0, 0.0, 1.0);\n"
        "}\n";

    unsigned int program = CreateShader(vertexShader,fragmentShader);
    /* 绑定着色器 */
    glUseProgram(program);
/* 绘制三角形 : glDrawArrays() 没有索引缓冲区时用的方法      glDrawElements():有索引缓冲区时用的方法 */
        glDrawArrays(GL_TRIANGLES,0,3); //0-first偏移   3-数量
        //GL_POINTS、GL_TRIANGLES、GL_LINE_STRIP。

文件的方式实现着色器 (也可以写在两个文件)

 

 

 

#shader vertex
#version 330 core

layout(location = 0) in vec4 position;

void main()
{
   gl_Position = position;
};

#shader fragment
#version 330 core

layout(location = 0) out vec4 color;

void main()
{
   color = vec4(1.0, 0.0, 0.0, 1.0);
};
#include <fstream>
#include <string>
#include <sstream>

struct ShaderPogramSource
{
    std::string VertexSource;
    std::string FragmentSource;
};
/* 读取着色器 */
static ShaderPogramSource ParseShader(const std::string& filepath)
{
    std::ifstream stream(filepath);

    enum class ShaderType
    {
        NONE = -1, VERTEXT = 0, FRAGMENT = 1
    };

    std::string line;
    std::stringstream ss[2];
    ShaderType type = ShaderType::NONE;
    while (getline(stream, line))
    {
        if (line.find("#shader") != std::string::npos)
        {
            if (line.find("vertex") != std::string::npos) 
            {
                type = ShaderType::VERTEXT;
            }
            else if(line.find("fragment") != std::string::npos)
            {
                type = ShaderType::FRAGMENT;
            }
        }
        else
        {
            ss[(int)type] << line << '\n';
        }
    }
    return { ss[0].str(),ss[1].str() };
}

获取shader

ShaderPogramSource source = ParseShader("res/shaders/Basic.shader");
    std::cout << "Vertex " << std::endl;
    std::cout << source.VertexSource << std::endl;
    std::cout << "Fragment " << std::endl;
    std::cout << source.FragmentSource << std::endl;

    unsigned int program = CreateShader(source.VertexSource, source.FragmentSource);

使用索引缓冲区

索引缓冲区 : 任何缓冲区,都必须由无符号的整数组成

float positions[8] = {
        -0.5f, -0.5f,
         0.5f, -0.5f,
         0.5f,  0.5f,
        -0.5f,  0.5f,
    };
glBufferData(GL_ARRAY_BUFFER, sizeof(positions) * sizeof(float), positions, GL_STATIC_DRAW);
/* 索引缓冲区 */
    unsigned int indices[] = {
        0,1,2,
        2,3,0
    };
    unsigned int ibo;
    glGenBuffers(1, &ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);//数组,缓冲区
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices) * sizeof(int), indices, GL_STATIC_DRAW);
/* 绘制矩形 : glDrawArrays() 没有索引缓冲区时用的方法      glDrawElements():有索引缓冲区时用的方法 */
        glDrawElements(GL_TRIANGLES, sizeof(indices), GL_UNSIGNED_INT,nullptr);//已经在glBindBuffer设置,这里给nullptr
        //glDrawArrays(GL_TRIANGLES,0, sizeof(positions) / 2); //0-first偏移   6-数量

Uniforms

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

如果shader里的uniform没有使用,会被剥离,就会返回-1
glfwSwapInterval()---设置刷新帧率

#shader fragment
#version 330 core

layout(location = 0) out vec4 color;

uniform vec4 u_Color;

void main()
{
   color = u_Color;
};

改变颜色

int location = glGetUniformLocation(program, "u_Color");
glUniform4f(location,0.8f,0.3f,0.8f,1.0f);
float r = 0.0f;
float increment = 0.05f;


glDrawElements(GL_TRIANGLES, sizeof(indices), GL_UNSIGNED_INT,nullptr);

/* 动态编颜色     太快,设置glfwSwapInterval() */
 if (r > 1.0f)
   increment = -0.05f;
else if (r < 0.0f)
   increment = 0.05f;

r += increment;

改变频率

/* Make the window's context current */
    glfwMakeContextCurrent(window);

    /* 闪屏、刷新频率 */
    glfwSwapInterval(1);

 

每个输入变量也叫顶点属性(Vertex Attribute)。我们能声明的顶点属性是有上限的,它一般由硬件来决定。OpenGL确保至少有16个包含4分量的顶点属性可用,但是有些硬件或许允许更多的顶点属性,你可以查询GL_MAX_VERTEX_ATTRIBS来获取具体的上限

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

 

posted @ 2023-03-09 11:08  西瓜皮不甜  阅读(71)  评论(0编辑  收藏  举报