OpenGL获取多个重叠对象的轮廓


10

我只是对使用c ++的opengl进行的游戏有一个想法:当玩家赢得某些东西时,我希望在多个重叠的物体上有一个大轮廓(5-6像素)。

我以为最好的方法是使用模板缓冲区,但是尝试在屏幕外渲染模板缓冲区需要几个小时,而且我无法获得任何结果。还有其他一些技巧!

这就是我想要得到的:

在此处输入图片说明

有任何想法吗?


使用边缘检测滤镜,用粗线填充边缘,然后提取形状的渲染图像并覆盖在彩线层的顶部?
Shotgun Ninja

边缘检测滤波器意味着什么?着色器?图像处理滤镜?像opencv(渲染到纹理,对纹理应用滤镜,推回修改后的纹理)?
nkint

我不知道; 首先,我不太精通3d渲染。
Shotgun Ninja

你有这样的模板缓冲区的例子吗?我认为使用模板缓冲区将是一种更清洁的方法,但是我无法使任何模板缓冲区工作
nkint

Answers:


4
  1. 启用并清除模板缓冲区。
  2. 绘制对象,设置模板缓冲区。对象可以是半透明的等。
  3. 现在将模具模式设置为仅写入未设置模具的像素。
  4. 然后以所需的边框颜色且没有纹理的方式再次略微放大每个对象。
  5. 禁用模板缓冲区。

这是从我正在使用的一些webGL模具代码改编而成的代码:

// drawing will set stencil stencil
    gl.enable(gl.STENCIL_TEST);
    gl.stencilFunc(gl.ALWAYS,1,1);
    gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE);
    gl.stencilMask(1);
    gl.clearStencil(0);
    gl.clear(gl.STENCIL_BUFFER_BIT);
// draw objects
for(var object in objects)
  objects[object].draw();
// set stencil mode to only draw those not previous drawn
    gl.stencilFunc(gl.EQUAL,0,1);
    gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP);
    gl.stencilMask(0x00);
// draw object halo
for(var object in objects)
  objects[object].drawHalo(1.1,red); // each mesh should be individually scaled e.g. by 1.1
// done
    gl.disable(gl.STENCIL_TEST);

我想我已经在RTS游戏中使用了这种方法来在选定的单位周围绘制光晕,但是那是很久以前的事了,我不记得是否有任何陷阱和所有细微差别。


你有这样的模板缓冲区的例子吗?我认为使用模板缓冲区将是更干净的方法,但是我无法使任何模板缓冲区起作用
nkint

7
注意,稍微放大比例的渲染对象将不会导致一致的线宽。较远的边缘会更细。如果在缩放对象时考虑到这一点,则延伸到该距离的长对象的厚度将不均匀。这种效果还有一点要使线条均匀。
肖恩·米德迪奇

2
比简单地按比例放大对象更好的方法是编写一个顶点着色器,该顶点着色器使每个顶点沿其法线偏移一小段距离。这对于光滑的对象非常有效,但是会在硬边缘上产生裂纹。您可以尝试使用另一组法线来构建网格,这些法线在任何地方都可以平滑化,然后查看可以将您带到何处。
内森·里德

2

首先找到所有对象组,其中一组对象是重叠的对象的集合。标准碰撞检测应能胜任。为每个组分配唯一的颜色。任何颜色都可以。

使用组色将所有对象渲染为纯色,以形成纹理。

创建一个具有与渲染目标相同尺寸的新轮廓纹理。扫描渲染目标的每个纹理像素,并确定其颜色是否与周围的任何纹理像素不同。如果是这样,请将轮廓纹理中的相应纹理像素更改为所需的线条颜色。

最后,获取轮廓纹理并将其渲染到要在屏幕上绘制的图像上方(您当然可以在片段着色器中进行边缘检测的同时进行此操作,避免在第一个着色器中创建边缘纹理地点)。

如果您通过使用for循环在渲染目标上执行texel在cpu上执行此步骤,那么这将非常慢,但可能足以测试甚至在某些情况下使用。要实时使用它,最好在着色器中处理它。

用于执行边缘检测的片段着色器可能如下所示:

precision mediump float;

uniform sampler2D s_texture;

varying vec2 v_texCoord;

void main()
{
    gl_FragColor = vec4(0.0);

    vec4 baseColor = texture2D(s_texture, v_texCoord);
    gl_FragColor += baseColor - texture2D(s_texture, top);
    gl_FragColor += baseColor - texture2D(s_texture, topRight);
    gl_FragColor += baseColor - texture2D(s_texture, right);
    gl_FragColor += baseColor - texture2D(s_texture, bottomRight);
    gl_FragColor += baseColor - texture2D(s_texture, bottom);
    gl_FragColor += baseColor - texture2D(s_texture, bottomLeft);
    gl_FragColor += baseColor - texture2D(s_texture, left);
    gl_FragColor += baseColor - texture2D(s_texture, topLeft);
}

在texture2D中查找的第二个值是相对于v_texCoord的2d坐标。您可以通过在全屏四边形上将第一个渲染目标渲染为纹理来应用此效果。这类似于您应用全屏模糊效果(如高斯模糊)的方式。

将第一个渲染目标使用纯色的原因只是为了确保重叠的不同对象之间没有可察觉的边缘。如果仅在屏幕图像上执行边缘检测,则可能会发现它也检测重叠处的边缘(假设对象具有不同的颜色/纹理/照明)。


2
抱歉,但是“扫描每个纹理像素”是什么意思?一个for循环,虽然每个像素?在CPU?就像是:用纯色渲染到纹理,将图像传输到cpu,进行扫描,然后将它们再次放入纹理中?还是在着色器中做?
nkint 2013年

最好通过使用渲染目标作为纹理渲染全屏四边形,在着色器中进行此操作,类似于执行后期处理模糊效果的方法,但是您可以首先使用for循环使其在cpu上工作,以了解如果效果足够好。
OriginalDaemon
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.