光线跟踪MaxScript版

看到一篇JavaScript写的光线跟踪,于是我想搬进MaxScript。

JavaScript的帖子可见Milo的博客 http://www.cnblogs.com/miloyip/archive/2010/03/29/1698953.html

先从画布填充开始.

画布填充
GC()
FreeSceneBitmaps()
if theCanvas != undefined do UnDisplay theCanvas
CanvasWidth
= 256
CanvasHeight
= 256
theCanvas
= bitmap CanvasWidth CanvasHeight color:white

for PosY = 0.0 to CanvasHeight - 1 do
for PosX = 0.0 to CanvasWidth - 1 do
(
R
= (PosX / CanvasWidth) * 255
G
= (PosY / CanvasHeight) * 255
B
= 0
SetPixels theCanvas [PosX,PosY] #((color R G B ))
)

Display theCanvas


棋盘格填充
GC()
FreeSceneBitmaps()
if theCanvas != undefined do UnDisplay theCanvas
CanvasWidth
= 256
CanvasHeight
= 256
theCanvas
= bitmap CanvasWidth CanvasHeight color:white

for PosY = 0.0 to CanvasHeight - 1 do
for PosX = 0.0 to CanvasWidth - 1 do
(
checkWidth
= 16
XI
= PosX as Integer / checkWidth
YI = PosY as Integer / checkWidth

theState
= mod XI 2 == mod YI 2
if theState do
(
SetPixels theCanvas [PosX,PosY] #(black)
)
)

Display theCanvas

 

GC()与FreeSceneBitmaps()是MaxScript用于释放内存的函数,后者针对位图的。bitmap和SetPixels可以在MaxScript参考中搜索 BitMap。

 

射线与圆球相交:

MaxScript中已经有Point3、Ray,以及两种球形几何体,Sphere和GeoSphere;还有IntersectRay、IntersectRayEX、IntersectRayScene来做各种相交检测及获取信息。这里暂不使用MaxScript内置的相交检测。过去的博客里面有配图版 

http://cubevfx.blog.163.com/blog/static/127492421201052482756123/

射线与圆球的相交
Struct IntersectRayResult
(
Geometry
= undefined,
Distance
= 0.0 ,
Pos
= [ 0 , 0 , 0 ],
Normal
= [ 0 , 0 , 0 ]
)

Fn IntersectRaySphere theRay theSphere
=
(
RayResult
= IntersectRayResult()
helperVector
= theSphere.Pos - theRay.Pos
helperValue
= Dot helperVector theRay.Dir
theRayShadow
= theRay.Dir * HelperValue
theEdgeLength
= (theSphere.Radius ^ 2 ) - (distance helperVector theRayShadow) ^ 2

if theEdgeLength >= 0 then
(
theEdgeLengthSqrtRoot
= sqrt theEdgeLength
if (distance theRay.Pos theSphere.Pos) < theSphere.Radius then
(
helperValue
+= theEdgeLengthSqrtRoot
)
else
(
helperValue
-= theEdgeLengthSqrtRoot
)

RayResult.Geometry
= theSphere
RayResult.Pos
= theRay.Dir * helperValue + theRay.Pos
RayResult.Distance
= Distance RayResult.Pos theRay.Pos
RayResult.Normal
= RayResult.Pos - theSphere.Pos
)
RayResult
)

Global theSpline,theSphere,thePoint

if not IsValidNode theSpline do
(
theSpline
= SplineShape()
AddNewSpline theSpline
AddKnot theSpline
1 #corner #line [ 0 , 0 , 0 ]
AddKnot theSpline
1 #corner #line [ 0 , 0 , 100 ]
UpdateShape theSpline
theSpline.Pos
= [ - 100 , 0 , 0 ]
)
if not IsValidNode theSphere do theSphere = Sphere ReCenter:off
if not IsValidNode thePoint do thePoint = Point WireColor:Green Box:on axistripod:off centermarker:off constantscreensize:off drawontop:off

theRay
= ray theSpline.Pos theSpline.Dir
RayResult
= IntersectRaySphere theRay theSphere
thePoint.Pos
= RayResult.Pos
CompleteRedraw()

Global theSpline,theSphere,thePoint 一行开始,为建立测试函数用的场景。 IsValidNode用于检测一个物体是否有效。MaxScript自身的Ray需要两个参数,位置和方向,而每个Max的物体,都有位置和方向属性,方向即物体自身坐标z轴的朝向。测试场景使用一条直线的位置和方向建立射线,并将一个虚拟体位置更改到交点,现在可以随意旋转直线反复运行这段脚本。

有了圆与射线的相交,就可以渲染深度图。

渲染深度图
Struct SceneInfo
(
Fn WrapperCamera TheCamera
=
(
local NewCamera,tempCamera,CreateRay
Struct NewCamera
(
Pos,Front,Up,Right,TM,Fov,FovScale,
fn GenerateRay canvX canvY
=
(
local camX,camY,rayDir
camX
= right * ((canvX - 0.5 ) * fovScale)
camY
= UP * ((canvY - 0.5 ) * fovScale)
rayDir
= Normalize (Front + camX + camY)
ray Pos rayDir
)
)
tempCamera
= NewCamera()
tempCamera.Pos
= theCamera.Pos
tempCamera.TM
= theCamera.Transform
tempCamera.Front
= Normalize ( - theCamera.Transform.Row3)
tempCamera.UP
= Normalize theCamera.Transform.Row2
tempCamera.Right
= Normalize theCamera.Transform.Row1
tempCamera.Fov
= theCamera.Fov
tempCamera.FovScale
= (tan (theCamera.Fov * 0.5 )) * 2
tempCamera
),

SphereList
= for tGeo in Geometry where
(
ClassOf tGeo
== Sphere or ClassOf tGeo == GeoSphere
) collect tGeo,
CameraList
= for tCam in Cameras collect WrapperCamera tCam
)

Struct IntersectRayResult
(
Geometry
= undefined,
Distance
= 0.0 ,
Pos
= [ 0 , 0 , 0 ],
Normal
= [ 0 , 0 , 0 ]
)

Fn IntersectRaySphere theRay theSphere
=
(
RayResult
= IntersectRayResult()
helperVector
= theSphere.Pos - theRay.Pos
helperValue
= Dot helperVector theRay.Dir
theRayShadow
= theRay.Dir * HelperValue
theEdgeLength
= (theSphere.Radius ^ 2 ) - (distance helperVector theRayShadow) ^ 2

if theEdgeLength >= 0 then
(
theEdgeLengthSqrtRoot
= sqrt theEdgeLength
if (distance theRay.Pos theSphere.Pos) < theSphere.Radius then
(
helperValue
+= theEdgeLengthSqrtRoot
)
else
(
helperValue
-= theEdgeLengthSqrtRoot
)

RayResult.Geometry
= theSphere
RayResult.Pos
= theRay.Dir * helperValue + theRay.Pos
RayResult.Distance
= Distance RayResult.Pos theRay.Pos
RayResult.Normal
= Normalize( RayResult.Pos - theSphere.Pos)
)
RayResult
)


Fn RenderDepth theCanvas theScene maxDepth
=
(
local canvasHeight , canvasWidth,theCamera
canvasHeight
= theCanvas.Height - 1
canvasWidth
= theCanvas.Width - 1
theCamera
= theScene.CameraList[ 1 ]
for canvY = 0.0 to canvasHeight do
(
screenY
= 1 - canvY / theCanvas.Height
for canvX = 0.0 to canvasWidth do
(
screenX
= canvX / theCanvas.Width
theRay = theCamera.GenerateRay screenX screenY
rayResults
= for tObj in theScene.SphereList collect IntersectRaySphere theRay tObj
rayResults
= for tRel in rayResults where tRel.Geometry != undefined collect tRel
rayDepths
= for tRel in rayResults collect tRel.Distance
firstID
= FindItem rayDepths (aMin rayDepths)
if firstID != 0 do
(
theRayResult
= rayResults[firstID]
resultDepth
= (theRayResult.Distance / maxDepth) * 255
depth
= 255 - ( if resultDepth < 255 then resultDepth else 255 )
R
= G = B = depth
SetPixels theCanvas [canvX,canvY] #((color R G B ))
)
)
)
Display theCanvas
)

/*
FreeCamera Pos:[0,0,100]
Sphere()
*/

theScene
= SceneInfo()

GC()
FreeSceneBitmaps()
if theCanvas != undefined do UnDisplay theCanvas
CanvasWidth
= 256
CanvasHeight
= 256
theCanvas
= Bitmap CanvasWidth CanvasHeight color:black
RenderDepth theCanvas theScene
100

也可以渲染法向量

渲染法向量
Struct SceneInfo
(
Fn WrapperCamera TheCamera
=
(
local NewCamera,tempCamera,CreateRay
Struct NewCamera
(
Pos,Front,Up,Right,TM,Fov,FovScale,
fn GenerateRay canvX canvY
=
(
local camX,camY,rayDir
camX
= right * ((canvX - 0.5 ) * fovScale)
camY
= UP * ((canvY - 0.5 ) * fovScale)
rayDir
= Normalize (Front + camX + camY)
ray Pos rayDir
)
)
tempCamera
= NewCamera()
tempCamera.Pos
= theCamera.Pos
tempCamera.TM
= theCamera.Transform
tempCamera.Front
= Normalize ( - theCamera.Transform.Row3)
tempCamera.UP
= Normalize theCamera.Transform.Row2
tempCamera.Right
= Normalize theCamera.Transform.Row1
tempCamera.Fov
= theCamera.Fov
tempCamera.FovScale
= (tan (theCamera.Fov * 0.5 )) * 2
tempCamera
),

SphereList
= for tGeo in Geometry where
(
ClassOf tGeo
== Sphere or ClassOf tGeo == GeoSphere
) collect tGeo,
CameraList
= for tCam in Cameras collect WrapperCamera tCam
)

Struct IntersectRayResult
(
Geometry
= undefined,
Distance
= 0.0 ,
Pos
= [ 0 , 0 , 0 ],
Normal
= [ 0 , 0 , 0 ]
)

Fn IntersectRaySphere theRay theSphere
=
(
RayResult
= IntersectRayResult()
helperVector
= theSphere.Pos - theRay.Pos
helperValue
= Dot helperVector theRay.Dir
theRayShadow
= theRay.Dir * HelperValue
theEdgeLength
= (theSphere.Radius ^ 2 ) - (distance helperVector theRayShadow) ^ 2

if theEdgeLength >= 0 then
(
theEdgeLengthSqrtRoot
= sqrt theEdgeLength
if (distance theRay.Pos theSphere.Pos) < theSphere.Radius then
(
helperValue
+= theEdgeLengthSqrtRoot
)
else
(
helperValue
-= theEdgeLengthSqrtRoot
)

RayResult.Geometry
= theSphere
RayResult.Pos
= theRay.Dir * helperValue + theRay.Pos
RayResult.Distance
= Distance RayResult.Pos theRay.Pos
RayResult.Normal
= Normalize( RayResult.Pos - theSphere.Pos)
)
RayResult
)


Fn RenderDepth theCanvas theScene maxDepth
=
(
local canvasHeight , canvasWidth,theCamera
canvasHeight
= theCanvas.Height - 1
canvasWidth
= theCanvas.Width - 1
theCamera
= theScene.CameraList[ 1 ]
for canvY = 0.0 to canvasHeight do
(
screenY
= 1 - canvY / theCanvas.Height
for canvX = 0.0 to canvasWidth do
(
screenX
= canvX / theCanvas.Width
theRay = theCamera.GenerateRay screenX screenY
rayResults
= for tObj in theScene.SphereList collect IntersectRaySphere theRay tObj
rayResults
= for tRel in rayResults where tRel.Geometry != undefined collect tRel
rayDepths
= for tRel in rayResults collect tRel.Distance
firstID
= FindItem rayDepths (aMin rayDepths)
if firstID != 0 do
(
theRayResult
= rayResults[firstID]
theNormal
= theRayResult.Normal
R
= (theNormal.X + 1 ) * 128
G
= (theNormal.Y + 1 ) * 128
B
= (theNormal.Z + 1 ) * 128
SetPixels theCanvas [canvX,canvY] #((color R G B ))
)
)
)
Display theCanvas
)

/*
FreeCamera Pos:[0,0,100]
Sphere()
*/

theScene
= SceneInfo()

GC()
FreeSceneBitmaps()
if theCanvas != undefined do UnDisplay theCanvas
CanvasWidth
= 256
CanvasHeight
= 256
theCanvas
= Bitmap CanvasWidth CanvasHeight color:black
RenderDepth theCanvas theScene
100

以上两段代码中的

/*

FreeCamera Pos:[0,0,100]
Sphere()

*/

为建立场景的MaxScript,也可以手动建立物体调整位置。

下面是渲染Phong材质,对前面的代码进行了修改,SceneInfo中SphereList更改为GeometryList,光线求交的函数也放在Plane和Sphere的结构内。此外,摄像机的代码里没有做旋转=。。=,需要手动去旋转摄像机,MaxScript里面默认情况下修改物体的rotation属性是以世界中心为坐标轴,想要以自身为坐标可以到MaxScript自带手册中查找coordsys,手册中有范例。

渲染Phong
Struct LightInfo
(
Dir,
Color,
Fn CreateByLight theLight
= LightInfo theLight.Dir theLight.RGB
)

Struct PhongInfo
(
Diffuse,Specular,Shiniess,
Reflectiveness
= 0.5 ,
Fn Sample theRay thePos theNormal theLight
=
(
normalDotLight
= dot theNormal theLight.Dir
H
= Normalize (theLight.Dir - theRay.Dir)
normalDotH
= dot theNormal H
diffuseTerm
= Diffuse * (aMax #(normalDotH , 0 ))
specularTerm
= Specular * (pow (aMax #(normalDotH , 0 )) Shiniess)
theLight.Color
* (diffuseTerm + specularTerm)
),
Fn CreateByMaterial theMat
= PhongInfo theMat.Diffuse theMat.Specular theMat.glossiness
)

Struct CheckerMaterial
(
Scale ,Reflectiveness
= 0.5 ,
Fn Sample theRay thePos theNormal NullArg
=
(
theFloorPosX
= (Floor thePos.X * Scale) as integer
theFloorPosY
= (Floor thePos.Y * Scale) as integer
State1
= (thePos.X > 0 and thePos.Y > 0 ) or (thePos.X < 0 and thePos.Y < 0 )
State2
= abs (mod theFloorPosX 2 ) == abs (mod theFloorPosY 2 )
if State1 then
(
if State2 then black else white
)
else
(
if State2 then white else black
)
)
)

Struct CameraInfo
(
Pos,Front,Up,Right,TM,Fov,FovScale,
fn GenerateRay canvX canvY
=
(
local camX,camY,rayDir
camX
= right * ((canvX - 0.5 ) * fovScale)
camY
= UP * ((canvY - 0.5 ) * fovScale)
rayDir
= Normalize (Front + camX + camY)
ray Pos rayDir
)
)

Struct IntersectRayResult
(
Geometry
= undefined,
Distance
= 0.0 ,
Pos
= [ 0 , 0 , 0 ],
Normal
= [ 0 , 0 , 0 ]
)

Struct PlaneInfo
(
Pos,Normal,Material,
Fn IntersectRay theRay thePlane
=
(
RayResult
= IntersectRayResult()
RayDir_Dot_Normal
= dot theRay.Dir Normal
if RayDir_Dot_Normal < 0 do
(
helperValue
= dot Normal (theRay.Pos - Pos)
RayResult.Geometry
= thePlane
RayResult.Distance
= - helperValue / RayDir_Dot_Normal
RayResult.Pos = theRay.Pos + theRay.Dir * RayResult.Distance
RayResult.Normal
= thePlane.Normal
)
RayResult
)
)

Struct SphereInfo
(
Radius,
Pos,
Material,
Fn IntersectRay theRay theSphere
=
(
RayResult
= IntersectRayResult()
helperVector
= theSphere.Pos - theRay.Pos
helperValue
= Dot helperVector theRay.Dir
theRayShadow
= theRay.Dir * HelperValue
theEdgeLength
= (theSphere.Radius ^ 2 ) - (distance helperVector theRayShadow) ^ 2

if theEdgeLength >= 0 then
(
theEdgeLengthSqrtRoot
= sqrt theEdgeLength
if (distance theRay.Pos theSphere.Pos) < theSphere.Radius then
(
helperValue
+= theEdgeLengthSqrtRoot
)
else
(
helperValue
-= theEdgeLengthSqrtRoot
)

RayResult.Geometry
= theSphere
RayResult.Pos
= theRay.Dir * helperValue + theRay.Pos
RayResult.Distance
= Distance RayResult.Pos theRay.Pos
RayResult.Normal
= Normalize( RayResult.Pos - theSphere.Pos)
)
RayResult
),
Fn CreateBySphere theSphere
=
(
if theSphere.Material == undefined do theSphere.Material = Standard()
SphereInfo theSphere.Radius theSphere.Pos (PhongInfo.CreateByMaterial theSphere.Material)
)
)


Struct SceneInfo
(
Fn WrapperCamera TheCamera
=
(
local tempCamera,CreateRay
tempCamera
= CameraInfo()
tempCamera.Pos
= theCamera.Pos
tempCamera.TM
= theCamera.Transform
tempCamera.Front
= Normalize ( - theCamera.Transform.Row3)
tempCamera.UP
= Normalize theCamera.Transform.Row2
tempCamera.Right
= Normalize theCamera.Transform.Row1
tempCamera.Fov
= theCamera.Fov
tempCamera.FovScale
= (tan (theCamera.Fov * 0.5 )) * 2
tempCamera
),

LightList
= for tLight in Lights collect LightInfo.CreateByLight tLight ,
GeometryList
=
(
for tGeo in Geometry where
(
ClassOf tGeo
== Sphere or ClassOf tGeo == GeoSphere or ClassOf tGeo == Plane
)
collect
(
if ClassOf tGeo == Plane then PlaneInfo tGeo.Pos tGeo.Dir (CheckerMaterial 0.1 )
else SphereInfo.CreateBySphere tGeo
)
),
CameraList
= for tCam in Cameras collect WrapperCamera tCam
)

Fn RenderRayTrace theCanvas theScene
=
(
local canvasHeight , canvasWidth,theCamera
canvasHeight
= theCanvas.Height - 1
canvasWidth
= theCanvas.Width - 1
theCamera
= theScene.CameraList[ 1 ]
theLight
= theScene.LightList[ 1 ]
for canvY = 0.0 to canvasHeight do
(
screenY
= 1 - canvY / theCanvas.Height
for canvX = 0.0 to canvasWidth do
(
screenX
= canvX / theCanvas.Width
theRay = theCamera.GenerateRay screenX screenY
rayResults
= for tObj in theScene.GeometryList collect tObj.IntersectRay theRay tObj
rayResults
= for tRel in rayResults where tRel.Geometry != undefined collect tRel
rayDepths
= for tRel in rayResults collect tRel.Distance
firstID
= FindItem rayDepths (aMin rayDepths)
if firstID != 0 do
(
theRayResult
= rayResults[firstID]
theColor
= theRayResult.Geometry.Material.Sample theRay theRayResult.Pos theRayResult.Normal theLight
SetPixels theCanvas [canvX,canvY] #(theColor)
)
)
)
Display theCanvas
)

/* Create Scene

Directionallight Pos:[50,50,50] Rotation:(random (quat 0 0 1 1) (quat 1 0 0 1))
FreeCamera Pos:[0,0,100]
(Sphere()).Material = MeditMaterials[1]
Plane Width:150 Length:150
MeditMaterials[1].Diffuse = random [0,0,0] [255,255,255]

*/

theScene
= SceneInfo()
GC()
FreeSceneBitmaps()
if theCanvas != undefined do UnDisplay theCanvas
CanvasWidth
= 256
CanvasHeight
= 256
theCanvas
= Bitmap CanvasWidth CanvasHeight color:black
RenderRayTrace theCanvas theScene

 

转载于:https://www.cnblogs.com/sitt/archive/2010/06/29/1767174.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值