OpenGL 模板测试

工程场景:安卓系统下的surfaceview的绘制实际是在Window上挖出一块透明区域,然后先绘制surfaceview再把宿主窗口覆盖上去形成的,因此在接手surfaceFlinger的窗口合成上屏工作后要完成surfaceview和挖好孔的宿主窗口的合并渲染,实际宿主窗口在surfaceFlinger进程之前已经完成了透明区域的挖孔处理,直接合成即可,本文介绍假如宿主窗口在surfaceFlinger之前未做好挖孔时,使用模板测试来处理挖孔并做最终合成渲染的一种方法,主要还是巩固下模板测试的使用

1. 准备一个挖孔的program

Vertex Shader

#version 300 es
layout(std140, column_major) uniform;
layout(location =0) in vec2 aPosition;
uniform mat4 aMVPMatrix;
void main()
{
    gl_Position =aMVPMatrix * vec4(aPosition, 0.0, 1.0);
}

Frag Shader

#version 300 es
precision highp float;
layout(std140, column_major) uniform;
out vec4 FragColor;
void main()
{
    FragColor = vec4(0.0,0.0,0.0,0.0);
}

上面的program渲染时开启blend,混合一个透明区域,实际对渲染画面无影响,主要用来修改模板值

2. 顶点和uv
mVerticesCoords->value = {
        -1.0f, -1.0f,
        1.0f, -1.0f,
        -1.0f,  1.0f,
        1.0f,  1.0f,
};
mTextureCoords = std::make_shared<GLInputValue>();
mTextureCoords->value = {
        0.0f,  0.0f,
        1.0f,  0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
};

顶点范围可设置为整个viewport大小,即帧缓存大小,然后通过挖孔区域的相对宿主窗口的区域计算缩放平移矩阵:

3.挖孔区域的矩阵计算
static mat4f getMatrix(struct PanelBound baseBound,struct PanelBound subBound){
    // LOGD("baseBound [left:%d,top:%d,right:%d,bottom:%d],subBound [left:%d,top:%d,right:%d,bottom:%d]",
    //     baseBound.left,baseBound.top,baseBound.right,baseBound.bottom,
    //     subBound.left,subBound.top,subBound.right,subBound.bottom);
    int32_t base_width=baseBound.right-baseBound.left;
    int32_t base_height=baseBound.bottom-baseBound.top;
    int32_t sub_width=subBound.right-subBound.left;
    int32_t sub_height=subBound.bottom-subBound.top;
    float2 sub_centre=float2(((subBound.right+subBound.left)/2.0-baseBound.left)/base_width
            ,((subBound.bottom+subBound.top)/2.0-baseBound.top)/base_height);
    sub_centre.x = clamp(sub_centre.x * 2.0 - 1.0, -1.0, 1.0);
    sub_centre.y = clamp((sub_centre.y) * 2.0 - 1.0, -1.0, 1.0);
    return mat4f::translation(float3(sub_centre,0))
        *mat4f::scaling(float3((float)sub_width/base_width,(float)sub_height/base_height,1));
}

baseBound为宿主窗口的rect,subBound为子窗口的rect,对应Android系统中窗口的绝对位置rect:bound

4.模板测试

清空帧缓存,模板缓存

 glClearColor(0.0f,0.0f,0.0f,0.0f);
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 

先渲染所有子窗口到一个空的帧缓存,这部分不在讨论范围,跳过啦

glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
            
write_image->active();
glClearColor(0.0f,0.0f,0.0f,0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 
mProgram->enable();
std::vector<PanelBound> bounds;
for(auto p: children){
	auto subBound=p->getBound();
	renderPanel(p,baseBound);
}
mProgram->disable();

接下来使用步骤1中的program来修改模板缓存中的模板值,将挖孔区域在模板缓存中设置为1

//
mProgramDrawRects->enable();
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilMask(0xFF);
drawEmptyRects(bounds,baseBound);
mProgramDrawRects->disable();

至此已经将模板缓存中要挖孔的区域的模板值设置为了1,未挖孔区域模板值为0
接下来渲染宿主窗口,并开启模板测试,将模板值为1的片段丢弃调,即完成在宿主窗口的挖孔逻辑
在这里插入图片描述

模板函数:

和深度测试一样,我们对模板缓冲应该通过还是失败,以及它应该如何影响模板缓冲,也是有一定控制的。一共有两个函数能够用来配置模板测试:glStencilFunc和glStencilOp。

glStencilFunc(GLenum func, GLint ref, GLuint mask)一共包含三个参数:

func:设置模板测试函数(Stencil Test Function)。这个测试函数将会应用到已储存的模板值上和glStencilFunc函数的ref值上。可用的选项有:GL_NEVER、GL_LESS、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、GL_NOTEQUAL和GL_ALWAYS。它们的语义和深度缓冲的函数类似。
ref:设置了模板测试的参考值(Reference Value)。模板缓冲的内容将会与这个值进行比较。
mask:设置一个掩码,它将会与参考值和储存的模板值在测试比较它们之前进行与(AND)运算。初始情况下所有位都为1。
在一开始的那个简单的模板例子中,函数被设置为:

glStencilFunc(GL_EQUAL, 1, 0xFF)
这会告诉OpenGL,只要一个片段的模板值等于(GL_EQUAL)参考值1,片段将会通过测试并被绘制,否则会被丢弃。

但是glStencilFunc仅仅描述了OpenGL应该对模板缓冲内容做什么,而不是我们应该如何更新缓冲。这就需要glStencilOp这个函数了。

glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)一共包含三个选项,我们能够设定每个选项应该采取的行为:

sfail:模板测试失败时采取的行为。
dpfail:模板测试通过,但深度测试失败时采取的行为。
dppass:模板测试和深度测试都通过时采取的行为。
每个选项都可以选用以下的其中一种行为:

行为 描述
GL_KEEP 保持当前储存的模板值
GL_ZERO 将模板值设置为0
GL_REPLACE 将模板值设置为glStencilFunc函数设置的ref值
GL_INCR 如果模板值小于最大值则将模板值加1
GL_INCR_WRAP 与GL_INCR一样,但如果模板值超过了最大值则归零
GL_DECR 如果模板值大于最小值则将模板值减1
GL_DECR_WRAP 与GL_DECR一样,但如果模板值小于0则将其设置为最大值
GL_INVERT 按位翻转当前的模板缓冲值
默认情况下glStencilOp是设置为(GL_KEEP, GL_KEEP, GL_KEEP)的,所以不论任何测试的结果是如何,模板缓冲都会保留它的值。默认的行为不会更新模板缓冲,所以如果你想写入模板缓冲的话,你需要至少对其中一个选项设置不同的值。

所以,通过使用glStencilFunc和glStencilOp,我们可以精确地指定更新模板缓冲的时机与行为了,我们也可以指定什么时候该让模板缓冲通过,即什么时候片段需要被丢弃。

和深度测试的glDepthMask函数一样,模板缓冲也有一个类似的函数。glStencilMask允许我们设置一个位掩码(Bitmask),它会与将要写入缓冲的模板值进行与(AND)运算。默认情况下设置的位掩码所有位都为1,不影响输出,但如果我们将它设置为0x00,写入缓冲的所有模板值最后都会变成0.这与深度测试中的glDepthMask(GL_FALSE)是等价的。

glStencilMask(0xFF); // 每一位写入模板缓冲时都保持原样
glStencilMask(0x00); // 每一位在写入模板缓冲时都会变成0(禁用写入)
大部分情况下你都只会使用0x00或者0xFF作为模板掩码(Stencil Mask),但是知道有选项可以设置自定义的位掩码总是好的。

详细可参考:链接

mProgram->enable();
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
renderPanel(panel,baseBound);

glDisable(GL_BLEND);
glDisable(GL_STENCIL_TEST);
mProgram->disable();
write_image->inactive();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值