图形学兔兔作业

好久没写过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初始化窗口

1
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上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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] = normal[2];
sortedVertices[v1 * 7 + 6] = (float)i / numFaces;

sortedVertices[v2 * 7 + 3] = normal[0];
sortedVertices[v2 * 7 + 4] = normal[1];
sortedVertices[v2 * 7 + 5] = normal[2];
sortedVertices[v2 * 7 + 6] = (float)i / numFaces;

sortedVertices[v3 * 7 + 3] = normal[0];
sortedVertices[v3 * 7 + 4] = normal[1];
sortedVertices[v3 * 7 + 5] = normal[2];
sortedVertices[v3 * 7 + 6] = (float)i / numFaces;
}
fin.close();

  • 求面法线:求两向量叉积,实现为化简过后的过程。

1
2
3
4
5
6
7
8
9
void calcNormal(float* v1, float* v2, float* v3, float* nor) {
float na = (v2[1] - v1[1]) * (v3[2] - v1[2]) - (v2[2] - v1[2]) * (v3[1] - v1[1]);
float nb = (v2[2] - v1[2]) * (v3[0] - v1[0]) - (v2[0] - v1[0]) * (v3[2] - v1[2]);
float nc = (v2[0] - v1[0]) * (v3[1] - v1[1]) - (v2[1] - v1[1]) * (v3[0] - v1[0]);

nor[0] = na;
nor[1] = nb;
nor[2] = nc;
}

初始化Shader

  • 本实现中使用了三个Shader,分别用于绘制面索引,光照模型,高亮三角

    1
    2
    3
    4
    5
    void Application::InitShader() {   
    mShaders["BlinnPhong"] = new Shader("BlinnPhong");
    mShaders["FaceIndex"] = new Shader("FaceIndex");
    mShaders["postprocess"] = new Shader("postprocess");
    }

  • 从文件读入Shader代码(这里有个bug,在文件尾会读入几个乱码字符,这里在shader最后手动加换行暂时删掉了乱码)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    Shader::Shader(const std::string& shaderName)
    {
    const std::string vertShaderName = shaderName + ".vert";
    const std::string fragShaderName = shaderName + ".frag";
    std::ifstream vertFile(vertShaderName);
    std::ifstream fragFile(fragShaderName);
    if (!vertFile || !fragFile) {
    assert(false);
    }
    vertFile.seekg(0, std::ios::end);
    int length = vertFile.tellg();
    GLchar* vertexShaderCode = new GLchar[length];
    vertFile.seekg(0, std::ios::beg);
    vertFile.read(vertexShaderCode, length);
    while (vertexShaderCode[length - 1] != '\n') { //TODO
    vertexShaderCode[length - 1] = 0;
    length--;
    }

    fragFile.seekg(0, std::ios::end);
    length = fragFile.tellg();
    GLchar* fragmentShaderCode = new GLchar[length];
    fragFile.seekg(0, std::ios::beg);
    fragFile.read(fragmentShaderCode, length);
    while (fragmentShaderCode[length - 1] != '\n') { //TODO
    fragmentShaderCode[length - 1] = 0;
    length--;
    }

  • 编译Shader代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
GLuint VertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(VertexShader, 1, &vertexShaderCode, NULL);
glCompileShader(VertexShader);

GLuint FragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(FragmentShader, 1, &fragmentShaderCode, NULL);
glCompileShader(FragmentShader);


mProgramID = glCreateProgram();
glAttachShader(mProgramID, VertexShader);
glAttachShader(mProgramID, FragmentShader);
glLinkProgram(mProgramID);
glUseProgram(mProgramID);

GLint success;
glGetProgramiv(mProgramID, GL_LINK_STATUS, &success);
if (!success) {
GLchar infoLog[1024];
glGetProgramInfoLog(mProgramID, 512, NULL, infoLog);
std::cout << infoLog << std::endl;
}

glDeleteShader(VertexShader);
glDeleteShader(FragmentShader);

  • 传入uniform 以及 绑定program

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Check location
#define CHECK_LOC(loc) \
if(loc == -1){ \
assert(false);\
}

void Shader::SetMat(const std::string& name, const glm::mat4& mat)
{
GLint loc = glGetUniformLocation(mProgramID, name.c_str());
CHECK_LOC(loc);
glUniformMatrix4fv(loc, 1, GL_FALSE, &mat[0][0]);
}

//setVec.......
//setFloat.....
//setInt.......

void Shader::Bind()
{
glUseProgram(mProgramID);
}

初始化顶点缓冲

  • 主顶点缓冲,包含所有顶点信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

InitShader();

glGenVertexArrays(1, &vao); //Vertex Array Buffer
glBindVertexArray(vao);

GLuint vbo;
glGenBuffers(1, &vbo); //Vertex Buffer Object
glBindBuffer(GL_ARRAY_BUFFER, vbo);
//Total size : Face Number * 3 * sizeof(Per Vertex)
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * numFaces * 3 * 7, sortedVertices, GL_STATIC_DRAW);

//Set Per Vertex Data Format
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)0); //position
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(3 * sizeof(float))); //normal
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(6 * sizeof(float))); //FaceIndex

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);

  • 高亮三角缓冲,仅三个点,使用Dynamic Draw声明该数据会实时变化,令GPU对数据存储进行优化

1
2
3
4
5
6
7
8
9
glGenVertexArrays(1, &CoveredVAO);
glBindVertexArray(CoveredVAO);

glGenBuffers(1, &CoveredVBO);
glBindBuffer(GL_ARRAY_BUFFER, CoveredVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 9, nullptr, GL_DYNAMIC_DRAW); // A hint for GPU to optimize VBO

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

初始化FrameBuffers

  • 一个FrameBuffer表示一组Render Target(可以理解为绘制到的一张窗口大小的图片)的集合,包含若干color attachment以及一个depth-stencil attachment,本例中为了绘制FaceIndex使用一个R32F的纹理作为RT绑定。

1
2
3
4
5
6
glGenFramebuffers(1, &FaceIndexFBO);							// generate frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, FaceIndexFBO); // bind frame buffer
glGenTextures(1, &FaceIndexTex); // generate the texture to storage FaceIndex
glBindTexture(GL_TEXTURE_2D, FaceIndexTex); // bind texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, 800, 600, 0, GL_RED, GL_FLOAT, NULL); // Set texture Format to R32F
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, FaceIndexTex, 0); // Bind as Color Attachment 0 of the FrameBuffer

主绘制流程

  • 处理鼠标输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void Application::HandleMouse() {
glfwGetCursorPos(window, &MouseX, &MouseY); // Use GLFW API to get current Mouse Postion
auto MouseDelta = glm::vec2(0, 0); // Delta Vector between last and current
if (LastMouseX >= 0 && LastMouseY >= 0) { // if last pos is legal (in the window)
MouseDelta = glm::vec2(MouseX - LastMouseX, MouseY - LastMouseY);
}
auto state = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); // Get current left button state
if (state == GLFW_PRESS) { // if left buttton is pressed
// rotate viewDir around vertical axis (Up)
viewDir = glm::mat3(glm::rotate(MouseDelta.x * 0.0015f, viewUp)) * viewDir;
// rotate viewDir around horizontal axis (ViewUp x ViewDir)
viewDir = glm::mat3(glm::rotate(-MouseDelta.y * 0.003f, glm::cross(viewUp, viewDir))) * viewDir;

LastMouseX = MouseX;
LastMouseY = MouseY;
}
else {
LastMouseX = -1.0f;
LastMouseX = -1.0f;
}
}

  • 绘制顶点索引

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    void Application::Render() {
    HandleMouse();
    glEnable(GL_CULL_FACE); // NOTICE : Must Enable Cull Face to ensure NOT rendering back face of the BUNNY !!!
    glEnable(GL_DEPTH_TEST);
    glBindVertexArray(vao);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glm::mat4 projection = glm::perspective((float)glm::radians(45.0), 800.0f / 600.0f, 0.1f, 100.0f);
    glm::mat4 model = glm::scale(glm::mat4(1.0f), glm::vec3(5.0f));
    glm::mat4 view = glm::lookAt(viewPos, viewPos + viewDir, viewUp);

    mShaders["FaceIndex"]->Bind();
    // set Uniforms
    mShaders["FaceIndex"]->SetMat("projection", projection);
    mShaders["FaceIndex"]->SetMat("model", model);
    mShaders["FaceIndex"]->SetMat("view", view);

    glBindFramebuffer(GL_FRAMEBUFFER, FaceIndexFBO);

    glDrawArrays(GL_TRIANGLES, 0, numFaces * 3);

    • 顶点索引 Shader 代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      //FaceIndex.vert

      #version 330 core
      layout(location = 0) in vec3 in_position;
      layout(location = 1) in vec3 in_normal;
      layout(location = 2) in float in_index;

      uniform mat4 model;
      uniform mat4 view;
      uniform mat4 projection;

      out vec3 thePosition;
      out vec3 theNormal;
      flat out float theIndex; // NOTICE: Index should NOT be interpolated

      void main() {
      vec4 v = vec4(in_position,1.0);
      gl_Position = projection * view * model * v;

      thePosition = vec3(model * v);
      theNormal = normalize(vec3(model * vec4(in_normal,0)));
      theIndex = in_index;
      }

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      //FaceIndex.frag

      #version 430

      out vec4 daColor;

      in vec3 thePosition;
      in vec3 theNormal;
      flat in float theIndex;

      void main()
      {
      daColor = vec4(theIndex,0,0,0);
      }

  • 回读鼠标位置的颜色(索引值)

    1
    2
    3
    4
    5
    6
    7
    float res[4];
    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);

    glReadPixels((GLint)MouseX, viewport[3] - MouseY, 1, 1, GL_RED, GL_FLOAT, &res);
    // y is flipped in OpenGL screen coordinate system
    // res[0] is the face index picked by mouse

  • 绘制冯氏光照模型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    mShaders["BlinnPhong"]->Bind();
    mShaders["BlinnPhong"]->SetMat("projection", projection);
    mShaders["BlinnPhong"]->SetMat("model", model);
    mShaders["BlinnPhong"]->SetMat("view", view);

    mShaders["BlinnPhong"]->SetVec("CameraPosition", glm::vec3(0, 0, -20.0f));
    mShaders["BlinnPhong"]->SetVec("LightPosition", glm::vec3(-50.0f, 10.0f, -20.0f));
    glBindFramebuffer(GL_FRAMEBUFFER, 0); // Bind the default framebuffer (render to screen)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glDrawArrays(GL_TRIANGLES, 0, numFaces * 3);

    • 冯氏光照Shader

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      //BlinnPhong.vert

      #version 330 core
      layout(location = 0) in vec3 in_position;
      layout(location = 1) in vec3 in_normal;
      layout(location = 2) in float in_index;

      uniform mat4 model;
      uniform mat4 view;
      uniform mat4 projection;

      out vec3 thePosition;
      out vec3 theNormal;

      void main() {
      vec4 v = vec4(in_position,1.0);
      gl_Position = projection * view * model * v;

      thePosition = vec3(model * v);
      theNormal = normalize(vec3(model * vec4(in_normal,0)));
      }

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      #version 430

      out vec4 daColor;

      in vec3 thePosition;
      in vec3 theNormal;

      uniform vec3 CameraPosition;
      uniform vec3 LightPosition;

      void main()
      {
      float finalBrightness = 0.0;

      float ambient = 0.05;
      //diiffuse
      vec3 lightDir = normalize(LightPosition - thePosition);
      float diff = max(dot(lightDir,theNormal),0.0);

      //specular
      vec3 viewDir = normalize(CameraPosition - thePosition);
      vec3 reflectDir = reflect(-lightDir,theNormal);
      float spec = 0.0;
      vec3 halfwayDir = normalize(lightDir + viewDir);
      spec = pow(max(dot(theNormal,halfwayDir),0.0),32.0);
      float specular = 0.3 * spec;
      finalBrightness = ambient + diff + specular;

      vec3 f = vec3(finalBrightness);
      vec3 test = min(f,vec3(1.0));
      daColor = vec4(test, 1.0);
      }

  • 绘制高亮三角形

    • 高亮面的编号为 res[0] * numFaces

      1
      DrawCoverVertices(res[0] * numFaces, projection, model, view);

    • 获取高亮面的三个点坐标

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      void Application::DrawCoverVertices(int index, glm::mat4& projection, glm::mat4& model, glm::mat4& view)
      {
      if (index < 0 || index >= numFaces) {
      return;
      }
      int a = index * 3 + 0;
      int b = index * 3 + 1;
      int c = index * 3 + 2;
      CoveredVertices[0] = sortedVertices[a * 7 + 0];
      CoveredVertices[1] = sortedVertices[a * 7 + 1];
      CoveredVertices[2] = sortedVertices[a * 7 + 2];

      CoveredVertices[3] = sortedVertices[b * 7 + 0];
      CoveredVertices[4] = sortedVertices[b * 7 + 1];
      CoveredVertices[5] = sortedVertices[b * 7 + 2];

      CoveredVertices[6] = sortedVertices[c * 7 + 0];
      CoveredVertices[7] = sortedVertices[c * 7 + 1];
      CoveredVertices[8] = sortedVertices[c * 7 + 2];

    • 绘制高亮面 (关闭depth test 从而与之前的有光照的兔兔叠加)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      glDisable(GL_DEPTH_TEST);	// NOTICE: Disable depth test to cover last result
      glDisable(GL_CULL_FACE);
      glBindVertexArray(CoveredVAO);
      glBufferSubData(GL_ARRAY_BUFFER, 0, 9 * sizeof(float), CoveredVertices);

      mShaders["postprocess"]->Bind();
      mShaders["postprocess"]->SetMat("projection", projection);
      mShaders["postprocess"]->SetMat("model", model);
      mShaders["postprocess"]->SetMat("view", view);
      glBindVertexArray(CoveredVAO);
      glDrawArrays(GL_TRIANGLES, 0, 9);

      • 绘制高亮面Shader

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        //postprocess.vert
        #version 330 core
        layout(location = 0) in vec3 in_position;

        uniform mat4 model;
        uniform mat4 view;
        uniform mat4 projection;

        void main() {
        vec4 v = vec4(in_position,1.0);
        gl_Position = projection * view * model * v;
        }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        //postprocess.frag
        #version 430

        out vec4 daColor;

        void main()
        {
        daColor = vec4(1.0,0,0,1.0);
        }

  • 加一个简单延迟,锁定在三十帧左右(也可以直接用glfw的垂直同步)

1
2
3
    DrawCoverVertices(res[0] * numFaces, projection, model, view);
Sleep(20);
}

  • 附上一个Application与Shader的声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Application {
public:
void Run();

private:
void InitShader();
void Init();
void Render();
void HandleMouse();
void DrawCoverVertices(int index, glm::mat4& projection, glm::mat4& model, glm::mat4& view);
private:
using ShaderLibrary = std::unordered_map<std::string,Shader*>;
ShaderLibrary mShaders;

float* vertices;
float* sortedVertices;
int numFaces;
int numVertices;
GLuint vao;

GLuint FaceIndexTex;
GLuint FaceIndexFBO;

double MouseX, MouseY;
double LastMouseX, LastMouseY;
GLFWwindow* window;

float CoveredVertices[9];
GLuint CoveredVAO;
GLuint CoveredVBO;

glm::vec3 viewDir = glm::vec3(0.0f, 0.0f, 20.0f);
glm::vec3 viewPos = glm::vec3(0.0f, 0.0f, -3.0f);
glm::vec3 viewUp = glm::vec3(0.0f, 1.0f, 0.0f);

float CoverVertices[9];
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Shader {
public:
Shader(const std::string& shaderName);

GLuint GetProgramID() const { return mProgramID; }
void SetMat(const std::string& name, const glm::mat4& mat);
void SetMat(const std::string& name, const glm::mat3& mat);
void SetVec(const std::string& name, const glm::vec3& vec);
void SetVec(const std::string& name, const glm::vec4& vec);
void SetInt(const std::string& name, int val);
void SetFloat(const std::string& name, float val);
void Bind();
private:
GLuint mProgramID;
};

实现效果

image-20201223022259881

image-20201223022325626

碎碎念

主要难点的地方还是实现拾取,这里用了三个drawcall显然应该是多余的,可优化的地方还很多

给别人讲图形学是真的非常难,概念冗杂不说,还一环套一环,必须全部搞懂才能实现一个非常简单的东西

Nothing-2021-2-9 Nothing-2020-9-22

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×