OpenGL 绘制你的 github skyline 模型
前言
好久没更新博客了,上一篇文章还是在去年6月份,乍一看居然快要过去一年了,不时还能看到粉丝数和排名在涨,可是却一直没有内容更新,怪不好意思的- -(主要是一直没想好要写些什么,中间也有过一些想法,比如从 Python 的 yield 关键字讲到协程、基于蒙特卡洛方法的 Raytracing、ECS等等,但是总觉得不是能够讲的很好就一直没写,然后平时不是在打 Dota2 就是在赶项目,所以就一直咕咕咕了。。。)
回到正题,前段时间看到的 github skyline,就是将过去一年在 github 的 commit 可视化成一个三维的模型,如下图。
当时看到这个第一反应就是好炫!然后看到了 Download STL file
这个按钮,又想到目前正在设计的并将作为学校图形学实验使用的项目(项目地址:https://github.com/leo6033/disc0ver-Engine ),就萌生了将绘制这个 model 做为模型绘制实验的想法。于是便开始了动手实现。(关于 OpenGL 模型加载的文章,我在两年前的从零开始 OpenGL 系列中倒是有写过,不过当时的实现是基于固定渲染管线的,从去年开始接触了 OpenGL 可编程流水线,就打算后面进行逐步更新)
模型绘制
STL 模型
将 STL file 下载下来之后,看到后缀名 stl,头一次见到这种后缀名,打开文件看了看
第一反应,这个格式有那么点点奇怪啊,字符串好像有那么点多。。。不过仔细分析了一下,大概就是这样
solid stlmesh
facet normal n1 n2 n3
outer loop
vertex x1 y1 z1
vertex x2 y2 z2
vertex x3 y3 z3
endloop
end facet
...
...
facet normal n1 n2 n3
outer loop
vertex x1 y1 z1
vertex x2 y2 z2
vertex x3 y3 z3
endloop
end facet
endsolid stlmesh
那么,用一个大循环即可解决,伪代码如下
s = readline()
assert(s == "solid filename")
while(!eof){
normal = readline()
if "facet" in normal{
normal -> (n1, n2, n3)
readline() skip outer loop
for i in range(3){
vertex = readline()
vertex -> (x, y, z)
}
readline() skip end loop
}
}
代码实现
#include <sstream>
#include <fstream>
#include <iostream>
void disc0ver::Model::loadModel(const std::string path)
{
std::cout << "load model " << path << std::endl;
std::ifstream infile;
std::string tmp_str;
infile.open(path);
if(!infile.is_open())
{
throw "model not found";
}
// 读取头 solid filename
char line[256];
infile.getline(line, sizeof(line));
std::istringstream solid(line);
solid >> tmp_str;
assert(tmp_str == "solid");
while(!infile.eof())
{
infile.getline(line, sizeof(line));
float n1, n2, n3;
float x, y, z;
std::istringstream face(line);
face >> tmp_str;
if(tmp_str == "facet")
{
face >> tmp_str >> n1 >> n2 >> n3;
// outer loop
infile.getline(line, sizeof(line));
for(int i = 0; i < 3;i ++)
{
infile.getline(line, sizeof(line));
std::istringstream vertex(line);
vertex >> tmp_str >> x >> y >> z;
vertices.emplace_back(x, y, z, n1, n2, n3, 0.0f, 0.0f);
}
// end loop
infile.getline(line, sizeof(line));
}
// end facet
infile.getline(line, sizeof(line));
}
}
模型绘制
在项目中,我封装了个 Mesh 类,将 OpenGL 图形绘制都放置于 Mesh 中进行
void disc0ver::Mesh::Draw(Shader &shader)
{
for(unsigned int i = 0; i < textures.size(); i ++)
{
textures[i].use(i);
shader.setInt(textures[i].getName(), static_cast<int>(i));
}
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glActiveTexture(GL_TEXTURE0);
}
void disc0ver::Mesh::setupMesh()
{
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int),
&indices[0], GL_STATIC_DRAW);
// 顶点位置
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), static_cast<void*>(0));
// 顶点法线
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, normal)));
// 顶点纹理坐标
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, texCoords)));
glBindVertexArray(0);
}
因此,我们需要做的就是如模型读取代码那样将读取到的 normal 和 vertex 存至 vertices 数组中,然后在 Init 函数中创建 indices 数组,作为顶点索引,最后直接调用 mesh.Draw() 来实现模型的绘制。
void disc0ver::Model::Init()
{
std::vector<Texture> tmp;
for (auto it = textures.begin(); it != textures.end(); ++it)
{
tmp.push_back(it->second);
}
for (int i = 0; i < vertices.size(); i++)
{
indices.push_back(i);
}
Mesh mesh(vertices, indices, tmp);
meshes.push_back(mesh);
}
void disc0ver::Model::draw(Shader& shader)
{
transform.use();
for (auto& mesh : meshes)
{
mesh.Draw(shader);
}
}
效果展示
加入了简单的光线反射 shader 效果
小节
由于很多 OpenGL 的绘制接口在项目中被封装起来了,所以给的代码片段看起来会比较迷糊,感觉比较单独看比较好理解的也就是模型数据读取的那部分代码了,欢迎大家阅读项目的源码,并希望能给些建议,如果可以点个 star 的话就更好了 _ (:з」∠)_ 。后续将继续更新一些项目内容的实现原理,以及去年做的基于蒙特卡洛方法的 Raytracing。
本文首发于我的知乎
OpenGL 绘制你的 github skyline 模型的更多相关文章
- OpenGL绘制自由落体小球
OpenGL绘制自由落体小球 一. 程序运行的软硬件环境 本次设计在window10系统下进行,运用C++进行编写,在CodeBlocks环境下使用OpenGL进行设计. 所需环境配置分为2部分 ...
- OpenGL绘制简单场景,实现旋转缩放平移和灯光效果
本项目实现了用OpenGL绘制一个简单场景,包括正方体.球体和网格,实现了物体的旋转.缩放.平移和灯光效果.附有项目完整代码.有具体凝视.适合刚開始学习的人熟悉opengl使用. 开发情况 开发环境V ...
- Opengl绘制我们的小屋(二)第一人称漫游
这章我们先讲第一人称漫游的实现.在openTK里,我们用函数Matrix4.LookAt(caram.Eye,caram.Target,Vector3.UnitY)来放置摄像机,其中三个参数分别与摄像 ...
- WPF绘制深度不同颜色的3D模型填充图和线框图
原文:WPF绘制深度不同颜色的3D模型填充图和线框图 在机械测量过程中,测量的数据需要进行软件处理.通常测量一个零件之后,需要重建零件的3D模型,便于观察测量结果是否与所测工件一致. 重建的3D模型需 ...
- [Modern OpenGL系列(三)]用OpenGL绘制一个三角形
本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/51347008 在上一篇文章中已经介绍了OpenGL窗口的创建.本文接着说如 ...
- opengl绘制正弦曲线
利用opengl绘制正弦曲线 ,见代码: #include <windows.h> //#include <GLUT/glut.h> #include <GL/glut. ...
- [OpenGL ES 03]3D变换:模型,视图,投影与Viewport
[OpenGL ES 03]3D变换:模型,视图,投影与Viewport 罗朝辉 (http://blog.csdn.net/kesalin) 本文遵循“署名-非商业用途-保持一致”创作公用协议 系列 ...
- OpenGl 绘制一个立方体
OpenGl 绘制一个立方体 为了绘制六个正方形,我们为每个正方形指定四个顶点,最终我们需要指定6*4=24个顶点.但是我们知道,一个立方体其实总共只有八个顶点,要指定24次,就意味着每个顶点其实重复 ...
- OPENGL绘制文字
OPENGL没有提供直接绘制文字的功能,需要借助于操作系统. 用OPENGL绘制文字比较常见的方法是利用显示列表.创建一系列显示列表,每个字符对应一个列表编号.例如,'A'对应列表编号1000+'A' ...
随机推荐
- 2021-2-16:请问你知道分布式设计模式中的Quorum思想么?
有效个数(Quorum) 有效个数(Quorum)这个设计模式一般是指分布式系统的每一次修改都要在大多数实例上通过来确定修改通过. 问题背景 在一个分布式存储系统中,用户请求会发到一个实例上.通常在一 ...
- vue中怎么动态生成form表单
form-create 是一个可以通过 JSON 生成具有动态渲染.数据收集.验证和提交功能的表单生成组件.支持3个UI框架,并且支持生成任何 Vue 组件.内置20种常用表单组件和自定义组件,再复杂 ...
- JavaScript code 性能优化
1 1 1 JavaScript 性能优化 prototype 闭包 Closure 内存泄漏 event system 1 定义类方法以下是低效的,因为每次构建baz.Bar的实例时,都会为foo创 ...
- Arctic Code Vault Contributor
Arctic Code Vault Contributor GitHub Archive Program https://archiveprogram.github.com/ Preserving o ...
- Github & DMCA Takedown & git remove history
Github & DMCA Takedown & git remove history Github & DMCA Takedown Policy Removing files ...
- js 获取包含emoji的字符串的长度
let emoji_exp = /(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ ...
- 为什么说NGK公链的商用落地是可行的?
互联网.大数据以及云计算的发展给人们的生活.工作带来了诸多便利,也让人们一次又一次感叹科技的进步.而NGK公链的诞生,更是让众人称之为传奇.其商用落地可行性,也让人惊叹.那么,为什么说NGK公链的商用 ...
- wxWidgets源码分析(3) - 消息映射表
目录 消息映射表 静态消息映射表 静态消息映射表处理过程 动态消息映射表 动态消息映射表处理过程 消息映射表 消息是GUI程序的核心,所有的操作行为均通过消息传递. 静态消息映射表 使用静态Event ...
- react入个门
起步 react 特点 不使用模板 不是一个mvc框架 响应式 轻量级的js库 原理 虚拟dom 将dom抽象成js对象 diff算法 搭建开发环境 react.js 核心文件 react-dom.j ...
- 从零搭建一个IdentityServer——单页应用身份验证
上一篇文章我们介绍了Asp.net core中身份验证的相关内容,并通过下图描述了身份验证及授权的流程: 注:改流程图进行过修改,第三方用户名密码登陆后并不是直接获得code/id_token/acc ...