概述
看到一篇JavaScript写的光线跟踪,于是我想搬进MaxScript。
JavaScript的帖子可见Milo的博客 http://www.cnblogs.com/miloyip/archive/2010/03/29/1698953.html
先从画布填充开始.
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
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/
(
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轴的朝向。测试场景使用一条直线的位置和方向建立射线,并将一个虚拟体位置更改到交点,现在可以随意旋转直线反复运行这段脚本。
有了圆与射线的相交,就可以渲染深度图。
(
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
也可以渲染法向量
(
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,手册中有范例。
(
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
最后
以上就是朴实手链为你收集整理的光线跟踪MaxScript版的全部内容,希望文章能够帮你解决光线跟踪MaxScript版所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复