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
.
- OpenGL---二维光线追踪
- 光线追踪
- 光线追踪
- 光线追踪
- 光线追踪基础算法
- 模拟光线追踪阴影
- 光线追踪试玩
- 蒙特卡洛光线追踪
- [Raytracing]光线追踪算法
- java 光线追踪
- [Raytracing]光线追踪算法
- 图形学笔记:光线追踪
- 光线追踪算法
- 光线追踪——最终版
- 转 光线追踪的基本原理
- 光线追踪技术 第二章
- 【UnityShader】光线追踪体积光
- 光线追踪(Ray Trace) 变形金刚大图
- 动态规划(dynamic programming)及示例(矩阵连乘、最长公共子序列、三角剖分)
- sql where 和on 的区别
- 性能测试7-性能调优
- 在本地将spark作业运行到远程集群
- 读写Android系统联系人
- OpenGL---二维光线追踪
- 2.分布式锁
- 修改Tomcat默认访问页面
- Semaphore
- LeetCode : Move Zeroes
- 并发学习2-使用Thread类来驱动实现Runnable接口的类
- 我眼中的光明·第六周·莲池·三
- 多线程异常处理
- 啊是大