OpenGL超级宝典 使用缓冲为顶点着色器提供数据

本文详细介绍了OpenGL中的缓冲创建、内存分配及数据传输,包括glCreateBuffer、glBufferStorage等函数的使用。同时,展示了如何通过Shader类为顶点着色器提供数据,包括设置和链接不同类型的着色器。代码示例中,演示了如何封装Shader类以及使用缓冲更新顶点和颜色数据。

缓冲

缓冲,顾名思义,就是暂时不急着用的内容。
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);

效果

在这里插入图片描述

总结

  1. 创建vao并且绑定过到OpenGL上下文
  2. 创建vbo
  3. 为vbo分配内存空间
  4. 将vbo绑定到OpenGL上
  5. 将vbo绑定到vao上
  6. 设置数据的布局和格式
  7. 指示vao的顶点属性应该从哪个绑定点获取数据
  8. 启用顶点数据
  9. 为缓存添加数据(这步可以不放最后)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ht巷子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值