反射

 

真实描绘现实中的环境或物体的光影是计算机图形学中一个重要的课题,最常用的一个方法是光线追踪显示法,即当光线进入物体中间时候,计算(追踪)光的路径。试写一个程序计算在特定环境中关的路线。

为了简单起见,我们将仅仅考虑二维的情景。所有物体是完全的球面体,当光线击中这样一种球时,它被完全发射并遵守反射定律,即入射角于反射角相等,如图1所示:

w

w

1:光线射中球面时光的反射

2所示为某一环境下的光路:

 

2     某一环境下的光路

你的任务时写一特定程序,对给定一个特定环境下的光线进入的情况,确定哪些球被光线击中。

输入格式:

第一行包含一个整数nn<=25),表示有n个球。接着n行包含三个整数,Xi, Yi, Ri,表示一个球的数据,(Xi, Yi) 是球心坐标,Ri>0是球半径。最后一行包含4个整数:X, Y, Dx, Dy, 用于描述光线,光线起源于(X, Y, 且前进方向的增量是(Dx, Dy,)。Dx, Dy,至少有一个非零。

输出格式:

输出只有一行,被光线击中的顺序,打印光线前10次改变方向时击中的球(球的编号是按它们输入是的顺序号)。如果光线击中至多10个球(然后射向无穷远),则打印“inf”在最后一个它击中的球后。如果光线击中多于10个球,在第10个球后打印三个点“

输入输出样例:

Input 1

Output 1

3

3 3 2

7 7 1

8 1 1

3 8 1 -4

1 2 1 3 inf

 

Input 2

Output 2

1

4 4 1

-2 -2 1 0

Inf

 

Input 3

Output 3

2

9 14 3

-4 6 3

0 0 3 5

1 2 inf

 

解:

这道题有两个难点:1、确定光线于那个球相遇;2、计算光线反射后的角度。

这里光线的方程采用参数方程:

x=x0+t×dx

y=y0+t×dy

(可以推出光线的直线方程是:dy×x-dx×y+dx×y0dy×x0=0)

1、  确定光线于哪个球相遇

计算光线于每个球的圆心距:abs(a×x+b×y+c) / sqrt(a×a+b×b)xy为圆心坐标,abc可由光线的直线方程得到)。距离小于半径既有交点。然后联立参数方程和圆方程,解出t的值(二次方程取较小的根,即与光源距离交小的点),如果t值小于0就舍去(表示焦点在光线的反方向)。所有圆的交点t值中最小的即为所求的放射点参数。如果没有一个t满足条件,就表示光线射向无穷远。

2、计算光线反射后的角度

设光源指向放射点的角度为a1,圆心指向放射点的角度为a2。则放射后的角度为a2-(a1a2)(在草纸上划下很容易证的)。所对应的dxdy,即为cosa1)和sina1)。

 

代码:

#include < stdio.h >
#include
< math.h >
#include
< string .h >
#include
< stdlib.h >


#define  MAX 25     // 最多25个球
#define  pi 3.14159265     // 圆周率
#define  sqr(a) ((a)*(a))     // 平方


struct  TCircle{
    
double  x,y,r;
}circle[MAX];

double  x,y,dx,dy,t;     // 光线参数
int  n;     // 共有n个球



inline 
void
readData(){    
// 读取数据
     int  i;
    scanf(
" %d " , & n);
    
for (i = 0 ;i < n;i ++ )
        scanf(
" %lf%lf%lf " , & circle[i].x, & circle[i].y, & circle[i].r);
    scanf(
" %lf%lf%lf%lf " , & x, & y, & dx, & dy);
}


inline 
double
distance(
int  i){     // 计算光直线到圆心的距离
     double  aa,bb,cc;
    aa
= dy;
    bb
= dx;
    cc
= dx * y - dy * x;
    
return  fabs(aa * circle[i].x - bb * circle[i].y + cc) / sqrt(aa * aa + bb * bb);
}

inline 
double  
getAngle(
double  x, double  y, double  hx, double  hy){     // 计算直线的角度
     double  q;
    
if (fabs(x - hx) < 1e - 10 ){     // 直线垂直x轴
         if (y - hy > 0 ) q = pi / 2 ;     // y轴正方向 90度
         else  q = 3 * pi / 2 ;         // y轴负方向 270度
    }
    
else {
        
if (x - hx > 0 ) q = atan((y - hy) / (x - hx));
        
else  q = atan((y - hy) / (x - hx)) + pi;
    }
    
if (q < 0 ) q = q + 2 * pi;
    
return  q;
}

inline 
void
setLine(
int  i){     // 重置光线坐标
     double  q1,q2;
    
double  hx,hy;
    hx
= x + t * dx;
    hy
= y + t * dy;
    q1
= getAngle(x,y,hx,hy);
    q2
= getAngle(circle[i].x,circle[i].y,hx,hy);
    dx
= cos( - (q1 - q2) + q2);
    dy
= sin( - (q1 - q2) + q2);
    x
= hx;
    y
= hy;
}

inline 
double  
cacuT(
int  i){     // 计算t的值
     double  aa,bb,cc;
    aa
= dx * dx + dy * dy;
    bb
= 2 * (x - circle[i].x) * dx + 2 * (y - circle[i].y) * dy;
    cc
= sqr(x - circle[i].x) + sqr(y - circle[i].y) - sqr(circle[i].r);
    
return  ( - bb - sqrt(bb * bb - 4 * aa * cc)) / 2 / aa;
}

inline 
int
findCircle(){    
// 查找光线和哪个圆相遇,如果没有球相遇则放回-1否则放回球编号
     int  no =- 1 ,i;
    
double  dis,tt;
    t
=- 1 ;
    
for (i = 0 ;i < n;i ++ ){
        dis
= distance(i);
        
if (dis < circle[i].r){
            tt
= cacuT(i);
            
if (tt > 0 && (tt < ||  t < 0 )) {
                t
= tt;
                no
= i;
            }
        }
    }
    
return  no;
}


void
main(){
    
int  fd;
    fd
= open( " test.txt " ,O_RDONLY);
    dup2(fd,
0 );

    
int  i,no;
    readData();
    
for (i = 0 ;i < 10 ;i ++ )
        
if ((no = findCircle()) !=- 1 ){
            printf(
" %d  " ,no + 1 );
            setLine(no);
        }
        
else {
            puts(
" inf " );
            
return ;
        }
    
if (findCircle() ==- 1 )
        puts(
" info " );
    
else
        puts(
" ... " );
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值