본문으로 바로가기

Solid 섹션 컷 구현

category Devlogs 2024. 3. 8. 11:26

OpenGL로 우리가 표현하는 솔리드 (tetra, hexa…) 들은 triangle의 조합으로 이루어져있음.
실제는 내부가 차있는 형상은 아니란 말임.
어쨌건 보통 랜더링 할때는 내부를 볼일이 없어 문제가 없지만 섹션컷을 수행하면..
안이 비어 보이게 되어 흉함..
보통은 그래서 섹션컷 수행후에 특별한 방법으로 해당 면을 덮어씌워줌.

플레인 앞쪽은 짤린 부분으로 오른쪽에 짤린 부분을 채워주는 효과를 묘사해주고 있음.


출처: https://gist.github.com/jtsiomb/43b5da0515d4786a80a861369d35097c

컴파일...

% gcc xsection.c -lopengl32 -lfreeglut -lglu32


코드...

/* Example of rendering cross-sections of closed non-self-intersecting geometry
 * with the stencil buffer. See draw_cross_section for details.
 *
 * Controls:
 *  - rotate object by dragging with the left mouse button
 *  - move cross-section plane back and forth by dragging up/down with the right
 *    mouse button
 *
 * Compile with: cc -o xsection xsection.c -lGL -lGLU -lglut
 *
 *                              - Nuclear / Mindlapse <nuclear@member.fsf.org>
 */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <GL/glut.h>
 
void disp(void);
void draw_regular(void);
void draw_cross_section(void);
void draw_object(void);
void reshape(int x, int y);
void keyb(unsigned char key, int x, int y);
void mouse(int bn, int st, int x, int y);
void motion(int x, int y);
 
int win_width, win_height;
float theta, phi, clipz;
 
int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitWindowSize(1024, 600);
    glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL | GLUT_DOUBLE);
    glutCreateWindow("GL cross-section hack");
 
    glutDisplayFunc(disp);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyb);
    glutMouseFunc(mouse);
    glutMotionFunc(motion);
 
    /* we're going to need backface culling for this */
    glEnable(GL_CULL_FACE);
 
    glutMainLoop();
    return 0;
}
 
void disp(void)
{
    glViewport(0, 0, win_width, win_height);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 
    /* ----- render the regular view on the left ----- */
    glViewport(0, 0, win_width / 2, win_height);
    /* set up perspective projection matrix */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, win_width * 0.5 / win_height, 0.5, 50.0);
 
    draw_regular();
 
    /* ----- render the cross-section on the right ----- */
    glViewport(win_width / 2, 0, win_width / 2, win_height);
    /* set up an orthographic projection matrix */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-2, 2, -2, 2, -10, 10);
 
    draw_cross_section();
 
    /* draw a separator, just to make it clear that we have two viewports */
    glViewport(0, 0, win_width, win_height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, win_width, 0, win_height, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
 
    glColor3f(0.7, 0.7, 0.7);
    glRectf(win_width / 2 - 2, 0, win_width / 2 + 2, win_height);
 
    glutSwapBuffers();
    assert(glGetError() == GL_NO_ERROR);
}
 
void draw_regular(void)
{
    int i;
 
    /* set up a view matrix */
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0, 0, -6);
    glRotatef(20, 1, 0, 0);
 
    glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_LINE_BIT);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
 
    draw_object();
 
    /* also draw a quad to visualize the cross-section plane */
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glDisable(GL_LIGHTING);
    glDepthMask(0);
    glLineWidth(2.0);
 
    for(i=0; i<2; i++) {
        glBegin(i ? GL_QUADS : GL_LINE_LOOP);
        glColor4f(1, 0, 0, i ? 0.25 : 0.5);
        glVertex3f(-1.8, -1.8, clipz);
        glVertex3f(1.8, -1.8, clipz);
        glVertex3f(1.8, 1.8, clipz);
        glVertex3f(-1.8, 1.8, clipz);
        glEnd();
    }
 
    glPopAttrib();
}
 
void draw_cross_section(void)
{
    double plane[] = {0, 0, -1, 0};
 
    /* just identity for the view matrix */
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
 
    glPushAttrib(GL_ENABLE_BIT);
    glEnable(GL_STENCIL_TEST);    /* enable stencil testing */
 
    /* initially we don't care about the stencil buffer, we always want to draw */
    glStencilFunc(GL_ALWAYS, 0, 0);
 
    /* set up a clip plane to cut everything from the plane Z to the user */
    plane[3] = clipz;
    glClipPlane(GL_CLIP_PLANE0, plane);
    glEnable(GL_CLIP_PLANE0);
 
    glColorMask(0, 0, 0, 1); /* disable color writing, we only want to affect the stencil buffer */
 
    /* render back faces and increment stencil where we draw */
    glStencilOp(GL_INCR, GL_INCR, GL_INCR);
    glCullFace(GL_FRONT);
    draw_object();
 
    /* render front faces and decrement stencil where we draw */
    glStencilOp(GL_DECR, GL_DECR, GL_DECR);
    glCullFace(GL_BACK);
    draw_object();
 
    /* now everywhere outside the cross-section the stencil is 0, and inside the
     * cross-section the stencil is 1
     */
 
    glColorMask(1, 1, 1, 1);    /* re-enable color writing */
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);    /* don't modify the stencil buffer further */
    glStencilFunc(GL_LEQUAL, 1, 0xffffffff); /* test passes only where stencil >= 1 */
 
    /* just draw a fullscreen quad, will be drawn only inside the cross-section
     * (stencil == 1) area
     */
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
 
    glDisable(GL_CLIP_PLANE0);    /* disable the clip plane, we don't want the quad to be clipped */
 
    glBegin(GL_QUADS);
    glColor3f(1, 0.3, 0.2);    /* color of the cross-section */
    glVertex2f(-1, -1);
    glVertex2f(1, -1);
    glVertex2f(1, 1);
    glVertex2f(-1, 1);
    glEnd();
 
    glPopAttrib();
}
 
void draw_object(void)
{
    glPushMatrix();
    glRotatef(phi, 1, 0, 0);
    glRotatef(theta, 0, 1, 0);
 
    glutSolidTorus(0.25, 1.0, 8, 28);
    glutSolidSphere(0.5, 16, 8);
 
    glPopMatrix();
}
 
void reshape(int x, int y)
{
    win_width = x;
    win_height = y;
}
 
void keyb(unsigned char key, int x, int y)
{
    switch(key) {
    case 27:
        exit(0);
    }
}
 
int bnstate[16];
int prev_x, prev_y;
 
void mouse(int bn, int st, int x, int y)
{
    int bidx = bn - GLUT_LEFT_BUTTON;
    bnstate[bidx] = st == GLUT_DOWN ? 1 : 0;
    prev_x = x;
    prev_y = y;
}
 
void motion(int x, int y)
{
    int dx = x - prev_x;
    int dy = y - prev_y;
    prev_x = x;
    prev_y = y;
 
    if(!dx && !dy) return;
 
    if(bnstate[0]) {
        theta += dx * 0.5;
        phi += dy * 0.5;
 
        if(phi < -90) phi = -90;
        if(phi > 90) phi = 90;
        glutPostRedisplay();
    }
 
    if(bnstate[2]) {
        clipz += dy * 0.01;
 
        if(clipz < -2) clipz = -2;
        if(clipz > 2) clipz = 2;
        glutPostRedisplay();
    }
}

Korea Tcl/Tk Community
블로그 이미지 ihmin 님의 블로그
VISITOR 오늘 / 전체