gpt4 book ai didi

带有法线的 DirectX 11 索引绘图

转载 作者:行者123 更新时间:2023-12-04 02:51:35 24 4
gpt4 key购买 nike

我正在为 DirectX 11 开发 OBJ 加载器。在 OBJ 格式中,一个正方形(两个三角形)看起来像这样:

v 0 0 0
v 0 1 0
v 1 1 0
v 1 0 0

f 1 2 3
f 1 3 4

所以首先顶点数据由 v 给出,然后面由 f 给出。所以我只是将顶点读入顶点缓冲区,将索引读入索引缓冲区。但现在我需要为像素着色器计算法线。我可以在使用索引渲染时以某种方式存储 FACES 的法线数据,还是必须创建一个没有索引的顶点缓冲区? (因为这样我就可以存储每个顶点的法线数据,因为每个顶点只在 1 个面中使用)

最佳答案

通常的方法是为一个面的所有 3 个顶点存储相同的法向量。像这样:

Vertex
{
Vector3 position;
Vector3 normal;
}

std::vector<Vertex> vertices;
std::vector<uint32_t> indices;

for(each face f)
{
Vector3 faceNormal = CalculateFaceNormalFromPositions(f); // Generate normal for given face number `f`;
for(each vertex v)
{
Vertex vertex;
vertex.position = LoadPosition(f, v); // Load position from OBJ based on face index (f) and vertex index (v);
vertex.normal = faceNormal;
vertices.push_back(vertex);

indices.push_back(GetPosIndex()); // only position index from OBJ file needed
}
}

注意:通常您会希望使用顶点法线而不是面法线,因为顶点法线允许应用更好看的光照算法(逐像素光照):

for(each face f)
{
for(each vertex v)
{
Vertex vertex;
vertex.position = LoadPosition(f, v);
vertex.normal = ...precalculated somewhere...
vertices.push_back(vertex);
}
}

注意 2:通常您会希望从 Assets 文件中读取预先计算的法线,而不是在运行时计算它:

for(each face f)
{
for(each vertex v)
{
Vertex vertex;
vertex.position = LoadPosition(f, v);
vertex.normal = LoadNormal(f, v);
vertices.push_back(vertex);
}
}

.obj 格式允许存储每个顶点的法线)。来自谷歌的示例:

# cube.obj
#

g cube

# positions
v 0.0 0.0 0.0
v 0.0 0.0 1.0
v 0.0 1.0 0.0
v 0.0 1.0 1.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 1.0 1.0 0.0
v 1.0 1.0 1.0

# normals
vn 0.0 0.0 1.0
vn 0.0 0.0 -1.0
vn 0.0 1.0 0.0
vn 0.0 -1.0 0.0
vn 1.0 0.0 0.0
vn -1.0 0.0 0.0

# faces: indices of position / texcoord(empty) / normal
f 1//2 7//2 5//2
f 1//2 3//2 7//2
f 1//6 4//6 3//6
f 1//6 2//6 4//6
f 3//3 8//3 7//3
f 3//3 4//3 8//3
f 5//5 7//5 8//5
f 5//5 8//5 6//5
f 1//4 5//4 6//4
f 1//4 6//4 2//4
f 2//1 6//1 8//1
f 2//1 8//1 4//1

C++ 示例代码(未测试)

struct Vector3{ float x, y, z; };

struct Face
{
uint32_t position_ids[3];
uint32_t normal_ids[3];
};

struct Vertex
{
Vector3 position;
Vector3 normal;
};

std::vector<Vertex> vertices; // Your future vertex buffer
std::vector<uint32_t> indices; // Your future index buffer

void ParseOBJ(std::vector<Vector3>& positions, std::vector <Vector3>& normals, std::vector<Face>& faces) { /*TODO*/ }

void LoadOBJ(const std::wstring& filename, std::vector<Vertex>& vertices, std::vector<uint32_t>& indices)
{
// after parsing obj file
// you will have positions, normals
// and faces (which contains indices for positions and normals)
std::vector<Vector3> positions;
std::vector<Vector3> normals;
std::vector<Face> faces;
ParseOBJ(positions, normals, faces);

for (auto itFace = faces.begin(); itFace != faces.end(); ++itFace) // for each face
{
for (uint32_t i = 0; i < 3; ++i) // for each face vertex
{
uint32_t position_id = itFace->position_ids[i]; // just for short writing later
uint32_t normal_id = itFace->normal_ids[i];

Vertex vertex;
vertex.position = positions[position_id];
vertex.normal = normals[normal_id];

indices.push_back(position_id); // Note: only position's indices
vertices.push_back(vertex);
}
}
}

请注意,在顶点内合并法线数据后,您将不再需要法线索引。因此,法线变得没有索引(两个相等的法线可以存储在不同的顶点,这是一种空间浪费)。但是您仍然可以使用索引渲染,因为位置是索引的。

我必须说,当然,现代 GPU 的可编程流水线允许做更多棘手的事情:

  • 为每个创建缓冲区:位置、法线、pos_indices 和 nor_indices
  • 使用current vertex_id,读入shader当前position_id和对应的position,normal_id和对应的normal
  • 您甚至可以在着色器中生成法线(因此根本不需要 normal_id 和法线缓冲区)
  • 你可以在几何着色器中组装你的脸
  • ...这里还有一个坏主意 =)在这样的算法中,渲染系统变得更加复杂,几乎没有任何 yield 。

关于带有法线的 DirectX 11 索引绘图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17548298/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com