实现图形算法API[软光栅渲染器,C++]

news2024/11/29 4:52:34

最近有点烦,发烧感冒了三天[事实上是俩天,第三天是因为摆得太舒服了索性多玩一天],啥都没学,打守望先锋也把把被虐...,想着今天来提起键盘把之前的东西都总结一下。

那么话归真题,首先我是仿造opengl来写的图形api,因为是在cpu端运行,所以要比opengl优雅很多。

首先肯定要预先计划好写哪些功能,我想的第一个是vertexbuffer,因为这个是最经常接触的,而且实现思路也非常清晰明了。

class VertexBuffer
{
	struct DataWarp {
		ITypeTag* info = nullptr;
		size_t InfoSize = 0;
		void* data = nullptr;
		size_t dataElemSize = 0;
		~DataWarp() {
			memory::dealloc(data, info->tSize);
			memory::dealloc(info, InfoSize);
		}
	};
public:
	/*
	* T			  数据的类型
	* bufferindex 所在的缓冲下标
	* dataIndex   缓冲中对应的数据下标
	*/
	template<class T>
	T* Sample(size_t bufferIndex, size_t dataIndex);
	/*
	* index 布局的下标
	* size  数据的字节数
	* data  数据指针
	* T	    数据的类型
	*/
	template<class T>
	void SetData(size_t index, size_t size, void* data);
private:
	std::vector<DataWarp*> buffers;
	friend class IInvoker;
};

这个是我设计的类;

只暴露了两个方法,一个push数据,一个采样数据,push数据的方法模仿了opengl,给定对应的布局下标以及数据内存的大小和数据指针,然后将数据拷贝;

当然,我这边有一个问题,buffers里面的指针应该用智能指针代替;

SetData的方法:

template<class T>
inline void VertexBuffer::SetData(size_t index, size_t size, void* data)
{
	if (!data) return;
	if (index >= buffers.size())
		buffers.resize(index + 1);
	buffers[index] = new DataWarp;
	buffers[index]->data = memory::alloc<void>(size);
	memcpy(buffers[index]->data, data, size);
	buffers[index]->info = new TypeTag<T>();//注意memory::alloc不会调用构造函数
	buffers[index]->InfoSize = sizeof(TypeTag<T>);
	buffers[index]->dataElemSize = size / sizeof(T);
}

这边要提及一下TypeTag这个类了,这个是本人的一个小发明,主要是将数据类型抹除,然后用数据去封装,以达到运行时数据类型检测的效果,这个在整个框架设计中都非常重要;

这边我们将采样步长[T的字节数]等信息都封存到TypeTag中,这些信息在我们采样数据的时候是必须要有的;

这样我们就有了最基础的push数据和采样数据的功能了;

另外一个类的设计是uniformbuffer

class UniformBuffer
{
	struct DataWarp {
		ITypeTag* info;
		size_t InfoSize;
		void* data;
		~DataWarp() { 
			memory::dealloc(data, info->tSize);
			memory::dealloc(info, InfoSize);
		}
	};
public:
	template<class T>
	void PushBufferData(std::string name,void* data);
	template<class T>
	T* GetBufferData(std::string name);
	template<class T>
	void SetBufferData(const std::string& name,const T& data);
	void SetCallBack(std::function<void(const std::string&, void*)>);
private:
	std::unordered_map<std::string, DataWarp*> buffers;
	std::function<void(const std::string&, void*)> callback;
	friend class IInvoker;
};

我们这边和opengl一样,是通过变量名表意字符串进行索引的,大概思路和vertexbuffer差不多,一个push数据的方法,一个采样数据的方法,其外有两个不同的方法,一个是修改数据的方法,因为vertexbuffer中的数据一般都不会被修改,所以就没提供这个方法,但是uniform不一样,里面的数据是会被经常修改的所以提供了一个修改数据的方法;

uniformbuffer中的数据会被渲染器分配到shader中,所以uniformbuffer中的数据修改时,shader中的数据也必须被修改,这个时候有两个方向两个思路的解决方向;

先说思路,一个是主动思路,一个是被动思路,其中主动思路的意思是,每帧的渲染中渲染器都将uniformbuffer中的数据重新分配给shader,还有一种思路是被动思路,意思是只有当uniformbuffer中的数据被修改的时候才会去重新分配,这样的好处是降低性能开销,我选择了后者;

所以我增加了一个setCallback的方法,当数据被修改的时候会回call,以达到重新分配的目的;

另外一个类是indexbuffer这个buffer就没什么说头了和vertexbuffer差不多,只不过相比上两个buffer,indexbuffer的数据是提供给渲染器的,而那两个是提供给shader的;

另外一个类的话,是invoker;

class IInvoker
{
protected:
	std::shared_ptr<UniformBuffer> uniformBuffer;// shader have uniformBuffer;
	std::shared_ptr<VertexBuffer> vertexBuffer;
	std::shared_ptr<IndexBuffer> indexBuffer;
	std::shared_ptr<IShader> shader;
	std::shared_ptr<ITexture> zBuffer;
	OutBufferManger* outBufferManger;
	Interpolator* intpor;//intpor是由vertexShader收集信息创建的,但由IInvoke持有,其不是一个指针对象,所以不用shared管理
	glm::mat4 viewport;
	glm::vec2 sampleSpace;
	glm::vec2 zbufferSS;
	std::function<bool(const float&, float&)> depthTest;

	void CheckUniformBuffer();
	void CheckVertexBufferLayout();
	void CheckIntpor();

	void UniformCallback(const std::string&, void*);

	//这个是vertexbuffer与vertexShader数据连接的桥梁,将数据按照layout进行匹配
	void SetLayoutDataForVertexShader(size_t);
	void SetUniformForShader();

	void fill_triangle_optimized(glm::ivec2 v1, glm::ivec2 v2, glm::ivec2 v3, const glm::vec3* vn, const glm::vec4* vPos);
	void SettleFragmentShaderRes(float x, float y);
	void TreatPixel(const int& x, const int& y,const glm::vec3* vn,const glm::vec4* vPos);
	glm::vec3 barycentric(glm::vec2 v1, glm::vec2 v2, glm::vec2 v3, glm::vec2 p);
	void Interplator(glm::vec3 Bar);
public:
	enum class Status {
		COMPELETED,FAILED
	};
	void Link();

	virtual void lnvokeForTriangle();
	
	Status vertexBufferStatus = Status::FAILED;
	Status uniformBufferStatus = Status::FAILED;
	Status InterpolatorStatus = Status::FAILED;
	
	friend int main();
	friend struct LiRenderer;
};

这个invoker是一个算法模板,我是为了将算法模板和渲染器解耦才设计这个类的,这个类中包括但不限于三角形填充算法、线性插值、重心插值、深度测试等等算法,而且为了可装配性,我将大部分算法模块都抽象成了一个std::function<>的接口,这样就可以在渲染器具中开放接口,让客户端程序员进行装配;

另外这个类我有一个做的不够好的地方,那就是校验数据的工作我给分配到其中了,但它只是一个算法模板,后面我们给修改的;

另外还有一个类,OutBufferManger,这个类的话的作用就相当于OpenGL中的多渲染目标了,一次渲染可以渲染出多张纹理;

class OutBufferManger
{
	struct TexAndNVar {
		void* var;//链接到framebuffer中的值的指针
		std::shared_ptr<ITexture> tex;
	};
public:
	OutBufferManger() {}
	void PushTexure(size_t index, std::shared_ptr<ITexture> texture);
	void SetData(size_t index, float x, float y, void* data);
	void SetData(size_t index, size_t x, size_t y, void* data);

	void TakeData(size_t index, float x, float y);
	void TakeData(size_t index, size_t x, size_t y);

	std::shared_ptr<ITexture>& GetTex(size_t index) { return ts.at(index).tex; }
private:
	std::vector<TexAndNVar> ts;
	friend struct LiRenderer;
	friend struct IInvoker;
};

这个类最终是要与fragmentshader进行对接的,将fragmentshader中数据与其中的Texture类进行绑定,渲染出的数据push到其中;

至于shader类就比较复杂了,因为涉及到很多种数据的准备工作,比如vertexshader要准备布局数据和传递到fragmentshader中的数据还有uniform数据,fragment也要准备很多数据;

这些数据的准备都是在构造函数中完成的,在基类中会完成必要的数据准备,子类中就是各种客户端程序员的自定义数据准备了;

另外还有很多有意思的问题,比如插值,怎么对一个三角形进行插值?用重心坐标,但是可能被插值的数据类型有那么多种,int、float、vec2、vec3...怎么能抹除数据的差别,用一种方法插值呢?

等等等等等都是问题,但好在我解决了;

下面奉上效果图:

 

 

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

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

相关文章

tensorflow/keras如何自定义layer

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

【Linux高级 I/O(3)】如何使用阻塞 I/O 与非阻塞 I/O?——poll()函数

poll()函数介绍 系统调用 poll()与 select()函数很相似&#xff0c;但函数接口有所不同。在 select()函数中&#xff0c;我们提供三个 fd_set 集合&#xff0c;在每个集合中添加我们关心的文件描述符&#xff1b;而在 poll()函数中&#xff0c;则需要构造一个 struct pollfd 类…

opencv图像增强实现方法

opencv是一款开源的图像增强工具&#xff0c;主要用于在 python环境下实现图像增强功能。 使用 opencv实现图像增强&#xff0c;需要使用 opencv的 GUI模块&#xff0c;如图1所示。 在 opencv中&#xff0c;有一个 datasets模块&#xff0c;这个模块主要用于处理数据和可视化操…

剑指 Offer 30. 包含min函数的栈【辅助栈】

剑指 Offer 30. 包含min函数的栈【辅助栈】 文章目录 剑指 Offer 30. 包含min函数的栈【辅助栈】题目描述题解 题目描述 题解 class MinStack {/*** initialize your data structure here.*/Stack<Integer> A, B;public MinStack(){A new Stack<Integer>();B ne…

使用OpenCV进行肺炎诊断检测

肺炎是一种由感染引起的严重呼吸道疾病&#xff0c;特别是在高危人群中&#xff0c;可能会出现危及生命的并发症。必须尽快诊断和治疗肺炎&#xff0c;以最大限度地提高患者康复的机会。 诊断过程并不容易&#xff0c;需要一些医学实验室工具和先进的医疗技能&#xff0c;但我们…

计算机专业毕业,有人Offer 50w,有人挂科重修!

昨天有两个VIP的小伙伴问我问题&#xff1a; 同学小明&#xff1a;孟哥&#xff0c;我小硕一枚&#xff0c;有两个offer&#xff0c;一个拿到了阿里的offer&#xff0c;乱七八糟加起来有四五十&#xff1b;还有一个是老家的电网。但是父母想让我回去&#xff0c;毕竟稳定&#…

Kali-linux绕过Utilman登录

Utilman是Windows辅助工具管理器。该程序是存放在Windows系统文件中最重要的文件&#xff0c;通常情况下是在安装系统过程中自动创建的&#xff0c;对于系统正常运行来说至关重要。在Windows下&#xff0c;使用WindowsU组合键可以调用Utilman进程。本节将介绍绕过Utilman程序登…

瑞芯微RGMII的配置

主要配置项 除去复位等信号&#xff0c;我们主要关注两大块的配置&#xff1a; 时钟配置 MAC 采用125M时钟&#xff0c;PHY采用25M时钟。 主要配置时钟源&#xff0c;这个和具体硬件连线强相关。例如125M时钟可以来源于soc内部的PLL&#xff0c;也可以由对端PHY 提供。 由对端P…

【JS】1688- 重学 JavaScript API - Fetch API

❝ 前期回顾&#xff1a; 1. Page Visibility API 2. Broadcast Channel API 3. Beacon API 4. Resize Observer API 5. Clipboard API ❞ &#x1f3dd; 1. 什么是 Fetch API 1.1 概念介绍 Fetch API[1] 是一种现代的 JavaScript API&#xff0c;用于进行「网络请求」。它提供…

过滤器(filter)、watch 侦听器 、计算属性 、axios、vue-cli 的使用 、vue组件化

过滤器&#xff08;filter&#xff09;、watch 侦听器 、计算属性 、axios、vue-cli 的使用 、vue组件化 1.过滤器&#xff08;filter&#xff09;过滤器的注意点定义全局变量 2.watch 侦听器侦听器的格式 3.计算属性4.axiosaxios 的基本使用 5.vue-cli 的使用6.vue组件化 1.过…

安卓与串口通信-基础篇

前言 安卓并不仅仅只是一个手机操作系统&#xff0c;在很多领域都能见到安卓的身影。 无论是车载系统、工控系统、屏控系统还是物联网设备基本都有安卓的一席之地。 在所谓的寒冬之下&#xff0c;纯粹的安卓开发似乎已经不再吃香&#xff0c;于是越来越多的安卓开发者转向了…

浅谈新兴室内外无线局域精准定位技术UWB(超宽带)

浅谈新兴室内外无线局域精准定位技术UWB&#xff08;超宽带&#xff09; 1、UWB高精度定位系统概述2、与传统定位比较3、应用场景4、实现uwb高精度定位4、UWB室内定位的缺陷5、应用案例 1、UWB高精度定位系统概述 UWB室内定位技术是一种全新的、与传统通信技术有极大差异的通信…

【MySQL学习】事务管理

文章目录 一、事务的基本认识1.1 事务的基本概念1.2 事务的基本属性1.3 支持事务的存储引擎 二、为什么要有事务三、事务的基本操作3.1 事务的提交方式3.2 事务的操作案例 四、事务的隔离级别4.1 对事务隔离性的初步理解4.2 四种隔离级别4.3 读未提交&#xff08;Read Uncommit…

Mybatis Plus代码生成器

文章目录 1 代码生成器原理分析2 代码生成器实现步骤1:创建一个Maven项目代码2:导入对应的jar包步骤3:编写引导类步骤4:创建代码生成类步骤5:运行程序 3 MP中Service的CRUD 1 代码生成器原理分析 造句: 我们可以往空白内容进行填词造句&#xff0c;比如: 在比如: 观察我们之…

FAT NTFS Ext3文件系统有什么区别

10 年前 FAT 文件系统还是常见的格式&#xff0c;而现在 Windows 上主要是 NTFS&#xff0c;Linux 上主要是Ext3、Ext4 文件系统。关于这块知识&#xff0c;一般资料只会从支持的磁盘大小、数据保护、文件名等各种维度帮你比较&#xff0c;但是最本质的内容却被一笔带过。它们最…

SQL教程(四)简单实例学习:时间函数(一)基础入门级

目录 一、&#x1f30e;SQL 简介 1.1 &#x1f4dc;SQL 是什么&#xff1f; 1.2 &#x1f4dc;SQL 能做什么&#xff1f; 1.3 &#x1f4dc;SQL 是一种标准 - 但是... 1.4 &#x1f4dc;在您的网站中使用 SQL 1.4 &#x1f4dc;RDBMS 1.5 &#x1f4dc;请记住... 1.6 &…

物业管理可视化大屏

物业管理可视化大屏是一种可视化的智能物业管理&#xff0c;它可以将物业管理中的各种数据进行可视化展示&#xff0c;帮助物业管理人员更好地管理社区或园区。 什么是物业可视化数据大屏&#xff1f; 物业可视化数据大屏就是利用大数据技术&#xff0c;将物业管理中的各种信…

1091 Acute Stroke

One important factor to identify acute stroke (急性脑卒中) is the volume of the stroke core. Given the results of image analysis in which the core regions are identified in each MRI slice, your job is to calculate the volume of the stroke core. Input Spec…

C++中的取余函数%、remainder、fmod以及matlab中的取余函数mod

C 1 整数取余 % 2 remainder函数 https://cplusplus.com/reference/cmath/remainder/?kwremainder double remainder (double numer , double denom); float remainder (float numer , float denom); long double remainder (long double numer, long double denom); doub…

一文带你了解MySQL之单表访问方法

前言 本文章收录在MySQL性能优化原理实战专栏&#xff0c;点击此处查看更多优质内容。 本文摘录自 ▪ 小孩子4919《MySQL是怎样运行的&#xff1a;从根儿上理解MySQL》 对于我们这些MySQL的使用者来说&#xff0c;MySQL其实就是一个软件&#xff0c;平时用的最多的就是查询功…