掩盖部分纹理绘制与OpenGL在固定的管道

时间:2012-08-10 06:38:49

标签: c++ opengl sdl

我试图找出在绘制纹理时屏蔽部分的最佳方法。我的问题在于我似乎运行了我们的alpha蒙版!

我们使用openGL绘制自定义构建的2D游戏引擎。游戏是由精灵和简单的块纹理构成的。

我想要的结果是这样的:

  • 一个角色精灵被绘制到位(使用它的alpha颜色不仅仅是一个盒子)
  • 一个项目被绘制到玩家手中(也使用它的alpha颜色绘制到场景中而不是一个盒子)
  • 该项目应出现在角色手臂/手背后,但高于身体的其他部分。

目前我唯一可以弄清楚如何实现这一目标的方法是按顺序绘制它们(Body,Item,Arm),但我想避免这样做,以使艺术资产更容易处理。我的想法解决方案是绘制角色,然后使用alpha蒙版绘制项目,该蒙版阻挡纹理应该在手臂“下方”的区域。

我见过的其他解决方案就像this,其中使用了glBlendFuncSeparate()函数。我试图避免引入扩展,因为我当前版本的OpenGL不支持它。并不是说我反对这个想法,但是为了绘制一个alpha蒙版,它似乎有点像处理它?<​​/ p>

我完全承认这对我来说是一个学习过程,我用它作为真正了解OpenGL处理​​方式的借口。有关我应该去哪里正确绘制的建议吗?有没有办法让固定管道中的OpenGL采用纹理,在其上面应用alpha蒙版,然后将其绘制到缓冲区中?我应该让我的角色分成其模型的几个部分吗?

[更新:2012年12月12日]

试图添加Tim建议的代码,但我似乎遇到了问题。当我启用模板缓冲区时,一切都被阻挡了,而不仅仅是我想要的。这是我的测试示例代码。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

// Disable writing to any of the color fields
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); 

glStencilFunc(GL_ALWAYS, 0,0);

// Draw our blocking poly
glBegin(GL_POLYGON);                                   
    glVertex2f( 50,     50 );
    glVertex2f( 50,     50+128 );
    glVertex2f( 50+128, 50+128 );
glEnd();

glStencilFunc(GL_GREATER, 0, -1);

glEnable(GL_STENCIL_TEST);

// Re enable drawing of colors
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

// Enable use of textures
glEnable(GL_TEXTURE_2D);

// Bind desired texture for drawing
glBindTexture(GL_TEXTURE_2D,(&texture)[0]);

// Draw the box with colors
glBegin(GL_QUADS);                                   
    glTexCoord2d( 0, 0 );    glVertex2f( 50,     50 );
    glTexCoord2d( 0, 1 );    glVertex2f( 50,     50+128 );
    glTexCoord2d( 1, 1 );    glVertex2f( 50+128, 50+128 );
    glTexCoord2d( 1, 0 );    glVertex2f( 50+128, 50 );
glEnd();

// Swap buffers and display!
SDL_GL_SwapBuffers();

为了清楚起见,here也是我的初始化代码以设置此系统。

使用模板禁用运行代码时,我会得到:Without glEnable(GL_STENCIL_TEST);

当我使用glEnable(GL_STENCIL_TEST)时,我得到了这个:enter image description here

我尝试过使用各种选项,但我看不出我的模板缓冲区阻止所有内容的明确原因。

[更新#2 8/12/12]

我们得到了一些工作代码,谢谢蒂姆!这是我最终运行正常工作。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

// Disable writing to any of the color fields
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

glStencilOp(GL_INCR, GL_INCR, GL_INCR);

glEnable(GL_STENCIL_TEST);
// Draw our blocking poly
glBegin(GL_POLYGON);                                   
    glVertex2f( 50,     50 );
    glVertex2f( 50,     50+128 );
    glVertex2f( 50+128, 50+128 );
glEnd();

glStencilFunc(GL_EQUAL, 1, 1);

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 

// Re enable drawing of colors
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

// Enable use of textures
glEnable(GL_TEXTURE_2D);

// Bind desired texture for drawing
glBindTexture(GL_TEXTURE_2D,(&texture)[0]);

// Draw the box with colors
glBegin(GL_QUADS);                                   
    glTexCoord2d( 0, 0 );    glVertex2f( 50,     50 );
    glTexCoord2d( 0, 1 );    glVertex2f( 50,     50+128 );
    glTexCoord2d( 1, 1 );    glVertex2f( 50+128, 50+128 );
    glTexCoord2d( 1, 0 );    glVertex2f( 50+128, 50 );
glEnd();
glDisable(GL_STENCIL_TEST);

// Swap buffers and display!
SDL_GL_SwapBuffers();

3 个答案:

答案 0 :(得分:1)

蒂姆在评论中非常清楚,但我想向你展示我认为最直观的解决方案。它是3D,所以坚持......;)

基本上,您可以使用图像的Z坐标来创建虚拟“图层”。然后它并不重要,你绘制它们的顺序。只需单独测试每个图像,然后在正确的Z值上绘制它。如果仍然不够,你可以使用包含每个像素“深度”的单独纹理,然后使用第二个纹理进行某种深度测试。

如果您想使用此方法,请务必致电glEnable(GL_DEPTH_TEST);

答案 1 :(得分:1)

这是我对于你有一个纹理和一个alpha蒙版的情况的想法:

  1. 像往常一样将角色画到场景上。
  2. 锁定RGB颜色通道,使其无法使用glColorMask
  3. 进行更改
  4. 使用glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); glStencilFunc(GL_ALWAYS, 0,0);
  5. 设置模板缓冲区
  6. 在启用alpha测试的情况下绘制alpha蒙版。这将在alpha测试通过的任何地方增加模板缓冲区(您可能必须根据掩模极性翻转它)
  7. 此时,帧缓冲区中有一个字符纹理,模板缓冲区中有一个遮罩轮廓。
  8. 使用glColorMask
  9. 重新启用颜色通道
  10. 使用glStencilFunc(GL_GREATER, 0, -1);设置武器的模板缓冲区。这将仅绘制模板缓冲区大于零的武器纹素,并拒绝未更新模板的像素。
  11. 正常绘制武器纹理。

答案 2 :(得分:0)

正如我所看到的,问题在于你有一个纹理,但是它的一部分代表了手臂,而它的一部分代表了角色的其余部分。问题是你想要在角色上绘制武器,但是将手臂拉过两者。

这意味着,在绘制两个对象时,您希望将它们放入三个不同的“层”中。这基本上没有意义,所以你有点卡住了。

这是一个想法:使用片段程序(即着色器)。

我建议你重载角色的纹理alpha通道来编码透明度和图层。例如,让我们使用0 =透明体,64 =不透明体,128 =透明臂,255 =不透明臂。

从这里,您可以绘制对象,但有条件地将对象的深度设置为三层。基本上,你编写了一个片段程序,将你的角色绘制成两个不同的层,当手臂向前拉时,角色会被向后推。绘制武器时,它会在没有着色器的情况下绘制,但会根据角色的像素深度进行测试。它的工作原理是这样的(显然未经测试)。

定义着色器my_shader,其中包含一个片段程序:

uniform sampler2D character_texture;
void main(void) {
    vec4 sample = texture2D(character_texture,gl_TexCoord[0].st);

    int type; //Figure out what type of character texel we're looking at
    if      (fabs(sample.a-0.00)<0.01) type = 0; //transparent body
    else if (fabs(sample.a-0.25)<0.01) type = 1; //opaque body
    else if (fabs(sample.a-0.50)<0.01) type = 2; //transparent arm
    else if (fabs(sample.a-1.00)<0.01) type = 3; //opaque arm

    //Don't draw transparent pixels.
    if (type==0 || type==2) discard;

    gl_FragColor = vec4(sample.rgb,1.0);

    //Normally, you (can) write "gl_FragDepth = gl_FragCoord.z".  This
    //is how OpenGL will draw your weapon.  However, for the character,
    //we alter that so that the arm is closer and the body is farther.

    //Move body farther
    if      (type==1) gl_FragDepth = gl_FragCoord.z * 1.1;
    //Move arm closer
    else if (type==3) gl_FragDepth = gl_FragCoord.z * 0.9;
}

这是绘制函数的一些伪代码:

//...

//Algorithm to draw your character
glUseProgram(my_shader);
glBindTexture(GL_TEXTURE_2D,character.texture.texture_gl_id);
glUniform1i(glGetUniformLocation(my_shader,"character_texture"),1);
character.draw();
glUseProgram(0);

//Draw your weapon
glEnable(GL_DEPTH_TEST);
character.weapon.draw();
glDisable(GL_DEPTH_TEST);

//...