OpenGL — контур объекта

Я пытаюсь реализовать функцию выделения контура. Это то, что я получаю до сих пор.

введите описание изображения здесь

Как вы можете видеть, объекты выбираются правильно, когда мышь наводится и вокруг выбранного объекта рисуется контур.

То, что я хотел бы сделать сейчас, это обрисовать видимые края объекта таким образом

введите описание изображения здесь

На изображении слева — это то, что я имею сейчас, а на правом изображении — то, чего я хочу достичь.

Это процедура, которую я использую сейчас.

void paintGL()
{
/* ... */

int w = geometry().width();
int h = geometry().height();

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);

glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilMask(0xFF);

setClearColor(Qt::GlobalColor::darkGray);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

glStencilMask(0x00);
DrawGeometry();

if (HoveredSphere != RgbFromColorToString(Qt::GlobalColor::black))
{
glBindFramebuffer(GL_FRAMEBUFFER, addFBO(FBOIndex::OUTLINE));
{
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

DrawOutline(HoveredSphere, 1.0f - 0.025f);
}
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());

glBindFramebuffer(GL_READ_FRAMEBUFFER, addFBO(FBOIndex::OUTLINE));
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, defaultFramebufferObject());
{
// copy stencil buffer
GLbitfield mask = GL_STENCIL_BUFFER_BIT;
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, mask, GL_NEAREST);

glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00);

glDepthFunc(GL_LEQUAL);
DrawOutline(HoveredSphere, 1.0f);
}
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
}

update();
}

куда DrawGeometry рисует все объекты, и DrawOutline рисует выделенный объект, масштабированный по коэффициенту, переданному в качестве второго параметра.

Спасибо за любые предложения.

4

Решение

Следуя советам @MichaelMahn, я нашел решение.

Прежде всего, я рисую силуэт видимых частей выделенного объекта в текстуре.

IMG

А затем я использую эту текстуру для вычисления контура, проверяя соседние пиксели, чтобы выяснить, стою ли я на краю силуэта.

введите описание изображения здесь

контурный фрагмент шейдера

#version 450

uniform sampler2D silhouette;

in FragData
{
smooth vec2 coords;
} frag;

out vec4 PixelColor;

void main()
{
// if the pixel is black (we are on the silhouette)
if (texture(silhouette, frag.coords).xyz == vec3(0.0f))
{
vec2 size = 1.0f / textureSize(silhouette, 0);

for (int i = -1; i <= +1; i++)
{
for (int j = -1; j <= +1; j++)
{
if (i == 0 && j == 0)
{
continue;
}

vec2 offset = vec2(i, j) * size;

// and if one of the neighboring pixels is white (we are on the border)
if (texture(silhouette, frag.coords + offset).xyz == vec3(1.0f))
{
PixelColor = vec4(vec3(1.0f), 1.0f);
return;
}
}
}
}

discard;
}

paintgl

void paintGL()
{
int w = geometry().width();
int h = geometry().height();

setClearColor(Qt::GlobalColor::darkGray);

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

DrawGeometry();

// if we hover a sphere
if (HoveredSphere != RgbFromColorToString(Qt::GlobalColor::black))
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, defaultFramebufferObject());
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, addFBO(FBOIndex::SILHOUETTE));
{
// copy depth buffer
GLbitfield mask = GL_DEPTH_BUFFER_BIT;
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, mask, GL_NEAREST);

// set clear color
setClearColor(Qt::GlobalColor::white);
// enable depth test
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
// clear color buffer
glClear(GL_COLOR_BUFFER_BIT);

// draw silhouette
DrawSilhouette(HoveredSphere);
}
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());

// clear depth buffer
glClear(GL_DEPTH_BUFFER_BIT);

// draw outline
DrawOutline();
}
}

ПРОБЛЕМА :: Теперь я хотел бы параметризовать ширину контура, толщина которого в настоящее время зафиксирована в 1 пикселе.

Большое спасибо за любое предложение!

6

Другие решения

Благодаря совету @Andrea я нашел следующее решение.

контурный фрагмент шейдера

#version 450

uniform sampler2D silhouette;

in FragData
{
smooth vec2 coords;
} frag;

out vec4 PixelColor;

void main()
{
// outline thickness
int w = 3;

// if the pixel is black (we are on the silhouette)
if (texture(silhouette, frag.coords).xyz == vec3(0.0f))
{
vec2 size = 1.0f / textureSize(silhouette, 0);

for (int i = -w; i <= +w; i++)
{
for (int j = -w; j <= +w; j++)
{
if (i == 0 && j == 0)
{
continue;
}

vec2 offset = vec2(i, j) * size;

// and if one of the pixel-neighbor is white (we are on the border)
if (texture(silhouette, frag.coords + offset).xyz == vec3(1.0f))
{
PixelColor = vec4(vec3(1.0f), 1.0f);
return;
}
}
}
}

discard;
}

Теперь у меня все еще есть небольшая проблема, когда выделенный объект находится на краю окна.

введите описание изображения здесь

Как видите, контур резко обрезан.

Я пытался «поиграть» с glTexParameter по параметрам GL_TEXTURE_WRAP_S а также GL_TEXTURE_WRAP_T,

На изображении выше вы можете увидеть эффект, который я получаю с

glTexParameters(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameters(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

в то время как на изображении ниже вы можете увидеть эффект, который я получаю с

glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &(QVector4D (1.0f, 1.0f, 1.0f, 1.0f)[0]));
glTexParameters(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameters(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

введите описание изображения здесь

Я бы хотел, чтобы контур отображался на краю окна, но только там, где это необходимо.

Большое спасибо!

2

По вопросам рекламы [email protected]