- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我目前正在尝试为 OpenGL 项目制作 Wavefront (.obj) 文件加载器。我目前使用的方法是逐行分离 vector (std::vectors) 中的顶点位置、纹理位置和法线位置,我将它们的索引(顶点、纹理和法线索引)存储在三个单独的位置 vector (来自文件的“f”行,对于每张脸)。
我无法根据纹理索引对充满纹理坐标的 vector 进行排序。我能够在正确的位置渲染顶点,因为我的“加载器”类需要索引,但我无法弄清楚如何以任何方式对纹理坐标进行排序,因此纹理在某些三角形上看起来偏移为结果。
带有偏移纹理的立方体图像:
纹理图像 (.png),它应该如何出现在每个面上:
编辑:这是 .obj 文件和 .mtl 文件的链接。 Google Drive.
这是我的 OBJLoader.cpp 文件:
rawObj.open(filePath); // Open file
while (!rawObj.eof()) {
getline(rawObj, line); // Read line
// Read values from each line
// starting with a 'v' for
// the vertex positions with
// a custom function (gets the word in a line
// at position i)
if (strWord(line, 1) == "v") {
for (int i = 2; i <= 4; i++) {
std::string temp;
temp = strWord(line, i);
vertexStrings.push_back(temp);
}
// Same for texture positions
} else if (strWord(line, 1) == "vt") {
for (int i = 2; i <= 3; i++) {
std::string temp;
temp = strWord(line, i);
textureStrings.push_back(temp);
}
// Same for normal positions
} else if (strWord(line, 1) == "vn") { // normals
for (int i = 2; i <= 4; i++) {
std::string temp;
temp = strWord(line, i);
normalStrings.push_back(temp);
}
// Separate each of the three vertices and then separate
// each vertex into its vertex index, texture index and
// normal index
} else if (strWord(line, 1) == "f") { // faces (indices)
std::string temp;
for (int i = 2; i <= 4; i++) {
temp = strWord(line, i);
chunks.push_back(temp);
k = std::stoi(strFaces(temp, 1));
vertexIndices.push_back(k-1);
l = std::stoi(strFaces(temp, 2));
textureIndices.push_back(l-1);
m = std::stoi(strFaces(temp, 3));
normalIndices.push_back(m-1);
}
}
}
// Convert from string to float
for (auto &s : vertexStrings) {
std::stringstream parser(s);
float x = 0;
parser >> x;
vertices.push_back(x);
}
for (auto &s : textureStrings) {
std::stringstream parser(s);
float x = 0;
parser >> x;
texCoords.push_back(x);
}
// Y coords are from top left instead of bottom left
for (int i = 0; i < texCoords.size(); i++) {
if (i % 2 != 0)
texCoords[i] = 1 - texCoords[i];
}
// Passes vertex positions, vertex indices and texture coordinates
// to loader class
return loader.loadToVao(vertices, vertexIndices, texCoords);
}
我试过在循环中插入来自 texCoords[textureIndices[i]] 的值 (vector.insert),但这没有用,使输出更糟。我尝试了一个简单的:
tempVec[i] = texCoords[textureIndices[i]]
在 for 循环中,但这也不起作用。
我检查了整个项目并确定排序是问题的原因,因为当我为立方体插入硬编码值时,它工作得很好并且纹理根本没有偏移。 (OpenGL 命令/图像加载器正常工作。)
最后,是否有另一种方法可以根据 textureIndices 对 texCoords 进行排序?
最佳答案
如果顶点坐标和纹理坐标有不同的索引,那么顶点位置一定是“重复的”。
顶点坐标及其属性(如纹理坐标)形成一个元组。每个顶点坐标必须有自己的纹理坐标和属性。您可以将 3D 顶点坐标和 2D 纹理坐标视为单个 5D 坐标。
See Rendering meshes with multiple indices .
假设您有一个像这样的 .obj 文件:
v -1 -1 -1
v 1 -1 -1
v -1 1 -1
v 1 1 -1
v -1 -1 1
v 1 -1 1
v -1 1 1
v 1 1 1
vt 0 0
vt 0 1
vt 1 0
vt 1 1
vn -1 0 0
vn 0 -1 0
vn 0 0 -1
vn 1 0 0
vn 0 1 0
vn 0 0 1
f 3/1/1 1/2/1 5/4/1 7/3/1
f 1/1/2 2/2/2 3/4/2 6/3/2
f 3/1/3 4/2/3 2/4/3 1/3/3
f 2/1/4 4/2/4 8/4/4 6/3/4
f 4/1/5 3/2/5 7/4/5 8/3/5
f 5/1/6 6/2/6 8/4/6 7/3/6
从这里你必须找到顶点坐标、纹理纹理坐标和法 vector 索引的所有组合,这些组合用于面部规范:
0 : 3/1/1
1 : 1/2/1
2 : 5/4/1
3 : 7/3/1
4 : 1/1/2
5 : 2/2/2
6 : 3/4/2
7 : 6/3/2
8 : ...
然后你必须创建一个对应索引组合数组的顶点坐标、纹理坐标和法 vector 数组。顶点坐标及其属性既可以组合在一个数组中作为数据集,也可以组合成三个具有相同数量属性的数组:
index vx vy vz u v nx ny nz
0 : -1 1 -1 0 0 -1 0 0
1 : -1 -1 -1 0 1 -1 0 0
2 : -1 -1 1 1 1 -1 0 0
3 : -1 1 1 1 0 -1 0 0
4 : -1 -1 -1 0 0 0 -1 0
5 : 1 -1 -1 0 1 0 -1 0
6 : -1 1 -1 1 1 0 -1 0
7 : 1 -1 1 1 0 0 -1 0
8 : ...
查看非常简单的 c++ 函数,它可以读取 .obj 文件,就像您链接到的那样。该函数读取文件并将数据写入元素 vector 和属性 vector 。
注意,函数可以优化,不关心性能。对于小文件(比如您喜欢的 cube3.obj),这无关紧要,但对于大文件,特别是索引表中的线性搜索,将不得不改进。
我只是想告诉您如何读取 .obj 文件以及如何创建元素和属性 vector ,这些元素和属性 vector 可以直接用于通过 OpenGL 绘制网格。
#include <vector>
#include <array>
#include <string>
#include <fstream>
#include <strstream>
#include <algorithm>
bool load_obj(
const std::string filename,
std::vector<unsigned int> &elements,
std::vector<float> &attributes )
{
std::ifstream obj_stream( filename, std::ios::in );
if( !obj_stream )
return false;
// parse the file, line by line
static const std::string white_space = " \t\n\r";
std::string token, indices, index;
float value;
std::vector<float> v, vt, vn;
std::vector<std::array<unsigned int, 3>> f;
for( std::string line; std::getline( obj_stream, line ); )
{
// find first non whispce characterr in line
size_t start = line.find_first_not_of( white_space );
if ( start == std::string::npos )
continue;
// read the first token
std::istringstream line_stream( line.substr(start) );
line_stream.exceptions( 0 );
line_stream >> token;
// ignore comment lines
if ( token[0] == '#' )
continue;
// read the line
if ( token == "v" ) // read vertex coordinate
{
while ( line_stream >> value )
v.push_back( value );
}
else if ( token == "vt" ) // read normal_vectors
{
while ( line_stream >> value )
vt.push_back( value );
}
else if ( token == "vn" ) // read normal_vectors
{
while ( line_stream >> value )
vn.push_back( value );
}
else if ( token == "f" )
{
// read faces
while( line_stream >> indices )
{
std::array<unsigned int, 3> f3{ 0, 0, 0 };
// parse indices
for ( int j=0; j<3; ++ j )
{
auto slash = indices.find( "/" );
f3[j] = std::stoi(indices.substr(0, slash), nullptr, 10);
if ( slash == std::string::npos )
break;
indices.erase(0, slash + 1);
}
// add index
auto it = std::find( f.begin(), f.end(), f3 );
elements.push_back( (unsigned int)(it - f.begin()) );
if ( it == f.end() )
f.push_back( f3 );
}
}
}
// create array of attributes from the face indices
for ( auto f3 : f )
{
if ( f3[0] > 0 )
{
auto iv = (f3[0] - 1) * 3;
attributes.insert( attributes.end(), v.begin() + iv, v.begin() + iv + 3 );
}
if ( f3[1] > 0 )
{
auto ivt = (f3[1] - 1) * 2;
attributes.insert( attributes.end(), vt.begin() + ivt, vt.begin() + ivt + 2 );
}
if ( f3[2] > 0 )
{
auto ivn = (f3[2] - 1) * 3;
attributes.insert( attributes.end(), vn.begin() + ivn, vn.begin() + ivn + 3 );
}
}
return true;
}
关于c++ - 如何根据 Wavefront (.obj) 文件中给定的纹理索引对纹理位置进行排序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51708275/
我正在尝试创建键值对并推送到数组,但我得到的只是 [Obj Obj]、[Obj Obj]。我创建了一个传递名称和值的函数,用于将键和值分配给 JavaScript 对象。这是我的代码。不确定我错过了什
似乎在 for 循环的上下文中,关于对象的语法发生了变化。 为什么 console.log() 不应该运行相同的东西?第一个按预期运行,第二个呈现错误“steve is not defined”: v
在 Ady Osmani 的 blog post关于 js 中的命名空间,他提到了 5 个常见做法来测试先前定义的命名空间/对象是否存在以防止覆盖。我在这里复制我关注的 3 个: var myAppl
有没有办法(我怀疑它涉及继承和多态)来区分OBJ o, OBJ& o, const OBJ& o?我希望在 3 个不同的程序中使用相同的代码,并使用相同的方法名称调用不同的方法。 int main()
我正在寻找一个正则表达式来分割这种内容: obj.method(obj.attr,obj.attr2) 我希望拆分返回一个数组: ["obj", "method(obj.attr, obj.attr2
我想知道这些方法中哪种更好: var Obj = Obj || {}; 或 if (Obj === undefined || typeof Obj !== 'object') { Obj = {}
我正在尝试将一个值推送到数组的属性,如下所示 var obj = {}; obj.a = (obj.a || []).push(10); console.log( typeof obj.a );
为了避免误解,我们首先要就某些词语的含义达成一致。以下含义并非普遍接受的含义,我仅建议将它们作为此问题的背景。 function -- Function 的一个实例。它有一个与其关联的过程。 obje
我总是不确定哪个是正确的以及该使用哪个。 通常我会进行(obj == null)检查。我认为最好直接问。 我应该使用以下哪一项: if (obj == null) { alert(
我正在处理一些使用 pygraph 模块的类,当我使用 add_node() 方法时,它总是出现“node xxx already in graph”。所以我尝试使用 deepcopy() 创建一个新
在 this page您可以看到以下示例,了解如何实现数组的indexOf: if (!Array.prototype.indexOf) { Array.prototype.indexOf = f
(1) 和 (2) 之间是否存在任何重要差异(语义、性能相关等)? var obj = obj || {}; var obj = Object(obj); 上下文。第一个是我在 Django 的模板和
我想知道 obj !== obj 什么时候可以为真? 这是我在书上看到的一行代码,我很纳闷。 var result = class2type[(obj == null || obj !== obj)]
我有时会看到这种模式...... obj.method.call(obj, arg) 我不明白为什么它不同于... obj.method(arg) 为什么要使用第一种模式? 我的天啊,似乎引起了很
我刚刚在一段 React 代码中发现了以下结构(名称已更改): 据我了解,bind 只是执行相应的函数,并将函数的 this 设置为第一个参数,并向其传递更多参数。由于 func 已经是我们想要的
当我们查看Underscore.js源码时,我们可以看到如下内容: _.isObject = function (obj) { return obj === Object(obj);
我在将项目发布到本地系统时收到此错误 Copying file obj\Debug\build.force to obj\Release\Package\PackageTmp\obj\Debug\bu
我有一个类型为 Expression> 的现有表达式;它包含类似 cust => cust.Name 的值. 我还有一个父类,其字段类型为 T .我需要一个接受上述作为参数并生成一个以父类 ( TMo
我在当前目录中有 add.c sub.c 并通过 makefile 编译它们。 我做了以下事情: program 1: objs=$(patsubst %.cpp, %.o, $(wildcard *
这个问题在这里已经有了答案: Is there a difference between copy initialization and direct initialization? (9 个回答)
我是一名优秀的程序员,十分优秀!