Просмотр исходного кода

Fixing & improving renderable culling

BearishSun 9 лет назад
Родитель
Сommit
a6d8f683f3

+ 2 - 2
Source/RenderBeast/Include/BsRenderBeast.h

@@ -191,8 +191,8 @@ namespace bs
 		UnorderedMap<SamplerOverrideKey, MaterialSamplerOverrides*> mSamplerOverrides;
 
 		Vector<RendererObject*> mRenderables;
-		Vector<Bounds> mWorldBounds;
-		Vector<bool> mVisibility; // Transient
+		Vector<CullInfo> mRenderableCullInfos;
+		Vector<bool> mRenderableVisibility; // Transient
 
 		Vector<RendererLight> mDirectionalLights;
 		Vector<RendererLight> mPointLights;

+ 30 - 6
Source/RenderBeast/Include/BsRendererCamera.h

@@ -91,6 +91,24 @@ namespace bs { namespace ct
 		SPtr<Texture> skyboxTexture;
 	};
 
+	/** Information whether certain scene objects are visible in a view, per object type. */
+	struct VisibilityInfo
+	{
+		Vector<bool> renderables;
+		Vector<bool> lights;
+	};
+
+	/** Information used for culling an object against a view. */
+	struct CullInfo
+	{
+		CullInfo(const Bounds& bounds, UINT64 layer = -1)
+			:bounds(bounds), layer(layer)
+		{ }
+
+		Bounds bounds;
+		UINT64 layer;
+	};
+
 	/** Contains information about a Camera, used by the Renderer. */
 	class RendererCamera
 	{
@@ -106,7 +124,7 @@ namespace bs { namespace ct
 
 		/** Updates the internal information with a new view transform. */
 		void setTransform(const Vector3& origin, const Vector3& direction, const Matrix4& view,
-						  const Matrix4& proj);
+						  const Matrix4& proj, const ConvexVolume& worldFrustum);
 
 		/** Updates all internal information with new view information. */
 		void setView(const RENDERER_VIEW_DESC& desc);
@@ -174,8 +192,8 @@ namespace bs { namespace ct
 		 * Populates view render queues by determining visible renderable objects. 
 		 *
 		 * @param[in]	renderables			A set of renderable objects to iterate over and determine visibility for.
-		 * @param[in]	renderableBounds	A set of world bounds for the provided renderable objects. Must be the same size
-		 *									as the @p renderables array.
+		 * @param[in]	cullInfos			A set of world bounds & other information relevant for culling the provided
+		 *									renderable objects. Must be the same size as the @p renderables array.
 		 * @param[out]	visibility			Output parameter that will have the true bit set for any visible renderable
 		 *									object. If the bit for an object is already set to true, the method will never
 		 *									change it to false which allows the same bitfield to be provided to multiple
@@ -184,11 +202,17 @@ namespace bs { namespace ct
 		 *									As a side-effect, per-view visibility data is also calculated and can be
 		 *									retrieved by calling getVisibilityMask().
 		 */
-		void determineVisible(const Vector<RendererObject*>& renderables, const Vector<Bounds>& renderableBounds, 
+		void determineVisible(const Vector<RendererObject*>& renderables, const Vector<CullInfo>& cullInfos,
 			Vector<bool>* visibility = nullptr);
 
+		/**
+		 * Culls the provided set of bounds against the current frustum and outputs a set of visibility flags determining
+		 * which entry is or ins't visible by this view. Both inputs must be arrays of the same size.
+		 */
+		void calculateVisibility(const Vector<CullInfo>& cullInfos, Vector<bool>& visibility) const;
+
 		/** Returns the visibility mask calculated with the last call to determineVisible(). */
-		const Vector<bool>& getVisibilityMask() const { return mVisibility; }
+		const VisibilityInfo& getVisibilityMasks() const { return mVisibility; }
 
 		/** 
 		 * Returns a structure containing information about post-processing effects. This structure will be modified and
@@ -223,7 +247,7 @@ namespace bs { namespace ct
 		bool mUsingRenderTargets;
 
 		SPtr<GpuParamBlockBuffer> mParamBuffer;
-		Vector<bool> mVisibility;
+		VisibilityInfo mVisibility;
 	};
 
 	/** @} */

+ 21 - 14
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -96,7 +96,7 @@ namespace bs { namespace ct
 		mRenderTargets.clear();
 		mCameras.clear();
 		mRenderables.clear();
-		mVisibility.clear();
+		mRenderableVisibility.clear();
 
 		PostProcessing::shutDown();
 		RenderTexturePool::shutDown();
@@ -119,8 +119,8 @@ namespace bs { namespace ct
 		renderable->setRendererId(renderableId);
 
 		mRenderables.push_back(bs_new<RendererObject>());
-		mWorldBounds.push_back(renderable->getBounds());
-		mVisibility.push_back(false);
+		mRenderableCullInfos.push_back(CullInfo(renderable->getBounds(), renderable->getLayer()));
+		mRenderableVisibility.push_back(false);
 
 		RendererObject* rendererObject = mRenderables.back();
 		rendererObject->renderable = renderable;
@@ -271,7 +271,7 @@ namespace bs { namespace ct
 		{
 			// Swap current last element with the one we want to erase
 			std::swap(mRenderables[renderableId], mRenderables[lastRenderableId]);
-			std::swap(mWorldBounds[renderableId], mWorldBounds[lastRenderableId]);
+			std::swap(mRenderableCullInfos[renderableId], mRenderableCullInfos[lastRenderableId]);
 
 			lastRenerable->setRendererId(renderableId);
 
@@ -281,8 +281,8 @@ namespace bs { namespace ct
 
 		// Last element is the one we want to erase
 		mRenderables.erase(mRenderables.end() - 1);
-		mWorldBounds.erase(mWorldBounds.end() - 1);
-		mVisibility.erase(mVisibility.end() - 1);
+		mRenderableCullInfos.erase(mRenderableCullInfos.end() - 1);
+		mRenderableVisibility.erase(mRenderableVisibility.end() - 1);
 
 		bs_delete(rendererObject);
 	}
@@ -292,7 +292,7 @@ namespace bs { namespace ct
 		UINT32 renderableId = renderable->getRendererId();
 
 		mRenderables[renderableId]->updatePerObjectBuffer();
-		mWorldBounds[renderableId] = renderable->getBounds();
+		mRenderableCullInfos[renderableId].bounds = renderable->getBounds();
 	}
 
 	void RenderBeast::notifyLightAdded(Light* light)
@@ -394,7 +394,8 @@ namespace bs { namespace ct
 				camera->getPosition(),
 				camera->getForward(),
 				camera->getViewMatrix(),
-				camera->getProjectionMatrixRS());
+				camera->getProjectionMatrixRS(),
+				camera->getWorldFrustum());
 		}
 
 		rendererCam->updatePerViewBuffer();
@@ -609,6 +610,8 @@ namespace bs { namespace ct
 		gCoreThread().queueCommand(std::bind(&RenderBeast::renderAllCore, this, gTime().getTime(), gTime().getFrameDelta()));
 	}
 
+	static SPtr<Texture> dbgSkyTex;
+
 	void RenderBeast::renderAllCore(float time, float delta)
 	{
 		THROW_IF_NOT_CORE_THREAD;
@@ -625,10 +628,10 @@ namespace bs { namespace ct
 		mObjectRenderer->setParamFrameParams(time);
 
 		// Generate render queues per camera
-		mVisibility.assign(mVisibility.size(), false);
+		mRenderableVisibility.assign(mRenderableVisibility.size(), false);
 
 		for (auto& entry : mCameras)
-			entry.second->determineVisible(mRenderables, mWorldBounds, &mVisibility);
+			entry.second->determineVisible(mRenderables, mRenderableCullInfos, &mRenderableVisibility);
 
 		// Retrieve animation data
 		AnimationManager::instance().waitUntilComplete();
@@ -638,7 +641,7 @@ namespace bs { namespace ct
 		UINT32 numRenderables = (UINT32)mRenderables.size();
 		for (UINT32 i = 0; i < numRenderables; i++)
 		{
-			if (!mVisibility[i])
+			if (!mRenderableVisibility[i])
 				continue;
 
 			// Note: Before uploading bone matrices perhaps check if they has actually been changed since last frame
@@ -651,6 +654,9 @@ namespace bs { namespace ct
 
 			mRenderables[i]->perObjectParamBuffer->flushToGPU();
 		}
+		
+		//if (dbgSkyTex == nullptr)
+		//	dbgSkyTex = captureSceneCubeMap(Vector3(0, 2, 0), true, 1024);
 
 		// Render everything, target by target
 		for (auto& rtInfo : mRenderTargets)
@@ -701,11 +707,11 @@ namespace bs { namespace ct
 		Matrix4 viewProj = viewInfo->getViewProjMatrix();
 
 		// Assign camera and per-call data to all relevant renderables
-		const Vector<bool>& visibility = viewInfo->getVisibilityMask();
+		const VisibilityInfo& visibility = viewInfo->getVisibilityMasks();
 		UINT32 numRenderables = (UINT32)mRenderables.size();
 		for (UINT32 i = 0; i < numRenderables; i++)
 		{
-			if (!visibility[i])
+			if (!visibility.renderables[i])
 				continue;
 
 			RendererObject* rendererObject = mRenderables[i];
@@ -827,6 +833,7 @@ namespace bs { namespace ct
 		// Render skybox (if any)
 		SPtr<Texture> skyTexture = viewInfo->getSkybox();
 		if (skyTexture != nullptr && skyTexture->getProperties().getTextureType() == TEX_TYPE_CUBE_MAP)
+		//if (dbgSkyTex != nullptr)
 		{
 			mSkyboxMat->bind(perCameraBuffer);
 			mSkyboxMat->setParams(skyTexture);
@@ -1101,7 +1108,7 @@ namespace bs { namespace ct
 
 			view.setView(viewDesc);
 			view.updatePerViewBuffer();
-			view.determineVisible(mRenderables, mWorldBounds);
+			view.determineVisible(mRenderables, mRenderableCullInfos);
 
 			render(&view, 0.0f);
 		}

+ 47 - 37
Source/RenderBeast/Source/BsRendererCamera.cpp

@@ -77,12 +77,14 @@ namespace bs { namespace ct
 		mPostProcessInfo.settingDirty = true;
 	}
 
-	void RendererCamera::setTransform(const Vector3& origin, const Vector3& direction, const Matrix4& view, const Matrix4& proj)
+	void RendererCamera::setTransform(const Vector3& origin, const Vector3& direction, const Matrix4& view, 
+									  const Matrix4& proj, const ConvexVolume& worldFrustum)
 	{
 		mViewDesc.viewOrigin = origin;
 		mViewDesc.viewDirection = direction;
 		mViewDesc.viewTransform = view;
 		mViewDesc.projTransform = proj;
+		mViewDesc.cullFrustum = worldFrustum;
 	}
 
 	void RendererCamera::setView(const RENDERER_VIEW_DESC& desc)
@@ -119,53 +121,36 @@ namespace bs { namespace ct
 		}
 	}
 
-	void RendererCamera::determineVisible(const Vector<RendererObject*>& renderables, const Vector<Bounds>& renderableBounds,
+	void RendererCamera::determineVisible(const Vector<RendererObject*>& renderables, const Vector<CullInfo>& cullInfos,
 		Vector<bool>* visibility)
 	{
-		mVisibility.clear();
-		mVisibility.resize(renderables.size(), false);
+		mVisibility.renderables.clear();
+		mVisibility.renderables.resize(renderables.size(), false);
 
 		if (mViewDesc.isOverlay)
 			return;
 
-		UINT64 cameraLayers = mViewDesc.visibleLayers;
-		const ConvexVolume& worldFrustum = mViewDesc.cullFrustum;
+		calculateVisibility(cullInfos, mVisibility.renderables);
 
 		// Update per-object param buffers and queue render elements
-		for(UINT32 i = 0; i < (UINT32)renderables.size(); i++)
+		for(UINT32 i = 0; i < (UINT32)cullInfos.size(); i++)
 		{
-			Renderable* renderable = renderables[i]->renderable;
-			UINT32 rendererId = renderable->getRendererId();
-
-			if ((renderable->getLayer() & cameraLayers) == 0)
+			if (!mVisibility.renderables[i])
 				continue;
 
-			// Do frustum culling
-			// Note: This is bound to be a bottleneck at some point. When it is ensure that intersect methods use vector
-			// operations, as it is trivial to update them. Also consider spatial partitioning.
-			const Sphere& boundingSphere = renderableBounds[rendererId].getSphere();
-			if (worldFrustum.intersects(boundingSphere))
-			{
-				// More precise with the box
-				const AABox& boundingBox = renderableBounds[rendererId].getBox();
+			const AABox& boundingBox = cullInfos[i].bounds.getBox();
+			float distanceToCamera = (mViewDesc.viewOrigin - boundingBox.getCenter()).length();
 
-				if (worldFrustum.intersects(boundingBox))
-				{
-					mVisibility[i] = true;
-
-					float distanceToCamera = (mViewDesc.viewOrigin - boundingBox.getCenter()).length();
-
-					for (auto& renderElem : renderables[i]->elements)
-					{
-						bool isTransparent = (renderElem.material->getShader()->getFlags() & (UINT32)ShaderFlags::Transparent) != 0;
-
-						if (isTransparent)
-							mTransparentQueue->add(&renderElem, distanceToCamera);
-						else
-							mOpaqueQueue->add(&renderElem, distanceToCamera);
-					}
-
-				}
+			for (auto& renderElem : renderables[i]->elements)
+			{
+				// Note: I could keep opaque and transparent renderables in two separate arrays, so I don't need to do the
+				// check here
+				bool isTransparent = (renderElem.material->getShader()->getFlags() & (UINT32)ShaderFlags::Transparent) != 0;
+
+				if (isTransparent)
+					mTransparentQueue->add(&renderElem, distanceToCamera);
+				else
+					mOpaqueQueue->add(&renderElem, distanceToCamera);
 			}
 		}
 
@@ -175,7 +160,7 @@ namespace bs { namespace ct
 			{
 				bool visible = (*visibility)[i];
 
-				(*visibility)[i] = visible || mVisibility[i];
+				(*visibility)[i] = visible || mVisibility.renderables[i];
 			}
 		}
 
@@ -183,6 +168,31 @@ namespace bs { namespace ct
 		mTransparentQueue->sort();
 	}
 
+	void RendererCamera::calculateVisibility(const Vector<CullInfo>& cullInfos, Vector<bool>& visibility) const
+	{
+		UINT64 cameraLayers = mViewDesc.visibleLayers;
+		const ConvexVolume& worldFrustum = mViewDesc.cullFrustum;
+
+		for (UINT32 i = 0; i < (UINT32)cullInfos.size(); i++)
+		{
+			if ((cullInfos[i].layer & cameraLayers) == 0)
+				continue;
+
+			// Do frustum culling
+			// Note: This is bound to be a bottleneck at some point. When it is ensure that intersect methods use vector
+			// operations, as it is trivial to update them. Also consider spatial partitioning.
+			const Sphere& boundingSphere = cullInfos[i].bounds.getSphere();
+			if (worldFrustum.intersects(boundingSphere))
+			{
+				// More precise with the box
+				const AABox& boundingBox = cullInfos[i].bounds.getBox();
+
+				if (worldFrustum.intersects(boundingBox))
+					visibility[i] = true;
+			}
+		}
+	}
+
 	Vector2 RendererCamera::getDeviceZTransform(const Matrix4& projMatrix) const
 	{
 		// Returns a set of values that will transform depth buffer values (e.g. [0, 1] in DX, [-1, 1] in GL) to a distance