文中若有代码、术语等错误,欢迎指正
前言
-
此节目的
- 上一节已经显示了帧缓冲第二个颜色纹理缓冲的颜色
- 这节需要把帧缓冲第二个缓冲区改变类型,为有符号整形对应实体ID。
- 并且需要增加获取当前鼠标在viewport视口的相对位置,然后读取鼠标位置像素的**帧缓冲中第二个缓冲区(渲染目标)**的数据。
-
如何实现
-
修改帧缓冲第二个缓冲区类型
利用上一节已经封装好了的OpenGlframebuffer,只需简单的设置为GL_RED_INTEGER
-
鼠标在viewport视口的相对位置
鼠标的绝对位置是:当前位置距离整个屏幕**左上角(0,0)**的位置
鼠标的相对位置是:ImGui的API,得到viewport视口左上角的绝对位置,再鼠标绝对位置减去viewport窗口的左上角绝对位置即可
-
读取缓冲区数据
用OpenGL的API,glReadBuffer、glReadPixels
-
-
注意细节
由于ImGui的viewport视口左上角为00,而OpenGL的左下角才是00,所以读取缓冲区数据时候需要翻转y(用视口高度**-** 鼠标y相对位置即可)。
完善代码+代码流程
完善代码:修改帧缓冲类
// 1.1纹理附加到帧缓冲
switch (m_ColorAttachmentSpecifications[i].TextureFormat)
{
case FramebufferTextureFormat::RGBA8:
Utils::AttachColorTextures(m_ColorAttachments[i], m_Specification.Samples, GL_RGBA8, GL_RGBA, m_Specification.Width, m_Specification.Height, i);
break;
// 添加整形缓冲区附件
case FramebufferTextureFormat::RED_INTEGER:
Utils::AttachColorTextures(m_ColorAttachments[i], m_Specification.Samples, GL_R32I, GL_RED_INTEGER, m_Specification.Width, m_Specification.Height, i);
break;
}
代码流程:获取鼠标在viewport视口的相对位置
-
先得到viewport视口左上角的绝对位置
// 1.先获取Viewport视口左上角与viewport视口标题栏距离的偏移位置(0,24)- 必须放这,因为标题栏后就是视口的左上角 auto viewportOffset = ImGui::GetCursorPos(); // 2.获取vieport视口大小 - 包含标题栏的高 auto windowSize = ImGui::GetWindowSize(); // 3.获取当前vieport视口标题栏左上角距离当前整个屏幕左上角(0,0)的位置 ImVec2 minBound = ImGui::GetWindowPos(); // 4.计算viewport视口的左上角距离当前整个屏幕左上角(0,0)的位置 minBound.x += viewportOffset.x; minBound.y += viewportOffset.y; // 5. 计算viewport视口的右下角距离当前整个屏幕左上角(0,0)的位置 ImVec2 maxBound = { minBound.x + windowSize.x, minBound.y + windowSize.y - viewportOffset.y }; // 6. 保存左上角和右下角距离整个屏幕左上角的位置 m_ViewportBounds[0] = { minBound.x, minBound.y }; m_ViewportBounds[1] = { maxBound.x, maxBound.y };
图画解释计算viewport视口的绝对位置(相对于整个屏幕左上角(0,0))
-
鼠标绝对位置减去viewport窗口的左上角绝对位置
// 1.获取当前鼠标距离整个屏幕左上角(0,0)的位置 auto [mx, my] = ImGui::GetMousePos(); // 2.鼠标绝对位置减去viewport窗口的左上角绝对位置=鼠标相对于viewport窗口左上角的位置 mx -= m_ViewportBounds[0].x; my -= m_ViewportBounds[0].y; // 3.viewport窗口的右下角绝对位置-左上角的绝对位置=viewport窗口的大小 glm::vec2 viewportSize = m_ViewportBounds[1] - m_ViewportBounds[0]; // 翻转y,使其左下角开始才是(0,0) my = viewportSize.y - my; int mouseX = (int)mx; int mouseY = (int)my; if (mouseX >= 0 && mouseY >= 0 && mouseX < (int)viewportSize.x && mouseY < (int)viewportSize.y) { //HZ_CORE_WARN("Mouse xy = {0} {1}", mouseX, mouseY); // 4.读取帧缓冲第二个缓冲区的数据 int pixelData = m_Framebuffer->ReadPixel(1, mouseX, mouseY); HZ_CORE_WARN("Pixel data = {0}", pixelData); }
-
读取鼠标位置像素的**帧缓冲中第二个缓冲区(渲染目标)**的数据
int OpenGLFramebuffer::ReadPixel(uint32_t attachmentIndex, int x, int y) { HZ_CORE_ASSERT(attachmentIndex < m_ColorAttachments.size()); // 关键函数glReadBuffer+glReadPixels glReadBuffer(GL_COLOR_ATTACHMENT0 + attachmentIndex);// 读取第二个缓冲区 int pixelData = -1; glReadPixels(x, y, 1, 1, GL_RED_INTEGER, GL_INT, &pixelData);// 读取第二个缓冲区中的xy位置的缓冲区值 return pixelData; }
-
第二个缓冲区(渲染目标)的数据在Glsl的片段着色器中设置
#type fragment #version 330 core layout(location = 0) out vec4 color; layout(location = 1) out int color2;// 第二个渲染目标(有符号整形) in vec4 v_Color; in vec2 v_TexCoord; in float v_TexIndex; in float v_TilingFactor; uniform sampler2D u_Textures[32]; void main() { color = texture(u_Textures[int(v_TexIndex)], v_TexCoord * v_TilingFactor) * v_Color; // 新 color2 = 50;// 第二个渲染目标(有符号整形的值)设为50 }
效果
-
将鼠标放在绘画的图像上
读取的值为50,是正确的
-
但是如果将鼠标放在黑色的区域上
读取的值为1036831949,是奇怪的
因为在Cpp代码中使用了glClearColor(color.r, color.g, color.b, color.a);将缓冲区默认填上了颜色,这个颜色本来是float值,转换为int读取出来则是奇怪的数字