zoukankan      html  css  js  c++  java
  • 光线跟踪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
  • 相关阅读:
    Orcale分析函数OVER(PARTITION BY... ORDER BY...)的讲解
    Linux下安装Redmine(项目管理软件)
    CentOS5.4安装redmine详细步骤
    CentOS安装redmine 2后的简单配置
    在linux上安装redmine
    Linux安装MediaWiki
    Linux下安装配置MediaWiKi全过程
    用Navicat_SSH 连接数据库服务器
    基于C#的MongoDB数据库开发应用(4)--Redis的安装及使用
    基于C#的MongoDB数据库开发应用(3)--MongoDB数据库的C#开发之异步接口
  • 原文地址:https://www.cnblogs.com/sitt/p/1767174.html
Copyright © 2011-2022 走看看