如何从 OBJ 文件中正确读取信息并使用它来渲染带有 DirectX 11 的模型
How to correctly read information from OBJ file and use it to render a model with DirectX 11
我正在尝试使用 DirectX 渲染 OBJ 文件格式的模型。我正在使用简单的立方体模型,但我得到了一些非常奇怪的结果(下图)
我尝试通过手动填充顶点数组来渲染立方体,并且效果很好。在研究了 OBJ 文件格式后,我认为我应该做类似的事情,但我不明白为什么这不起作用。
发送两个函数和结构描述:
- 初始化几何
在这个函数中,我设置了我的几何图形、着色器并从文件中读取信息。我认为我在这个函数的末尾做错了什么,因为我没有更改着色器部分和初始化部分,只是添加了新的方法来填充顶点数组
- 渲染
这个函数与我的第一个版本完全没有改变,我在第一个版本中手动填充了一个 vertecies 数组。
- 结构
只是一些结构描述。
HRESULT RenderDevice::initGeometry() {
///////READING INFO FROM FILE//////////
ifstream *inp = new ifstream("test.obj");
ofstream *out = new ofstream("result.txt");
char str[256];
while (!inp->eof()) {
inp->getline(str, 256);
meshInfo.coord.push_back(new std::string(str));
}
HRESULT hr = S_OK;
ID3DBlob *pVSBlob = NULL;
hr = compileShaderFromFile(L"texture.fx", "VS", "vs_4_0", &pVSBlob);
if (FAILED(hr)) {
MessageBox(NULL, L"Can't compile Vertex Shader", L"Error", MB_OK);
return hr;
}
hr = g_pd3dDevice->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &g_pVertexShader);
if (FAILED(hr)) {
pVSBlob->Release();
return hr;
}
D3D11_INPUT_ELEMENT_DESC layout[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
};
UINT numElemenets = ARRAYSIZE(layout);
hr = g_pd3dDevice->CreateInputLayout(layout, numElemenets, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &g_pVertexLayout);
pVSBlob->Release();
if (FAILED(hr)) {
return hr;
}
g_pImmediateContext->IASetInputLayout(g_pVertexLayout);
ID3DBlob *pPSBlob = NULL;
hr = compileShaderFromFile(L"texture.fx", "PS", "ps_4_0", &pPSBlob);
if (FAILED(hr)) {
MessageBox(NULL, L"Can't compile Pixel Shader", L"Error", MB_OK);
return hr;
}
hr = g_pd3dDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &g_pPixelShader);
pPSBlob->Release();
if (FAILED(hr)) {
return hr;
}
pPSBlob = NULL;
hr = compileShaderFromFile(L"texture.fx", "PSSolid", "ps_4_0", &pPSBlob);
if (FAILED(hr)) {
MessageBox(NULL, L"Can't compile Solid Pixel Shader", L"Error", MB_OK);
return hr;
}
hr = g_pd3dDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &g_pPixelShaderSolid);
pPSBlob->Release();
if (FAILED(hr)) {
return hr;
}
/////////SPLITING INFO INTO DIFFERENT VECTORS/////////////////
for (int i = 0; i < meshInfo.coord.size(); i++) {
if (meshInfo.coord[i]->c_str()[0] == 'v' && meshInfo.coord[i]->c_str()[1] != 'n') {
float tmpx, tmpy, tmpz;
sscanf_s(meshInfo.coord[i]->c_str(), "v %f %f %f", &tmpx, &tmpy, &tmpz);
meshInfo.positions.push_back(XMFLOAT3(tmpx, tmpy, tmpz));
} else if (meshInfo.coord[i]->c_str()[0] == 'v' && meshInfo.coord[i]->c_str()[1] == 'n') {
float tmpx, tmpy, tmpz;
sscanf_s(meshInfo.coord[i]->c_str(), "vn %f %f %f", &tmpx, &tmpy, &tmpz);
meshInfo.normals.push_back(XMFLOAT3(tmpx, tmpy, tmpz));
} else if (meshInfo.coord[i]->c_str()[0] == 'f') {
int iX, iY, iZ, nX, nY, nZ;
sscanf_s(meshInfo.coord[i]->c_str(), "f %d//%d %d//%d %d//%d", &iX, &nX, &iY, &nY, &iZ, &nZ);
meshInfo.indexiesPoints.push_back(iX);
meshInfo.indexiesPoints.push_back(iY);
meshInfo.indexiesPoints.push_back(iZ);
meshInfo.indexiesNormals.push_back(nX);
meshInfo.indexiesNormals.push_back(nY);
meshInfo.indexiesNormals.push_back(nZ);
}
}
meshInfo.indexiesAmount = meshInfo.indexiesPoints.size();
meshInfo.vertexAmount = meshInfo.positions.size();
meshInfo.normalsAmount = meshInfo.normals.size();
Vertex *vertices = new Vertex[meshInfo.indexiesAmount];
//////////////FILLING VERTECIES ARRAY///////////////
for (int i = 0; i < meshInfo.indexiesAmount; i++) {
vertices[i].normal.x = meshInfo.normals[meshInfo.indexiesNormals[i] - 1].x;
vertices[i].normal.y = meshInfo.normals[meshInfo.indexiesNormals[i] - 1].y;
vertices[i].normal.z = meshInfo.normals[meshInfo.indexiesNormals[i] - 1].z;
vertices[i].pos.x = meshInfo.positions[meshInfo.indexiesPoints[i] - 1].x;
vertices[i].pos.y = meshInfo.positions[meshInfo.indexiesPoints[i] - 1].y;
vertices[i].pos.z = meshInfo.positions[meshInfo.indexiesPoints[i] - 1].z;
}
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(Vertex) * meshInfo.indexiesAmount;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA initData;
ZeroMemory(&initData, sizeof(initData));
initData.pSysMem = vertices;
hr = g_pd3dDevice->CreateBuffer(&bd, &initData, &g_pVertexBuffer);
if (FAILED(hr)) {
return hr;
}
///////////////////FILLING INIXIES ARRAY///////////////////
WORD *indixies = new WORD[meshInfo.indexiesAmount];
for (int i = 0; i < meshInfo.indexiesAmount; i++) {
indixies[i] = meshInfo.indexiesPoints[i];
}
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(int) * meshInfo.indexiesAmount;
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
initData.pSysMem = indixies;
hr = g_pd3dDevice->CreateBuffer(&bd, &initData, &g_pIndexBuffer);
if (FAILED(hr)) {
return hr;
}
UINT stride = sizeof(Vertex);
UINT offset = 0;
g_pImmediateContext->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &stride, &offset);
g_pImmediateContext->IASetIndexBuffer(g_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(ConstantBuffer);
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bd.CPUAccessFlags = 0;
hr = g_pd3dDevice->CreateBuffer(&bd, NULL, &g_pCBMatrixes);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
void RenderDevice::render() {
float clearColor[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
g_pImmediateContext->ClearRenderTargetView(g_pRenderTargetView, clearColor);
g_pImmediateContext->ClearDepthStencilView(g_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
updateLigth();
for (int i = 0; i < 6; i++) {
updateMatrix(MX_SETWORLD, i * (XM_PI * 2) / 6);
g_pImmediateContext->VSSetShader(g_pVertexShader, NULL, 0);
g_pImmediateContext->VSSetConstantBuffers(0, 1, &g_pCBMatrixes);
g_pImmediateContext->VSSetConstantBuffers(1, 1, &g_pCBLigth);
g_pImmediateContext->PSSetConstantBuffers(0, 1, &g_pCBMatrixes);
g_pImmediateContext->PSSetConstantBuffers(1, 1, &g_pCBLigth);
g_pImmediateContext->PSSetShader(g_pPixelShader, NULL, 0);
g_pImmediateContext->DrawIndexed(meshInfo.indexiesAmount, 0, 0);
}
g_pImmediateContext->PSSetShader(g_pPixelShaderSolid, NULL, 0);
for (int m = 0; m < 2; m++) {
updateMatrix(m, 0);
g_pImmediateContext->DrawIndexed(meshInfo.indexiesAmount, 0, 0);
}
g_pSwapChain->Present(0, 0);
}
typedef struct Vertex {
XMFLOAT3 pos;
XMFLOAT3 normal;
}Vertex;
typedef struct MeshInfo {
int vertexAmount;
int normalsAmount;
vector<XMFLOAT3> positions;
vector<XMFLOAT3> normals;
vector<string*> coord;
vector<int> indexiesPoints;
vector<int> indexiesNormals;
int indexiesAmount;
}MeshInfo;
typedef struct ConstantBuffer {
XMMATRIX mWorld;
XMMATRIX mView;
XMMATRIX mProjection;
XMFLOAT4 vLigthDir[2];
XMFLOAT4 vLigthColor[2];
XMFLOAT4 vOutputColor;
}ConstantBuffer;
我希望在我的程序 window 中看到 6 个立方体,
but I get this thing
OBJ 文件格式使用 one-based 编号存储索引(第一个顶点索引为 1)Wavefron Obg file format。 DirectX 使用 zero-based 编号(第一个顶点索引为 0,与 C、C++ 相同)。
因此您应该转换索引值以使用它们。你可以这样做:
for (int i = 0; i < meshInfo.indexiesAmount; i++) {
indixies[i] = meshInfo.indexiesPoints[i] - 1;
}
我实际上已经找到了解决问题的方法。主要问题是为绘制顶点制定正确的顺序。但在 .OBJ 文件中,只有 UNIQUE 顶点、法线和纹理坐标被写入。它们也彼此分开(例如它可以是 8 UNIQUE 顶点坐标和 6 UNIQUE 法线坐标)。主要的事情发生在部分,描述面孔的地方。每个面都指向一个特定的位置索引、法线和纹理坐标,这意味着我们可以在不同时间使用相同的法线、位置和纹理坐标(我知道这很明显)。我意识到在我的实现中我根本不需要索引数组,因为我填充的 Vertecies 数组已经是一个模型了!我最终在 ID3D11DeviceContext 中使用方法 Draw 而不是 DrawIndexed。
我正在尝试使用 DirectX 渲染 OBJ 文件格式的模型。我正在使用简单的立方体模型,但我得到了一些非常奇怪的结果(下图)
我尝试通过手动填充顶点数组来渲染立方体,并且效果很好。在研究了 OBJ 文件格式后,我认为我应该做类似的事情,但我不明白为什么这不起作用。
发送两个函数和结构描述:
- 初始化几何
在这个函数中,我设置了我的几何图形、着色器并从文件中读取信息。我认为我在这个函数的末尾做错了什么,因为我没有更改着色器部分和初始化部分,只是添加了新的方法来填充顶点数组 - 渲染
这个函数与我的第一个版本完全没有改变,我在第一个版本中手动填充了一个 vertecies 数组。 - 结构
只是一些结构描述。
HRESULT RenderDevice::initGeometry() {
///////READING INFO FROM FILE//////////
ifstream *inp = new ifstream("test.obj");
ofstream *out = new ofstream("result.txt");
char str[256];
while (!inp->eof()) {
inp->getline(str, 256);
meshInfo.coord.push_back(new std::string(str));
}
HRESULT hr = S_OK;
ID3DBlob *pVSBlob = NULL;
hr = compileShaderFromFile(L"texture.fx", "VS", "vs_4_0", &pVSBlob);
if (FAILED(hr)) {
MessageBox(NULL, L"Can't compile Vertex Shader", L"Error", MB_OK);
return hr;
}
hr = g_pd3dDevice->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &g_pVertexShader);
if (FAILED(hr)) {
pVSBlob->Release();
return hr;
}
D3D11_INPUT_ELEMENT_DESC layout[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
};
UINT numElemenets = ARRAYSIZE(layout);
hr = g_pd3dDevice->CreateInputLayout(layout, numElemenets, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &g_pVertexLayout);
pVSBlob->Release();
if (FAILED(hr)) {
return hr;
}
g_pImmediateContext->IASetInputLayout(g_pVertexLayout);
ID3DBlob *pPSBlob = NULL;
hr = compileShaderFromFile(L"texture.fx", "PS", "ps_4_0", &pPSBlob);
if (FAILED(hr)) {
MessageBox(NULL, L"Can't compile Pixel Shader", L"Error", MB_OK);
return hr;
}
hr = g_pd3dDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &g_pPixelShader);
pPSBlob->Release();
if (FAILED(hr)) {
return hr;
}
pPSBlob = NULL;
hr = compileShaderFromFile(L"texture.fx", "PSSolid", "ps_4_0", &pPSBlob);
if (FAILED(hr)) {
MessageBox(NULL, L"Can't compile Solid Pixel Shader", L"Error", MB_OK);
return hr;
}
hr = g_pd3dDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &g_pPixelShaderSolid);
pPSBlob->Release();
if (FAILED(hr)) {
return hr;
}
/////////SPLITING INFO INTO DIFFERENT VECTORS/////////////////
for (int i = 0; i < meshInfo.coord.size(); i++) {
if (meshInfo.coord[i]->c_str()[0] == 'v' && meshInfo.coord[i]->c_str()[1] != 'n') {
float tmpx, tmpy, tmpz;
sscanf_s(meshInfo.coord[i]->c_str(), "v %f %f %f", &tmpx, &tmpy, &tmpz);
meshInfo.positions.push_back(XMFLOAT3(tmpx, tmpy, tmpz));
} else if (meshInfo.coord[i]->c_str()[0] == 'v' && meshInfo.coord[i]->c_str()[1] == 'n') {
float tmpx, tmpy, tmpz;
sscanf_s(meshInfo.coord[i]->c_str(), "vn %f %f %f", &tmpx, &tmpy, &tmpz);
meshInfo.normals.push_back(XMFLOAT3(tmpx, tmpy, tmpz));
} else if (meshInfo.coord[i]->c_str()[0] == 'f') {
int iX, iY, iZ, nX, nY, nZ;
sscanf_s(meshInfo.coord[i]->c_str(), "f %d//%d %d//%d %d//%d", &iX, &nX, &iY, &nY, &iZ, &nZ);
meshInfo.indexiesPoints.push_back(iX);
meshInfo.indexiesPoints.push_back(iY);
meshInfo.indexiesPoints.push_back(iZ);
meshInfo.indexiesNormals.push_back(nX);
meshInfo.indexiesNormals.push_back(nY);
meshInfo.indexiesNormals.push_back(nZ);
}
}
meshInfo.indexiesAmount = meshInfo.indexiesPoints.size();
meshInfo.vertexAmount = meshInfo.positions.size();
meshInfo.normalsAmount = meshInfo.normals.size();
Vertex *vertices = new Vertex[meshInfo.indexiesAmount];
//////////////FILLING VERTECIES ARRAY///////////////
for (int i = 0; i < meshInfo.indexiesAmount; i++) {
vertices[i].normal.x = meshInfo.normals[meshInfo.indexiesNormals[i] - 1].x;
vertices[i].normal.y = meshInfo.normals[meshInfo.indexiesNormals[i] - 1].y;
vertices[i].normal.z = meshInfo.normals[meshInfo.indexiesNormals[i] - 1].z;
vertices[i].pos.x = meshInfo.positions[meshInfo.indexiesPoints[i] - 1].x;
vertices[i].pos.y = meshInfo.positions[meshInfo.indexiesPoints[i] - 1].y;
vertices[i].pos.z = meshInfo.positions[meshInfo.indexiesPoints[i] - 1].z;
}
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(Vertex) * meshInfo.indexiesAmount;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA initData;
ZeroMemory(&initData, sizeof(initData));
initData.pSysMem = vertices;
hr = g_pd3dDevice->CreateBuffer(&bd, &initData, &g_pVertexBuffer);
if (FAILED(hr)) {
return hr;
}
///////////////////FILLING INIXIES ARRAY///////////////////
WORD *indixies = new WORD[meshInfo.indexiesAmount];
for (int i = 0; i < meshInfo.indexiesAmount; i++) {
indixies[i] = meshInfo.indexiesPoints[i];
}
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(int) * meshInfo.indexiesAmount;
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
initData.pSysMem = indixies;
hr = g_pd3dDevice->CreateBuffer(&bd, &initData, &g_pIndexBuffer);
if (FAILED(hr)) {
return hr;
}
UINT stride = sizeof(Vertex);
UINT offset = 0;
g_pImmediateContext->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &stride, &offset);
g_pImmediateContext->IASetIndexBuffer(g_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(ConstantBuffer);
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bd.CPUAccessFlags = 0;
hr = g_pd3dDevice->CreateBuffer(&bd, NULL, &g_pCBMatrixes);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
void RenderDevice::render() {
float clearColor[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
g_pImmediateContext->ClearRenderTargetView(g_pRenderTargetView, clearColor);
g_pImmediateContext->ClearDepthStencilView(g_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
updateLigth();
for (int i = 0; i < 6; i++) {
updateMatrix(MX_SETWORLD, i * (XM_PI * 2) / 6);
g_pImmediateContext->VSSetShader(g_pVertexShader, NULL, 0);
g_pImmediateContext->VSSetConstantBuffers(0, 1, &g_pCBMatrixes);
g_pImmediateContext->VSSetConstantBuffers(1, 1, &g_pCBLigth);
g_pImmediateContext->PSSetConstantBuffers(0, 1, &g_pCBMatrixes);
g_pImmediateContext->PSSetConstantBuffers(1, 1, &g_pCBLigth);
g_pImmediateContext->PSSetShader(g_pPixelShader, NULL, 0);
g_pImmediateContext->DrawIndexed(meshInfo.indexiesAmount, 0, 0);
}
g_pImmediateContext->PSSetShader(g_pPixelShaderSolid, NULL, 0);
for (int m = 0; m < 2; m++) {
updateMatrix(m, 0);
g_pImmediateContext->DrawIndexed(meshInfo.indexiesAmount, 0, 0);
}
g_pSwapChain->Present(0, 0);
}
typedef struct Vertex {
XMFLOAT3 pos;
XMFLOAT3 normal;
}Vertex;
typedef struct MeshInfo {
int vertexAmount;
int normalsAmount;
vector<XMFLOAT3> positions;
vector<XMFLOAT3> normals;
vector<string*> coord;
vector<int> indexiesPoints;
vector<int> indexiesNormals;
int indexiesAmount;
}MeshInfo;
typedef struct ConstantBuffer {
XMMATRIX mWorld;
XMMATRIX mView;
XMMATRIX mProjection;
XMFLOAT4 vLigthDir[2];
XMFLOAT4 vLigthColor[2];
XMFLOAT4 vOutputColor;
}ConstantBuffer;
我希望在我的程序 window 中看到 6 个立方体, but I get this thing
OBJ 文件格式使用 one-based 编号存储索引(第一个顶点索引为 1)Wavefron Obg file format。 DirectX 使用 zero-based 编号(第一个顶点索引为 0,与 C、C++ 相同)。
因此您应该转换索引值以使用它们。你可以这样做:
for (int i = 0; i < meshInfo.indexiesAmount; i++) {
indixies[i] = meshInfo.indexiesPoints[i] - 1;
}
我实际上已经找到了解决问题的方法。主要问题是为绘制顶点制定正确的顺序。但在 .OBJ 文件中,只有 UNIQUE 顶点、法线和纹理坐标被写入。它们也彼此分开(例如它可以是 8 UNIQUE 顶点坐标和 6 UNIQUE 法线坐标)。主要的事情发生在部分,描述面孔的地方。每个面都指向一个特定的位置索引、法线和纹理坐标,这意味着我们可以在不同时间使用相同的法线、位置和纹理坐标(我知道这很明显)。我意识到在我的实现中我根本不需要索引数组,因为我填充的 Vertecies 数组已经是一个模型了!我最终在 ID3D11DeviceContext 中使用方法 Draw 而不是 DrawIndexed。