找回密码
 注册
查看: 42569|回复: 9

[转载教程] 光线跟踪渲染

[复制链接]
发表于 2006-9-21 19:14:47 | 显示全部楼层 |阅读模式
光线跟踪技术是从光线投射(ray-casting)技术发展而来的. 光线投射的基本原理很简单: 假设从视点V通过屏幕象素e向场景投射一光线,交场景中的景物于P1,P2,…, Pm点(见图1),那么离视点最近的P1点就是画面在象素e处的可见点,象素e的光亮度应由P1点向P1V方向辐射的光亮度决定. 如果通过V点向屏幕上的每一象素都投射光线以求得每一投射光线与场景的第一个交点(可见点),并置相应象素的光亮度为交点处理的光亮度(用局部光照明模型计算),那么我们就得到一幅完整的真实感图形.
01.jpg
 楼主| 发表于 2006-9-21 19:15:15 | 显示全部楼层
光线投射算法只能实现局部光照明效果. 这种方法的突出优点是不必再单独消隐. Whitted在求解整体光照明模型时,把上述光线投射技术发展成光线跟踪技术. 根据Whitted光照明模型,可见点P1朝L方向的光亮度I由局部光照明光亮度Ic,环境镜面反射光亮度KsIs和规则透射光亮度KtIt三部分组成,即:

I= Ic + Ks Is + Kt It (1)

 

Ic可利用Phong光照明模型计算,Is和It分别为从VP1的镜面反射方向 r 和规则透射方向t 向P1点辐射的光亮度,所以可以用求解I同样的方法来求解Is和It. 具体地,先从V出发通过象素e发射一根光线求得它与场景的最近交点P1,容易计算出P1点的局部照明光亮度Ic。为了计算整体环境向P1点发射的镜面反射光和规则透射光,从P1点出发向r方向和t方向发射两条光线求得它们与景物的交点Pr和Pt,如图2所示.
02.jpg
 楼主| 发表于 2006-9-21 19:15:40 | 显示全部楼层
计算Pr和Pt点向P1点辐射的光亮度Is和It,代入式(1)即可算出总的光亮度I. 注意在计算Is和It处的光亮度时,除了计算由于直接照射引起的局部光照明效果外,还考虑周围环境对这两点产生的整体光照明效果,于是需要继续从Pr和Pt点出发向相应r方向和t方向发射光线并重复上述过程,这就是光线跟踪. 因此, 重复这一过程,直到被跟踪的光线射出画面或跟踪深度达到给定层次时,停止发射光线. 考虑到被跟踪光线经过多次反射和透射后会衰减(由于Ks和Kt的作用),也可通过判别跟踪光线对显示象素光亮度的贡献是否小于一阈值来动态控制跟踪深度. 设颜色灰度等级为G (通常为255,m为所取阈值,那么凡是对显示象素光亮度I的贡献小于m个灰度级,或者说其贡献系数小于m/G的反射,透射光亮度都没有必要再计算下去.

 

 

 

这里演示一个最简单的光线跟踪算法的程序

如图,这是我为程序绘制的示意图,兰色假想为眼睛位置(0,0-600),黄色假想为灯光位置(100,100,-400),红色假想为球体位置(10,100,00),为了简单,只绘制零号球且未表达出Z坐标,但绘制比例是精确的
03.gif
 楼主| 发表于 2006-9-21 19:16:11 | 显示全部楼层
主要变量:

Private backBuffer(640& * 480&) As Long  '定义了存放图片数据的数组

 

Private Type Ray
  Origin As Vector
  Direction As Vector
End Type

Private primaryRay As Ray  '定义了观察者的位置及观察方向

 

Private directionTable() As Vector  '定义了射线方向(数组)

 

Public LightLoc As Vector  '定义了灯光的位置

 

主要函数:

Sub Main  '主函数

 

Rotate  '球体位置旋转的函数

 

GenerateRayDirectionTable()  '创建了开始的射线(数组)

 

TraceScene  '场景跟踪,球体的遮挡关系也在此进行

 

IntersectSphere  '射线与球体相交的长度计算

 

ShadeSphere  '通过灯光与法线的夹角计算颜色,渲染

 

VectorDot    '向量点积(计算向量间的夹角或长度)

 

VectorNormalize   '单位化向量

 

待续

 

Public Sub TraceScene(ByRef Spheres() As Sphere, ByRef numSpheres As Integer, ByRef LightLoc As Vector)
'// 开始计算射线
Dim X As Long, Y As Long, Z As Long
Dim closestIntersectionDistance As Single
Dim closestIntersectedSphereNum As Integer
Dim currResult As TraceResult
Dim lngBuffer As Long
'//改变loop大小让FPS 显示更快
'//尤其是外部的 loop 影响最大!!!
'//缩方, 这会改变窗口
For Y = 150 To 350
For X = 250 To 400
'//单一的数值会快很多...
primaryRay.Direction = directionTable(X + (Y * 640))
closestIntersectionDistance = 1000000
closestIntersectedSphereNum = -1
'//循环所有的球体找出最靠近眼睛的球体号

For Z = 0 To numSpheres
currResult = IntersectSphere(primaryRay, Spheres(Z))
If currResult.Hit Then
If currResult.Distance < closestIntersectionDistance Then
closestIntersectionDistance = currResult.Distance
closestIntersectedSphereNum = Z
Exit For
End If
End If
Next Z
'//仅计算相交最靠近眼睛的球体
If (closestIntersectedSphereNum > -1) Then
'//bits数组赋值
Bits(X, Y) = ShadeSphere(Spheres(closestIntersectedSphereNum), primaryRay, closestIntersectionDistance, LightLoc)
Else
With Bits(X, Y)
.rgbRed = 0
.rgbBlue = 0
.rgbGreen = 0
End With
End If
Next X
Next Y
With frmMain.picRay
'//复制 bits 数据到图片
SetDIBits lngHDC, lngImageHandle, 0, BInfo.bmiHeader.biHeight, Bits(0, 0), BInfo, DIB_RGB_COLORS
'图片刷新
.Refresh
End With
End Sub

 

Public Function IntersectSphere(ByRef myRay As Ray, ByRef mySphere As Sphere) As TraceResult
Dim rayToSphereCenter As Vector  '眼睛到球体中心的向量
Dim lengthRTSC2 As Single
Dim closestApproach As Single
Dim halfCord2 As Single
With IntersectSphere
.Hit = False
'// 眼睛到球体中心的向量并归到单一的原点

Call VectorSub(mySphere.Center, myRay.Origin, rayToSphereCenter)
'// lengthRTSC2 ='眼睛到球体中心距离的平方
lengthRTSC2 = VectorDot(rayToSphereCenter, rayToSphereCenter)
closestApproach = VectorDot(rayToSphereCenter, myRay.Direction)
'//如果是在背面,就返回失败(背面照不到光线)
If closestApproach < 0 Then Exit Function
'//halfCord2 = 球体中心到射线的垂直点与射线与球体交点的距离平方。
halfCord2 = (mySphere.Radius * mySphere.Radius) - lengthRTSC2 + (closestApproach * closestApproach) ' // sphere.radius 的平方可以在循环前先计算出来
04.gif
 楼主| 发表于 2006-9-21 19:16:35 | 显示全部楼层
'射线未与球体相交(光线未照到球体)
If (halfCord2 < 0) Then Exit Function
.Hit = True
.Distance = closestApproach - Sqr(halfCord2)
End With
End Function

 

Public Function ShadeSphere(ByRef mySphere As Sphere, ByRef myRay As Ray, ByRef Distance As Single, ByRef LightLoc As Vector) As RGBQUAD
Dim Intersection As Vector
Dim Normal As Vector
Dim LightDir As Vector
Dim LightCoef As Single
Dim OneOverRadius As Single
'//射线与球体交点的坐标位置计算 。
With myRay
Intersection.X = .Origin.X + Distance * .Direction.X
Intersection.Y = .Origin.Y + Distance * .Direction.Y
Intersection.Z = .Origin.Z + Distance * .Direction.Z
End With
'//计算交点处的法线
OneOverRadius = 1 / mySphere.Radius '预先计算数据会快一些,但也可能更慢,因为结果是32位数
With mySphere
'//相当于( intersection.x - sphere.center.x ) / sphere.radius
Normal.X = (Intersection.X - .Center.X) * OneOverRadius
Normal.Y = (Intersection.Y - .Center.Y) * OneOverRadius
Normal.Z = (Intersection.Z - .Center.Z) * OneOverRadius
'//计算灯光到交点的单位向量
Call VectorSub(LightLoc, Intersection, LightDir)
Call VectorNormalize(LightDir)
'计算灯光系数—它会与球体颜色相乘
LightCoef = VectorDot(Normal, LightDir)
If (LightCoef < 0) Then LightCoef = 0
'计算颜色并返回
ShadeSphere.rgbRed = .Color.R * LightCoef
ShadeSphere.rgbGreen = .Color.G * LightCoef
ShadeSphere.rgbBlue = .Color.B * LightCoef
End With
End Function

光线跟踪是自然界光照明物理过程的近似逆过程,即逆向跟踪从光源发出的光经过环境景物间的多次反射、折射后投射到景物表面、最终进入人眼的过程.
发表于 2006-9-21 19:59:31 | 显示全部楼层
这么深奥  谢谢老大 带来的好资料
发表于 2006-9-21 21:05:48 | 显示全部楼层
这么长,,
发表于 2007-10-26 17:12:10 | 显示全部楼层
这么长....
发表于 2008-1-25 22:30:47 | 显示全部楼层
感谢
发表于 2009-5-22 15:53:10 | 显示全部楼层
太深奥了  
您需要登录后才可以回帖 登录 | 注册

本版积分规则

本站无意传播任何侵权软件与信息,部分资源为网友搜集或发布,仅供学习和研究使用,请支持正版。站内所发布的资源,如有侵犯你的权益,请联系我们,本站将立即改正或删除。

QQ|Archiver|手机版|小黑屋|联系我们|中华设计论坛 ( 苏ICP备20023187号-1

GMT+8, 2024-11-14 13:11

Powered by Discuz! X3.5

© 2006-2024 Daliang Team.

快速回复 返回顶部 返回列表