openGL兔兔大作业-面的鼠标拾取

好久没写过GL了,正好来发发教程

要求

给出兔兔的顶点坐标与三角面索引,需要实现:

  1. 绘制模型,冯氏光照
  2. 模型及视角移动
  3. 鼠标点选高亮某三角面

总体思路 & 坑点

For 可以自己实现OpenGL编写的朋友

  • 导入模型 :给出的数据为顶点坐标,没有法线信息,需要求解法线,在我的实现中对每一三角面根据三点坐标求解了法线,没有进行法线插值,这会使得兔兔表面不够圆润,并且需要 NumFace * 3 大小的VBO,较浪费空间。按道理可以对每一点求解法线,使用该点与邻接点的向量进行加权平均,具体可以参考链接 Weighted Vertex Normals

  • 光照模型:没什么特别的,Blinn-Phong或者Phong的Shader

  • 模型及视角移动:也没什么特别的,取下帧间鼠标Δ值和键盘按键变model view矩阵就行了

  • 点选高亮:这个还蛮有趣的,想了个办法,应该不是最优解,用一个drawcall绘制一张每个面颜色都是该面索引值 / 总面数的RT,然后readBack一下鼠标位置的颜色,拿到高亮面的顶点,这里我直接再加了一个drawcall画这三角,应该是多余了。

实现细节

For 不怎么熟悉OpenGL的朋友

配置OpenGL环境
  • 使用GLFW初始化窗口
GLFW is a lightweight utility library for use with OpenGL. GLFW stands for Graphics Library Framework. It provides programmers with the ability to create and manage windows and OpenGL contexts, as well as handle joystick, keyboard and mouse input.

一般使用GLFW作为跨平台的窗口工具,在本例中就作为创建OpenGL绘制窗口,处理鼠标键盘事件的API。

  • 使用GLAD链接OpenGL API

可以简单认为,虽然各个平台都支持OpenGL绘制,但一般都仅仅是提供了按OpenGL标准所实现的二进制链接库,如WIndows下默认静态链接库会有opengl32.lib,但仍然需要一个第三方库去在运行时加载这一dll,提供一个符合标准的c++头文件,并且将二进制库中的实现加载到对应的函数API上。

void Application::Run()
{
   
    //Initialize GLFW Window
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    window = glfwCreateWindow(800, 600, "Bunny", NULL, NULL);
    if (window == NULL)
    {
   
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
    }
    glfwMakeContextCurrent(window);
    //Initialize GLAD
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
   
        std::cout << "Failed to initialize GLAD" << std::endl;
    }
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    Init(); //Read Data From File & Initialize VAOs FBOs
    while (!glfwWindowShouldClose(window))
    {
   
        Render();   //Main Render Function
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glfwTerminate();
}
处理输入
  • 将所有点与索引输入,按面顺序排序,每个点有7个float的长度:
    • 位置:vec3
    • 法线:vec3
    • 面编号(归一化):float
void Application::Init() {
   
    //Read From File
    std::ifstream fin("bunny_iH.ply2");
    if (!fin) {
   
        assert(false);
    }
    numVertices = 0;
    fin >> numVertices;
    fin >> numFaces;
    vertices = new float[(size_t)numVertices * 3];
    sortedVertices = new float[(size_t)numFaces * 3 * 7];

    for (int i = 0; i < numVertices; i++) {
   
        fin >> vertices[i * 3] >> vertices[i * 3 + 1] >> vertices[i * 3 + 2];
    }
    for (int i = 0; i < numFaces; i++) {
   
        int a, b, c, d;
        fin >> a >> b >> c >> d;    // a always equals to 3

        int v1 = i * 3;
        int v2 = i * 3 + 1;
        int v3 = i * 3 + 2;

        // position : vec3      [v*7, v*7+2]
        // normal : vec3        [v*7+3, v*7+5]
        // faceIndex : float    [v*7+6, v*7+6]

        sortedVertices[v1 * 7] = vertices[b * 3];
        sortedVertices[v1 * 7 + 1] = vertices[b * 3 + 1];
        sortedVertices[v1 * 7 + 2] = vertices[b * 3 + 2];

        sortedVertices[v2 * 7] = vertices[c * 3];
        sortedVertices[v2 * 7 + 1] = vertices[c * 3 + 1];
        sortedVertices[v2 * 7 + 2] = vertices[c * 3 + 2];

        sortedVertices[v3 * 7] = vertices[d * 3];
        sortedVertices[v3 * 7 + 1] = vertices[d * 3 + 1];
        sortedVertices[v3 * 7 + 2] = vertices[d * 3 + 2];

        float* normal = new float[3];
        calcNormal(&sortedVertices[v1 * 7], &sortedVertices[v2 * 7], &sortedVertices[v3 * 7], normal);

        sortedVertices[v1 * 7 + 3] = normal[0];
        sortedVertices[v1 * 7 + 4] = normal[1];
        sortedVertices[v1 * 7 + 5] 
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值