光线跟踪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 -1do
for PosX =0.0 to CanvasWidth -1do
(
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 -1do
for PosX =0.0 to CanvasWidth -1do
(
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 !=0do
(
theRayResult
= rayResults[firstID]
resultDepth
= (theRayResult.Distance / maxDepth) *255
depth
=255- (if resultDepth <255 then resultDepth else255)
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 !=0do
(
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 <0do
(
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 !=0do
(
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/p/1767174.html