混合
默认情况下 OpenGL 不执行任何混合,它只需要你们渲染的东西,然后把它渲染成不透明的东西。
之前我们渲染了红色方块,在它上面我们以某种形式渲染了一个半透明的蓝色方块(不透明的蓝色方块会直接覆盖红色方块),我们期望得到这两种颜色的组合:紫色。如果我们现实生活中拿红玻璃和蓝玻璃然后把它们像这样叠在一起,我们会看到穿过的光实际上是紫色的,那就是我们用眼睛所看到的。
- 所以混合只决定了我们如何将输出颜色与目标缓冲区中已经存在的颜色结合起来
- 我们的输出颜色为片段着色器中输出的颜色,也被称为 source
- 在这个例子中我们把它画到一个已经存在的缓冲区上,也就是我们的目标 destination:红色方块
混合控制
那么我们如何控制这两种颜色混合的方式呢? 我们在 OpenGL 在有三种不同的方法,他们做的事情非常不同:
- 首先,启用和禁用 glEnable(GL_BLEND),gl_Disable
(
GL_BLEND),参数 GL_BLEND。默认情况下不会启用混合 - gl_BlendFuc(src,dest) 指定如何将两种颜色混合在一起(传参1,0 表示丢掉 dest 并用 src 覆盖),其中 RGB 因子将乘以所有颜色通道,所以 src 默认为 1 而 dest 默认为 0
- gl_BlendEquation(mode)混合不等式,参数 mode 为组合方式(默认为 GL_FUNC_ADD)
数学库
GLM 库
glm 是一个只包含头文件的库,这意味着没有 cpp 文件不需要编译它,所以我们不需要链接到库直接使用。
投影矩阵
投影矩阵是我们告诉窗口的一种方式,告诉它我们想要如何将所有不同的顶点映射到它。所以我们有一个填充了顶点位置顶点缓冲区这样的概念,但是我们需要把它转换到 2D 平面,因为当我们把它绘制在笔记本电脑屏幕或电脑显示器上时需要以 2D 的方式绘制出来。
那么想象一个 3D 世界,我们有一个 3D 世界的数学表示,但我们需要把它绘制在 2D 表面上。数学上我们如何从 3D 几何图形到 2D 平面图形的?那就是投影矩阵的用处。
投影矩阵的实际变换就是把所有那些位置转换成所谓的标准坐标,也就是某种标准化空间,然后映射到我们的窗口。标准空间意味着在每个 xyz 轴上都有一个 -1 和 1 之间的坐标系统,所以我想说的是一个窗口的图像分辨率在点上无关紧要。
MVP 是我们的模型 (Model) 视图 (View) 投影 (Projection) 矩阵
接着在顶点着色器用这个矩阵乘以我的顶点位置,每个顶点运行一次基于我们提供的正交矩阵将它移到合适的空间。
void main()
{gl_Position = u_MVP * position;v_TexCoord = texCoord;
}
关于模型视图投影矩阵
在我们的着色器代码中或者在 C++ 的 glm 代码中,它可以是 pvm:投影 x 视图 x 模型。而像 Direct3D 和 DirectX 因为它们确实处理的矩阵是行主序的,那么就是 mvp:模型 x 视图 x 投影。
视图矩阵
OpenGL 中没有相机这个东西,我们能做的就是移动几何体和顶点。所以为了模拟相机向左移动,我们需要做的是将所有东西向右移动,可以算一种逆向运算。
glm::mat4 proj = glm::ortho(0.0f, 960.0f, 0.0f, 540.0f, -1.0f, 1.0f);glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(-100, 0, 0));glm::mat4 model = glm::translate(glm::mat4(1.0f), glm::vec3(200, 200, 0));//glm::translate(view, glm::vec3(-100, 0, 0));glm::mat4 mvp = proj * view;mvp = mvp * model;
像这样,图形先左移后 右上移
关于 ImGui
今天我们会整合一些 ImGui 的东西,它本质上是一个我们可以在 OpenGL、DirectX 以及任何渲染接口中使用的 GUI 图形用户接口库。 它的大多数接口是独立的,这是我们在屏幕上绘制 UI 界面的一种方式。
/* Setup Dear ImGui context */const char* glsl_version = "#version 130";IMGUI_CHECKVERSION();ImGui::CreateContext();ImGui_ImplGlfw_InitForOpenGL(window, true);ImGui_ImplOpenGL3_Init(glsl_version);ImGui::StyleColorsDark();glm::vec3 translation(200, 200, 0);float r = 0.0f;float increment = 0.05f;/* Loop until the user closes the window */while (!glfwWindowShouldClose(window)){render.Clear();// Start the Dear ImGui frameImGui_ImplOpenGL3_NewFrame();ImGui_ImplGlfw_NewFrame();ImGui::NewFrame();glm::mat4 model = glm::translate(glm::mat4(1.0f), translation);glm::mat4 mvp = proj * view * model;shader.SetUniformMat4f("u_MVP", mvp);render.Draw(va, ib, shader);if (r > 1.0f) {increment = -0.0001f;}else if (r <= 0.0f) {increment = 0.0001f;}r += increment;{static float f = 0.0f;ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.ImGui::SliderFloat3("translation", &translation.x, 0.0f, 960.0f); // Edit 1 float using a slider from 0.0f to 1.0fImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);ImGui::End();}ImGui::Render();ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());/* Swap front and back buffers */glfwSwapBuffers(window);/* Poll for and process events */glfwPollEvents();}
}ImGui_ImplOpenGL3_Shutdown();ImGui_ImplGlfw_Shutdown();ImGui::DestroyContext();glfwTerminate();return 0;
}
可以实现鼠标控制图片位移,利用 model 矩阵
批量渲染对象
两种方式:一种是再弄一块 vertex buffer ,一种是再传入其他的 MVP 矩阵(instance实例化技术?)
这里选择第二种方式,代码如下:
绘制代码:
{glm::mat4 model = glm::translate(glm::mat4(1.0f), translationA);glm::mat4 mvp = proj * view * model;shader.Bind();shader.SetUniformMat4f("u_MVP", mvp);renderer.Draw(va, ib, shader);
}{glm::mat4 model = glm::translate(glm::mat4(1.0f), translationB);glm::mat4 mvp = proj * view * model;shader.Bind();shader.SetUniformMat4f("u_MVP", mvp);renderer.Draw(va, ib, shader);
}ImGui的代码:
{ImGui::SliderFloat3("Translation A", &translationA.x, 0.0f, 960.0f); ImGui::SliderFloat3("Translation B", &translationB.x, 0.0f, 960.0f); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
}
调用了两次 drawcall,费性能。真正要用在项目里头应该会弄个材质类出来。
设置一个测试框架
参考博文:Cherno OpenGL 教程 | Fl0w3r
视频地址:S17.关于纹理_哔哩哔哩_bilibili