OpenGL GLFW PMX模型 骨骼 学习笔记 m8w3
之前的文章中,实现了顶点,纹理和材质。接下来还剩骨骼,变形以及DisplayFrame。
PMX格式资料:https://www.cnblogs.com/ifwz/p/17544729.html
PMX载入方法参考(C++):GitHub - benikabocha/saba: OpenGL Viewer (OBJ PMD PMX)
优化控制逻辑
参考OpenGL实现的控制,是一种类似FPS的控制方式,鼠标控制视角,键盘控制移动。但这种在调试中其实不是很方便。这里参考Unity实现了一套控制系统,Unity的基本控制方式就三种,平移,缩放(或者说前进后退),改变视角方向。
平移:鼠标滚轮控制缩放(或者说前进后退);
缩放:按住鼠标中键后,在相机所在的垂直相机朝向的平面内移动;
视角:按住鼠标右键后,根据鼠标移动改变视角方向;
通过glfw的回调函数都可以实现,设置bool变量做标志位,分别在mouse_button_callback中通过是按住对应按键修改标志位,然后在mouse_callback(GLFWwindow* window, double xpos, double ypos)中根据标志位判断是否根据鼠标偏移量(上一帧和当前帧的位置差)调整摄像机,这里最好用deltatime固定一下摄像机速度。
平移(只改变cameraPos):
缩放(只改变cameraPos):
视角(只改变cameraFront,不改变cameraPos):
GUI和渲染窗口分离
多窗口创建独立ImGui。这块如果出现问题,大概率是窗口和GUI上下文没有切换到位/各个ImGui没有自己独立的IO或上下文环境;
#创建glfw窗口的部分省略
#每个glfw窗口的Imgui都需要创建自己的上下文环境和io
context = ImGui::CreateContext();
ImGui::SetCurrentContext(context);
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
/************************************************************/
#然后在每一帧绘制时,需要设置好上下文环境(glfw窗口和imgui)
glfwMakeContextCurrent(window);
ImGui::SetCurrentContext(context);
在实现下拉框选择纹理过程中,由于纹理路径是utf16,需要先转成utf8来使用,用了以下方法。utf16ToUtf8是使用wstring_convert实现的,输入u16string,输出string:
for (const auto& str : texFileName){ utf8Items.push_back(utf16ToUtf8(str)); items.push_back(utf8Items.back().c_str()); }
会出现问题,比如utf8Items[3] = "spa\\spa_MM2.png",但在item里变成了item[3] = 0x0000020a83e559b0 "葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺",指针失效了。调试了一下,发现是由于utf8Items在push back第5个元素的时候,给第4个元素也就是utf8Items[3]重新分配了地址导致其首地址发生变化(push back会动态分配内存),而items中又不会同步首地址,所以在item中指针就失效了。
但是这与push back动态分配内存的策略描述不一样,utf8Items[3]重新分配了地址但前面的3个元素的首地址又没有变化。而且不管执行多少次,即使每次分配的地址不一样,但结果都是第3个元素的首地址被修改。
解决方法:先把utf8Items的所有数据都处理好,再把首地址给到items即可。(resize提前分配空间给utf8Items也不行,items[3]还是有问题,可能和数据类型是string有关)
!!!【后续会回来再看看】!!!
纹理读取这一块,我是用的另一个glfw窗口读取的主glfw窗口的纹理,由于默认情况下glfw窗口都有自己的OpenGL上下文,且相互隔离。所以想在窗口2中用窗口1的纹理,需要在创建另一个窗口时指定共享OpenGL上下文的窗口,这样就可以在窗口2中读到主窗口的纹理了。
GLFWwindow* window1 = glfwCreateWindow(800, 600, "Window 1", NULL, NULL);
GLFWwindow* window2 = glfwCreateWindow(800, 600, "Window 2", NULL, window1); // 共享 window1 的上下文
多窗口ImGui输入冲突这一块,具体表现为,创建了两个glfw窗口,同时都有对应独立的imgui,但是在运行时,在任何窗口上的操作都会作用与同一个窗口,而不是当前窗口。
具体原因:glfwPollEvents()处理的用户输入,都只在当前imgui上下文生效(而多glfw窗口是会在对应窗口生效的)。举个栗子,当前imgui上下文是窗口B的imgui,用户在窗口A位置(10,10)处点击了一下,又在窗口B(10,10)处点击了一下。对glfw窗口来说,会调用窗口A和窗口B的鼠标点击回调函数分别一次,而对imgui来说,在窗口B的imgui上会处理两次(10,10)处的点击。
处理方法:!!!在想办法了在想办法了!!!
纹理读取问题
我用OpenGL渲染的PMX模型,在眼睛这一块有问题。第一张是用OpenGl渲染出来的,眼睛上的贴图有问题,第二张是PxmEditor里面打开的正常显示应该有的样子。
这块是读取纹理图像的时候,没有区分通道组成,都统一用RGB处理。但有的是RGBA,需要根据nrChannels设置glTexImage2D的参数。
纹理数量: 9 载入纹理图像... 纹理0: ../Assets/Model/yangyangQ/textures/T_R2T1YangyangMd10011_Q_body_D.png textures[i].nrChannels: 3 纹理1: ../Assets/Model/yangyangQ/spa/toon_skin0.png textures[i].nrChannels: 4 纹理2: ../Assets/Model/yangyangQ/spa/toon_fog2.png textures[i].nrChannels: 4 纹理3: ../Assets/Model/yangyangQ/spa/spa_MM2.png textures[i].nrChannels: 4 纹理4: ../Assets/Model/yangyangQ/textures/R2T1YangyangMd10011_Q_hair_D.png textures[i].nrChannels: 3 纹理5: ../Assets/Model/yangyangQ/textures/face.png textures[i].nrChannels: 3 纹理6: ../Assets/Model/yangyangQ/spa/toon_no.png textures[i].nrChannels: 4 纹理7: ../Assets/Model/yangyangQ/textures/eye.png textures[i].nrChannels: 4 纹理8: ../Assets/Model/yangyangQ/textures/bqb.png textures[i].nrChannels: 4 纹理图像载入完成.