如何在自定义GUI窗口中实现滚动时的绘制区域裁剪限制
用glScissor实现GUI滚动区域裁剪的实操方案
你要的滚动区域外元素自动截断的效果,用glScissor完全可以实现,核心是搞对坐标转换和状态保存恢复,步骤如下:
前置注意点
glScissor使用的是以窗口左下角为原点的像素坐标系,绝大多数自定义GUI框架默认用左上角为原点,Y轴方向是反的,这是90%的人用这个函数出问题的原因。
具体实现流程
- 第一步:计算裁剪区域的正确坐标
假设你的滚动容器在GUI坐标系(左上角为原点)下的参数是:左上角横坐标x、左上角纵坐标y、宽度w、高度h,当前OpenGL绑定的帧缓冲总高度是framebufferHeight,先转成glScissor需要的坐标格式:int scissorLeft = x; int scissorBottom = framebufferHeight - y - h; int scissorWidth = w; int scissorHeight = h; - 第二步:保存原有OpenGL状态,避免污染全局绘制
开裁剪之前一定要把之前的裁剪状态存下来,不然画完滚动区域之后其他UI也会被裁掉:// 存之前是否开了裁剪测试 GLboolean prevScissorState = glIsEnabled(GL_SCISSOR_TEST); // 存之前的裁剪框参数 GLint prevScissor[4]; glGetIntegerv(GL_SCISSOR_BOX, prevScissor); // 开启裁剪测试,设置当前滚动区域的裁剪范围 glEnable(GL_SCISSOR_TEST); glScissor(scissorLeft, scissorBottom, scissorWidth, scissorHeight); - 第三步:正常绘制滚动区域内的所有元素
这一步完全不需要改你原来的绘制逻辑,按滚动偏移算每个子元素的位置正常画就行,所有落在裁剪框外的片元会被OpenGL自动丢弃,不会显示到容器外面,刚好就是你要的截断效果。 - 第四步:绘制完成后恢复原有状态
// 把裁剪框改回之前的参数 glScissor(prevScissor[0], prevScissor[1], prevScissor[2], prevScissor[3]); // 如果之前没开裁剪测试,就把状态关回去 if (!prevScissorState) { glDisable(GL_SCISSOR_TEST); }
常见问题处理
- 嵌套滚动场景:每一层容器进入绘制时都按上面的逻辑存状态、设更小的裁剪框,当前层绘制完立刻恢复上一层的状态即可,不要直接全局重置裁剪。
- 窗口大小变化:帧缓冲尺寸改变时要重新计算所有裁剪区域的坐标,不要用缓存的旧值。
- 性能问题:scissor测试是GPU硬件实现的,性能开销极低,哪怕每帧给大量滚动区域设裁剪也不会有明显性能损耗。
内容的提问来源于stack exchange,提问作者Hesles
相关产品推荐
相关产品推荐

