LearnGL - 18.2 - Instancing/Instanced Rendering - 多实例渲染3 - using TBO 使用缓存纹理对象


LearnGL - 学习笔记目录

上些篇:

这一篇:使用 TBO (Texture Buffer Object) 来实现多实例属性的存储


使用 TBO 来实现 Instancing 之前,可以先参考我前一篇:OpenGL - TBO (Texture Buffer Object) - 缓存纹理 - Instancing Using TBO 前置篇


实践


应用层


C++

相比前一篇的 glVertexAttribDivisor 的方式,代码更简单了

		mat->instancing = true;
		mat->instanc_count = INSTANCING_COUNT;

		// mesh renderer - component
		MeshRenderer* mr = new MeshRenderer();
		mr->setQueue(RenderQueueType::Transprent);
		getOwner()->addComp(mr);

		GameObject* go = getOwner();
		vec3 loc_scl = go->getTrans()->local_scale;

		for (size_t i = 0; i < INSTANCING_COUNT; i++) {
			mat4 tMat, rMat, sMat, com_rMat;
			tMat = glm::identity<mat4>();
			rMat = glm::identity<mat4>();
			sMat = glm::identity<mat4>();
			com_rMat = glm::identity<mat4>();

			// s
			vec3 s = vec3(
				loc_scl.x + ran_range(0.0f, -0.9f),
				loc_scl.y + ran_range(0.0f, -0.9f),
				loc_scl.z + ran_range(0.0f, -0.9f));
			// r
			vec3 r = vec3(
				ran_range(-360.0f, 360.0f),
				ran_range(-360.0f, 360.0f),
				ran_range(-360.0f, 360.0f));
			// t
			float y_scale = INSTANCING_COUNT < 30000 ? 1.0f : ((float)INSTANCING_COUNT / 30000);
			vec3 t = vec3(ran_range(80.0f, 100.0f), ran_range(-10.0f, 10.0f) * y_scale, 0.0f);

			// s
			sMat = glm::scale(sMat, s);
			// r
			rMat = glm::rotate(rMat, D2R(r.y), vec3(0, 1, 0));
			rMat = glm::rotate(rMat, D2R(r.x), vec3(1, 0, 0));
			rMat = glm::rotate(rMat, D2R(r.z), vec3(0, 0, 1));
			// t
			tMat = glm::translate(tMat, t);

			// 公转角度
			com_rMat = glm::rotate(com_rMat, D2R(ran_range(-360.0f, 360.f)), vec3(0, 1, 0));

			// trs 矩阵
			instancingMat[i] = (com_rMat * tMat * rMat * sMat);
		}

		Pass* pass = mat->getPasses().at(0);

		assert(instancing_mMat_buffer_obj == NULL);

		if (instancing_mMat_buffer_obj == NULL) {
			instancing_mMat_buffer_obj = new GLuint();
			glGenBuffers(1, instancing_mMat_buffer_obj);
			glBindBuffer(GL_TEXTURE_BUFFER, *instancing_mMat_buffer_obj);
			glBufferData(GL_TEXTURE_BUFFER, INSTANCING_COUNT * sizeof(glm::mat4), &instancingMat[0], GL_STATIC_DRAW);

			instancing_mMat_tbo = new GLuint();
			glCreateTextures(GL_TEXTURE_BUFFER, 1, instancing_mMat_tbo);
			// 四个分量,每个分量都需要一个 float 所以,internalformat 使用:GL_RGBA32F
			glTextureBuffer(*instancing_mMat_tbo, GL_RGBA32F, *instancing_mMat_buffer_obj);

			pass->setExtTexture("instancing_mMat_tbo", *instancing_mMat_tbo);
		}

主要留意部分:

		if (instancing_mMat_buffer_obj == NULL) {
			instancing_mMat_buffer_obj = new GLuint();
			glGenBuffers(1, instancing_mMat_buffer_obj);
			glBindBuffer(GL_TEXTURE_BUFFER, *instancing_mMat_buffer_obj);
			glBufferData(GL_TEXTURE_BUFFER, INSTANCING_COUNT * sizeof(glm::mat4), &instancingMat[0], GL_STATIC_DRAW);

			instancing_mMat_tbo = new GLuint();
			glCreateTextures(GL_TEXTURE_BUFFER, 1, instancing_mMat_tbo);
			// 四个分量,每个分量都需要一个 float 所以,internalformat 使用:GL_RGBA32F
			glTextureBuffer(*instancing_mMat_tbo, GL_RGBA32F, *instancing_mMat_buffer_obj);

			pass->setExtTexture("instancing_mMat_tbo", *instancing_mMat_tbo);
		}

因为是 TBO,所以我们的缓存目标类型为: GL_TEXTURE_BUFFER

还有上面有个使用到 GL_RGBA32F 的 internalformat,告诉 GPU 这个缓存的每个元素都是 vec4 的类型(每个分量都是 32位的 float),这些数据可以不是归一化的,所以非常适合实现一些外部密集数据存储在 GPU 端来并行高速计算的方式

internalformat 的格式可参考:LearnGL - 05 - Texture - Sized Internal Format


Vertex Shader

还是一样,要调整的还只是 Vertex Shader

// jave.lin - testing_instancing_tbo.vert
#version 450 compatibility
#extension GL_ARB_shading_language_include : require
#include "/Include/my_global.glsl"

// vertex data
in vec3 vPos;		// 顶点坐标
in vec2 vUV0;		// 顶点纹理坐标
in vec3 vNormal;		// 顶点法线
// in mat4 instancing_mMat; // 多实例的 model matrix // glVertexAttribDivisor 的方式

uniform samplerBuffer instancing_mMat_tbo; // 使用 TBO 来存储多实例渲染的属性

// vertex data - interpolation
out vec2 fUV0;			// 给 fragment shader 传入的插值
out vec3 fNormal;		// 世界坐标顶点法线
out vec3 fWorldPos;		// 世界坐标

void main() {

	// 从 tbo 中读取数据
	mat4 instancing_mMat = mat4(
		texelFetch(instancing_mMat_tbo, gl_InstanceID * 4 + 0),
		texelFetch(instancing_mMat_tbo, gl_InstanceID * 4 + 1),
		texelFetch(instancing_mMat_tbo, gl_InstanceID * 4 + 2),
		texelFetch(instancing_mMat_tbo, gl_InstanceID * 4 + 3));

	mat4 new_mMat = mMat * instancing_mMat;					// 将原来的 mMat 累计变换到新的 model matrix
	mat4 it_mMat = transpose(inverse(new_mMat));			// 求得新的逆矩阵的转置矩阵,用于变换法线用

	// vec4 worldPos = mMat * vec4(vPos, 1.0);	// 原来直接 model matrix 变换即可
	vec4 worldPos = new_mMat * vec4(vPos, 1.0);	// 现在使用新的 model matrix 变换
	fUV0 = vUV0;							// UV0
	fNormal = normalize(mat3(it_mMat) * vNormal);	// 用新的 it_mMat 矩阵来将模型空间的法线,变换到,世界坐标法线
	fWorldPos = worldPos.xyz;				// 世界坐标
	gl_Position = vpMat * worldPos;			// Clip pos
}

运行效果

和前一篇的一样
在这里插入图片描述


GPU 负载

在这里插入图片描述

GPU 几乎是满载,FPS 大概在 30 帧左右

使用 TBO 的方式与前一篇使用:glVertexAttribDivisor 的方式虽然不同,但是从性能上来看从不多

但是 TBO 的方式相对代码上好处理一些,易于维护


疑问

在前置篇:OpenGL - TBO (Texture Buffer Object) - 缓存纹理 - Instancing Using TBO 前置篇

可以看到一句:

一维纹理的尺寸受限于 GL_MAX_TEXTURE_SIZE 对应的最大值,但是缓存纹理的尺寸受限于 GL_MAX_TEXTURE_BUFFER_SIZE 的值,通常能达到 2GB 甚至更多。

但是我自己输出了我笔记本上的硬件支持的 OpenGL 的 GL_MAX_TEXTURE_SIZE 值

	// GL_MAX_TEXTURE_BUFFER_SIZE
	GLint maxTexBuffSize;
	glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &maxTexBuffSize);
	std::cout
		<< "Maximun number of Texture Buffer Size : "
		<< maxTexBuffSize << " Bytes, "
		<< (maxTexBuffSize / 1024) << " KB, "
		<< (maxTexBuffSize / (1024 * 1024)) << " MB, "
		<< (maxTexBuffSize / (1024 * 1024 * 1024)) << " GB"
		<< std::endl;

输出:

Maximun number of Texture Buffer Size : 134217728 Bytes, 131072 KB, 128 MB, 0 GB

才 128 MB?

但是我上面运行的截图是跑了 600W 个多实例绘制

而每个实例属性就一个 mat4 ,一个 mat4 需要 64 bytes

那么 600W * 64 bytes = 384,000,000 bytes = 375,000 KB = 366.2109375 MB

已经超 上面的 GL_MAX_TEXTURE_BUFFER_SIZE 的 128 MB 三倍多了

为何还能正常运行呢?暂时没搞懂!


References

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值