auto culling

auto culling
自动剪裁,也就是把超出屏幕的quad自动移出渲染队列。我们的渲染关键是两个,一个是quad队列,一个是cmd队列。
void Renderer::renderBatch()
	{
		
		_drawnBatches = _drawnVertices = 0;
		if(_numQuads <= 0 || _batchedQuadCommands.empty())
		{
			return;
		}

		glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);
		glBufferData(GL_ARRAY_BUFFER, sizeof(_quads[0]) * _numQuads , _quads, GL_DYNAMIC_DRAW);
		
		
		
		glEnableVertexAttribArray(_vertexPosition); 	
		glVertexAttribPointer(
			_vertexPosition,			// attribute 0. No particular reason for 0, but must match the layout in the shader.
			3,			// size
			GL_FLOAT,	// type
			GL_FALSE,	// normalized?
			kQuadSize,			// stride
			(void*)(offsetof( Vertex, _position))	// array buffer offset
			);
		
		glEnableVertexAttribArray(_vertexUV); 	
		glVertexAttribPointer(
			_vertexUV,			// attribute 0. No particular reason for 0, but must match the layout in the shader.
			2,			// size
			GL_FLOAT,	// type
			GL_FALSE,	// normalized?
			kQuadSize,			// stride
			(void*)(offsetof( Vertex, _texCoord))	// array buffer offset
			);


			
		glEnableVertexAttribArray(_vertexColor); 				
		glVertexAttribPointer(
			_vertexColor,			// attribute 0. No particular reason for 0, but must match the layout in the shader.
			4,			// size
			GL_FLOAT,	// type
			GL_FALSE,	// normalized?
			kQuadSize,			// stride
			(void*)(offsetof( Vertex, _color))	// array buffer offset
			);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);
		glActiveTexture(GL_TEXTURE0);
		int quadsToDraw = 0;
		int startQuad = 0;

		GLuint _lastTextureID = 0;

		//Start drawing verties in batch
		for(const auto& cmd : _batchedQuadCommands)
		{
			auto newTextureID = cmd->getTextureID();
			if(_lastTextureID != newTextureID )
			{
				//Draw quads
				if(quadsToDraw > 0)
				{
					glDrawElements(GL_TRIANGLES, (GLsizei) quadsToDraw*6, GL_UNSIGNED_SHORT, (GLvoid*) (startQuad*6*sizeof(_indices[0])) );			
					_drawnBatches++;
					_drawnVertices += quadsToDraw*6;


					startQuad += quadsToDraw;
					quadsToDraw = 0;
				}

				//Use new Texture
				cmd->useTexture();
				//优化后直接给单位矩阵
				//caculateMVP(cmd->getModel());
				caculateMVP(Mat4(1.0));
				_lastTextureID = newTextureID;
			}

			quadsToDraw += cmd->getQuadCount();
		}





		if(quadsToDraw > 0)
		{			
			
			glDrawElements(GL_TRIANGLES, (GLsizei) quadsToDraw*6, GL_UNSIGNED_SHORT, (GLvoid*) (startQuad*6*sizeof(_indices[0])) );

			_drawnBatches++;
			_drawnVertices += quadsToDraw*6;
		}		
		for(auto& cmd : _batchedQuadCommands)
		{
			delete cmd;
		}
		_batchedQuadCommands.clear();
		_numQuads = 0;
	}

void Renderer::visit()
	{
 		int queueSize = _renderQueue.size();
		float perZorder = (zFar - zNear)/(float)(queueSize+1) ;

		//优化排序
		for (int i = 0; i < queueSize; ++i)
		{
			QuadCommand* cmd = _renderQueue[i];			
			Quad* quad = cmd->getQuads();
			int quadCount = cmd->getQuadCount();
			float zOrder = perZorder * (queueSize - i);
			for (int j = 0; j < quadCount; ++j)
			{				
				quad[j].tl._position.z = zOrder;
				quad[j].bl._position.z = zOrder;
				quad[j].tr._position.z = zOrder;
				quad[j].br._position.z = zOrder;
			}

			
		}		


		std::sort( std::begin(_renderQueue), std::end(_renderQueue),quadComparisonLess);

		//复制到批渲染队列
		copyToBatchRender(queueSize);

		//剪裁超出屏幕的quad
		cullQuadForBatchRender();
	}

void Renderer::copyToBatchRender( int queueSize )
	{
		for (int i = 0; i < queueSize; ++i)
		{
			QuadCommand* cmd = _renderQueue[i];
			memcpy(_preCullQuads + _preNumQuads, cmd->getQuads(), sizeof(Quad) * cmd->getQuadCount());
			convertToWorldCoordinates(_preCullQuads + _preNumQuads, cmd->getQuadCount(), cmd->getModel());
			BatchCommand* batchCommand = new BatchCommand();			
			memcpy(batchCommand,(BatchCommand*)cmd,sizeof(BatchCommand));			
			_batchedQuadCommands.push_back(batchCommand);
			_preNumQuads += cmd->getQuadCount();
		}		
		_renderQueue.clear();
	}
我们把渲染quad复制到一个临时块中,然后检测后复制到渲染队列中。
void Renderer::cullQuadForBatchRender()
	{
		if(_preNumQuads <= 0 || _batchedQuadCommands.empty())
		{
			return;
		}
		
		int quadIndex = 0;
		int startQuadNumber = 0;
		int cullQuadNumber = 0;
		Size winSize = Engine::getInstance()->getDesignSize();
		Rect rectWindow(-winSize.width/2 ,-winSize.height/2 ,winSize.width,winSize.height);

		std::vector<BatchCommand*>::iterator cmd = _batchedQuadCommands.begin();
		while (quadIndex < _preNumQuads)
		{
			Quad *q = &_preCullQuads[quadIndex];		
			

			Rect rectQuad(q->bl._position.x,q->bl._position.y,q->br._position.x - q->bl._position.x, q->tl._position.y - q->bl._position.y);
			if(rectWindow.intersectsRect(rectQuad))
			{
				memcpy(_quads+_numQuads,q,sizeof(Quad));
			}
			else
			{
				++cullQuadNumber;
			}

			++startQuadNumber;
			if(startQuadNumber == (*cmd)->getQuadCount())
			{
				int quadCount = startQuadNumber - cullQuadNumber;
				if (quadCount < 1)
				{
					cmd = _batchedQuadCommands.erase(cmd);
				}
				else
				{
					(*cmd)->setQuadCount(quadCount);
					++cmd; 
				}
				
				_numQuads -= cullQuadNumber;
				startQuadNumber = 0;
				cullQuadNumber = 0;
			}
			
			++quadIndex;
			++_numQuads;
			
		}
		

		_preNumQuads = 0;
	}



### 关于 Unity 遮挡剔除 (Occlusion Culling) 遮挡剔除是一种优化技术,用于减少场景中的绘制调用次数。通过计算哪些对象被其他对象遮挡住并不可见,从而避免对这些隐藏的对象进行不必要的渲染操作。这可以显著提高性能,尤其是在复杂场景中。 在 Unity 中实现遮挡剔除涉及几个关键步骤和注意事项: #### 启用 Occlusion Culling 要在项目中启用遮挡剔除功能,需进入 **Window > Rendering > Lighting Settings** 并勾选 **Auto Generate** 和 **Occlusion Culling** 选项[^1]。此设置会触发烘焙过程,在该过程中 Unity 将分析场景几何体以生成可见性数据。 #### 场景准备 为了获得最佳效果,开发者应确保场景满足以下条件: - 所有静态物体都标记为 `Static` 属性下的 `Occluder Static` 或 `Occludee Static` 类型。 - 动态物体不会参与遮挡剔除逻辑;因此建议尽可能多地将环境设为静态以便充分利用这一特性[^2]。 #### 性能考量 尽管遮挡剔除能够有效降低帧率消耗,但也存在一定的开销成本需要注意: - 烘焙时间可能较长,特别是对于大型复杂的地图来说更是如此; - 运行时内存占用增加因为需要存储额外的数据结构来表示可视区域关系图谱。 #### 常见问题及解决方案 1. **为什么我的某些物体即使完全被遮蔽仍然会被渲染?** 如果发现特定模型未能正常响应遮挡剔除机制,则可能是由于其未正确定义为静止状态或者LOD组配置不当所致。确认所有目标实体均已正确分类,并调整相机视锥裁剪参数至合理范围之内即可解决问题[^3]。 2. **如何调试 Occlusion Culling 的表现情况?** 使用 Scene 视图底部工具栏里的 Debug 模式切换按钮可以选择显示 "Occlusion Culling" 调试可视化信息。它将以不同颜色高亮展示当前摄像机视角下各个部分的状态——绿色代表可看见的部分而红色则意味着这部分处于遮掩之中不应该被执行渲染指令[^4]。 ```python // 示例代码片段:检测某个游戏对象是否受到遮挡影响 using UnityEngine; public class CheckIfObjectIsCulled : MonoBehaviour { void Update() { if (!Camera.current.IsVisible(gameObject)) { Debug.Log("This object is currently culled."); } } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值