OpenGL---二维光线追踪

来源:互联网 发布:淘宝上买处方药流程 编辑:程序博客网 时间:2024/05/09 06:30

代码、原理:计算机图形学(OpenGL)第三版 第4章
我在其中加上了注释

这里写图片描述

流程

需要用户从文件中读取多边形数据

1、由用户画出第一条射线,需调用函数raytrace2d_mouse,raytrace2d_motion。

2、函数raytrace2d_keyboard中,当按下空格键时,针对每个多边形调用函数timeToHitPoly,计算射线击中该多边形的时间,取最小的那个时间,根据该时间得到射线的可见部分,放入射线列表rays中。接着计算下一条射线,即反射射线:记录反射射线的起点,计算反射射线的方向向量。

3、函数raytrace2d_render绘制各个多边形,并绘制保存在rays中的射线。

timeToHitPoly:针对当前多边形的所有边,调用LipInterval,将射线与每条相交的时间保存在hitTimes中,并不断更新射线留在多边中的时间[t_in, t_out]。判断当前多边形是最外围的多边形,还是里边的小多边形,如果是前者,则击中多边形的时间hit_t = t_out;否则,hit_t = t_in。根据hit_t与hitTimes中的元素比较,得出多边形的哪条边被击中了。

LipInterval:根据射线与当前边的法向量的夹角,得到射线是射入多边形,还是射出多边形,并求出与当前边相交的时间t_hit。如果是射入多边形,t_in = max(t_in, t_hit);否则,t_out = min(t_out, t_hit)

—————————————-原理———————————–

反射
这里写图片描述
这里写图片描述
这里写图片描述

直线的参数形式
这里写图片描述
这里写图片描述

射线与直线或平面的交点
P为交点
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

判断射线是射向物体,还是射出物体
这里写图片描述
这里写图片描述
则对于多边形P:(n是直线的外法向量)
这里写图片描述

射线与凸多边形的交点以及裁剪问题
这里写图片描述
进入是的击中点:A + c * t_in
出去是的击中点:A + c * t_out
射线在多边形中的部分对应的t在区间[t_in, t_out]中。

裁剪算法如下:
将[t_in, t_out]看成候选区间。
1、 初始化候选区间[0, 无穷]
2、对每一条直线,计算击中时间t_hit,并判断这一击中是射入还是射出:

  • 如果是射入,t_in = max(t_in, t_hit);
  • 如果是射出,t_out = min(t_out, t_hit)

如果t_in大于t_out,说明射线与多边形完全不相交,结束检测。
3、检测完多边形的所有边后,如果候选区间非空,那么从A + c * t_in
到A + c * t_out是在多边形内的。

裁剪例子:
这里写图片描述


RayTrace2D.cpp

#include <iostream>#include <fstream>#include <vector>#include <cmath>#include <ctime>#include "GlutWin.h"#include "Line2.h"#include "Vector2.h"using namespace std;// globals //////////////////////////////////////////////////////////Vector2                     C, S,       // ray vars p(t) = C + tS                            EndPt;      // for displayingbool                        showNormals;// draw line norms or notint                         numPoints;  // number of points placed by uservector< vector<Line2> >     polys;      // line listsvector<Line2>               rays;       // list of rays we already tracedGlutWin *                   win;// loadpolys ////////////////////////////////////////////////////////bool loadPolys( const char * fileName ) {    /* Expected file format:     *      * x0 y0     * x1 y1     * x2 y2     * .     * x3 y3     * etc...     *     * where . represents the end of a poly     *     * NOTE:     * all polys must be wound CCW     * this is to make normal generation less complicated     */    Vector2         first, curr, last;    bool                newPoly = true;    vector<Line2>       v;    Line2               currSeg;    int                 n = 0;    char c;    ifstream            i( fileName );    if( ! i.is_open() )         return( false );    while( ! i.eof() )     {        i >> c;        if( c == '.' )         {            // end of a polygon            newPoly = true;            // set seg to last point and first one            currSeg.setVectors( curr, first, LINE_SEGMENT );            v.push_back( currSeg );            // if less than 2 points in poly then error            if( n < 1 )             {                cerr << "Bad Poly\n";                i.close();                return( false );            }            // put poly onto vector of polys            polys.push_back( v );            v.clear();            // unset c (prevent bug out)            c = 0;            continue;        }         else         {            i.putback( c );            if( newPoly )             {                // read first point                i >> curr;                first = last = curr;                n = 0;                newPoly = false;            }             else             {                last = curr;                // read next point                i >> curr;                currSeg.setVectors( last, curr, LINE_SEGMENT );                n++;                // put on polys line list                v.push_back( currSeg );            }        }       }       i.close();    return( true );}// LipInterval ///////////////////////////////////////////////////////////求出直线射入多边形的时间in,或射出多边形的时间out//in:射线射入多边形的时间//out:射线从多边形中射出的时间//num:n * (B - A)  A为射入直线的起点,B为被射入直线上的点,n为被射入直线的法向量//den:n * c        c为射入直线的方向向量bool LipInterval( float & in, float & out, float num, float den ) {    float hit = num / den;  //击中时间    if( den < 0 )       //射线是射入的    {        if( hit > out ) return( false ); // 提前退出        if( hit > in  ) in = hit;        // 选择较大的    }    else if( den > 0 )  //射线是射出的    {        if( hit < in  ) return( false ); // 提前退出        if( hit < out ) out = hit;       // 选择较小的    }    //else if( num <= 0 ) return( false ); // 射线与直线平行    return( true );}// timetohitpoly //////////////////////////////////////////////////////求射线击中多边形的时间//nPolyInd:多边形的下标//nLineInd:多边形中被击中边的下标//hit:射线击中多边形的时间bool timeToHitPoly( int nPolyInd, int & nLineInd, float & hit) {    Vector2         t, p, n;    float           num, den;    float           out = 10000;    float           in = 0.0f;    bool            bHit = false;    vector<float>   hitTimes;    //计算射线与多边形每条边相交的时间    for( int c = 0; c < polys[nPolyInd].size(); c++ )     {        polys[nPolyInd][c].getVectors( p, n, LINE_PTNORM );        t = p - S;     //p为当前多边形中当前边上的点,S为射线的起点        num = n * t;   //n为当前多边形中当前边的法向量        den = n * C;   //C为射线的方向向量        hitTimes.push_back( num / den );  //得到射线击中时间,并加入列表        //得到射线与当前多边形中每条边相交的时间        if( ! LipInterval( in, out, num, den ) )  //射线没有击中多边形            return( false );        if( num / den < 0 ) // ray goes backwards to hit line            continue;        bHit = true;    }    //得到多边形中哪条边被击中了    // 多边形0是外围的最大多边形,包含了其他多边形    if( nPolyInd == 0 )     {        if( out < 10000.0f )         {            hit = out;            for( int c = 0; c < hitTimes.size(); c++ )             {                if( hitTimes[c] == hit )                 {                    nLineInd = c;                    break;                }            }        }    }     else     {        // other polys would be entered by the ray        if( in > 0.0f )         {            hit = in;            for( int c = 0; c < hitTimes.size(); c++ )             {                if( hitTimes[c] == hit )                 {                    nLineInd = c;                    break;                }            }        }    }    return( bHit );}// raytrace2d_render ///////////////////////////////////////////////void raytrace2d_render() {    glClear( GL_COLOR_BUFFER_BIT );    glMatrixMode( GL_MODELVIEW );    glLoadIdentity();    glColor3f( 1.0f, 0.0f, 0.0f );    Vector2 a, b, m, n;    //画出各个多边形    for( int x = 0; x < polys.size(); x++ )     {        glBegin( GL_LINES );            for( int y = 0; y < polys[x].size(); y++ )             {                polys[x][y].getVectors( a, b, LINE_SEGMENT );                polys[x][y].getVectors( m, n, LINE_PTNORM );                glVertex2f( a.x, a.y );                glVertex2f( b.x, b.y );                //是否画出多边形上的法向量                if( showNormals )                 {                    glVertex2f( (a + (0.5f * (b - a))).x, (a + (0.5f * (b - a))).y );                      glVertex2f( ((a + (0.5f * (b - a)) + (10 * n))).x, ((a + (0.5f * (b - a)) + (10 * n))).y );                 }            }        glEnd();    }    glColor3f( 0.0f, 0.0f, 1.0f );    glBegin( GL_LINES );    //画射线    if( numPoints == 0 )  //第一条射线已经绘制完    {        for( int c = 0; c < rays.size(); c++ )         {            rays[c].getVectors( a, b, LINE_SEGMENT );            glVertex2f( a.x, a.y );            glVertex2f( b.x, b.y );        }    }     else   //画第一条射线    {        glVertex2f( S.x, S.y );        glVertex2f( EndPt.x, EndPt.y );    }    glEnd();    glutSwapBuffers();    glFlush();}// raytrace2d_keyboard //////////////////////////////////////////////void raytrace2d_keyboard( unsigned char key, int x, int y ) {    Vector2 a, b, norm;    Line2       l;    bool        h;    float       t, f = 10000;    int         nPolyHit, nFaceHit, n;    //如果按下空格键    if( key == ' ' && numPoints != 1 )     {        //求射线击中每个多边形的时间,取时间最小的那个        for( int x = 0; x < polys.size(); x++ )         {            h = timeToHitPoly(x, n, t);            cout << x << ":" << (h ? "true" : "false") << " " << t << endl;            if( h && t < f )             {                f = t;   //射线击中多边形的时间                nPolyHit = x;                nFaceHit = n;            }        }        cout << "=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-" << endl;        // 计算当前射线的可见部分(线段形式)        a = S;            //射线起点        b = S + (f * C);  //射线击中点,C为射线的方向向量        l = Line2( a, b, LINE_SEGMENT );  //得到射线的可见部分        // 放入rays中        rays.push_back( l );        // 计算反射射线的方向        S   = b;          //下一条射线的起点        polys[nPolyHit][nFaceHit].getVectors( a, norm, LINE_PTNORM );        C = C - ( ( 2.0f * ( C * norm ) ) * norm );  //下一条射线的方向向量    }    if( key == 'n' )        showNormals = !showNormals;    glutPostRedisplay();}// raytrace2d_mouse /////////////////////////////////////////////////void raytrace2d_mouse( int button, int state, int x, int y ) {    //鼠标按下事件    if( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN )     {        numPoints++;        if( numPoints == 1 )         {            // user is placing the starting point for the ray            S = Vector2( x, (600 - y) );            rays.clear();            EndPt = Vector2( x, 600 - y );        }        if( numPoints == 2 )         {            // user is placing point deining rays direction            C = Vector2( x, (600 - y) ) - S;            if( C == Vector2( 0, 0 ) )                C = Vector2( 1, 0 );            numPoints = 0;            raytrace2d_keyboard( ' ', 0, 0 ); // cheese tactics        }    }    glutPostRedisplay();}// raytrace2d_motion /////////////////////////////////////////////////鼠标移动事件void raytrace2d_motion( int x, int y ) {    if( numPoints == 1 )     {        EndPt = Vector2( x, 600 - y );        glutPostRedisplay();    }}// main /////////////////////////////////////////////////////////////int main( int argc, char **argv ) {    // initialize GLUT class    win = new GlutWin( 600, 800,                       100, 100,                       GLUT_DOUBLE | GLUT_RGB,                       "2d ray tracing demo" );    if( ! loadPolys( "polys.txt" ) )        cerr << "unable to open file: polys.txt" << endl;    C = Vector2( 10, 0 );    S = Vector2( 150, 300 );    showNormals = false;    numPoints = 0;    glutDisplayFunc( raytrace2d_render );    glutKeyboardFunc( raytrace2d_keyboard );    glutMouseFunc( raytrace2d_mouse );    glutPassiveMotionFunc( raytrace2d_motion );    // start rendering loop    glutMainLoop();    delete win;    return( 0 );}

Line2.h

#ifndef _LINE2_H_#define _LINE2_H_#include "Vector2.h"#include <iostream>using namespace std;enum LineType {    LINE_SEGMENT,     LINE_PTNORM,    LINE_PARAM};class Line2 {public:    Line2( const Vector2 & a, const Vector2 & b, LineType t );  // totally utility    Line2( float x1, float y1, float x2, float y2 );            // nasty line seg    Line2(){};    bool getVectors( Vector2 & a, Vector2 & b, LineType t ) const;    void setVectors( const Vector2 & a, const Vector2 & b, LineType t );    friend ostream & operator << ( ostream & o, Line2 & l )    {        o << l.A << " " << l.B;        return( o );    }protected:    Vector2 C, S,   // param form,p(t) = S + Ct,直线的参数形式             A, B,   // line seg form,A -> B,线段形式            P, N;   // point normal form,P为直线上的点,N为法线    bool    isSeg;  // is a segment or a line?};#endif

Line2.cpp

#include "Line2.h"Line2::Line2( const Vector2 & a, const Vector2 & b, LineType t ) {    setVectors( a, b, t );}Line2::Line2( float x1, float y1, float x2, float y2 ) {    setVectors( Vector2( x1, y1 ),                Vector2( x2, y2 ),                LINE_SEGMENT );}void Line2::setVectors( const Vector2 & a, const Vector2 & b, LineType t ) {    switch( t )     {    case LINE_SEGMENT:        // init segment vars        A = a;        B = b;        isSeg = true;        // init param form        C = (a - b);        C.normalize();        S = a;        // init PN form         P = a;        N = C.perp();        break;    case LINE_PTNORM:        // init PV vars        P = a;        N = b;        N.normalize();        // init param form        S = a;        C = N.perp();        // segment form makse no sense here        isSeg = false;        break;    case LINE_PARAM:        // init param vars        S = a;        C = b;        C.normalize();        // init PV vars        P = a;        N = C.perp();        // segment form makse no sense here        isSeg = false;        break;    default: break;    }}bool Line2::getVectors( Vector2 & a, Vector2 & b, LineType t ) const {    switch( t )     {    case LINE_SEGMENT:        if( isSeg )         {            a = A;            b = B;        }         else         {            // user asked for segment endpoints, and this is not a seg            return( false ); // = error        }        break;    case LINE_PTNORM:        a = P;        b = N;        break;    case LINE_PARAM:        a = S;        b = C;        break;    default:        return( false );    }    return( true );}

Vector2.h

#ifndef _Vector2_H#define _Vector2_H#include <cmath>#include <iostream>using namespace std;class Vector2 {public:    // vars    float x, y;    // constructors    Vector2() {}    Vector2( float x1, float y1 ) : x(x1), y(y1) {}    // vector ops    void normalize()     {        float temp = 1 / length();        x *= temp;        y *= temp;    }    inline double length() const     {        return( sqrt((x * x) + (y * y)) );    }    Vector2 perp() const     {        return( Vector2( y, -x ) );    }    //  operators    Vector2 operator + ( const Vector2 & rhs ) const     {        return( Vector2(x + rhs.x, y + rhs.y) );    }    Vector2 operator - ( const Vector2 & rhs ) const     {        return( Vector2( x - rhs.x, y - rhs.y ) );    }    Vector2 operator / ( float k ) const     {        return( Vector2(x / k, y / k) );    }    float operator * ( const Vector2 & rhs ) const     {        // dot product        return( (x * rhs.x) + (y * rhs.y) );    }    Vector2 operator * ( const float & rhs ) const     {        // scale by scalar        return( Vector2( x * rhs, y * rhs ) );    }    bool operator == ( const Vector2 & rhs )     {        return( rhs.x == x && rhs.y == y );    }    friend Vector2 operator * ( const float & lhs, const Vector2 & rhs )     {        // scale by scalar        return( Vector2( rhs.x * lhs, rhs.y * lhs ) );    }    float & operator [] ( int n )     {        // access like an array        switch( n )         {        case 0  : return( x );        case 1  : return( y );        default : /* bug out */;        }    }    friend istream & operator >> ( istream & i, Vector2 & v )     {        i >> v.x >> v.y;        return( i );    }    friend ostream & operator << ( ostream & o, Vector2 & v )     {        o << v.x << " " << v.y;        return( o );    }}; // end class#endif // _Vector2_H

GlutWin.h

#ifndef _GLUTWIN_H_#define _GLUTWIN_H_#include <windows.h>#include <gl/Gl.h>#include <gl/Glu.h>#include <gl/glut.h>//////////////////////////////////////////////////////////////                       GlutWin                          ////                                                        //// our class for general purpose GLUT initialization      //////////////////////////////////////////////////////////////class GlutWin {public:    // constructor    GlutWin( int windowHeight, int windowWidth,              int windowPosX, int windowPosY,              unsigned int displayMode,              const char * windowTitle );    ~GlutWin() {};private:    const char *    windowTitle;    int             windowHeight, windowWidth;    int             windowPosX,   windowPosY;    int             windowID;    unsigned int    displayMode;    bool            fullScreen;};#endif // _CGLUTWIN_H_

GlutWin.cpp

#include "GlutWin.h"GlutWin::GlutWin( int windowHeight, int windowWidth,                  int windowPosX, int windowPosY,                  unsigned int displayMode,                  const char * windowTitle ) {    // initialize members    windowTitle = windowTitle;    windowHeight= windowHeight;    windowWidth = windowWidth;    windowPosX  = windowPosX;    windowPosY  = windowPosY;    displayMode = displayMode;    fullScreen  = false;    // make some dummy command line for glut    char    cmd_line[8];    char *  argv[1];    argv[0] = cmd_line;    int     argc = 1;    // initialize glut    glutInit( &argc, argv );    // initialize window    glutInitWindowSize( windowWidth, windowHeight );    glutInitWindowPosition( windowPosX, windowPosY );    glutInitDisplayMode( displayMode );    // create window    windowID    = glutCreateWindow( windowTitle );    // set the view frustum    glMatrixMode( GL_PROJECTION );     glLoadIdentity();    gluOrtho2D( 0, windowWidth, 0, windowHeight );    glMatrixMode( GL_MODELVIEW );    // clear rendering surface    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);  // background is black    glViewport(0, 0, windowWidth, windowHeight);}

这里写图片描述

ploys.txt

70 260
150 550
600 550
700 250
450 75
.
300 240
275 260
300 300
350 250
340 240
.
500 440
475 460
500 500
550 450
540 440
.
500 240
475 260
500 300
550 250
540 240
.
500 440
475 460
500 500
550 450
540 440
.

1 0
原创粉丝点击