1

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

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

我想要的结果是这样的:

  • 一个角色精灵被绘制在适当的位置(使用它的 alpha 颜色不仅仅是一个盒子)
  • 一个物品被绘制到玩家手中(也使用它的 alpha 颜色绘制到场景中而不是一个盒子)
  • 该项目应出现在角色手臂/手的后面,但在身体的其余部分上方。

目前我能弄清楚如何做到这一点的唯一方法是按顺序绘制它们(身体、物品、手臂),但我想避免这种情况,以使艺术资产更容易处理。我的想法解决方案是绘制角色,然后使用 alpha 蒙版绘制项目,该蒙版遮挡应该在手臂“下方”的纹理区域。

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

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

[更新:2012 年 8 月 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();

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

当代码在 stencil disabled的情况下运行时,我得到这个:没有 glEnable(GL_STENCIL_TEST);

当我使用 glEnable(GL_STENCIL_TEST) 时,我得到了这个:在此处输入图像描述

我尝试过各种选项,但我看不出我的模板缓冲区阻塞一切的明确原因。

[更新#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();
4

3 回答 3

1

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

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

Tim 在他的评论中非常清楚,但我想向您展示我认为最直观的解决方案。它是 3D 的,所以请稍等... ;)

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

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

于 2012-08-10T11:21:00.310 回答
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);

//...
于 2012-08-10T21:47:23.243 回答