文章目录
缓冲
缓冲,顾名思义,就是暂时不急着用的内容。
OpenGL中,缓冲是指内存的线性分配。每个缓冲都有个缓冲名,同时还有分配给缓冲的内存,叫做缓冲的数据库。
若得到一个缓冲的名称,则可以通过将它绑定到缓存绑定点并附加到OpenGL环境中。绑定点又叫做靶点(target)
创建缓冲和分配内存
在OpenGL中缓冲的创建和vao是一样的流程
void glCreateBuffer(GLsizei n, GLuint* buffers)
void glBindBuffer(GLenum target, GLuint buffer)
//绑定缓冲到OpenGL中
分配内存
void glBufferStorage(GLenum target, GLsizeiptr size, const void* data, GLbitfield flags)
这是给这类靶点分配内存空间的
void glNamedBufferStorage(GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags)
这是给单独的缓冲分配空间的
将数据传入缓冲中去
void glBufferSubData(GLenum targer, GLintptr offset, GLsizeptr size, const GLvoid* data);
void glNamedBufferSubData(GLuint buffer, GLintptr offset, GLsizeptr size, const GLvoid* data);
还有一种就是映射缓冲的数据库
void glMapNamedBuffer(GLuint buffer, GLenum usage)
这是生成一个指针指向缓冲的内存
还有一种就是限定映射内存的位置
void glMapNamedBufferRange(GLuint buffer, GLintptr offset, GLsizeptr length, GLbitfield access);
使用缓冲为顶点着色器提供数据
自己封装的Shader类(修改)
上一版的Shader类有点小问题,所以这次做了修改,主要是在链接之前检查了shader对象
Shader.h
#pragma once
#include "sb7.h"
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <exception>
class Shader
{
public:
Shader();
Shader(const std::string vertexPath, const std::string fragmentPath);
Shader(const std::string vertexPath, const std::string tescPath, const std::string tesePath, const std::string fragmentPath);
//设置顶点着色器
void setVertexShder(const std::string vertexPath);
//设置曲面细分控制着色器
void setTescShader(const std::string tescPath);
//设置曲面细分评估着色器
void setTeseShader(const std::string tesePath);
//设置片段着色器
void setFragmentShader(const std::string fragmentPath);
//program对象使用
void use();
private:
GLuint program;
//源代码
std::string shaderString;
const char* shaderSource;
//用于读取shader文件
std::ifstream shaderFile;
//转换源码
std::stringstream shaderSStream;
private:
//查错程序,是否有编译错误
void checkCompileErrors(unsigned int ID, std::string type);
//读取文件,并且创建着色器对象
void readShaderSource(std::string shaderFilePath);
//链接着色器
void linkShader(GLuint shaderObject, std::string type);
};
Shader.cpp
#include "Shader.h"
Shader::Shader() {}
Shader::Shader(const std::string vertexPath, const std::string fragmentPath)
{
//创建程序对象
program = glCreateProgram();
setVertexShder(vertexPath);
setFragmentShader(fragmentPath);
}
Shader::Shader(const std::string vertexPath, const std::string tescPath, const std::string tesePath, const std::string fragmentPath)
{
//创建程序对象
program = glCreateProgram();
setVertexShder(vertexPath);
setFragmentShader(fragmentPath);
setTescShader(tescPath);
setTeseShader(tesePath);
}
//设置顶点着色器
void Shader::setVertexShder(const std::string vertexPath)
{
//读取shader源代码
readShaderSource(vertexPath);
//创建shader对象
GLuint vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
//链接shader对象
linkShader(vertexShader, "VERTEX");
}
//设置曲面细分控制着色器
void Shader::setTescShader(const std::string tescPath)
{
//读取shader源代码
readShaderSource(tescPath);
//创建shader对象
GLuint tescShader;
tescShader = glCreateShader(GL_TESS_CONTROL_SHADER);
//链接shader对象
linkShader(tescShader, "TESC");
}
//设置曲面细分评估着色器
void Shader::setTeseShader(const std::string tesePath)
{
//读取shader源代码
readShaderSource(tesePath);
//创建shader对象
GLuint teseShader;
teseShader = glCreateShader(GL_TESS_EVALUATION_SHADER);
//链接shader对象
linkShader(teseShader, "TESE");
}
//设置片段着色器
void Shader::setFragmentShader(const std::string fragmentPath)
{
//读取shader源代码
readShaderSource(fragmentPath);
//创建shader对象
GLuint fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
//链接shader对象
linkShader(fragmentShader, "FRAGMENT");
}
void Shader::use()
{
glUseProgram(program);
}
//读取文件,并且创建着色器对象
void Shader::readShaderSource(std::string shaderFilePath)
{
//将上一个shader的源码清理
shaderSStream.str("");
memset(&shaderSource, '\0', sizeof(shaderSource));
//open shader file
if (!shaderFilePath.empty()) //判断文件路径是否为空
{
//文件路径不为空,打开文件
shaderFile.open(shaderFilePath);
shaderFile.exceptions(std::ifstream::badbit || std::ifstream::failbit);
try
{
if (!shaderFile.is_open()) //文件打开失败
{
throw std::exception("open fragment shader file fail");
}
//文件成功打开
//字符串流读取文件中的数据
shaderSStream << shaderFile.rdbuf();
//读完了,把文件关了
shaderFile.close();
//把源代码读入字符串中去
shaderString = shaderSStream.str();
//把string转成系数需要的const char *类型
shaderSource = shaderString.c_str();
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
}
}
}
//链接着色器
void Shader::linkShader(GLuint shaderObject, std::string type)
{
//将源代码读入shader对象
glShaderSource(shaderObject, 1, &shaderSource, NULL);
//编译shader对象中的源码
glCompileShader(shaderObject);
//检查着色器对象是否有误
checkCompileErrors(shaderObject, type);
//将shader对象附加到program对象上去
glAttachShader(program, shaderObject);
//链接program对象上的shader对象
glLinkProgram(program);
//查错
checkCompileErrors(program, "PROGRAM");
//完事,可以把shader对象删了
glDeleteShader(shaderObject);
}
/*
* 查错
*/
void Shader::checkCompileErrors(unsigned int ID, std::string type)
{
int success;
char infoLog[512];
if (type != "PROGRAM")
{
glGetShaderiv(ID, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(ID, 512, NULL, infoLog);
std::cout << "shader compile error: " << infoLog << std::endl;
}
}
else
{
glGetProgramiv(ID, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(ID, 512, NULL, infoLog);
std::cout << "program linking error: " << infoLog << std::endl;
}
}
}
shader
vertex shader
#version 450 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec4 aColor;
out vec4 vs_color;
void main()
{
vs_color = aColor;
gl_Position = vec4(aPos, 1.0);
}
fragment shader
#version 450 core
out vec4 color;
in vec4 vs_color;
void main()
{
color = vs_color;
}
代码
#include "sb7.h"
#include "Shader.h"
#include <iostream>
class my_application : public sb7::application
{
public:
void startup() {
shader = new Shader("vs.vert", "fs.frag");
//创建vao
glCreateVertexArrays(1, &vertex_arrays_object);
//绑定vao到上下文
glBindVertexArray(vertex_arrays_object);
//创建缓存对象
glCreateBuffers(2, buffer);
//分配缓存空间,并且设置模式
//第一个参数是分配缓冲的名称,第二个参数是分配的内存大小
//第三个是分配的数据,没有可以写NULL,第四个参数是分配内存的目的
glNamedBufferStorage(buffer[0], sizeof(vertices), NULL, GL_DYNAMIC_STORAGE_BIT);
//绑定缓存对象到OpenGL
/*glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);
glBindBuffer(GL_ARRAY_BUFFER, buffer[1]);*/
//第一步,绑定一个缓存到vao
//第一个参数是需要绑定缓存的vao,第二个参数是绑定的绑定点,
//第三个参数是绑定的缓存,第4个参数是绑定的数据在缓存中的位置,第5个参数是每个顶点之间的距离
glVertexArrayVertexBuffer(vertex_arrays_object, 0, buffer[0], 0, sizeof(float) * 3);
//告诉OpenGL数据的布局和格式
//第一个参数是哪个vao,第二个参数是顶点属性,第三个参数是顶点的分量数量
//第4个参数是数据的数据类型,第5个参数是是否标准化,第6个参数是与起点处顶点的偏移量
glVertexArrayAttribFormat(vertex_arrays_object, 0, 3, GL_FLOAT, GL_FALSE, 0);
//指示vao的顶点属性(第二个参数)要从绑定点(第三个参数)拿数据
glVertexArrayAttribBinding(vertex_arrays_object, 0, 0);
//启动属性自动填充
//第一个参数是启动的vao,第二个参数是顶点属性
glEnableVertexArrayAttrib(vertex_arrays_object, 0);
glNamedBufferStorage(buffer[1], sizeof(bindcolor), NULL, GL_DYNAMIC_STORAGE_BIT);
glVertexArrayVertexBuffer(vertex_arrays_object, 1, buffer[1], 0, sizeof(float) * 4);
glVertexArrayAttribFormat(vertex_arrays_object, 1, 4, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(vertex_arrays_object, 1, 1);
glEnableVertexArrayAttrib(vertex_arrays_object, 1);
}
void render(double currentTime) {
GLfloat color[] = { 1.0, 1.0, 1.0, 1.0 };
glClearBufferfv(GL_COLOR, 0, color);
//glEnable(GL_MULTISAMPLE);
shader->use();
glNamedBufferSubData(buffer[0], 0, sizeof(vertices), vertices);
glNamedBufferSubData(buffer[1], 0, 4 * 4 * 1, bindcolor);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
void shutdown(){
}
private:
Shader* shader;
GLuint vertex_arrays_object;
GLuint buffer[2];
GLfloat vertices[9] = {
0.5, 0.0, 0.5,
0.0, 0.5, 0.5,
-0.5, 0.0, 0.5
};
GLfloat bindcolor[4] = { 1.0, 1.0, 0.0, 1.0 };
};
DECLARE_MAIN(my_application);
效果
总结
- 创建vao并且绑定过到OpenGL上下文
- 创建vbo
- 为vbo分配内存空间
- 将vbo绑定到OpenGL上
- 将vbo绑定到vao上
- 设置数据的布局和格式
- 指示vao的顶点属性应该从哪个绑定点获取数据
- 启用顶点数据
- 为缓存添加数据(这步可以不放最后)