From 8c820969cc4a12578aa8002709ba83a04aa5d70c Mon Sep 17 00:00:00 2001 From: dbp Date: Thu, 30 Jul 2015 12:49:43 +0300 Subject: [PATCH] 0026437: Visualization - Improve path tracing rendering engine Fix compile warnings. --- samples/tcl/pathtrace.tcl | 78 ++++++ src/Graphic3d/Graphic3d_RenderingParams.hxx | 4 +- src/OpenGl/OpenGl_View.hxx | 10 +- src/OpenGl/OpenGl_View_Raytrace.cxx | 46 ++-- src/Shaders/PathtraceBase.fs | 273 ++++++++----------- src/Shaders/RaytraceBase.fs | 136 ++++----- src/Shaders/RaytraceRender.fs | 16 +- src/ViewerTest/ViewerTest_ViewerCommands.cxx | 35 +++ 8 files changed, 322 insertions(+), 276 deletions(-) create mode 100644 samples/tcl/pathtrace.tcl diff --git a/samples/tcl/pathtrace.tcl b/samples/tcl/pathtrace.tcl new file mode 100644 index 0000000000..82abdc3532 --- /dev/null +++ b/samples/tcl/pathtrace.tcl @@ -0,0 +1,78 @@ +######################################################################### +# 26437: Visualization - Improve path tracing rendering engine +######################################################################### + +pload ALL + +# setup 3D viewer content +vinit name=View1 w=512 h=512 +vglinfo + +vvbo 0 +vsetdispmode 1 +vcamera -persp + +box b 1 1 1 +explode b FACE +vdisplay b_1 b_2 b_3 b_5 b_6 + +vright +vfit + +vsetmaterial b_1 plastic +vsetmaterial b_2 plastic +vsetmaterial b_3 plastic +vsetmaterial b_5 plastic +vsetmaterial b_6 plastic + +vbsdf b_1 -kd 1 -ks 0 +vbsdf b_2 -kd 1 -ks 0 +vbsdf b_3 -kd 1 -ks 0 +vbsdf b_5 -kd 1 -ks 0 +vbsdf b_6 -kd 1 -ks 0 + +vbsdf b_2 -kd 0.3 0.5 1 +vbsdf b_1 -kd 1 0.3 0.3 + +vsetlocation b_1 1 0 0 +vsetlocation b_2 -1 0 0 +vsetlocation b_5 0 0 1 +vsetlocation b_6 0 0 -1 +vsetlocation b_3 0 1 0 + +vlight del 0 +vlight del 1 +vlight add positional head 0 pos 0.5 0.5 0.85 +vlight change 0 sm 0.06 +vlight change 0 int 60.0 + +psphere s 0.2 +vdisplay s +vsetlocation s 0.21 0.3 0.2 +vsetmaterial s glass +vbsdf s -absorpcolor 0.8 0.8 1.0 +vbsdf s -absorpcoeff 6 + +box c 0.3 0.3 0.2 +vdisplay c +vsetlocation c 0.55 0.3 0.0 +vlocrotate c 0 0 0 0 0 1 -30 +vsetmaterial c plastic +vbsdf c -kd 1.0 0.8 0.2 -ks 0.3 -n + +box g 0.15 0.15 0.3 +vdisplay g +vsetlocation g 0.7 0.25 0.2 +vlocrotate g 0 0 0 0 0 1 10 +vsetmaterial g glass +vbsdf g -absorpcolor 0.8 1.0 0.8 +vbsdf g -absorpcoeff 6 + +psphere r 0.1 +vdisplay r +vsetmaterial r plastic +vbsdf r -kd 0.5 0.9 0.3 -ks 0.0 -kr 0.3 -n +vbsdf r -fresnel Constant 1.0 +vsetlocation r 0.5 0.65 0.1 + +vrenderparams -ray -gi -rayDepth 8 diff --git a/src/Graphic3d/Graphic3d_RenderingParams.hxx b/src/Graphic3d/Graphic3d_RenderingParams.hxx index 5222741c38..1ee0e656f7 100644 --- a/src/Graphic3d/Graphic3d_RenderingParams.hxx +++ b/src/Graphic3d/Graphic3d_RenderingParams.hxx @@ -55,8 +55,9 @@ public: IsReflectionEnabled (Standard_False), IsAntialiasingEnabled (Standard_False), IsTransparentShadowEnabled (Standard_False), - UseEnvironmentMapBackground (Standard_False), + CoherentPathTracingMode (Standard_False), + StereoMode (Graphic3d_StereoMode_QuadBuffer), AnaglyphFilter (Anaglyph_RedCyan_Optimized), ToReverseStereo (Standard_False) @@ -84,6 +85,7 @@ public: Standard_Boolean IsAntialiasingEnabled; //!< enables/disables adaptive anti-aliasing, False by default Standard_Boolean IsTransparentShadowEnabled; //!< enables/disables light propagation through transparent media, False by default Standard_Boolean UseEnvironmentMapBackground; //!< enables/disables environment map background + Standard_Boolean CoherentPathTracingMode; //!< enables/disables 'coherent' tracing mode (single RNG seed within 16x16 image blocks) Graphic3d_StereoMode StereoMode; //!< stereoscopic output mode, Graphic3d_StereoMode_QuadBuffer by default Anaglyph AnaglyphFilter; //!< filter for anaglyph output, Anaglyph_RedCyan_Optimized by default diff --git a/src/OpenGl/OpenGl_View.hxx b/src/OpenGl/OpenGl_View.hxx index f5d5176e9c..af8c704c8b 100644 --- a/src/OpenGl/OpenGl_View.hxx +++ b/src/OpenGl/OpenGl_View.hxx @@ -170,7 +170,6 @@ class OpenGl_View : public MMgt_TShared const Aspect_CLayer2d& theCOverLayer, const Standard_Boolean theToDrawImmediate); - void DrawBackground (const Handle(OpenGl_Workspace)& theWorkspace); //! Returns list of OpenGL Z-layers. @@ -334,6 +333,7 @@ protected: //! @name data types related to ray-tracing OpenGl_RT_uSphereMapEnabled, OpenGl_RT_uSphereMapForBack, OpenGl_RT_uTexSamplersArray, + OpenGl_RT_uBlockedRngEnabled, // sampled frame params OpenGl_RT_uSampleWeight, @@ -502,10 +502,10 @@ protected: //! @name methods related to ray-tracing const Handle(OpenGl_Context)& theGlContext); //! Adds OpenGL groups to ray-traced scene geometry. - Standard_Boolean addRaytraceGroups (const OpenGl_Structure* theStructure, - const Standard_Integer theStructMat, - const Standard_ShortReal* theTransform, - const Handle(OpenGl_Context)& theGlContext); + Standard_Boolean addRaytraceGroups (const OpenGl_Structure* theStructure, + const OpenGl_RaytraceMaterial& theStructMat, + const Standard_ShortReal* theTransform, + const Handle(OpenGl_Context)& theGlContext); //! Creates ray-tracing material properties. OpenGl_RaytraceMaterial convertMaterial (const OpenGl_AspectFace* theAspect, diff --git a/src/OpenGl/OpenGl_View_Raytrace.cxx b/src/OpenGl/OpenGl_View_Raytrace.cxx index 4072437644..f7bd08ba15 100644 --- a/src/OpenGl/OpenGl_View_Raytrace.cxx +++ b/src/OpenGl/OpenGl_View_Raytrace.cxx @@ -407,15 +407,11 @@ Standard_Boolean OpenGl_View::addRaytraceStructure (const OpenGl_Structure* } // Get structure material - Standard_Integer aStructMatID = -1; + OpenGl_RaytraceMaterial aStructMaterial; if (theStructure->AspectFace() != NULL) { - aStructMatID = static_cast (myRaytraceGeometry.Materials.size()); - - OpenGl_RaytraceMaterial aStructMaterial = convertMaterial (theStructure->AspectFace(), theGlContext); - - myRaytraceGeometry.Materials.push_back (aStructMaterial); + aStructMaterial = convertMaterial (theStructure->AspectFace(), theGlContext); } Standard_ShortReal aStructTransform[16]; @@ -431,7 +427,7 @@ Standard_Boolean OpenGl_View::addRaytraceStructure (const OpenGl_Structure* } } - Standard_Boolean aResult = addRaytraceGroups (theStructure, aStructMatID, + Standard_Boolean aResult = addRaytraceGroups (theStructure, aStructMaterial, theStructure->Transformation()->mat ? aStructTransform : NULL, theGlContext); // Process all connected OpenGL structures @@ -439,7 +435,7 @@ Standard_Boolean OpenGl_View::addRaytraceStructure (const OpenGl_Structure* if (anInstanced != NULL && anInstanced->IsRaytracable()) { - aResult &= addRaytraceGroups (anInstanced, aStructMatID, + aResult &= addRaytraceGroups (anInstanced, aStructMaterial, theStructure->Transformation()->mat ? aStructTransform : NULL, theGlContext); } @@ -452,32 +448,26 @@ Standard_Boolean OpenGl_View::addRaytraceStructure (const OpenGl_Structure* // function : addRaytraceGroups // purpose : Adds OpenGL groups to ray-traced scene geometry // ======================================================================= -Standard_Boolean OpenGl_View::addRaytraceGroups (const OpenGl_Structure* theStructure, - const Standard_Integer theStructMat, - const Standard_ShortReal* theTransform, - const Handle(OpenGl_Context)& theGlContext) +Standard_Boolean OpenGl_View::addRaytraceGroups (const OpenGl_Structure* theStructure, + const OpenGl_RaytraceMaterial& theStructMat, + const Standard_ShortReal* theTransform, + const Handle(OpenGl_Context)& theGlContext) { for (OpenGl_Structure::GroupIterator aGroupIter (theStructure->DrawGroups()); aGroupIter.More(); aGroupIter.Next()) { // Get group material - Standard_Integer aGroupMatID = -1; + OpenGl_RaytraceMaterial aGroupMaterial; if (aGroupIter.Value()->AspectFace() != NULL) { - aGroupMatID = static_cast (myRaytraceGeometry.Materials.size()); - - OpenGl_RaytraceMaterial aGroupMaterial = convertMaterial ( + aGroupMaterial = convertMaterial ( aGroupIter.Value()->AspectFace(), theGlContext); - - myRaytraceGeometry.Materials.push_back (aGroupMaterial); } - Standard_Integer aMatID = aGroupMatID < 0 ? theStructMat : aGroupMatID; - if (aMatID < 0) - { - aMatID = static_cast (myRaytraceGeometry.Materials.size()); + Standard_Integer aMatID = static_cast (myRaytraceGeometry.Materials.size()); - myRaytraceGeometry.Materials.push_back (OpenGl_RaytraceMaterial()); - } + // Use group material if available, otherwise use structure material + myRaytraceGeometry.Materials.push_back ( + aGroupIter.Value()->AspectFace() != NULL ? aGroupMaterial : theStructMat); // Add OpenGL elements from group (extract primitives arrays and aspects) for (const OpenGl_ElementNode* aNode = aGroupIter.Value()->FirstNode(); aNode != NULL; aNode = aNode->next) @@ -1573,6 +1563,8 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Graphic3d_CView& theC aShaderProgram->GetUniformLocation (theGlContext, "uSphereMapEnabled"); myUniformLocations[anIndex][OpenGl_RT_uSphereMapForBack] = aShaderProgram->GetUniformLocation (theGlContext, "uSphereMapForBack"); + myUniformLocations[anIndex][OpenGl_RT_uBlockedRngEnabled] = + aShaderProgram->GetUniformLocation (theGlContext, "uBlockedRngEnabled"); myUniformLocations[anIndex][OpenGl_RT_uSampleWeight] = aShaderProgram->GetUniformLocation (theGlContext, "uSampleWeight"); @@ -2256,6 +2248,12 @@ Standard_Boolean OpenGl_View::setUniformState (const Graphic3d_CView& the theProgram->SetUniform (theGlContext, myUniformLocations[theProgramId][OpenGl_RT_uReflectEnabled], theCView.RenderParams.IsReflectionEnabled ? 1 : 0); + if (theCView.RenderParams.IsGlobalIlluminationEnabled) + { + theProgram->SetUniform (theGlContext, + myUniformLocations[theProgramId][OpenGl_RT_uBlockedRngEnabled], theCView.RenderParams.CoherentPathTracingMode ? 1 : 0); + } + // Set array of 64-bit texture handles if (theGlContext->arbTexBindless != NULL && myRaytraceGeometry.HasTextures()) { diff --git a/src/Shaders/PathtraceBase.fs b/src/Shaders/PathtraceBase.fs index 03f43ec9c9..d7e799e6f7 100644 --- a/src/Shaders/PathtraceBase.fs +++ b/src/Shaders/PathtraceBase.fs @@ -301,17 +301,6 @@ vec3 handleMaterial (in SMaterial theMaterial, in vec3 theInput, in vec3 theOutp theMaterial.Ks.rgb * handleBlinnReflection (theInput, theOutput, theMaterial.Fresnel, theMaterial.Ks.w); } -//======================================================================= -// function : sampleSpecularReflection -// purpose : Samples specular BRDF, W = BRDF * cos(N, PSI) / PDF(PSI) -//======================================================================= -void sampleSpecularReflection (in vec3 theOutput, out vec3 theInput) -{ - theInput = vec3 (-theOutput.x, - -theOutput.y, - theOutput.z); -} - //======================================================================= // function : sampleLambertianReflection // purpose : Samples Lambertian BRDF, W = BRDF * cos(N, PSI) / PDF(PSI) @@ -327,38 +316,51 @@ void sampleLambertianReflection (in vec3 theOutput, out vec3 theInput) aTemp * sin (2.f * M_PI * aKsi1), sqrt (1.f - aKsi2)); - if (theOutput.z < 0.f) - theInput.z = -theInput.z; + theInput.z = mix (-theInput.z, theInput.z, step (0.f, theOutput.z)); } +// Types of bounces +#define NON_SPECULAR_BOUNCE 0 +#define SPEC_REFLECT_BOUNCE 1 +#define SPEC_REFRACT_BOUNCE 2 + +#define IS_NON_SPEC_BOUNCE(theBounce) (theBounce == 0) +#define IS_ANY_SPEC_BOUNCE(theBounce) (theBounce != 0) +#define IS_REFL_SPEC_BOUNCE(theBounce) (theBounce == 1) +#define IS_REFR_SPEC_BOUNCE(theBounce) (theBounce == 2) + //======================================================================= // function : sampleSpecularTransmission // purpose : Samples specular BTDF, W = BRDF * cos(N, PSI) / PDF(PSI) //======================================================================= vec3 sampleSpecularTransmission (in vec3 theOutput, out vec3 theInput, - out bool isTransmit, in vec3 theThroughput, in vec3 theFresnelCoeffs) + out int theBounce, in vec3 theWeight, in vec3 theFresnelCoeffs) { vec3 aFresnel = fresnelMedia (theOutput.z, theFresnelCoeffs); - float aProbability = convolve (aFresnel, theThroughput); + float aProbability = convolve (aFresnel, theWeight); + + // Check if transmission takes place + theBounce = RandFloat() <= aProbability ? + SPEC_REFLECT_BOUNCE : SPEC_REFRACT_BOUNCE; // Sample input direction - if (RandFloat() <= aProbability) + if (theBounce == SPEC_REFLECT_BOUNCE) { theInput = vec3 (-theOutput.x, -theOutput.y, theOutput.z); - isTransmit = false; + theWeight = aFresnel * (1.f / aProbability); + } + else + { + transmitted (theFresnelCoeffs.y, theOutput, theInput); - return aFresnel * (1.f / aProbability); + theWeight = (UNIT - aFresnel) * (1.f / (1.f - aProbability)); } - transmitted (theFresnelCoeffs.y, theOutput, theInput); - - isTransmit = true; - - return (UNIT - aFresnel) * (1.f / (1.f - aProbability)); + return theWeight; } //======================================================================= @@ -430,98 +432,55 @@ vec3 sampleBlinnReflection (in vec3 theOutput, out vec3 theInput, in vec3 theFre return aFresnel * ((theExponent + 2.f) / (theExponent + 1.f) * aGeom / aCosThetaO); } -// Enables expiremental russian roulette sampling -// #define RUSSIAN_ROULETTE - //======================================================================= // function : sampleMaterial // purpose : Samples specified composite material (BSDF) //======================================================================= -bool sampleMaterial (in SMaterial theMaterial, +void sampleMaterial (in SMaterial theMaterial, in vec3 theOutput, - in vec3 theFactor, out vec3 theInput, - out vec3 theWeight, - inout bool isTransmit) + inout vec3 theWeight, + inout int theBounce) { - theWeight = ZERO; - // Compute the probability of ray reflection - float aPd = convolve (theMaterial.Kd.rgb, theFactor); - float aPs = convolve (theMaterial.Ks.rgb, theFactor); - float aPr = convolve (theMaterial.Kr.rgb, theFactor); - float aPt = convolve (theMaterial.Kt.rgb, theFactor); + float aPd = convolve (theMaterial.Kd.rgb, theWeight); + float aPs = convolve (theMaterial.Ks.rgb, theWeight); + float aPr = convolve (theMaterial.Kr.rgb, theWeight); + float aPt = convolve (theMaterial.Kt.rgb, theWeight); float aReflection = aPd + aPs + aPr + aPt; -#ifndef RUSSIAN_ROULETTE - if (aReflection < 1e-2f) - { - return false; // path termination - } -#else - float aSurvival = max (dot (theFactor, LUMA), 0.1f); - - if (RandFloat() > aSurvival) - { - return false; // path termination - } -#endif - - isTransmit = false; - // Choose BSDF component to sample float aKsi = aReflection * RandFloat(); + theBounce = NON_SPECULAR_BOUNCE; + if (aKsi < aPd) // diffuse reflection { sampleLambertianReflection (theOutput, theInput); -#ifndef RUSSIAN_ROULETTE - theWeight = theMaterial.Kd.rgb * (aReflection / aPd); -#else - theWeight = theMaterial.Kd.rgb * (aReflection / aPd / aSurvival); -#endif - - return false; // non-specular bounce + theWeight *= theMaterial.Kd.rgb * (aReflection / aPd); } else if (aKsi < aPd + aPs) // glossy reflection { - theWeight = sampleBlinnReflection (theOutput, theInput, theMaterial.Fresnel, theMaterial.Ks.w); - -#ifndef RUSSIAN_ROULETTE - theWeight *= theMaterial.Ks.rgb * (aReflection / aPs); -#else - theWeight *= theMaterial.Ks.rgb * (aReflection / aPs / aSurvival); -#endif - - return false; // non-specular bounce + theWeight *= theMaterial.Ks.rgb * (aReflection / aPs) * + sampleBlinnReflection (theOutput, theInput, theMaterial.Fresnel, theMaterial.Ks.w); } else if (aKsi < aPd + aPs + aPr) // specular reflection { - theWeight = sampleSpecularReflection (theOutput, theInput, theMaterial.Fresnel); + theWeight *= theMaterial.Kr.rgb * (aReflection / aPr) * + sampleSpecularReflection (theOutput, theInput, theMaterial.Fresnel); -#ifndef RUSSIAN_ROULETTE - theWeight *= theMaterial.Kr.rgb * (aReflection / aPr); -#else - theWeight *= theMaterial.Kr.rgb * (aReflection / aPr / aSurvival); -#endif - - return true; // specular bounce + theBounce = SPEC_REFLECT_BOUNCE; // specular bounce } else // specular transmission { - theWeight = sampleSpecularTransmission (theOutput, theInput, - isTransmit, theFactor, theMaterial.Fresnel); - -#ifndef RUSSIAN_ROULETTE - theWeight *= theMaterial.Kt.rgb * (aReflection / aPt); -#else - theWeight *= theMaterial.Kt.rgb * (aReflection / aPt / aSurvival); -#endif - - return true; // specular bounce + theWeight *= theMaterial.Kt.rgb * (aReflection / aPt) * + sampleSpecularTransmission (theOutput, theInput, theBounce, theWeight, theMaterial.Fresnel); } + + // path termination for extra small weights + theWeight = mix (theWeight, ZERO, float (aReflection < 1e-3f)); } ////////////////////////////////////////////////////////////////////////////////////////////// @@ -532,11 +491,14 @@ bool sampleMaterial (in SMaterial theMaterial, // function : handlePointLight // purpose : //======================================================================= -float handlePointLight (in vec3 theInput, in vec3 theToLight, in float theRadius) +float handlePointLight (in vec3 theInput, in vec3 theToLight, in float theRadius, in float theDistance) { - float aCosMax = sqrt (1.f - theRadius * theRadius / dot (theToLight, theToLight)); + float aDistance = dot (theToLight, theToLight); - return step (aCosMax, dot (theInput, theToLight)); + float aCosMax = inversesqrt (1.f + theRadius * theRadius / aDistance); + + return float (aDistance < theDistance * theDistance) * + step (aCosMax, dot (theToLight, theInput) * inversesqrt (aDistance)); } //======================================================================= @@ -549,47 +511,29 @@ float handleDirectLight (in vec3 theInput, in vec3 theToLight, in float theCosMa } //======================================================================= -// function : samplePointLight -// purpose : +// function : sampleLight +// purpose : general sampling function for directional and point lights //======================================================================= -vec3 samplePointLight (in vec3 theToLight, in float theRadius, inout float thePDF) +vec3 sampleLight (in vec3 theToLight, in bool isDirectional, in float theSmoothness, inout float thePDF) { SLocalSpace aSpace = LocalSpace (theToLight); - float aCosMax = sqrt (1.f - theRadius * theRadius / dot (theToLight, theToLight)); + // for point lights smoothness defines radius + float aCosMax = isDirectional ? theSmoothness : + inversesqrt (1.f + theSmoothness * theSmoothness / dot (theToLight, theToLight)); float aKsi1 = RandFloat(); float aKsi2 = RandFloat(); float aTmp = 1.f - aKsi2 * (1.f - aCosMax); - vec3 anInput = vec3 (sqrt (1.f - aTmp * aTmp) * cos (2.f * M_PI * aKsi1), - sqrt (1.f - aTmp * aTmp) * sin (2.f * M_PI * aKsi1), + vec3 anInput = vec3 (cos (2.f * M_PI * aKsi1), + sin (2.f * M_PI * aKsi1), aTmp); - thePDF *= (theRadius > 0.f) ? 1.f / (2.f * M_PI) / (1.f - aCosMax) : 1.f; + anInput.xy *= sqrt (1.f - aTmp * aTmp); - return normalize (fromLocalSpace (anInput, aSpace)); -} - -//======================================================================= -// function : sampleDirectLight -// purpose : -//======================================================================= -vec3 sampleDirectLight (in vec3 theToLight, in float theCosMax, inout float thePDF) -{ - SLocalSpace aSpace = LocalSpace (theToLight); - - float aKsi1 = RandFloat(); - float aKsi2 = RandFloat(); - - float aTmp = 1.f - aKsi2 * (1.f - theCosMax); - - vec3 anInput = vec3 (sqrt (1.f - aTmp * aTmp) * cos (2.f * M_PI * aKsi1), - sqrt (1.f - aTmp * aTmp) * sin (2.f * M_PI * aKsi1), - aTmp); - - thePDF *= (theCosMax < 1.f) ? 1.f / (2.f * M_PI) / (1.f - theCosMax) : 1.f; + thePDF *= (aCosMax < 1.f) ? 1.f / (2.f * M_PI) / (1.f - aCosMax) : 1.f; return normalize (fromLocalSpace (anInput, aSpace)); } @@ -609,26 +553,26 @@ vec2 Latlong (in vec3 thePoint) } // ======================================================================= -// function : EnvironmentRadiance -// purpose : +// function: intersectLight +// purpose : Checks intersections with light sources // ======================================================================= -vec3 EnvironmentRadiance (in SRay theRay, in bool isSpecular, in bool isBackground) +vec3 intersectLight (in SRay theRay, in bool isViewRay, in int theBounce, in float theDistance) { vec3 aRadiance = ZERO; - if (uSphereMapForBack != 0 || !isBackground) + if ((isViewRay || IS_REFR_SPEC_BOUNCE(theBounce)) && uSphereMapForBack == 0) { - aRadiance += FetchEnvironment (Latlong (theRay.Direct)).xyz; + aRadiance = BackgroundColor().xyz; } else { - aRadiance += BackgroundColor().xyz; + aRadiance = FetchEnvironment (Latlong (theRay.Direct)).xyz; } // Apply gamma correction (gamma is 2) - aRadiance *= aRadiance; + aRadiance = aRadiance * aRadiance * float (theDistance == MAXFLOAT); - for (int aLightIdx = 0; aLightIdx < uLightCount && isSpecular; ++aLightIdx) + for (int aLightIdx = 0; aLightIdx < uLightCount && (isViewRay || IS_ANY_SPEC_BOUNCE(theBounce)); ++aLightIdx) { vec4 aLight = texelFetch ( uRaytraceLightSrcTexture, LIGHT_POS (aLightIdx)); @@ -637,9 +581,10 @@ vec3 EnvironmentRadiance (in SRay theRay, in bool isSpecular, in bool isBackgrou if (aLight.w != 0.f) // point light source { - aRadiance += aParam.rgb * handlePointLight (theRay.Direct, aLight.xyz - theRay.Origin, aParam.w /* radius */); + aRadiance += aParam.rgb * handlePointLight ( + theRay.Direct, aLight.xyz - theRay.Origin, aParam.w /* radius */, theDistance); } - else // directional light source + else if (theDistance == MAXFLOAT) // directional light source { aRadiance += aParam.rgb * handleDirectLight (theRay.Direct, aLight.xyz, aParam.w /* angle cosine */); } @@ -659,6 +604,9 @@ vec3 EnvironmentRadiance (in SRay theRay, in bool isSpecular, in bool isBackgrou #define MATERIAL_FRESNEL(index) (18 * index + 16) #define MATERIAL_ABSORPT(index) (18 * index + 17) +// Enables expiremental russian roulette sampling +#define RUSSIAN_ROULETTE + //======================================================================= // function : PathTrace // purpose : Calculates radiance along the given ray @@ -670,28 +618,31 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse) vec3 aRadiance = ZERO; vec3 aThroughput = UNIT; - bool isInMedium = false; - bool isSpecular = false; - bool isTransmit = false; + int aBounce = 0; // type of previous hit point + int aTrsfId = 0; // offset of object transform - int anObjectId; // ID of intersected triangle + bool isInMedium = false; for (int aDepth = 0; aDepth < NB_BOUNCES; ++aDepth) { SIntersect aHit = SIntersect (MAXFLOAT, vec2 (ZERO), ZERO); - ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, anObjectId); + ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, aTrsfId); - if (aTriIndex.x == -1) + // check implicit path + vec3 aLe = intersectLight (theRay, + aDepth == 0 /* is view ray */, aBounce, aHit.Time); + + if (any (greaterThan (aLe, ZERO)) || aTriIndex.x == -1) { - return vec4 (aRadiance + aThroughput * - EnvironmentRadiance (theRay, isSpecular, aDepth == 0 || isTransmit), 0.f); + aRadiance += aThroughput * aLe; break; // terminate path } - vec3 aInvTransf0 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 0).xyz; - vec3 aInvTransf1 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 1).xyz; - vec3 aInvTransf2 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 2).xyz; + vec3 aInvTransf0 = texelFetch (uSceneTransformTexture, aTrsfId + 0).xyz; + vec3 aInvTransf1 = texelFetch (uSceneTransformTexture, aTrsfId + 1).xyz; + vec3 aInvTransf2 = texelFetch (uSceneTransformTexture, aTrsfId + 2).xyz; + // compute geometrical normal aHit.Normal = normalize (vec3 (dot (aInvTransf0, aHit.Normal), dot (aInvTransf1, aHit.Normal), dot (aInvTransf2, aHit.Normal))); @@ -711,9 +662,9 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse) aThroughput *= aSrcColorRGBA.w; } - theRay.Origin += theRay.Direct * aHit.Time; // intersection point + theRay.Origin += theRay.Direct * aHit.Time; // get new intersection point - // Fetch material (BSDF) + // fetch material (BSDF) SMaterial aMaterial = SMaterial ( vec4 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KD (aTriIndex.w))), vec3 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KR (aTriIndex.w))), @@ -742,6 +693,7 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse) } #endif + // compute smooth normal vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex); aNormal = normalize (vec3 (dot (aInvTransf0, aNormal), @@ -750,7 +702,7 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse) SLocalSpace aSpace = LocalSpace (aNormal); - // Account for self-emission (not stored in the material) + // account for self-emission (not stored in the material) aRadiance += aThroughput * texelFetch ( uRaytraceMaterialTexture, MATERIAL_LE (aTriIndex.w)).rgb; @@ -763,18 +715,13 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse) vec4 aParam = texelFetch ( uRaytraceLightSrcTexture, LIGHT_PWR (aLightIdx)); - float aPDF = 1.f / uLightCount, aDistance = MAXFLOAT; + // 'w' component is 0 for infinite light and 1 for point light + aLight.xyz -= mix (ZERO, theRay.Origin, aLight.w); - if (aLight.w != 0.f) // point light source - { - aDistance = length (aLight.xyz -= theRay.Origin); + float aPDF = 1.f / uLightCount, aDistance = length (aLight.xyz); - aLight.xyz = samplePointLight (aLight.xyz, aParam.w /* radius */, aPDF); - } - else // directional light source - { - aLight.xyz = sampleDirectLight (aLight.xyz, aParam.w /* angle cosine */, aPDF); - } + aLight.xyz = sampleLight (aLight.xyz * (1.f / aDistance), + aLight.w == 0.f /* is infinite */, aParam.w /* angle cosine */, aPDF); vec3 aContrib = (1.f / aPDF) * aParam.rgb /* Le */ * handleMaterial ( aMaterial, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace)); @@ -787,17 +734,16 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse) -uSceneEpsilon, uSceneEpsilon, step (0.f, dot (aHit.Normal, aLight.xyz))); float aVisibility = SceneAnyHit (aShadow, - InverseDirection (aLight.xyz), aDistance); + InverseDirection (aLight.xyz), aLight.w == 0.f ? MAXFLOAT : aDistance); aRadiance += aVisibility * aThroughput * aContrib; } } vec3 anInput; - vec3 aWeight; - isSpecular = sampleMaterial (aMaterial, - toLocalSpace (-theRay.Direct, aSpace), aThroughput, anInput, aWeight, isTransmit); + sampleMaterial (aMaterial, + toLocalSpace (-theRay.Direct, aSpace), anInput, aThroughput, aBounce); if (isInMedium) { @@ -805,14 +751,23 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse) aMaterial.Absorption.w * (UNIT - aMaterial.Absorption.rgb)); } - isInMedium = isTransmit ? !isInMedium : isInMedium; - - aThroughput *= aWeight; + isInMedium = IS_REFR_SPEC_BOUNCE(aBounce) ? !isInMedium : isInMedium; +#ifndef RUSSIAN_ROULETTE if (all (lessThan (aThroughput, MIN_THROUGHPUT))) { - return vec4 (aRadiance, 0.f); + aDepth = INVALID_BOUNCES; // terminate path } +#else + float aSurvive = aDepth < 3 ? 1.f : min (dot (LUMA, aThroughput), 0.95f); + + if (RandFloat() > aSurvive) + { + aDepth = INVALID_BOUNCES; // terminate path + } + + aThroughput /= aSurvive; +#endif anInput = normalize (fromLocalSpace (anInput, aSpace)); diff --git a/src/Shaders/RaytraceBase.fs b/src/Shaders/RaytraceBase.fs index 92bf7557f9..8e62c418ba 100644 --- a/src/Shaders/RaytraceBase.fs +++ b/src/Shaders/RaytraceBase.fs @@ -158,9 +158,9 @@ uint RandState; // purpose : Applies hash function by Thomas Wang to randomize seeds // (see http://www.burtleburtle.net/bob/hash/integer.html) // ======================================================================= -void SeedRand (in int theSeed, in int theSizeX) +void SeedRand (in int theSeed, in int theSizeX, in int theRadius) { - RandState = uint (int (gl_FragCoord.y) * theSizeX + int (gl_FragCoord.x) + theSeed); + RandState = uint (int (gl_FragCoord.y) / theRadius * theSizeX + int (gl_FragCoord.x) / theRadius + theSeed); RandState = (RandState + 0x479ab41du) + (RandState << 8); RandState = (RandState ^ 0xe4aa10ceu) ^ (RandState >> 5); @@ -365,9 +365,7 @@ ivec4 ObjectNearestHit (in int theBVHOffset, in int theVrtOffset, in int theTrgO ivec4 aTriIndex = INALID_HIT; - bool toContinue = true; - - while (toContinue) + for (bool toContinue = true; toContinue;) { ivec3 aData = texelFetch (uSceneNodeInfoTexture, aNode).xyz; @@ -487,11 +485,9 @@ float ObjectAnyHit (in int theBVHOffset, in int theVrtOffset, in int theTrgOffse int aHead = theSentinel; // stack pointer int aNode = theBVHOffset; // node to visit -#ifdef TRANSPARENT_SHADOWS - float aFactor = 1.0f; -#endif + float aFactor = 1.f; - while (true) + for (bool toContinue = true; toContinue;) { ivec4 aData = texelFetch (uSceneNodeInfoTexture, aNode); @@ -545,15 +541,10 @@ float ObjectAnyHit (in int theBVHOffset, in int theVrtOffset, in int theTrgOffse } else { -#ifdef TRANSPARENT_SHADOWS - if (aHead == theSentinel) - return aFactor; -#else - if (aHead == theSentinel) - return 1.0f; -#endif + toContinue = (aHead != theSentinel); - aNode = Stack[aHead--]; + if (toContinue) + aNode = Stack[aHead--]; } } } @@ -580,45 +571,38 @@ float ObjectAnyHit (in int theBVHOffset, in int theVrtOffset, in int theTrgOffse #ifdef TRANSPARENT_SHADOWS if (aTime < theDistance) { - aFactor *= 1.0f - texelFetch (uRaytraceMaterialTexture, MATERIAL_TRAN (aTriangle.w)).x; + aFactor *= 1.f - texelFetch (uRaytraceMaterialTexture, MATERIAL_TRAN (aTriangle.w)).x; } #else if (aTime < theDistance) - return 0.0f; + { + aFactor = 0.f; + } #endif } -#ifdef TRANSPARENT_SHADOWS - if (aHead == theSentinel || aFactor < 0.1f) - return aFactor; -#else - if (aHead == theSentinel) - return 1.0f; -#endif + toContinue = (aHead != theSentinel) && (aFactor > 0.1f); - aNode = Stack[aHead--]; + if (toContinue) + aNode = Stack[aHead--]; } } -#ifdef TRANSPARENT_SHADOWS return aFactor; -#else - return 1.0f; -#endif } // ======================================================================= // function : SceneNearestHit // purpose : Finds intersection with nearest scene triangle // ======================================================================= -ivec4 SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theHit, out int theObjectId) +ivec4 SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theHit, out int theTrsfId) { int aHead = -1; // stack pointer int aNode = 0; // node to visit ivec4 aHitObject = INALID_HIT; - while (true) + for (bool toContinue = true; toContinue;) { ivec4 aData = texelFetch (uSceneNodeInfoTexture, aNode); @@ -635,12 +619,12 @@ ivec4 SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theH if (max (aTimes.x, max (aTimes.y, aTimes.z)) < theHit.Time) { // fetch object transformation - int anObjectId = aData.x - 1; + int aTrsfId = (aData.x - 1) * 4; - vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 0); - vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 1); - vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 2); - vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 3); + vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, aTrsfId + 0); + vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, aTrsfId + 1); + vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, aTrsfId + 2); + vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, aTrsfId + 3); SRay aTrsfRay = SRay ( MatrixColMultiplyPnt (theRay.Origin, aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3), @@ -660,14 +644,14 @@ ivec4 SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theH aTriIndex.z, // vertex 2 aTriIndex.w); // material - theObjectId = anObjectId; + theTrsfId = aTrsfId; } } - if (aHead < 0) - return aHitObject; + toContinue = aHead >= 0; - aNode = Stack[aHead--]; + if (toContinue) + aNode = Stack[aHead--]; } else // if inner node { @@ -716,10 +700,10 @@ ivec4 SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theH } else { - if (aHead < 0) - return aHitObject; + toContinue = aHead >= 0; - aNode = Stack[aHead--]; + if (toContinue) + aNode = Stack[aHead--]; } } } @@ -737,23 +721,21 @@ float SceneAnyHit (in SRay theRay, in vec3 theInverse, in float theDistance) int aHead = -1; // stack pointer int aNode = 0; // node to visit -#ifdef TRANSPARENT_SHADOWS - float aFactor = 1.0f; -#endif + float aFactor = 1.f; - while (true) + for (bool toContinue = true; toContinue;) { ivec4 aData = texelFetch (uSceneNodeInfoTexture, aNode); if (aData.x != 0) // if leaf node { // fetch object transformation - int anObjectId = aData.x - 1; + int aTrsfId = (aData.x - 1) * 4; - vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 0); - vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 1); - vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 2); - vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 3); + vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, aTrsfId + 0); + vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, aTrsfId + 1); + vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, aTrsfId + 2); + vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, aTrsfId + 3); SRay aTrsfRay = SRay ( MatrixColMultiplyPnt (theRay.Origin, aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3), @@ -767,17 +749,16 @@ float SceneAnyHit (in SRay theRay, in vec3 theInverse, in float theDistance) aFactor *= ObjectAnyHit ( aData.y, aData.z, aData.w, aTrsfRay, aTrsfInverse, theDistance, aHead); - if (aHead < 0 || aFactor < 0.1f) - return aFactor; + toContinue = aHead >= 0 && aFactor >= 0.1f; #else - bool isShadow = 0.0f == ObjectAnyHit ( + aFactor = ObjectAnyHit ( aData.y, aData.z, aData.w, aTrsfRay, aTrsfInverse, theDistance, aHead); - if (aHead < 0 || isShadow) - return isShadow ? 0.0f : 1.0f; + toContinue = aHead >= 0 && aFactor != 0.0f; #endif - aNode = Stack[aHead--]; + if (toContinue) + aNode = Stack[aHead--]; } else // if inner node { @@ -789,7 +770,7 @@ float SceneAnyHit (in SRay theRay, in vec3 theInverse, in float theDistance) vec3 aNodeMaxLft = texelFetch (uSceneMaxPointTexture, aData.y).xyz; vec3 aNodeMinRgh = texelFetch (uSceneMinPointTexture, aData.z).xyz; vec3 aNodeMaxRgh = texelFetch (uSceneMaxPointTexture, aData.z).xyz; - + vec3 aTime0 = (aNodeMinLft - theRay.Origin) * theInverse; vec3 aTime1 = (aNodeMaxLft - theRay.Origin) * theInverse; @@ -800,7 +781,7 @@ float SceneAnyHit (in SRay theRay, in vec3 theInverse, in float theDistance) aTimeLft = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z)); int aHitLft = int(aTimeLft <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeLft <= theDistance); - + aTime0 = (aNodeMinRgh - theRay.Origin) * theInverse; aTime1 = (aNodeMaxRgh - theRay.Origin) * theInverse; @@ -809,7 +790,7 @@ float SceneAnyHit (in SRay theRay, in vec3 theInverse, in float theDistance) aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z)); aTimeRgh = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z)); - + int aHitRgh = int(aTimeRgh <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeRgh <= theDistance); if (bool(aHitLft & aHitRgh)) @@ -826,25 +807,16 @@ float SceneAnyHit (in SRay theRay, in vec3 theInverse, in float theDistance) } else { -#ifdef TRANSPARENT_SHADOWS - if (aHead < 0) - return aFactor; -#else - if (aHead < 0) - return 1.0f; -#endif + toContinue = aHead >= 0; - aNode = Stack[aHead--]; + if (toContinue) + aNode = Stack[aHead--]; } } } } -#ifdef TRANSPARENT_SHADOWS return aFactor; -#else - return 1.0f; -#endif } #define PI 3.1415926f @@ -955,7 +927,7 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse) vec3 aResult = vec3 (0.0f); vec4 aWeight = vec4 (1.0f); - int anObjectId; + int aTrsfId; float anOpenGlDepth = ComputeOpenGlDepth (theRay); @@ -963,7 +935,7 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse) { SIntersect aHit = SIntersect (MAXFLOAT, vec2 (ZERO), ZERO); - ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, anObjectId); + ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, aTrsfId); if (aTriIndex.x == -1) { @@ -982,12 +954,14 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse) aColor = vec4 (BackgroundColor().rgb * aGlColor.w + ComputeOpenGlColor().rgb, aGlColor.w); } - return vec4 (aResult.xyz + aWeight.xyz * aColor.xyz, aWeight.w * aColor.w); + aResult += aWeight.xyz * aColor.xyz; aWeight.w *= aColor.w; + + break; // terminate path } - vec3 aInvTransf0 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 0).xyz; - vec3 aInvTransf1 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 1).xyz; - vec3 aInvTransf2 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 2).xyz; + vec3 aInvTransf0 = texelFetch (uSceneTransformTexture, aTrsfId + 0).xyz; + vec3 aInvTransf1 = texelFetch (uSceneTransformTexture, aTrsfId + 1).xyz; + vec3 aInvTransf2 = texelFetch (uSceneTransformTexture, aTrsfId + 2).xyz; aHit.Normal = normalize (vec3 (dot (aInvTransf0, aHit.Normal), dot (aInvTransf1, aHit.Normal), diff --git a/src/Shaders/RaytraceRender.fs b/src/Shaders/RaytraceRender.fs index a15ddd23ab..25c9bbec19 100644 --- a/src/Shaders/RaytraceRender.fs +++ b/src/Shaders/RaytraceRender.fs @@ -9,6 +9,12 @@ uniform float uSampleWeight; //! Input accumulated image. uniform sampler2D uAccumTexture; +//! Enabled/disbales using of single RNG seed for image 16x16 blocks. +//! Increases performance up to 4 times, but noise becomes structured. +uniform int uBlockedRngEnabled; + +#define MAX_RADIANCE vec3 (10.f) + // ======================================================================= // function : main // purpose : @@ -20,7 +26,7 @@ void main (void) #else ivec2 aWinSize = textureSize (uAccumTexture, 0); - SeedRand (uFrameRndSeed, aWinSize.x); + SeedRand (uFrameRndSeed, aWinSize.x, uBlockedRngEnabled == 0 ? 1 : 16); SRay aRay = GenerateRay (vPixel + vec2 (RandFloat() + 1.f, RandFloat() + 1.f) / vec2 (aWinSize)); @@ -33,19 +39,17 @@ void main (void) aRay.Direct.z < 0.f ? -aInvDirect.z : aInvDirect.z); #ifdef PATH_TRACING - vec4 aColor = PathTrace (aRay, aInvDirect); if (any (isnan (aColor.xyz))) { - aColor.xyz = ZERO; + aColor.rgb = ZERO; } + aColor.rgb = min (aColor.rgb, MAX_RADIANCE); + OutColor = mix (texture2D (uAccumTexture, vPixel), aColor, uSampleWeight); - #else - OutColor = clamp (Radiance (aRay, aInvDirect), 0.f, 1.f); - #endif } \ No newline at end of file diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx index 3bfc9cf298..a3ebb4b983 100644 --- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx +++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx @@ -5320,6 +5320,22 @@ static int VFps (Draw_Interpretor& theDI, theDI << "FPS: " << aFpsAver << "\n" << "CPU: " << (1000.0 * aCpuAver) << " msec\n"; + // compute additional statistics in ray-tracing mode + Graphic3d_RenderingParams& aParams = aView->ChangeRenderingParams(); + + if (aParams.Method == Graphic3d_RM_RAYTRACING) + { + Standard_Integer aSizeX; + Standard_Integer aSizeY; + + aView->Window()->Size (aSizeX, aSizeY); + + // 1 shadow ray and 1 secondary ray pew each bounce + const Standard_Real aMRays = aSizeX * aSizeY * aFpsAver * aParams.RaytracingDepth * 2 / 1.0e6f; + + theDI << "MRays/sec (upper bound): " << aMRays << "\n"; + } + return 0; } @@ -8389,6 +8405,7 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI, theDI << "rayDepth: " << aParams.RaytracingDepth << "\n"; theDI << "gleam: " << (aParams.IsTransparentShadowEnabled ? "on" : "off") << "\n"; theDI << "GI: " << (aParams.IsGlobalIlluminationEnabled ? "on" : "off") << "\n"; + theDI << "blocked RNG: " << (aParams.CoherentPathTracingMode ? "on" : "off") << "\n"; theDI << "shadingModel: "; switch (aView->ShadingModel()) { @@ -8573,6 +8590,23 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI, aParams.RaytracingDepth = Min (aParams.RaytracingDepth, 10); } } + else if (aFlag == "-blockedrng" + || aFlag == "-brng") + { + if (toPrint) + { + theDI << (aParams.CoherentPathTracingMode ? "on" : "off") << " "; + continue; + } + + Standard_Boolean toEnable = Standard_True; + if (++anArgIter < theArgNb + && !parseOnOff (theArgVec[anArgIter], toEnable)) + { + --anArgIter; + } + aParams.CoherentPathTracingMode = toEnable; + } else if (aFlag == "-env") { if (toPrint) @@ -9360,6 +9394,7 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands) "\n '-fsaa on|off' Enables/disables adaptive anti-aliasing" "\n '-gleam on|off' Enables/disables transparency shadow effects" "\n '-gi on|off' Enables/disables global illumination effects" + "\n '-brng on|off' Enables/disables blocked RNG (fast coherent PT)" "\n '-env on|off' Enables/disables environment map background" "\n '-shadingModel model' Controls shading model from enumeration" "\n color, flat, gouraud, phong"