Direct3D网格(二)

ID3DXBuffer

ID3DXBuffer接口是一种泛型数据结构,该接口为D3DX库锁使用,可将数据存储在一个连续的内存块中,该接口只有俩个方法。为了保持该接口的通用性,该接口使用了void类型指针,所以使用时需要对该缓存进行强制类型转换。由于ID3DXBuffer是一个COM对象,该接口在使用完毕之后必须将其释放,以防止内存泄漏。

//返回指向缓存中数据起始位置的指针
LPVOID GetBufferPointer();
//返回缓存的大小,单位为字节
DWORD GetBufferSize();DWORD* info=(DWORD*) adjacencyInfo->GetBufferPointer();
adjacencyInfo->Release();

可用D3DXCreateBuffer函数来创建一个空的ID3DXBuffer对象

HRESULT WINAPI D3DXCreateBuffer(DWORD NumBytes, LPD3DXBUFFER *ppBuffer
);ID3DXBuffer* buffer=0;
D3DXCreateBuffer(4*sizeof(int),&buffer);

XFile

当我们通过手工指定顶点数据来创建3D物体时,将是一个相当枯燥的任务,为了减轻构建3D物体这种繁重的工作,人们开发了3D建模工具的专业应用程序,这些工具能够将模型数据导成文件格式为XFile(扩展名为.X)文件,XFile之所以方便,最主要原因是它是DirectX定义的格式,得到了D3DX库的支持。

加载XFile文件

可以使用D3DXLoadMeshFromX加载,该方法创建了一个ID3DXMesh对象并将XFile中的几何数据加载到该对象中。

HRESULT D3DXLoadMeshFromX(LPCWSTR pFilename,DWORD Options,LPDIRECT3DDEVICE9 pD3DDevice,LPD3DXBUFFER *ppAdjacency,LPD3DXBUFFER *ppMaterials,LPD3DXBUFFER *ppEffectInstances,DWORD *pNumMaterials,LPD3DXMESH *ppMesh
);

pFileName:所要加载的XFile文件名
Options:创建网格时所使用的创建标记,枚举为D3DXMESH类型
pDevice:设备指针
ppAdjacency:返回一个ID3DXBuffer对象,该对象包含了一个描述了该网格对象的邻接信息的DWORD类型的数组
ppMaterials:返回一个ID3DXBuffer对象,该对象包含一个存储了该网格的材质数据的D3DXMATERIAL类型的结构数组
ppEffectInstances:返回一个ID3DXBuffer对象,该对象包含了一个D3DXEFFECTINSTANCE结构,可以通过将该参数指定为0而将其忽略
pNumMaterials:返回网格网格中材质数目(ppMaterials输出数组的元素个数)
ppMesh:返回所创建并填充了XFile几何数据的ID3DXMesh对象

XFile材质

第二个参数为一个指向NULL结尾的字符串指针,该字符串指定了与网格相关的纹理文件名,XFile文件中并未存储纹理数据,它只包含了纹理图像文件名,该文件名是对包含了实际纹理数据的纹理图像的引用,所以使用D3DXLoadMeshFromX函数加载一个XFile文件后,我们必须根据指定的纹理文件名加载纹理数据

typedef struct _D3DXMATERIAL
{D3DMATERIAL9  MatD3D;LPSTR         pTextureFilename;
} D3DXMATERIAL;

D3DXLoadMeshFromX函数载入XFile数据后返回的D3DXMATERIAL结构数组中的第i项就与第i个子集相对应,所以将各子集按照0、1、2...n-1进行标记,n是子集和材质的总数,这样就可以用循环来对子集进行遍历绘制,从而完成网格的绘制。

XFile例程

ID3DXMesh* Mesh = 0;
std::vector<D3DMATERIAL9> Mtrls(0);
std::vector<IDirect3DTexture9*> Textures(0);//加载XFile文件
bool SetUpXFile()
{HRESULT hr = 0;ID3DXBuffer* adjBuffer = 0;ID3DXBuffer* mtrlBuffer = 0;DWORD numMtrls = 0;hr = D3DXLoadMeshFromX(L"bigshipl.x", D3DXMESH_MANAGED, Device, &adjBuffer, &mtrlBuffer, 0, &numMtrls, &Mesh);if (FAILED(hr)){::MessageBox(0, L"load Fail", 0, 0);return false;}//加载XFile数据后,必须遍历D3DXMATERIAL数组中的元素,并加载该网格锁引用的纹理数据if (mtrlBuffer != 0 && numMtrls != 0){D3DXMATERIAL* mtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer();for (int i = 0; i < numMtrls; ++i){mtrls[i].MatD3D.Ambient = mtrls[i].MatD3D.Diffuse;Mtrls.push_back(mtrls[i].MatD3D);if (mtrls[i].pTextureFilename != 0){IDirect3DTexture9* tex = 0;D3DXCreateTextureFromFileA(Device, mtrls[i].pTextureFilename, &tex);Textures.push_back(tex);}else{Textures.push_back(0);}}}d3d::Release<ID3DXBuffer*>(mtrlBuffer);return true;
}bool DisplayXFile(float timeDelta)
{if (Device){static float y = 0.0f;D3DXMATRIX yRot;D3DXMatrixRotationY(&yRot, y);y += timeDelta;if (y >= 6.28f)y = 0.0f;D3DXMATRIX World = yRot;Device->SetTransform(D3DTS_WORLD, &World);//renderDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);Device->BeginScene();for (int i = 0; i < Mtrls.size(); ++i){Device->SetMaterial(&Mtrls[i]);Device->SetTexture(0, Textures[i]);Mesh->DrawSubset(i);}Device->EndScene();Device->Present(0, 0, 0, 0);}return true;
}

生成顶点法线

XFile文件中有可能没有存放顶点的法向量,为了使用光照需要手工计算每个顶点的法向量,可以使用D3DXComputeNormals来产生任意网格的顶点法向量。

HRESULT WINAPI D3DXComputeNormals(LPD3DXBASEMESH pMesh,CONST DWORD *pAdjacency
);

该函数通过法向量平均的方法生成顶点的法向量,如果提供了邻接信息,重叠的顶点就会被剔除,如果没有提供邻接信息,则重叠顶点的法向量由该顶点所依附的各面在该点的局部法向量取平均而得到,重要的第一点是传入的pMesh的顶点格式中必须包含标记D3DFVF_NORMAL

如果一个XFile文件不含顶点法线数据,则通过函数D3DXLoadMeshFromX所创建出的网格对象在顶点格式中将不包含D3DFVF_NORMAL标记,所以在使用D3DXComputeNormals之前必须克隆该网格并指定其包含D3DFVF_NORMAL标记的顶点格式。

if (!(pMesh->GetFVF() & D3DFVF_NORMAL))
{ID3DXMesh* pTempMesh = 0;pMesh->CloneMeshFVF(D3DXMESH_MANAGED, pMesh->GetFVF() | D3DFVF_NORMAL, Device, &pTempMesh);D3DXComputeNormals(pTempMesh, 0);pMesh->Release();pMesh = pTempMesh;
}

渐进网格

渐进网格用ID3DXPMesh接口来表示,它允许我们运用一系列的边折叠变换(ECT)对网格进行简化,每次ECT都移除一个顶点以及一个或俩个面,由于每次ECT都是可逆的,我们可对简化过程进行逆转从而将网格精确回复到初始状态,但意味着我们无法获得比原网格更丰富的细节。

与纹理中多级渐进纹理类似,如果对一个小而远的图元应用高分辨率的纹理实在是一种浪费,因为观察者不可能注意到这些细节,一个小而远的网格也完全不用像大而近的网格一样使用大量面片,所以在满足要求的条件下,总是用尽量少的面片来表达一个网格,以节省宝贵的绘制时间。

生成渐进网格

HRESULT D3DXGeneratePMesh(LPD3DXMESH pMesh, CONST DWORD* pAdjacency, CONST D3DXATTRIBUTEWEIGHTS *pVertexAttributeWeights,CONST FLOAT *pVertexWeights,DWORD MinValue, DWORD Options, LPD3DXPMESH* ppPMesh
);

pMesh:该输入变量包含了网格数据,渐进网格将根据此网格产生
pAdjacency:指向了包含了pMesh的邻接信息的DWORD类型的数组指针
pVertexAttributeWeights:指向D3DXATTRIBUTEWEIGHTS类型的结构数组的指针,数组维数为GetNumVertices(),数组的第i项对应于pMesh中第i个顶点,并指定了相应顶点的属性权值。顶点属性权值用于决定在简化过程中顶点被移除的概率,可为该参数传入NULL,则每个顶点将被赋予默认的属性权值。
pVertexWeights:指向一个float类型数组的指针,维数为GetNumVertices(),第i项对应于pMesh中的第i个顶点,并指定了相应顶点的属性权值。值越高简化过程中被移除的概率越小,可赋为NULL,这样每个顶点就会被赋为默认顶点权值1.0。
MinValue:网格中的顶点数或面片数可被简化到的下限,该值为一种期望值,实际还要依赖于顶点权值或属性权值,所以简化结果可能与该值不一致
Options:为D3DXMESHSIMP枚举成员
D3DXMESHSIMP_VERTEX  指定了前面的参数MinValue是指顶点数
D3DXMESHSIMP_FACE  指定了前面的参数MinValue是指面片数
ppPMesh:返回所生成的渐进网格

顶点属性权

该顶点权值结构允许我们为顶点每一个可能的分量指定一个权值,如果某个分量的权值被赋为0.0,则表明该分量无权值,顶点分量的权值越高在简化过程中该顶点被移除的概率越小。

typedef struct _D3DXATTRIBUTEWEIGHTS
{FLOAT Position;FLOAT Boundary;FLOAT Normal;FLOAT Diffuse;FLOAT Specular;FLOAT Texcoord[8];FLOAT Tangent;FLOAT Binormal;
} D3DXATTRIBUTEWEIGHTS, *LPD3DXATTRIBUTEWEIGHTS;//各个分量默认权值,除非程序上有充分的理由,一般情况下建议使用这些默认值
D3DXATTRIBUTEWEIGHTS AttributeWeights;
AttributeWeights.Position = 1.0f;
AttributeWeights.Boundary = 1.0f;
AttributeWeights.Normal = 1.0f;	
AttributeWeights.Diffuse = 1.0f;
AttributeWeights.Specular = 1.0f;
AttributeWeights.Texcoord[8] = { 0.0f };

ID3DXPMesh其他接口

DWORD GetMaxFaces();		//返回渐进网格面片数可被指定的上限
DWORD GetMinFaces();		//返回渐进网格面片数可被指定的下限
DWORD GetMaxVertices();		//返回渐进网格顶点数可被指定的上限
DWORD GetMinVertices();		//返回渐进网格顶点数可被指定的下限

SetNumFaces设置网格面片数可被简化或细化到的个数,调整后的数量可能与期望的数量不一致,如果Faces小于GetMinFaces(),则将取为GetMinFaces,大于情况同理使用GetMaxFaces()

HRESULT SetNumFaces(DWORD Faces);	
//网格目前有50个面片,想将其简化为30
pMesh->SetNumFaces(30);

SetNumVertices设置网格顶点数可被简化或细化到的个数,同理会与期望顶点数不一致,过大过小会被限制到指定数量

HRESULT SetNumVertices(DWORD Vertices);

TrimByFaces允许重新设定面片最小值和最大值,俩个值必须位于[GetMinFaces(),GetMaxFaces()]内,同时也返回了重绘信息

HRESULT TrimByFaces(DWORD NewFacesMin, DWORD NewFacesMax, DWORD *rgiFaceRemap,	//面片重绘信息DWORD *rgiVertRemap		//顶点重绘信息
);

TrimByVertices允许重新设定顶点数的最小值和最大值,也是需要在区间[GetMinVerices(),GetMaxVerices()]内

HRESULT TrimByVertices(DWORD NewVerticesMin, DWORD NewVerticesMax, DWORD *rgiFaceRemap, DWORD *rgiVertRemap
);

外接体

有时我们需要计算一个网格的外接体,俩种常见的外接体是外接球(bounding sphere)和外接体(bounding box),外接球或外接体常用于加速可见性检测碰撞检测。例如一个网格的外接体或外接球不可见,我们就可认为该网格不可见,检测外接体的可见性要比检测网格中每个面片的可见性的代价低得多。

假定场景中有一个发射物,要确定它是否会击中场景中某一个物体,由于物体是由三角形面片构成的,我们需要遍历每个物体的每个面片,并检测发射物(数学模型为射线)是否会击中某一面片。该方法需要进行大量的射线/三角形相交测试,场景中每个物体的每个三角形面片都要进行一次。一种更高效的途径是计算出每个网格(物体)的外接体,然后再对每个物体进行射线/外接体相交测试。如果射线与外接体相交则认为击中。如果希望提高精度,可借助外接体快速排除那些显然不能被击中的物体,然后再对那些极有可能被击中的物体使用更精确的方法来检测。所谓极有可能被击中的物体就是其外接体被击中的那些物体。

创建外接球D3DXComputeBoundingSphere

HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3 *pFirstPosition,  // pointer to first positionDWORD NumVertices,DWORD dwStride,                     // count in bytes to subsequent position vectorsD3DXVECTOR3 *pCenter,FLOAT *pRadius
);

pFirstPosition:指向顶点数组中第一个顶点位置向量的指针
NumVertices:该顶点数组中顶点的个数
dwStride:每个顶点的大小,单位为字节,该值很重要,因为一种顶点结构可能包含了许多该函数所不需要的附加信息,如法向量和纹理坐标等,这样该函数就需要知道应跳过多少字节才能到下一个顶点的位置
pCenter:返回外接球的球心位置
pRedius:返回外接球的半径

创建外接体D3DXComputeBoundingBox

HRESULT WINAPI D3DXComputeBoundingBox(CONST D3DXVECTOR3 *pFirstPosition,  // pointer to first positionDWORD NumVertices, DWORD dwStride,                     // count in bytes to subsequent position vectorsD3DXVECTOR3 *pMin, D3DXVECTOR3 *pMax);

pMin:外接体的最小点
pMax:外接体的最大点

为了给外接体的使用提供便利,我们可为每种外接体实现一个类,将其放在d3d命名空间中

//外接体
struct BoundingBox
{BoundingBox();bool isPointInside(D3DXVECTOR3& p);D3DXVECTOR3 _min;D3DXVECTOR3 _max;
};//外接球
struct BoundingSphere
{BoundingSphere();D3DXVECTOR3 _center;float _radius;
};d3d::BoundingBox::BoundingBox()
{_min.x = FLT_MAX;_min.y = FLT_MAX;_min.z = FLT_MAX;_max.x = -FLT_MAX;_max.y = -FLT_MAX;_max.z = -FLT_MAX;
}bool d3d::BoundingBox::isPointInside(D3DXVECTOR3 & p)
{if (p.x >= _min.x && p.y >= _min.y && p.z >= _min.z && p.x <= _max.x && p.y <= _max.y && p.z <= _max.z)return true;return false;
}d3d::BoundingSphere::BoundingSphere()
{_radius = 0.0f;
}

特殊常量INFINITY、EPSILON

俩个十分有用的常量,将其添加到d3d命名空间中来方便使用,INFINITY表示float类型能存储的最大浮点数,可将改值概念化为无穷大,EPSILON是一个很小的值,如果某个小于该值,可认为该数为0。

const float ESPSILON = 0.001f;
const float INFINITY = FLT_MAX;

计算外接体例程

bool ComputeBoundingSphere(ID3DXMesh* mesh, d3d::BoundingSphere* sphere)
{HRESULT hr = 0;BYTE* v = 0;mesh->LockVertexBuffer(0, (void**)&v);hr = D3DXComputeBoundingSphere((D3DXVECTOR3*)v, mesh->GetNumVertices(), D3DXGetFVFVertexSize(mesh->GetFVF()),&sphere->_center, &sphere->_radius);mesh->UnlockVertexBuffer();if (FAILED(hr))return false;return true;
}bool ComputeBoundingBox(ID3DXMesh* mesh,d3d::BoundingBox* box)
{HRESULT hr = 0;BYTE* v = 0;mesh->LockVertexBuffer(0, (void**)&v);hr = D3DXComputeBoundingBox((D3DXVECTOR3*)v,mesh->GetNumVertices(),D3DXGetFVFVertexSize(mesh->GetFVF()),&box->_min,&box->_max);mesh->UnlockVertexBuffer();if (FAILED(hr))return false;return true;
}

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

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

相关文章

【Linux】使用timer_create()创建定时器发送信号并使用sigaction()处理信号

0x00 前言 最后更新时间&#xff1a;2023-10-16 0x01 主要函数及结构体介绍 1.sigaction函数 #include <signal.h> int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);功能&#xff1a; 用于改变进程接收到特定信号后的行为。 参数…

华为eNSP配置专题-ACL的配置

文章目录 华为eNSP配置专题-ACL的配置1、前置环境1.1、宿主机1.2、eNSP模拟器 2、基本环境搭建2.1、基本终端构成和连接2.2、各终端基本配置2.2.1、PC1和PC2的配置2.2.2、模拟互联网的路由器的配置2.2.3、财务部服务器的配置2.2.4、路由器AR1的配置 2.3、让各网段能够ping通互联…

ESP8266 Node Mcu开发板连接WIFI并上报数据到MQTT服务器——物联网应用开发

一、前言 本文主要介绍关于ESP8266 Node Mcu开发板如何连接WIFI并将本地采集的数据上传到MQTT服务器中。 大家调试可以使用MQTTBox 二、WIFI连接 首先&#xff0c;导入WIFI连接所需的头文件&#xff0c;引入所需库。 #include <ESP8266WiFi.h> 声明字符串常量&#xff0…

Vue项目路由加前缀

Vue项目路由加前缀 vue-cli3.0配置 1&#xff09;静态资源前缀 vue.config.js /module.exports 配置 publicPath&#xff1a;“/前缀” 2&#xff09;路由前缀 route/index.js export default new Router({ base:”/前缀" , }) 参考文章&#xff1a;https://blog.csd…

Python学习基础笔记七十三——调试程序

为什么要调试&#xff1f; 我们发现程序运行的结果和我们预期的不符。 程序运行的错误&#xff0c;我们通常叫做bug。 有两种类型的bug&#xff1a;语句错误和逻辑错误。 所谓语句错误&#xff0c;就是执行代码的时候&#xff0c;解释器就可以直接发现的代码错误&#xff0c…

【00】神经网络之初始化参数

问题描述 #随机初始化权重 w12 np.random.randn(100, 784)/np.sqrt(784) 为什么除以28 回答 这里的代码是初始化一个深度学习模型中的权重矩阵w12。权重矩阵的形状是(100, 784)&#xff0c;这是一个从784个输入节点到100个隐藏节点的全连接层。 除以np.sqrt(784)是权重初始…

Qt QMovie和QLabel配合播放GIF表情包

文章目录 效果演示main函数创建MoviePlayer对象头文件movieplayer.h源文件movieplayer.cpp代码解释在Qt框架中,QMovie是用于处理动画和视频的类。所有源码已在本篇文章公布。 效果演示 main函数创建MoviePlayer对象 #include <QApplication>#include "movie

git pull and git fetch 到底有什么区别?

大家好这里是tony4geek 。 今天给大家介绍git pull and git fetch 有什么区别&#xff1f; 开发过程中大家肯定很多人都用到过git。获取代码有很多的git命令&#xff0c;最长用的命令是pull和fetch。那么问题来了他们之间到底有什么区别&#xff0c;该怎么使用呢&#xff1f;…

scratch身高统计 2023年9月中国电子学会图形化编程 少儿编程 scratch编程等级考试三级真题和答案解析

目录 scratch身高统计 一、题目要求 1、准备工作 2、功能实现 二、案例分析

es6中proxy如何使用

ES6中的Proxy是一个强大的对象拦截器&#xff0c;它可以拦截对象的各种操作&#xff0c;比如属性访问、函数调用等。你可以使用Proxy来控制对象的访问和修改行为&#xff0c;从而实现一些高级特性。下面是使用Proxy的一些例子&#xff1a; 属性访问拦截 const obj {name: To…

docker容器内安装项目运行环境(python依赖包+allure)

目录 一、安装自动化项目依赖包1.导出项目依赖库2.上传到远程仓库3.进入jenkins容器内&#xff0c;检查是否安装git4.配置git用户信息5.生成秘钥6.把代码拉取下来7.安装python项目依赖8.运行项目 二、安装allure1.jenkins容器内安装allure&#xff0c;进入/usr/local/2.下载all…

获取远程仓库的信息和远程分支的信息

前记&#xff1a; git svn sourcetree gitee github gitlab gitblit gitbucket gitolite gogs 版本控制 | 仓库管理 ---- 系列工程笔记. Platform&#xff1a;Windows 10 Git version&#xff1a;git version 2.32.0.windows.1 Function&#xff1a;获取远程仓库的信息和远…

vue axios封装并发请求

场景&#xff1a;有时候进入某些页面时会同时请求很多个业务接口&#xff0c; 如果一个一个的发送请求&#xff0c;会产生很多冗余&#xff0c;所以封装了一个方法&#xff08;参考渡一教育袁老师课程&#xff09;。 此方法会根据请求顺序&#xff0c;返回一个返回结果数组&am…

MyBatisPlus的学习项目页面

MyBatisPlus通过扫描实体类&#xff0c;并基于反射获取实体类信息作为数据库表信息 类名驼峰转下划线作为表名 名为id的字段作为主键 变量名驼峰转下划线作为表的字段名 常见注解 TableName&#xff1a;用来指定表名 Tableld&#xff1a;用来指定表中的主键字段信息 Tabl…

修改http_charfinder.py使能在python311环境中运行

需要修改两个函数&#xff0c;第一个是init函数&#xff0c;修改如下&#xff1a; async def init(loop, address, port): # <1> # app web.Application(looploop) # <2> # app.router.add_route(GET, /, home) # <3> app web.Application(…

qt listwidget获取当前选中项

要获取Qt ListWidget中当前选中的项&#xff0c;你可以使用currentItem()函数。这个函数会返回一个QListWidgetItem对象&#xff0c;表示当前选中的项。以下是一个示例代码&#xff1a; QListWidgetItem *selectedItem listWidget->currentItem(); if (selectedItem) {// …

kong网关从入门到放弃

Kong网关是一个轻量级、快速、灵活的云名称API网关。Kong Gateway位于您的服务应用程序前面&#xff0c;可动态控制、分析和路由请求和响应。KongGateway通过使用灵活、低代码、基于插件的方法来实现您的API流量策略。 https://docs.konghq.com/gateway/latest/#features 架构…

微前端一:技术选型

微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。 微前端架构具备以下几个核心价值&#xff1a; 1、技术栈无关 主框架不限制接入应用的技术栈&#xff0c;微应用具备完全自主权 2、独立开发、独立部署 微应用仓库独立&#xff0…

面试题-React(十二):React中不可变数据的力量

一、不可变数据的概念 不可变数据意味着数据一旦创建&#xff0c;就不能被更改。在React中&#xff0c;每次对数据的修改都会返回一个新的数据副本&#xff0c;而不会改变原始数据。这种方式确保了数据的稳定性和一致性。 二、Props中的不可变数据 在React中&#xff0c;组件…

TikTok国际版 使用特网科技Bluestacks模拟器安装方法

特网科技Bluestacks模拟器主机 桌面自带Bluestacks模拟器 TikTok国际版Bluestacks模拟器搜索tiktot 登录google应用商店-安装TikTok 安装过程可能需要3-5分钟不等-配置过低可能会导致安装失败&#xff0c;建议升级更高内存。 安装完成-打开 安装成功APP-我的游戏查看 打开国际版…