Aquacolor

Aquacolor



LearningOpenGL【5】

Gumdrop · 2025-08-29 · 19浏览 · 未分类



//main.cpp
int u_vertexColorLocation = glGetUniformLocation(shaderProgram, "u_color");
glUseProgram(shaderProgram);
glUniform4f(u_vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);
//VertexShader.glsl
#version 330 core
layout (location = 0) in vec3 aPos; // the position variable has attribute position 0

out vec4 vertexColor; // specify a color output to the fragment shader

uniform vec4 u_color;

void main()
{
    gl_Position = vec4(aPos, 1.0); // see how we directly give a vec3 to vec4's constructor
    vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // set the output variable to a dark-red color
}
//FragmentShader.glsl
#version 330 core
out vec4 FragColor;

in vec4 vertexColor; // the input variable from the vertex shader (same name and same type)  
uniform vec4 u_color;

void main()
{
    FragColor = u_color;
} 

(上面三段程序是理解GLSL运行用的,主要是关于uniform变量)

GLSL是如何从程序获取和传递数据的?

每个stage所需要的数据逻辑上有三个来源:由OpenGL管理的数据、前一stage、由render loop传递。(硬编码数据来源的实现就是普通的编译实现)

由于渲染管线可以非常复杂,所以每个stage都可能三个通道共同使用。

  • OpenGL管理一部分数据,是因为这部分数据必要且操作机械,所以可以处于半封装的形式。

    • 如VertexShader中必须给gl_Position赋值,但是后面stages使用它但不需要直接写出,因为对三维点的处理方式是默认的。这就是.glsl第一种数据通道。

    • 还有layout(location = n) in从OpenGL的VAA中获取对应顶点属性,这里的location其实是与glVertexAttribPointer()中第一个参数关联的。

  • 数据的前后传递使用in/out加同一变量名的形式,直观模型就是同接口拼图或加工传送带。这是第二种

  • 由render loop提供的数据,使用的是.glsl中uniform变量和render loop中glGetUniformLocation()glUniformxx()共同实现的对接。这个是最难理解的。glGetUniformLocation()能获取与一个uniform变量对应的虚拟地址,这个地址就能作为glUniformxx()的参数实现在render loop中修改shader内变量的值。其实现方式是shader program在需要使用uniform变量时,会从uniform变量表中查找它的值。这是第三种。

(虽然有些功能看似能合写成一个函数但是作为api不应该这么做,这样才能为程序员提供更多封装方式)

第一和第三种其实可以不作区分,因为这三种传输数据的形式都是由OpenGL管理、并建议在render loop中进行传递的。所以可以四种都分开记。

记忆:gl_、layout、in/out、uniform

uniform变量如何很好地使用和封装

由于在render loop中又添加了一个新功能,所以其布局再次修改:

while(!glfwWindowShouldClose(window)){
    //process the device input
    processInput(window);

    //render the background
    glClearColor(0.2f,0.3f,0.3f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    //use the program
    glUseProgram(shaderProgram);

    //update the uniform variables
    ...//calculate the value
    type xxLocation = glGetUniformLocation(shaderProgram,"u_xx");
    glUniformxx(xxLocation,...);//assign the value

    //select object and render the triangles
    glBindVertexArray(VAO);
    glDrawxx(...);

    //swap and poll
    glfwSwapBuffers(window);
    glfwPollEvents();
}

cpp流对象异常处理进阶

明确需要理解使用的成员函数:exceptionsclearrdstate

位掩码的使用都会。

如果你希望在使用文件流时将整个文件传入,通常可能会选择一行一行加在std::string上,但是也可以:

ifstream xxFile;
stringstream xxSStream;
string xxStr;

xxFile.open("...");
xxStream << xxFile.rdbuf();
xxStr = xxStream.str();

const char* xxSource = xxStr.c_str();

.rdbuf()成员返回的是filebuf类型,所以使用字符串流,再用.str()过渡一下到字符串类型,再.c_str()到C字符串。其中每个变量都不能是临时的,通过了实践的检验。

前两个好理解,xxStr、xxSource必须具名的原因是在使用glShaderSource()进行一次字符串复制前,字符串必须要有被分配一个内存空间。以及如果不想预定义一个超大的字符数组,就要在初始化时给常字符指针赋字符串值。

(C风格字符串复制真挺麻烦的)

std::ifstream流对象代指所有流对象,流对象可能有四种状态:

  1. std::ifstream::goodbit

  2. std::ifstream::eofbit

  3. std::ifstream::failbit

  4. std::ifstream::badbit

都是位掩码形式。但是这些状态本身不会引起抛出异常,通常是可以继续运行的。

.exceptions(std::ifstream::failbit|std::ifstream::badbit)设置了会抛出异常的状态。

.rdstate()返回当前状态。

.clear(std::ifstream::badbit)设置当前状态,默认参数为goodbit相当于清楚异常状态。

.good()``.eof()``.fail()``.bad()当处于某状态时返回true。

如果你需要让流对象在出问题时能中断程序或进行处理等,请使用该方法。

void sourceInput(unsigned int vertexShader, unsigned int fragmentShader, const char* vertexShaderPath, const char* fragmentShaderPath)
{
    std::ifstream vertexShaderFile;
    std::ifstream fragmentShaderFile;
    std::string vertexShaderStr;
    std::string fragmentShaderStr;
    vertexShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    fragmentShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    try
    {
        std::stringstream vertexShaderStringStream;
        std::stringstream fragmentShaderStringStream;
        vertexShaderFile.open(vertexShaderPath);
        fragmentShaderFile.open(fragmentShaderPath);
        vertexShaderStringStream << vertexShaderFile.rdbuf();
        fragmentShaderStringStream << fragmentShaderFile.rdbuf();
        vertexShaderFile.close();
        fragmentShaderFile.close();
        vertexShaderStr = vertexShaderStringStream.str();
        fragmentShaderStr = fragmentShaderStringStream.str();
    }
    catch (const std::exception&)
    {
        std::cerr << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
    }
    const char* vertexShaderSource = vertexShaderStr.c_str();
    const char* fragmentShaderSource = fragmentShaderStr.c_str();
    std::cout << vertexShaderSource << std::endl;
    std::cout << fragmentShaderSource << std::endl;
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
}

多个VAA是如何处理的?

unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);

//......

float vertices[] = {
	// first triangle
	-0.9f, -0.5f, 0.0f,  1.0f,0.0f, 0.0f,  // left
	-0.0f, -0.5f, 0.0f,  0.0f,1.0f, 0.0f,  // right
	-0.45f, 0.5f, 0.0f,  0.0f,0.0f, 1.0f,  // top
};
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

//......

glBindVertexArray(0);

第二个参数为3,从这里可以更容易看出3指的是在由offset和stride确定的每个位置要取多大一段数据。

也可以看出一个VAO对应多个VAA。

2025-08-28T16:30:21.png



©

comment 评论区

添加新评论

face表情



  • ©2026 bilibili.com

textsms
内容不能为空
昵称不能为空
email
邮件地址格式错误
web
beach_access
验证码不能为空
keyboard发表评论


star_outline 咱快来抢个沙发吧!




©2026 Aquacolor

Theme Romanticism2.2 by Akashi
Powered by Typecho