OpenGL学习笔记(一)

news2024/10/7 7:27:44

文章目录

    • 1. 顶点
    • 2. 着色器(Shader)

1. 顶点

OpenGL本质是一个状态机,点作为表示图形最基本的元素,如何告诉OpenGL点的基本信息就是我们要做的事情

1.1VertexArray
VertexArray是所有顶点的集合,我们可以将VertexBuffer分成很多组,每组采取不同的着色方式
之后再解释Addlayout()

class VertexArray
{
public:
	VertexArray()
	{
		//创建一个VertexArray
		glGenVertexArrays(1, &m_vao);
		//绑定
		glBindVertexArray(m_vao);
	}
	~VertexArray()
	{
		glDeleteVertexArrays(1, &m_vao);
	}

	void Addlayout(const VertexBuffer& vb, const VertexBufferLayout& layout);
	void Bind()const;
	void UnBind()const;
private:
	unsigned int m_vao;
};

void VertexArray::Addlayout(const VertexBuffer& vb, const VertexBufferLayout& layout)
{
	vb.Bind();
	const auto& elements = layout.GetElements();

	unsigned int offset = 0;

	for (int i = 0; i < elements.size(); i++)
	{
		const auto& element = elements[i];
		//之后再解释
		glVertexAttribPointer(i, element.size, element.type, element.normalized, element.stride, (const void*)offset);
		glEnableVertexAttribArray(i);

		offset += VertexBufferElement::GetSizeofType(element.type) * element.size;
	}
}

void VertexArray::Bind()const
{
	glBindVertexArray(m_vao);

}
void VertexArray::UnBind()const
{
	glBindVertexArray(0);
}

1.2VertexBuffer
VertexBuffer就是将所有需要的点的数据进行缓存,但OpenGL仅仅只是知道了所有的数据,多少个字节表示一个点,OpenGL还是不知道

在这里插入图片描述
就像上图,整个大长方形表示我们所有点的内存总和,每个小长方形表示每一个点占的内存,这时OpenGL只是知道了存储这些点需要多少内存,但不知道有多少个点,每个点的每个字节的意义

class VertexBuffer
{
public:
	VertexBuffer(const void* data,unsigned int size);
	~VertexBuffer();
	
	void Bind() const;
	void Unbind() const;

private:
	unsigned int m_RendererID;
};

VertexBuffer::VertexBuffer(const void* data, unsigned int size)
{
    glGenBuffers(1, &m_RendererID);
    glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
    glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}
VertexBuffer::~VertexBuffer()
{
    glDeleteBuffers(1, &m_RendererID);
}

void VertexBuffer::Bind()const
{
    glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
}
void VertexBuffer::Unbind()const
{
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

1.3VertexBufferLayout

VertexBufferLayout描述VertexBuffer中每个点占的字节数多少和每个点内存的划分是怎样的

在这里插入图片描述
例如这个点,一共4个字节,我需要前两个字节表示坐标,后两个字节表示颜色,VertexBufferLayout就可分配字节,前两个字节为一组,后两个字节为一组,如果要具体的表示坐标与颜色后文会介绍

struct VertexBufferElement
{
	int size;
	int type;
	unsigned char normalized;
	static unsigned int stride;

	static unsigned int GetSizeofType(int type)
	{
		switch (type)
		{
			case GL_FLOAT:
			{
				return 4;
			}
			case GL_UNSIGNED_INT:
			{
				return 4;
			}
			case GL_UNSIGNED_BYTE:
			{
				return 1;
			}
			case GL_BYTE:
			{
				return 1;
			}
			default:
			{
				break;

			}
		}
		return false;

	}
};
unsigned int VertexBufferElement::stride = 0;

class VertexBufferLayout
{
public:
	template<class T>
	void Push(int size)
	{
		static_assert(false);
	}

	template<>
	void Push<float>(int size)
	{
		m_Elements.push_back({ size,GL_FLOAT,GL_FALSE });
		VertexBufferElement::stride += VertexBufferElement::GetSizeofType(GL_FLOAT) * size;
	}
	template<>
	void Push<unsigned int>(int size)
	{
		m_Elements.push_back({ size,GL_UNSIGNED_INT,GL_FALSE });
		VertexBufferElement::stride += VertexBufferElement::GetSizeofType(GL_UNSIGNED_INT) * size;
	}

	template<>
	void Push<char>(int size)
	{
		m_Elements.push_back({ size,GL_BYTE,GL_FALSE });
		VertexBufferElement::stride += VertexBufferElement::GetSizeofType(GL_BYTE) * size;
	}

	template<>
	void Push<unsigned char>(int size)
	{
		m_Elements.push_back({ size,GL_UNSIGNED_BYTE,GL_FALSE });
		VertexBufferElement::stride += VertexBufferElement::GetSizeofType(GL_UNSIGNED_BYTE) * size;
	}
	inline const std::vector<VertexBufferElement>& GetElements() const { return m_Elements; }

private:
	std::vector<VertexBufferElement> m_Elements;
};

1.4解释1.1中的Addlayout
解释之前理解下glVertexAttribPointer

glVertexAttribPointer 是一个 OpenGL 函数,用于将当前的顶点属性与顶点缓冲对象(VBO)关联起来。它的原型如下:

void glVertexAttribPointer(GLuint index, GLint size, GLenum type,
GLboolean normalized, GLsizei stride, const GLvoid *pointer);

index 指定要配置的顶点属性的编号。 size 指定每个顶点属性的分量数(1、2、3 或 4,就像向量的维度一样)。
type指定每个分量的数据类型,可以是GL_BYTE、GL_UNSIGNED_BYTE、GL_SHORT、GL_UNSIGNED_SHORT、GL_INT、GL_UNSIGNED_INT、GL_FLOAT或 GL_DOUBLE。 normalized1 指定是否将数据归一化到 [0,1] 或 [-1,1] 范围内。
stride(步长)指定连续两个顶点属性间的字节数。如果为 0,则表示顶点属性是紧密排列的。 pointer
指向缓冲对象中第一个顶点属性的第一个分量的地址。(offset的作用)

知道了这个之后再看看Addlayout通过VertexBufferLayout将每个顶点的布局进行加载

void VertexArray::Addlayout(const VertexBuffer& vb, const VertexBufferLayout& layout)
{
	vb.Bind();
	const auto& elements = layout.GetElements();

	unsigned int offset = 0;

	for (int i = 0; i < elements.size(); i++)
	{
		const auto& element = elements[i];

		glVertexAttribPointer(i, element.size, element.type, element.normalized, element.stride, (const void*)offset);
		glEnableVertexAttribArray(i);

		offset += VertexBufferElement::GetSizeofType(element.type) * element.size;
	}
}

2. 着色器(Shader)

我们已经知道了顶点的信息,但目前而言,顶点的信息只是一串数,让OpenGL如何解析这些数,就是Shader要做的事情

OpenGL需要在另一个文件单独写一个着色方法,因此本文只介绍怎么创建着色器

struct ShaderProgramSource
{
	std::string Vertex;
	std::string Fragment;
};

class Shader
{
public:
	Shader(const std::string& filepath);
	~Shader();

	void SetUniform4f(const std::string& name, float v1, float v2, float v3, float v4);
	void SetUniform1i(const std::string& name, int v1);

	void Bind()const;
	void Unbind() const;

private:
	int GetUniformLocation(const std::string& name);

	ShaderProgramSource ParseShader(const std::string& filepath);

	unsigned int ComplieShader(const std::string& source, unsigned int type);
	
	unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader);

private:
	unsigned int m_program;
	std::string m_filepath;
	std::unordered_map<std::string,int> m_UniformLocations;
};

Shader::Shader(const std::string& filepath)
	:m_filepath(filepath)
	,m_program(0)
{
    ShaderProgramSource shaderProgram = ParseShader(m_filepath);

    m_program = CreateShader(shaderProgram.Vertex, shaderProgram.Fragment);
    glUseProgram(m_program);
}

Shader::~Shader()
{
    glDeleteProgram(m_program);
}
void Shader::SetUniform4f(const std::string& name, float v0, float v1, float v2, float v3)
{
    glUniform4f(GetUniformLocation(name), v0, v1, v2, v3);

}
void Shader::SetUniform1i(const std::string& name, int v0)
{
    glUniform1i(GetUniformLocation(name), v0);
}


void Shader::Bind()const
{
    glUseProgram(m_program);
}
void Shader::Unbind() const
{
    glUseProgram(0);
}

int Shader::GetUniformLocation(const std::string& name)
{
    if (m_UniformLocations.find(name) != m_UniformLocations.end())
    {
        return m_UniformLocations[name];
    }

    GLCall(int location = glGetUniformLocation(m_program, name.c_str()));

    if (location == -1)
    {
        std::cout << "fail glGetUniformLocation" << std::endl;
        return 0;
    }
    else
    {
        m_UniformLocations[name] = location;
        return location;
    }

}


ShaderProgramSource Shader::ParseShader(const std::string& filepath)
{
    enum ShaderTypeEnum
    {
        NONE = -1,
        VERTEX = 0,
        FRAGMENT = 1
    };

    std::ifstream stream(filepath);
    std::string line;
    std::string ss[2];

    ShaderTypeEnum type = NONE;
    while (std::getline(stream, line))
    {
        if (line.find("#shader") != std::string::npos)
        {
            if (line.find("vertex") != std::string::npos)
            {
                type = VERTEX;
            }
            else
            {
                type = FRAGMENT;
            }
        }
        //没有#shader
        else
        {
            ss[type] += line;
            ss[type] += '\n';
        }
    }
    return { ss[0],ss[1] };
}

unsigned int Shader::ComplieShader(const std::string& source, unsigned int type)
{
    unsigned int id = glCreateShader(type);
    const char* str = source.c_str();
    glShaderSource(id, 1, &str, nullptr);
    glCompileShader(id);

    int result;
    glGetShaderiv(id, GL_COMPILE_STATUS, &result);

    if (result == false)
    {
        int length;
        glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        char* message = new char[length];
        glGetShaderInfoLog(id, length, &length, message);
        std::cout << "Failed to complie:" << ((type == GL_VERTEX_SHADER) ? "vertex" : "fragment") << std::endl;
        std::cout << message << std::endl;
        glDeleteShader(id);
        return 0;
    }

    return id;
}

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

    glAttachShader(program, vs);
    glAttachShader(program, fs);
    glLinkProgram(program);
    // glValidateProgram(program);

    glDeleteShader(vs);
    glDeleteShader(fs);

    return program;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/461267.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Volatile系列(一):Volatile测试案例一可见性

系列文章 Volatile测试案例一可见性 目录​​​​​​​ 前言 测试1 逻辑 代码 结果 测试2 逻辑 代码 结果 结论 原理探讨&#xff08;可见性&#xff09; 前言 多线程是 JAVA 并发编程的主要应用&#xff0c;并发环境能大幅提高应用性能&#xff0c;提高 CPU 使用…

基于springboot+mysql+html实现智能停车场管理系统

基于springbootmysqlhtml实现智能停车场管理系统 一、系统介绍1、系统主要功能&#xff1a;2.涉及技术框架&#xff1a;3.本项目所用环境&#xff1a; 二、功能展示三、其它系统四、获取源码 一、系统介绍 1、系统主要功能&#xff1a; 系统管理&#xff1a;角色管理、接口管…

华为OD机试真题(Java),货币单位换算(100%通过+复盘思路)

一、题目描述 记账本上记录了若干条多国货币金额&#xff0c;需要转换成人民币分 (fen)&#xff0c;汇总后输出每行记录一条金额&#xff0c;金额带有货币单位&#xff0c;格式为数字单位&#xff0c;可能是单独元&#xff0c;或者单独分&#xff0c;或者元与分的组合要求将这…

解决方案:Zotero实现参考文献中英文混排,将英文文献中的“等”转成“et al.”

Zotero 是一款非常实用且易于使用的参考文献管理工具&#xff0c;可帮助用户收集、整理和引用各种类型的文献&#xff0c;包括图书、期刊文章、网页等。在学术写作中起着重要作用。 但是其在中文世界中&#xff0c;运行起来偶尔会出现问题&#xff0c;这里记录一个问题及其解决…

Nacos安装配置2.0以上版本

一、Nacos使用 1、官网 Nacos Spring 快速开始 Nacos Spring Boot 快速开始 Nacos Spring Cloud 快速开始 Nacos Docker 快速开始 Dubbo 融合 Nacos 成为注册中心 Kubernetes Nacos NacosSync 介绍 2、配置文件 nacos-配置中心官方文档说明 nacos-注册中心官方文档说明 在nacos…

浅谈拉格朗日插值法

浅谈拉格朗日插值法 好像FFT要用到&#xff0c;所以就学习一手 文章目录 浅谈拉格朗日插值法什么是插值拉格朗日插值法 什么是插值 在离散数据的基础上补插连续的函数&#xff0c;使得这条连续函数经过所有离散数据点&#xff0c;这个过程就叫插值。其意义在于&#xff1a; …

缓存和数据库一致性问题

情况一 先更新缓存再更新数据库 先更新缓存 再更新数据库 如果数据库更新失败 那我们缓存也没有意义 而且这个缓存也是需要计算的消耗资源的 所以不可取到 情况二 先更新数据库 后更新缓存 两个线程 线程A更新数据库 但是由于某些原因被阻塞了 也就是A更新后的数值没有被更新到…

Ajax 创建对象

文章目录 AJAX 创建对象创建 XMLHttpRequest 对象XMLHttpRequest 对象创建 XMLHttpRequest 对象 AJAX 创建对象 创建 XMLHttpRequest 对象 XMLHttpRequest 是 AJAX 的基础。 XMLHttpRequest 对象 所有现代浏览器均支持 XMLHttpRequest 对象&#xff08;IE5 和 IE6 使用 Acti…

【pyTorch学习笔记③】PyTorch基础·上篇

文章目录 一、PyTorch介绍二、PyTorch的安装1、CPU版本2、GPU版本 三、Numpy与Tensor1.Tensor的创建2.Tensor的变形 相关推荐 一、PyTorch介绍 PyTorch是Facebook发布的一款深度学习框架&#xff0c;继承了Torch灵活、动态的编程环境和用户友好的界面&#xff0c;支持以快速和灵…

kafka使用详解、最佳实践和问题排查

kafka是一个常用的分布式消息中间件&#xff0c;与RabbitMQ对比&#xff0c;特点是可以无限横向扩容&#xff0c;并保持高可靠性、高吞吐量和低延迟&#xff0c;因此比RabbitMQ有更高的市场占有率&#xff08;网上搜了一下&#xff0c;kafka大约41%&#xff0c;RabbitMQ大约29%…

【网络安全】Metasploit工具

Metasploit Metasploit介绍默认安装目录主要应用文件modules文件内容介绍目录内容 Metasploit使用常用命令信息收集之端口扫描信息收集之c段扫描ms17_010&#xff08;永恒之蓝&#xff09; msfvenom生成木马命令入侵win全过程 Metasploit介绍 默认安装目录 /usr/share/metasp…

C语言指针2大问题:指针类型有什么用?指针如何运算?

如题&#xff0c;本篇博客主要解决2个疑点&#xff1a;指针类型的用处&#xff0c;指针如何运算。 1.指针类型 C语言中的指针类型&#xff0c;在X86环境下大小是4个字节&#xff0c;在X64环境下大小是8个字节。既然指针的大小和指针类型无关&#xff0c;那么指针类型究竟有什么…

AI看图说话,MiniGPT-4已经开源

MiniGPT-4 是一个人工智能工具&#xff0c;​最大的飞跃是增加了识图能力&#xff0c;​并且回答准确性也得到显著提高。​它可以识别图片并回答关于图片的问题&#xff0c;​例如图片内容、​颜色等等。 ​此外&#xff0c;​它还可以进行图像对话&#xff0c;​即通过图片和…

MFC之CRect详解

2023年4月25日&#xff0c;周二晚上。 今天查了不少关于CRect类及其相关内容的资料&#xff0c;学到了不少东西&#xff0c;所以我决定写一篇详细的关于CRect类及其相关内容的文章&#xff0c;以记录今天所学。 CRect类 在 MFC 中&#xff0c;CRect 类表示一个矩形区域。它是…

【Vue 移动端开发】适配百分之99的屏幕方案

之前提起移动端适配&#xff0c;都是一些视口的概念&#xff0c;包括物理像素和逻辑像素&#xff0c;理想视口&#xff0c;dpr等等等。利用 media query 和 rem 是最常见的移动端适配方案。如下代码&#xff1a; const deviceWidth document.documentElement.clientWidth || …

Axure RP 9 for Mac 原型设计软件安装,Mac软件打开提示:已损坏,无法打开。您应该将它移到废纸篓。怎么解决?

Axure RP 9 for Mac 原型设计软件安装&#xff0c;Mac软件打开提示&#xff1a;已损坏&#xff0c;无法打开。您应该将它移到废纸篓。怎么解决? 安装过程很简单&#xff1a; 1、下载后先将软件拖入应用程序中&#xff1b; 2、打开软件&#xff0c;弹出登录界面&#xff0c;点…

网络编程代码实例:用户数据报协议(UDP)简单版

文章目录 前言代码仓库内容代码&#xff08;有详细注释&#xff09;server.cclient.cMakefile 结果总结参考资料作者的话 前言 网络编程代码实例&#xff1a;用户数据报协议&#xff08;UDP&#xff09;简单版。 代码仓库 yezhening/Environment-and-network-programming-exa…

[ICLR 2020] Reducing Transformer Depth on Demand with Structured Dropout

Contents IntroductionTraining Transformers with Random Structured PruningRandomly Dropping Structures at Training TimePruning at Inference Time ExperimentsReferences Introduction 作者提出了一种新的 structural pruning 方法 LayerDrop&#xff0c;通过在训练时…

FastDFS集群搭建

简介 FastDFS是什么&#xff1f;我们这里可以看一下度娘的解释。FastDFS是一个开源的轻量级分布式文件系统&#xff0c;它对文件进行管理&#xff0c;功能包括&#xff1a;文件存储、文件同步、文件访问&#xff08;文件上传、文件下载&#xff09;等&#xff0c;解决了大容量…

Python base64模块加密解密

一、为何使用base64加密解密 为了安全机制的系统&#xff0c;在用户登录的时候&#xff0c;会采用一系列措施保护用户信息&#xff0c;防止程序被攻击&#xff0c;比如&#xff1a;将用户输入的密码加密处理&#xff0c;在控制台看请求接口看到的密码是加密过的密码&#xff0c…