}
//! Pass clip planes to the associated graphic driver structure
- void SetClipPlanes (const Handle(Graphic3d_SequenceOfHClipPlane)& thePlanes) { myClipPlanes = thePlanes; }
+ virtual void SetClipPlanes (const Handle(Graphic3d_SequenceOfHClipPlane)& thePlanes) { myClipPlanes = thePlanes; }
//! @return bounding box of this presentation
const Graphic3d_BndBox3d& BoundingBox() const
return Standard_True;
}
+// =======================================================================
+// function : OpenGl_RaytraceClipPlanes
+// purpose : Creates new set of clipping planes
+// =======================================================================
+OpenGl_RaytraceClipPlanes::OpenGl_RaytraceClipPlanes()
+{
+ for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < MAX_PLANE_NUMBER; ++aPlaneIdx)
+ {
+ myClipPlanes[aPlaneIdx * 2] = BVH_Vec4f(-1.f, -1.f, -1.f, -1.f);
+ }
+}
+
namespace OpenGl_Raytrace
{
// =======================================================================
};
+//! Set of clipping planes specific for OpenGL primitive array.
+class OpenGl_RaytraceClipPlanes
+{
+public:
+
+ //! Maximum number of clipping planes used in ray-tracing for each
+ //! OpenGL primitive array. This is not implementation restriction,
+ //! but it is reasonable to limit max number of planes in order to
+ //! simplify GLSL data representation.
+ static const Standard_Integer MAX_PLANE_NUMBER = 8;
+
+ //! State of clipping plane.
+ enum ClipPlaneState {
+
+ CLIP_PLANE_OFF = -1, //!< plane is deactivated
+ CLIP_PLANE_VIEW = 0, //!< plane is in view space
+ CLIP_PLANE_WORLD = 1 //!< plane is in world space
+ };
+
+ //! Wrapper for clipping plane configuration.
+ class ClipPlane
+ {
+ public:
+
+ //! Creates new clipping plane wrapper.
+ ClipPlane(BVH_Vec4f& theSettings,
+ BVH_Vec4f& theEquation) : mySettings(theSettings),
+ myEquation(theEquation) {}
+
+ //! Sets 4D equation vector for clipping plane.
+ void SetEquation(const BVH_Vec4d& theEquation, const ClipPlaneState theState = CLIP_PLANE_WORLD)
+ {
+ for (Standard_Integer anIndex = 0; anIndex < 4; ++anIndex)
+ {
+ myEquation[anIndex] = static_cast<Standard_ShortReal> (theEquation[anIndex]);
+ }
+
+ SetState(theState);
+ }
+
+ //! Returns state of clipping plane.
+ ClipPlaneState State()
+ {
+ return static_cast<ClipPlaneState> ((int)mySettings.x());
+ }
+
+ //! Sets state of clipping plane.
+ void SetState(const ClipPlaneState theState)
+ {
+ mySettings.x() = static_cast<Standard_ShortReal> (theState);
+ }
+
+ private:
+
+ //! Settings of clipping plane.
+ BVH_Vec4f& mySettings;
+
+ //! 4D equation vector of clipping plane.
+ BVH_Vec4f& myEquation;
+ };
+
+public:
+
+ //! Creates new set of clipping planes.
+ OpenGl_RaytraceClipPlanes();
+
+ //! Returns clipping plane for the given index.
+ ClipPlane operator[] (const Standard_Integer theIndex)
+ {
+ return ClipPlane(myClipPlanes[theIndex * 2 + 0],
+ myClipPlanes[theIndex * 2 + 1]);
+ }
+
+ //! Returns packed (serialized) representation of clipping planes set.
+ const Standard_ShortReal* Packed()
+ {
+ return reinterpret_cast<Standard_ShortReal*> (this);
+ }
+
+private:
+
+ //! Serialized clipping planes storage.
+ BVH_Vec4f myClipPlanes[MAX_PLANE_NUMBER * 2];
+
+};
+
+//! Stores transform properties of ray-tracing object.
+class OpenGl_RaytraceTransform : public BVH_Transform<Standard_ShortReal, 4>
+{
+public:
+
+ //! Value of invalid clipping plane set.
+ static const Standard_Integer NO_CLIPPING = -1;
+
+public:
+
+ //! Creates new identity transformation.
+ OpenGl_RaytraceTransform() : BVH_Transform<Standard_ShortReal, 4>()
+ {
+ myClipSetID = NO_CLIPPING; // no clipping by default
+ }
+
+ //! Creates new transformation with specified matrix.
+ OpenGl_RaytraceTransform(const BVH_Mat4f& theTransform) : BVH_Transform<Standard_ShortReal, 4>(theTransform)
+ {
+ myClipSetID = NO_CLIPPING; // no clipping by default
+ }
+
+ //! Returns ID of associated set of clipping planes.
+ Standard_Integer ClipSetID() const
+ {
+ return myClipSetID;
+ }
+
+ //! Sets ID of associated set of clipping planes.
+ void SetClipSetID(const Standard_Integer theClipSetID)
+ {
+ myClipSetID = theClipSetID;
+ }
+
+protected:
+
+ //! ID of associated set of clipping planes.
+ Standard_Integer myClipSetID;
+
+};
+
//! Stores geometry of ray-tracing scene.
class OpenGl_RaytraceGeometry : public BVH_Geometry<Standard_ShortReal, 3>
{
//! Array of 'front' material properties.
std::vector<OpenGl_RaytraceMaterial,
NCollection_StdAllocator<OpenGl_RaytraceMaterial> > Materials;
+
+ //! Array of sets of clipping plane parameters.
+ std::vector<OpenGl_RaytraceClipPlanes,
+ NCollection_StdAllocator<OpenGl_RaytraceClipPlanes> > ClipPlanes;
//! Global ambient from all light sources.
BVH_Vec4f Ambient;
&& myInstancedStructure->IsRaytracable();
}
+// =======================================================================
+// function : SetClipPlanes
+// purpose :
+// =======================================================================
+void OpenGl_Structure::SetClipPlanes(const Handle(Graphic3d_SequenceOfHClipPlane)&thePlanes)
+{
+ Graphic3d_CStructure::SetClipPlanes(thePlanes);
+
+ if (IsRaytracable())
+ {
+ ++myModificationState;
+ }
+}
+
// =======================================================================
// function : UpdateRaytracableState
// purpose :
//! Updates ray-tracable status for structure and its parents.
void UpdateStateIfRaytracable (const Standard_Boolean toCheck = Standard_True) const;
+ //! Pass clip planes to the associated graphic driver structure
+ void SetClipPlanes(const Handle(Graphic3d_SequenceOfHClipPlane)& thePlanes);
+
//! Renders groups of structure without applying any attributes (i.e. transform, material etc).
//! @param theWorkspace current workspace
//! @param theHasClosed flag will be set to TRUE if structure contains at least one group of closed primitives
OpenGl_RT_uSceneEps,
OpenGl_RT_uLightAmbnt,
OpenGl_RT_uLightCount,
+ OpenGl_RT_uNbClipPlanes,
// background params
OpenGl_RT_uBackColorTop,
Handle(OpenGl_TextureBuffer) myRaytraceMaterialTexture;
//! Texture buffer of light source properties.
Handle(OpenGl_TextureBuffer) myRaytraceLightSrcTexture;
+ //! Texture buffer of clipping planes parameters.
+ Handle(OpenGl_TextureBuffer) myRaytraceClippingTexture;
//! 1st framebuffer (FBO) to perform adaptive FSAA.
//! Used in compatibility mode (no adaptive sampling).
static const Graphic3d_TextureUnit OpenGl_RT_RaytraceMaterialTexture = Graphic3d_TextureUnit_9;
static const Graphic3d_TextureUnit OpenGl_RT_RaytraceLightSrcTexture = Graphic3d_TextureUnit_10;
+ static const Graphic3d_TextureUnit OpenGl_RT_RaytraceClippingTexture = Graphic3d_TextureUnit_11;
- static const Graphic3d_TextureUnit OpenGl_RT_FsaaInputTexture = Graphic3d_TextureUnit_11;
- static const Graphic3d_TextureUnit OpenGl_RT_PrevAccumTexture = Graphic3d_TextureUnit_12;
+ static const Graphic3d_TextureUnit OpenGl_RT_FsaaInputTexture = Graphic3d_TextureUnit_12;
+ static const Graphic3d_TextureUnit OpenGl_RT_PrevAccumTexture = Graphic3d_TextureUnit_13;
- static const Graphic3d_TextureUnit OpenGl_RT_RaytraceDepthTexture = Graphic3d_TextureUnit_13;
+ static const Graphic3d_TextureUnit OpenGl_RT_RaytraceDepthTexture = Graphic3d_TextureUnit_14;
}
// =======================================================================
const Handle(TopLoc_Datum3D)& theTrsf,
const Handle(OpenGl_Context)& theGlContext)
{
+ // ID of clipping plane set
+ Standard_Integer aClipSetID = OpenGl_RaytraceTransform::NO_CLIPPING;
+ // Collect clipping planes of structure scope
+ if (!theStructure->ClipPlanes().IsNull())
+ //if (!theStructure->ClipPlanes()->IsEmpty())
+ {
+ NCollection_Handle<Graphic3d_SequenceOfHClipPlane> aUserPlanes;
+
+ for (Graphic3d_SequenceOfHClipPlane::Iterator aClipIter(theStructure->ClipPlanes()); aClipIter.More(); aClipIter.Next())
+ {
+ const Handle(Graphic3d_ClipPlane)& aClipPlane = aClipIter.Value();
+
+ if (aClipPlane->IsOn())
+ {
+ if (aUserPlanes.IsNull())
+ {
+ aUserPlanes = new Graphic3d_SequenceOfHClipPlane();
+ }
+
+ aUserPlanes->Append(aClipPlane);
+ }
+ }
+
+ if (!aUserPlanes.IsNull())
+ {
+ myRaytraceGeometry.ClipPlanes.push_back(OpenGl_RaytraceClipPlanes());
+
+ Standard_Integer aPlaneIdx = 0;
+ for (Graphic3d_SequenceOfHClipPlane::Iterator aClipIter(*aUserPlanes); aClipIter.More(); aClipIter.Next(), ++aPlaneIdx)
+ {
+ if (myRaytraceGeometry.ClipPlanes.size() >= OpenGl_RaytraceClipPlanes::MAX_PLANE_NUMBER)
+ {
+ break;
+ }
+ myRaytraceGeometry.ClipPlanes.back()[aPlaneIdx].SetEquation(aClipIter.Value()->GetEquation());
+ }
+
+ aClipSetID = static_cast<Standard_Integer> (myRaytraceGeometry.ClipPlanes.size() - 1);
+ }
+ }
+
OpenGl_Mat4 aMat4;
for (OpenGl_Structure::GroupIterator aGroupIter (theStructure->Groups()); aGroupIter.More(); aGroupIter.Next())
{
if (aSetIter != myArrayToTrianglesMap.end())
{
OpenGl_TriangleSet* aSet = aSetIter->second;
- opencascade::handle<BVH_Transform<Standard_ShortReal, 4> > aTransform = new BVH_Transform<Standard_ShortReal, 4>();
+ OpenGl_RaytraceTransform* aTransform = new OpenGl_RaytraceTransform;
+
if (!theTrsf.IsNull())
{
theTrsf->Trsf().GetMat4 (aMat4);
aTransform->SetTransform (aMat4);
}
+ if (aClipSetID != OpenGl_RaytraceTransform::NO_CLIPPING)
+ {
+ aTransform->SetClipSetID(aClipSetID);
+ }
+
aSet->SetProperties (aTransform);
if (aSet->MaterialIndex() != OpenGl_TriangleSet::INVALID_MATERIAL && aSet->MaterialIndex() != aMatID)
{
{
if (Handle(OpenGl_TriangleSet) aSet = addRaytracePrimitiveArray (aPrimArray, aMatID, 0))
{
- opencascade::handle<BVH_Transform<Standard_ShortReal, 4> > aTransform = new BVH_Transform<Standard_ShortReal, 4>();
+ OpenGl_RaytraceTransform* aTransform = new OpenGl_RaytraceTransform;
+
if (!theTrsf.IsNull())
{
theTrsf->Trsf().GetMat4 (aMat4);
aTransform->SetTransform (aMat4);
}
+ if (aClipSetID != OpenGl_RaytraceTransform::NO_CLIPPING)
+ {
+ aTransform->SetClipSetID(aClipSetID);
+ }
+
aSet->SetProperties (aTransform);
myRaytraceGeometry.Objects().Append (aSet);
}
"uRaytraceMaterialTexture", OpenGl_RT_RaytraceMaterialTexture);
aShaderProgram->SetSampler (theGlContext,
"uRaytraceLightSrcTexture", OpenGl_RT_RaytraceLightSrcTexture);
+ aShaderProgram->SetSampler(theGlContext,
+ "uRaytraceClippingTexture", OpenGl_RT_RaytraceClippingTexture);
if (anIndex == 1)
{
aShaderProgram->GetUniformLocation (theGlContext, "uLightCount");
myUniformLocations[anIndex][OpenGl_RT_uLightAmbnt] =
aShaderProgram->GetUniformLocation (theGlContext, "uGlobalAmbient");
+ myUniformLocations[anIndex][OpenGl_RT_uNbClipPlanes] =
+ aShaderProgram->GetUniformLocation(theGlContext, "uNbClipPlanes");
myUniformLocations[anIndex][OpenGl_RT_uFsaaOffset] =
aShaderProgram->GetUniformLocation (theGlContext, "uFsaaOffset");
nullifyResource (theGlContext, myRaytraceLightSrcTexture);
nullifyResource (theGlContext, myRaytraceMaterialTexture);
+ nullifyResource (theGlContext, myRaytraceClippingTexture);
myRaytraceGeometry.ReleaseResources (theGlContext);
return Standard_False;
}
}
-
+
+ /////////////////////////////////////////////////////////////////////////////
+ // Create clip planes buffer
+
+ if (myRaytraceClippingTexture.IsNull()) // create clipping planes buffer
+ {
+ myRaytraceClippingTexture = new OpenGl_TextureBuffer;
+
+ if (!myRaytraceClippingTexture->Create(theGlContext))
+ {
+#ifdef RAY_TRACE_PRINT_INFO
+ std::cout << "Error: Failed to create buffer for clip plane data" << std::endl;
+#endif
+ return Standard_False;
+ }
+ }
+
/////////////////////////////////////////////////////////////////////////////
// Write transform buffer
OpenGl_TriangleSet* aTriangleSet = dynamic_cast<OpenGl_TriangleSet*> (
myRaytraceGeometry.Objects().ChangeValue (anElemIndex).operator->());
- const BVH_Transform<Standard_ShortReal, 4>* aTransform = dynamic_cast<const BVH_Transform<Standard_ShortReal, 4>* > (aTriangleSet->Properties().get());
+ const OpenGl_RaytraceTransform* aTransform = dynamic_cast<const OpenGl_RaytraceTransform*> (aTriangleSet->Properties().operator->());
+
Standard_ASSERT_RETURN (aTransform != NULL,
"OpenGl_TriangleSet does not contain transform", Standard_False);
aNodeTransforms[anElemIndex] = aTransform->Inversed();
+
+ // Note: write clip plane ID in last matrix component, because
+ // the last matrix row is not used for transformation purposes
+ aNodeTransforms[anElemIndex].SetValue(3, 3, aTransform->ClipSetID());
}
aResult &= mySceneTransformTexture->Init (theGlContext, 4,
}
}
+ /////////////////////////////////////////////////////////////////////////////
+ // Write clip planes buffer
+
+ if (myRaytraceGeometry.ClipPlanes.size() != 0)
+ {
+ aResult &= myRaytraceClippingTexture->Init(theGlContext, 4,
+ GLsizei(myRaytraceGeometry.ClipPlanes.size() * 16), myRaytraceGeometry.ClipPlanes.front().Packed());
+
+ if (!aResult)
+ {
+#ifdef RAY_TRACE_PRINT_INFO
+ std::cout << "Error: Failed to upload clipping planes buffer" << std::endl;
+#endif
+ return Standard_False;
+ }
+ }
+
myIsRaytraceDataValid = myRaytraceGeometry.Objects().Size() != 0;
#ifdef RAY_TRACE_PRINT_INFO
theProgram->SetUniform (theGlContext,
myUniformLocations[theProgramId][OpenGl_RT_uLightCount], aLightSourceBufferSize);
+ theProgram->SetUniform (theGlContext,
+ myUniformLocations[theProgramId][OpenGl_RT_uNbClipPlanes], (int)myRaytraceGeometry.ClipPlanes.size());
+
// Set array of 64-bit texture handles
if (theGlContext->arbTexBindless != NULL && myRaytraceGeometry.HasTextures())
{
mySceneTransformTexture ->BindTexture (theGlContext, OpenGl_RT_SceneTransformTexture);
myRaytraceMaterialTexture->BindTexture (theGlContext, OpenGl_RT_RaytraceMaterialTexture);
myRaytraceLightSrcTexture->BindTexture (theGlContext, OpenGl_RT_RaytraceLightSrcTexture);
+ myRaytraceClippingTexture->BindTexture (theGlContext, OpenGl_RT_RaytraceClippingTexture);
}
// =======================================================================
mySceneTransformTexture ->UnbindTexture (theGlContext, OpenGl_RT_SceneTransformTexture);
myRaytraceMaterialTexture->UnbindTexture (theGlContext, OpenGl_RT_RaytraceMaterialTexture);
myRaytraceLightSrcTexture->UnbindTexture (theGlContext, OpenGl_RT_RaytraceLightSrcTexture);
+ myRaytraceClippingTexture->UnbindTexture (theGlContext, OpenGl_RT_RaytraceClippingTexture);
theGlContext->core15fwd->glActiveTexture (GL_TEXTURE0);
}
uniform samplerBuffer uRaytraceMaterialTexture;
//! Texture buffer of light source properties.
uniform samplerBuffer uRaytraceLightSrcTexture;
+//! Texture buffer of clipping planes properties.
+uniform samplerBuffer uRaytraceClippingTexture;
#ifdef BACKGROUND_CUBEMAP
//! Environment cubemap texture.
uniform int uLightCount;
//! Intensity of global ambient light.
uniform vec4 uGlobalAmbient;
+//! Number of clipping planes.
+uniform int uNbClipPlanes;
//! Enables/disables hard shadows.
uniform int uShadowsEnabled;
#define MATERIAL_TRS2(index) (19 * index + 8)
#define MATERIAL_TRS3(index) (19 * index + 9)
+#define IS_PLANE_ACTIVE(params) (params.x != -1.F)
+#define IS_PLANE_HIDDEN(params) (params.x == -1.F)
+
+#define PLANE_SETTINGS(trsf, index) (16 * int(trsf.w) + 2 * index + 0)
+#define PLANE_EQUATION(trsf, index) (16 * int(trsf.w) + 2 * index + 1)
+
#define TRS_OFFSET(treelet) treelet.SubData.x
#define BVH_OFFSET(treelet) treelet.SubData.y
#define VRT_OFFSET(treelet) treelet.SubData.z
SSubTree aSubTree = SSubTree (theRay, theInverse, EMPTY_ROOT);
+ float aClipMax = MAXFLOAT;
+ float aClipMin = -MAXFLOAT;
+
for (bool toContinue = true; toContinue; /* none */)
{
ivec4 aData = texelFetch (uSceneNodeInfoTexture, aNode);
IntersectTriangle (aSubTree.TrsfRay, aPoints[0], aPoints[1], aPoints[2], aTimeUV, aNormal);
- if (aTimeUV.x < theHit.Time)
+ if (aTimeUV.x > aClipMin && aTimeUV.x < aClipMax && aTimeUV.x < theHit.Time)
{
aTriangle.TriIndex = aTriIndex;
for (int i = 0; i < 3; ++i)
}
else if (aData.x > 0) // switch node
{
+ aClipMax = MAXFLOAT;
+ aClipMin = -MAXFLOAT;
+
aSubTree.SubData = ivec4 (4 * aData.x - 4, aData.yzw); // store BVH sub-root
vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 0);
vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 1);
vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 2);
vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 3);
+
+ for (int aPlaneIdx = 0; aInvTransf3.w >= 0.0 && aPlaneIdx < uNbClipPlanes; ++aPlaneIdx)
+ {
+ vec4 aSettings = texelFetch (uRaytraceClippingTexture, PLANE_SETTINGS (aInvTransf3, aPlaneIdx));
+ vec4 aEquation = texelFetch (uRaytraceClippingTexture, PLANE_EQUATION (aInvTransf3, aPlaneIdx));
+
+ float aNdotD = -dot (aEquation.xyz, theRay.Direct);
+
+ if (IS_PLANE_ACTIVE (aSettings))
+ {
+ float aPlaneTime = (dot (aEquation.xyz, theRay.Origin) + aEquation.w) / aNdotD;
+
+ if (aNdotD < 0.0)
+ aClipMin = max (aClipMin, aPlaneTime);
+ else
+ aClipMax = min (aClipMax, aPlaneTime);
+ }
+ }
aSubTree.TrsfRay.Direct = MatrixColMultiplyDir (theRay.Direct,
aInvTransf0,
int aStop = -1; // BVH level switch
SSubTree aSubTree = SSubTree (theRay, theInverse, EMPTY_ROOT);
+
+ float aClipMax = MAXFLOAT;
+ float aClipMin = -MAXFLOAT;
for (bool toContinue = true; toContinue; /* none */)
{
IntersectTriangle (aSubTree.TrsfRay, aPoint0, aPoint1, aPoint2, aTimeUV, aNormal);
#ifdef TRANSPARENT_SHADOWS
- if (aTimeUV.x < theDistance)
+ if (aTimeUV.x > aClipMin && aTimeUV.x < aClipMax && aTimeUV.x < theDistance)
{
aFactor *= 1.f - texelFetch (uRaytraceMaterialTexture, MATERIAL_TRAN (aTriangle.w)).x;
}
#else
- if (aTimeUV.x < theDistance)
+ if (aTimeUV.x > aClipMin && aTimeUV.x < aClipMax && aTimeUV.x < theDistance)
{
aFactor = 0.f;
}
}
else if (aData.x > 0) // switch node
{
+ aClipMax = MAXFLOAT;
+ aClipMin = -MAXFLOAT;
+
aSubTree.SubData = ivec4 (4 * aData.x - 4, aData.yzw); // store BVH sub-root
vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 0);
vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 1);
vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 2);
vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 3);
+
+ for (int aPlaneIdx = 0; aInvTransf3.w >= 0.0 && aPlaneIdx < uNbClipPlanes; ++aPlaneIdx)
+ {
+ vec4 aSettings = texelFetch (uRaytraceClippingTexture, PLANE_SETTINGS (aInvTransf3, aPlaneIdx));
+ vec4 aEquation = texelFetch (uRaytraceClippingTexture, PLANE_EQUATION (aInvTransf3, aPlaneIdx));
+
+ float aNdotD = -dot (aEquation.xyz, theRay.Direct);
+
+ if (IS_PLANE_ACTIVE (aSettings))
+ {
+ float aPlaneTime = (dot (aEquation.xyz, theRay.Origin) + aEquation.w) / aNdotD;
+
+ if (aNdotD < 0.0)
+ aClipMin = max (aClipMin, aPlaneTime);
+ else
+ aClipMax = min (aClipMax, aPlaneTime);
+ }
+ }
aSubTree.TrsfRay.Direct = MatrixColMultiplyDir (theRay.Direct,
aInvTransf0,
--- /dev/null
+pload MODELING VISUALIZATION
+vclear
+vinit name=View1 w=512 h=512
+vglinfo
+
+# setup light sources
+vlight -clear
+vlight -add POSITIONAL -headLight 0 -pos 0.5 0.5 0.85 -smoothRadius 0.06 -intensity 30.0 -name pntlight
+
+vvbo 0
+vsetdispmode 1
+vcamera -persp
+
+# setup outer box
+box b 1 1 1
+explode b FACE
+vdisplay -noupdate b_1 b_2 b_3 b_5 b_6
+vlocation -noupdate b_1 -setLocation 1 0 0
+vlocation -noupdate b_2 -setLocation -1 0 0
+vlocation -noupdate b_3 -setLocation 0 1 0
+vlocation -noupdate b_5 -setLocation 0 0 1
+vlocation -noupdate b_6 -setLocation 0 0 -1
+
+vsetmaterial -noupdate b_1 plastic
+vsetmaterial -noupdate b_2 plastic
+vsetmaterial -noupdate b_3 plastic
+vsetmaterial -noupdate b_5 plastic
+vsetmaterial -noupdate b_6 plastic
+vbsdf b_1 -kd 1 0.3 0.3 -ks 0
+vbsdf b_2 -kd 0.3 0.5 1 -ks 0
+vbsdf b_3 -kd 1 -ks 0
+vbsdf b_5 -kd 1 -ks 0
+vbsdf b_6 -kd 1 -ks 0
+
+vfront
+vfit
+
+# setup first inner sphere
+psphere s 0.2
+vdisplay -noupdate s
+vlocation -noupdate s -setLocation 0.21 0.5 0.2
+vsetmaterial -noupdate s glass
+vbsdf s -absorpColor 0.8 0.8 1.0
+vbsdf s -absorpCoeff 6
+
+# setup first inner box
+box c 0.3 0.3 0.2
+vdisplay -noupdate c
+vlocation -noupdate c -reset -rotate 0 0 0 0 0 1 -30 -translate 0.55 0.5 0.0
+vsetmaterial -noupdate c plastic
+vbsdf c -kd 1.0 0.8 0.2 -ks 0.3 -n
+
+# setup second inner box
+box g 0.25 0.25 0.4
+vdisplay -noupdate g
+vlocation -noupdate g -reset -rotate 0 0 0 0 0 1 10 -translate 0.4 0.1 0.0
+vsetmaterial -noupdate g glass
+vbsdf g -absorpColor 0.8 1.0 0.8
+vbsdf g -absorpCoeff 6
+
+# setup second inner sphere
+psphere r 0.1
+vdisplay -noupdate r
+vsetmaterial -noupdate r plastic
+vbsdf r -kd 0.5 0.9 0.3 -ks 0.3 -baseRoughness 0.0 -n
+vbsdf r -baseFresnel Constant 1.0
+vlocation r -setLocation 0.5 0.65 0.1
+
+vclipplane create pln
+vclipplane change pln equation 0 0 1 -0.1
+vclipplane set pln object s
+vclipplane set pln object r
+vclipplane set pln object c
+
+vclipplane create pln2
+vclipplane change pln2 equation 0 0 -1 0.15
+vclipplane set pln2 object s
+vclipplane set pln2 object r
+vclipplane set pln2 object c
+
+
+puts "Trying path tracing mode..."
+vrenderparams -ray -gi -rayDepth 8
+
+puts "Make several path tracing iterations to refine the picture, please wait..."
+vfps 100
+
+vdump $imagedir/${casename}.png
+puts "Done. To improve the image further, or after view manipulations, give command:"
+puts "vfps \[nb_iteratons\] or vrepaint -continuous"