Создание 3D-модели с использованием Stencil-buffer
Во-первых, я хотел бы упомянуть, что я нашел то, что я считаю тем же самым вопросом, к сожалению, без ответа, здесь: Java Использование OpenGL Stencil для создания Outline
Я опубликую свой код ниже, но сначала возникла проблема: из этого снимка ** вы можете видеть, что вся структура кадра отображается вместо одной линии вокруг сферы. Я хотел бы избавиться от всех этих линий внутри!
** Очевидно, я не могу добавить картинки: посмотрите эту ссылку - представьте сферу со всеми краями квадратов, видимыми большими 3-пиксельными большими линиями.
http://srbwks36224-03.engin.umich.edu/kdi/images/gs_sphere_with_frame.jpg
Вот код, дающий этот результат:
// First render the sphere:
// inside "show" is all the code to display a textured sphere
// looking like earth
sphe->show();
// Now get ready for stencil buffer drawing pass:
// 1. Clear and initialize it
// 2. Activate stencil buffer
// 3. On the first rendering pass, we want to "SUCCEED ALWAYS"
// and write a "1" into the stencil buffer accordingly
// 4. We don't need to actually render the object, hence disabling RGB mask
glClearStencil(0); //Edit: swapped this line and below
glClear(GL_STENCIL_BUFFER_BIT);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NEVER, 0x1, 0x1); //Edit: GL_ALWAYS
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); //Edit: GL_KEEP, GL_KEEP, GL_REPLACE
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE); // As per Andon's comment
sphe->show();
// At this point, I expect to have "1" on the entire
// area covered by the sphere, so...
// 1. Stencil test should fail for anything, but 0 value
// RM: commented is another option that should work too I believe
// 2. The stencil op instruction at the point is somewhat irrelevant
// (if my understanding is correct), because we won't do anything
// else with the stencil buffer after that.
// 3. Re-enable RGB mask, because we want to draw this time
// 4. Switch to LINE drawing instead of FILL and
// 5. set a bigger line width, so it will exceed the model boundaries.
// We do want this, otherwise the line would not show
// 6. Don't mind the "uniform" setting instruction, this is so
// that my shader knows it should draw in plain color
// 7. Draw the sphere's frame
// 8. The principle, as I understand it is that all the lines should
// find themselves matched to a "1" in the stencil buffer and therefore
// be ignored for rendering. Only lines on the edges of the model should
// have half their width not failing the stencil test.
glStencilFunc(GL_EQUAL, 0x0, 0x1);
//glStencilFunc(GL_NOTEQUAL, 0x1, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
glLineWidth(3);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
psa::shader::setUniform("outlining", 1);
sphe->show();
psa::shader::setUniform("outlining", 0);
Теперь, чтобы доказать свою точку зрения, я устал делать что-то другое, используя буфер трафарета - я просто хотел убедиться, что в моем коде все есть, чтобы он работал.
** Опять же, к сожалению, я не могу показать снимок экрана с результатом, который я получаю: сцена такая
http://mathworld.wolfram.com/images/eps-gif/SphereSphereInterGraphic_700.gif
Но меньшая сфера невидима (маска RGB деактивирована), и сквозь отверстие можно видеть фон мира (вместо внутренней части большей сферы - выборка лица деактивирована).
И это код... Интересно, что я могу изменить многие вещи, такие как активировать / деактивировать STENCIL_TEST, изменить операцию на GL_KEEP везде, или даже изменить второй stencilFunc на "NOT EQUAL 0"... Результат всегда одинаков! Я думаю, что мне здесь чего-то не хватает.
void testStencil()
{
// 1. Write a 1 in the Stencil buffer for
// every pixels of the first sphere:
// All colors disabled, we don't need to see that sphere
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 0x1, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE); // Edit: added this
{
sphe->W = mat4::trans(psa::vec4(1.0, 1.0, 1.0)) * mat4::scale(0.9);
sphe->show();
}
// 2. Draw the second sphere with the following rule:
// fail the stencil test for every pixels with a 1.
// This means that any pixel from first sphere will
// not be draw as part of the second sphere.
glStencilFunc(GL_EQUAL, 0x0, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE); // Edit: added this
{
sphe->W = mat4::trans(psa::vec4(1.2, 1.2, 1.2)) * mat4::scale(1.1);
sphe->show();
}
}
И вуаля! Если бы кто-нибудь мог указать мне правильное направление, я был бы очень признателен. Я также обязательно отправлю ваш ответ на другой пост, который я нашел.
1 ответ
Код OpenGL, размещенный в этом вопросе, работает. Причина проблемы лежала в окне инициализации / создания:
Ниже приведены версии кода SDL1.2 и SDL2, которые работают. Обратите внимание, что в обоих случаях операторы SetAttribute помещаются перед созданием окна. Основная проблема заключается в том, что пропущенные операторы не обязательно потерпят неудачу во время выполнения, но также не будут работать.
SDL1.2:
if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
{
throw "Video initialization failed";
}
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,16);
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
const SDL_VideoInfo * i;
if((i = SDL_GetVideoInfo()) == NULL)
{
throw "Video query failed";
}
int flag = (fs ? SDL_OPENGL | SDL_FULLSCREEN : SDL_OPENGL);
if(SDL_SetVideoMode(w, h, i->vfmt->BitsPerPixel, flag) == 0)
{
throw "Video mode set failed";
}
glewExperimental = GL_TRUE;
if(glewInit() != GLEW_OK)
{
throw "Could not initialize GLEW";
}
if(!glewIsSupported("GL_VERSION_3_3"))
{
throw "OpenGL 3.3 not supported";
}
SDL2 (по сути тот же код, меняются только функции создания окна):
if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
{
throw "Video initialization failed";
}
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,16);
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
int flag = SDL_WINDOW_OPENGL;
if((win = SDL_CreateWindow("engine", 100, 100, w, h, flag)) == NULL)
{
throw "Create SDL Window failed";
}
context = SDL_GL_CreateContext(win);
glewExperimental = GL_TRUE;
if(glewInit() != GLEW_OK)
{
throw "Could not initialize GLEW";
}
if(!glewIsSupported("GL_VERSION_3_3"))
{
throw "OpenGL 3.3 not supported";
}
Несколько моментов, которые стоит упомянуть:
1. Даже если я понимаю, что это не рекомендуется, SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1) тоже работает
2. В SDL2 для меня SDL_GL_SetAttribute SDL_GL_MULTISAMPLESAMPLES увеличивается до 16, после того, как этот glewInit() завершается ошибкой, НО, переместите настройку мультисэмплинга после создания окна и внезапно перестанет жаловаться glewInit: я предполагаю, что это просто игнорируется.
3. В SDL1.2 любое значение мультисэмплинга "кажется" работает
4. Учитывая функцию буфера трафарета, тоже работает только приведенный ниже код, но я опубликую его по существу, чтобы поднять вопрос: сколько настроек атрибута действительно работает? А как узнать, так как код компилируется и запускается без явных проблем?
// PROBABLY WRONG:
// ----
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
const SDL_VideoInfo * i;
if((i = SDL_GetVideoInfo()) == NULL)
{
throw "Video query failed";
}
int flag = (fs ? SDL_OPENGL | SDL_FULLSCREEN : SDL_OPENGL);
if(SDL_SetVideoMode(w, h, i->vfmt->BitsPerPixel, flag) == 0)
{
throw "Video mode set failed";
}
// No idea if the below is actually applied!
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 16);
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);