B-6 meshshader实现,利用vulkan和obj模型

B-6 2024/9/26

MeshShader实现 Vulkan+obj模型

  • 经过多次尝试终于实现了meshshader
  • 环境:vulkan、fastobj(阅读obj模型)、meshoptimizer(网格分组)
  • 放两张截图吧。龙有261w个顶点,87w个三角形。

在这里插入图片描述

meshshader流程说明

  1. 获取模型的网格数据,也就是顶点和索引。这和传统管线一样。

  2. 进行网格分组,把这么一大坨顶点和索引表示的网格分成一片片的meshlet。这个meshlet的划分可以借助第三方库meshoptimizer:https://github.com/zeux/meshoptimizer。

  3. meshlet是什么样的数据结构呢?在传统管线中,我们要传输的是顶点和索引。在meshshader中我们依旧要传输顶点的数组。但是原本的索引数组就被分成了meshlet。meshlet_vertices指的是,顶点分组索引。meshlet_triangles是meshlet中三角形的索引。

  4. 比如说原本有150个顶点,300个三角形,我们规定一个meshlet里面最多64个点和126个三角形。于是乎,150个点就被分成了三组。如果meshlet_vertices的前64位是0~63,meshlet[0].meshlet_triangles里triangle_offset到triangle_offset+3*triangle_count的数字最大不超过63。如果meshlet_vertices的64位到128位是64 - 128.meshlet[1].meshlet_triangles里triangle_offset到triangle_offset*3也不超过63。当然meshlet和meshlet之间可能出现顶点公用的情况。相当于meshlet_vertices记录的是某个meshlet用到的顶点指向顶点数组的索引。而meshlet不知道,它只看见了meshlet_vertices,它觉得自己只有这么多顶点,也就是不超过64个顶点,所以索引不会大于64。因此可以用uint8来表示meshlet_triangles。然后把数组合并起来也就有了meshlet的结构,需要指定两个数组的起始位置和大小。meshlet中的offest和count相当于就是告诉读者,meshlet是从这开始到这结束。

  5. 利用vulkan将meshlet的相关数据放入shader中进行并行的渲染。

  6. 关于并行:我的2080ti在x,y,z方向上各有1024*1024*64个核心。这个通过是vulkan的物理设备特性队列获得的。一般来说我们设置32个核心为一个工作组,一个工作组处理一个meshlet。也就是我们可以设置1024*1024*64/32个工作组。shader中有两个内置变量gl_WorkGroupID、gl_LocalInvocationID。前一个指工作组的索引,也就是0~1024*1024*64/32。后面一个gl_LocalInvocationID,是工作组中核心的索引也就是0到31。meshshader有两个扩展,一个是vulkan的ext扩展,一个是英伟达的nv扩展。利用英伟达的扩展进行drawcall的时候,无需在外面分配工作组的数量。但是ext需要自己配置x,y,z。英伟达的扩展meshlet的数量不能超过1024*64个,要是超过了,那就分多次提交。

    •    PFN_vkCmdDrawMeshTasksNV vkCmdDrawMeshTasksNV = (PFN_vkCmdDrawMeshTasksNV)vkGetDeviceProcAddr(pcbDevice.device(), "vkCmdDrawMeshTasksNV");//我用英伟达的扩展,直接提交meshlet的数量就能自动分配vkCmdDrawMeshTasksNV(commandBuffer, meshlets.meshletssize(), 0);
      
  7. shader中代码如下:

#version 450//启用meshshader
#extension GL_NV_mesh_shader: require
//启用uint8_t和int8_t
#extension GL_EXT_shader_8bit_storage: require
#extension GL_EXT_shader_16bit_storage: require//设置工作组中线程的大小,一个工作组有32个线程
//将max_vertices的数量依次放入这些工作组中,要是有64个vertices,那就需要循环两次去执行
//有些meshlet中的vertices数量没有到64.那么最后几个点就重复执行一下
const uint myShaderGroupSize = 32;//计算在myShaderGroupSize大小的一个工作组中,处理64个顶点需要几次
//这里需要两次,因为顶点数量最大是64个,工作组大小是32个,所以需要两次
const uint VertexLoops = (64 + myShaderGroupSize - 1) / myShaderGroupSize;//计算需要循环几次才能把索引都写入
//124个三角形也就是说有3*124个索引
//4 * myShaderGroupSize意思是一次可以写入四个索引数字
const uint TriangleLoops = (3 * 124 + 4 * myShaderGroupSize - 1) / (myShaderGroupSize * 4);//设置工作组中线程的大小,一个工作组有32个线程
layout(local_size_x = myShaderGroupSize) in;//输入的UBO矩阵
struct PointLight {vec4 position; // ignore wvec4 color; // w is intensity
};
layout(set = 0, binding = 0) uniform GlobalUbo {mat4 projection;mat4 view;mat4 invView;vec4 ambientLightColor; // w is intensityPointLight pointLights[10];int numLights;
} ubo;struct Meshlet {uint vertex_offset;uint triangle_offset;uint vertex_count;uint triangle_count;};
//读取从CPU那里发送过来的数据
layout(set=1,binding = 0) readonly buffer Meshlets { Meshlet meshlets[]; };
layout(set=1,binding = 1) readonly buffer MeshletVertices { uint meshlet_vertices[]; };
layout(set=1,binding = 2) readonly buffer MeshletTriangles { uint meshlet_triangles[]; };struct Vertex {vec4 position;vec4 noraml;
};
layout(set=1,binding = 3) readonly buffer _Vertexes { Vertex vertexes[]; };layout(push_constant) uniform Push {mat4 modelMatrix;mat4 normalMatrix;
} push;//规定输出的图元是三角形,当然也可以是线段什么的
//规定顶点最大64个,三角形最多124个,这个是在CPU上会提前预设好的限制,目的是为了匹配GPU的限制
//就是一个meshlet中最多只有64个顶点和124个三角形。
layout(triangles, max_vertices = 64, max_primitives = 124) out;
layout(location = 0) out vec3 fragColor[];//不同的网格颜色用以达到不同网格分组的效果
vec3 meshletcolors[10] = {vec3(1,0,0), vec3(0,1,0),vec3(0,0,1),vec3(1,1,0),vec3(1,0,1),vec3(0,1,1),vec3(1,0.5,0),vec3(0.5,1,0),vec3(0,0.5,1),vec3(1,1,1)};void main() {//获取当前工作组的ID//工作组的大小在vulkan中设置,vkCmdDispatch就可以设置大小//由于Group和thread都是三维的,这里只用一维的x就可以了,也只设置x就可以啦uint groupIndex = gl_WorkGroupID.x;//获取工作组下的当前的进程的iduint groupThreadIndex = gl_LocalInvocationID.x;//每个工作组处理一个meshletMeshlet meshlet= meshlets[groupIndex]; for (uint i = 0; i < VertexLoops; ++i){//获取当前计算64个顶点中的那个点的索引uint localVertexIndex = groupThreadIndex + i * myShaderGroupSize;//要是顶点数量没有到64个,比如只有40个,那么也需要两次循环才能处理完//当只有40个顶点时,第二次循环运行到后面的时候,顶点索引会溢出,那么就需要对其进行处理,规定最大的索引不超过这个meshlet的最大顶点数localVertexIndex = min(localVertexIndex, meshlet.vertex_count - 1);//获取顶点数组的索引位置uint vertexIndex=meshlet_vertices[meshlet.vertex_offset+localVertexIndex];//在顶点数组中获取顶点Vertex thisvertex = vertexes[vertexIndex];//输出顶点位置vec4 positionWorld = push.modelMatrix * thisvertex.position;gl_MeshVerticesNV[localVertexIndex].gl_Position = ubo.projection * ubo.view * positionWorld;//获取一个颜色,并进行输出fragColor[localVertexIndex]= meshletcolors[groupIndex%10];}//要除个四,因为索引写入的逻辑是这样的:小索引的长度是8bit,而一个整型有32bit。因此把四个数字看成一个数字进行写入//按照这样的方法,原本的索引数组的offset要缩小四倍。//因为四个数字被一起写入了嘛,相当于原本比如长度9的8字节索引,分为:4 4 1,只需要三次写入。第0次,第1次,第2次uint packedTriangleOffset = meshlet.triangle_offset / 4;//这个是对索引的大小进行一个限制,以防一循环和上面的顶点一样溢出了uint packedTrianglesMax = (3 * meshlet.triangle_count - 1) / 4;for (uint i = 0; i < TriangleLoops; ++i){//根据工作组和工作组中的线程获取当前需要写入的索引uint localTriangleIndex = groupThreadIndex + i * myShaderGroupSize;//需要写入的索引不能超过这个localTriangleIndex = min(localTriangleIndex, packedTrianglesMax);//这个函数就是写入索引的//前一个参数是,从4 * localTriangleIndex的位置开始写入//比如比如长度9的8字节索引,分为:4 4 1,三次写入。第0次从0位置写入,第1次从4位置写入,第2次从8位置写入//注意的是,CPU里面写入的是uint8类型索引数组,而这里直接读取的是uint类型的数组。实际上这里直接把四个数组一起读取了。writePackedPrimitiveIndices4x8NV(4 * localTriangleIndex, meshlet_triangles[packedTriangleOffset + localTriangleIndex]);}//设置一下三角形的个数//这个很重要,因为读取索引的时候,因为压缩的问题,最后一次读取很可能会多读几位。所以要说明一下三角形的数量if (groupThreadIndex == 0) {gl_PrimitiveCountNV = meshlet.triangle_count;}}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Cpp内存管理(7)

文章目录 前言一、C/C内存区域划分二、C/C动态内存管理C语言动态内存管理C动态内存管理对于内置类型对于自定义类型 三、new和delete的底层实现四、new和delete的实现原理五、定位new六、malloc/free和new/delete的区别总结 前言 软件开发过程中&#xff0c;内存管理的重要性不…

关于 mybatis-plus-boot-starter 与 mybatis-spring-boot-starter 的错误

不是知道你是否 出现过这样的错误 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): 经过各种度娘&#xff0c;无非就是让你检查三种情况 情况一&#xff1a;mapper.xml没有按照传统的maven架构进行放置 情况二&#xff1a;mybatis的配置信…

c++ day06

类的栈 实现 #include <iostream>using namespace std;class Stack { private:static const size_t MAX 100; // 定义固定容量int data[MAX]; // 存储栈元素的数组size_t len; // 当前栈的大小public:// 构造函数Stack() : len…

【Python从入门到进阶】65、Pandas如何批量拆分与合并Excel文件

接上篇《64、Pandas如何实现数据的Concat合并》 上一篇我们学习了Pandas如何实现数据的Concat合并&#xff0c;本篇我们来继续学习Pandas如何批量拆分与合并Excel文件。 一、引言 在当今数据驱动的时代&#xff0c;Excel文件作为数据处理和分析的基石&#xff0c;扮演着不可或…

Selenium4.0实现自动搜索功能

01.Selenium4.0实现搜索功能 1.安装Selenium及查看Selenium版本 pip install selenium pip show seleniumfrom selenium import webdriver from chromedriver_py import binary_path import time from selenium.webdriver.common.by import By from selenium.webdriver.commo…

(补充)3DMAX初级小白班第三课:创建物体+物体材质编辑

1.可以点这里来改变材质颜色&#xff08;但是通过材质编辑器给了材质以后就只能在这里改线框颜色&#xff09;。但一般就是用灰色材质和黑色线框 2.材质编辑器快捷键为m 右键可更改个数&#xff0c;最多24个 将材质指定选定对象 如何把材质编辑器面板改成旧版 按f10 改成扫描…

《微信小程序实战(3) · 推广海报制作》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

火车票有电子发票吗?没纸质火车票怎么报销?

火车票有电子发票吗&#xff1f; 火车票、高铁票目前没有电子发票&#xff0c;但是现在已经实行电子客票&#xff0c;车票即购票证件&#xff0c;乘车时&#xff0c;只需购票证件原件&#xff08;如身份证、护照、临时身份证等&#xff09;即可乘车。 没纸质火车票怎么报销&am…

英伟达发布NVLM 1.0:屠榜多模态任务,纯文本性能逆势提升

前沿科技速递&#x1f680; 随着文本大模型的发展&#xff0c;解码器架构已经成为文本处理任务的标准。然而&#xff0c;现有的多模态大模型架构却尚未统一&#xff0c;不同模型在选择 LLM 主干、视觉编码器以及训练数据上存在较大差异&#xff0c;且无法直接进行对比研究。为了…

凤凰模拟器V6中无人机如何设置“有头模式”

凤凰模拟器是一款专为航模新手设计的飞行模拟器&#xff0c;它能够模拟大疆无人机、各种穿越机、固定翼等多种飞行器&#xff0c;提供逼真的飞行体验。该软件的操作简单易懂&#xff0c;适合新手练习使用。 一般来说&#xff0c;打开凤凰模拟器&#xff0c;选择好机型&#xf…

vscode 配置django

创建运行环境 使用pip安装Django&#xff1a;pip install django。 创建一个新的Django项目&#xff1a;django-admin startproject myproject。 打开VSCode&#xff0c;并在项目文件夹中打开终端。 在VSCode中安装Python扩展&#xff08;如果尚未安装&#xff09;。 在项…

鸿蒙HarmonyOS开发:一次开发,多端部署(界面级)天气应用案例

文章目录 一、布局简介二、典型布局场景三、侧边栏 SideBarContainer1、子组件2、属性3、事件 四、案例 天气应用1、UX设计2、实现分析3、主页整体实现4、具体代码 五、运行效果 一、布局简介 布局可以分为自适应布局和响应式布局&#xff0c;二者的介绍如下表所示。 名称简介…

Golang | Leetcode Golang题解之第421题数组中两个数的最大异或值

题目&#xff1a; 题解&#xff1a; const highBit 30type trie struct {left, right *trie }func (t *trie) add(num int) {cur : tfor i : highBit; i > 0; i-- {bit : num >> i & 1if bit 0 {if cur.left nil {cur.left &trie{}}cur cur.left} else …

leetcode-189:轮转数组

给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转 3 步: [5,6,7,1,2,3,4…

前端框架对比与选择

&#x1f916; 作者简介&#xff1a;水煮白菜王 &#xff0c;一位资深前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 前端专栏 &#xff0c;记录一下平时在博客写作中&#xff0c;总结出的一些开发技巧✍。 感谢支持&#x1f495;&#x1f495;&#x1f495; 目…

详细分析SpringMvc中HandlerInterceptor拦截器的基本知识(附Demo)

目录 前言1. 基本知识2. Demo3. 实战解析 前言 对于Java的基本知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09;【Java项目】实战CRUD的功能整理&#xff08;持续更新&#xff09; 1. 基本知识 HandlerInter…

量化交易四大邪术之三:春去花还在

网络相传亚洲有四大邪术&#xff0c;日本化妆&#xff0c;韩国整容&#xff0c;泰国变X&#xff0c;Z国PS。 这些都是让人在颜值上看起来很美&#xff0c;类似地&#xff0c;在量化交易领域&#xff0c;也有四大邪术能让净值曲线看起来很美&#xff0c;之前已经说了“般若波罗蜜…

CSS clip-path 属性的使用

今天记录一个css属性clip-path&#xff0c;首先介绍下这个属性。 clip-path 是CSS中的一个神奇属性&#xff0c;它能够让你像魔术师一样&#xff0c;对网页元素施展“裁剪魔法”——只展示元素的一部分&#xff0c;隐藏其余部分。想象一下&#xff0c;不用依赖图片编辑软件&am…

Python--类【详细教程】

类的介绍 面向对象编程&#xff08;object-oriented programming&#xff0c;OOP&#xff09;是最有效的软件编写方法之⼀。在面向对象编程中&#xff0c;你编写表示现实世界中的事物的类&#xff08;class&#xff09;&#xff0c;并基于这些类来创建对象&#xff08;object&…

C语言 | Leetcode C语言题解之第436题寻找右区间

题目&#xff1a; 题解&#xff1a; typedef struct {int start;int index; } Node;int cmp(const void *pa, const void *pb) {return ((Node *)pa)->start - ((Node *)pb)->start; }int* findRightInterval(int** intervals, int intervalsSize, int* intervalsColSiz…