Boxf SphereCollider3D::ComputeAABB(const Matrix4f& offsetMatrix, const Vector3f& scale) const { Vector3f size(m_radius * NazaraSuffixMacro(M_SQRT3, f) * scale); Vector3f position(offsetMatrix.GetTranslation()); return Boxf(position - size, position + size); }
void NzForwardRenderTechnique::DrawTransparentModels(const NzScene* scene) const { NzAbstractViewer* viewer = scene->GetViewer(); const NzShader* lastShader = nullptr; const ShaderUniforms* shaderUniforms = nullptr; unsigned int lightCount = 0; for (unsigned int index : m_renderQueue.transparentModels) { const NzForwardRenderQueue::TransparentModelData& modelData = m_renderQueue.transparentModelData[index]; // Matériau const NzMaterial* material = modelData.material; // On commence par appliquer du matériau (et récupérer le shader ainsi activé) const NzShader* shader = material->Apply(); // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas if (shader != lastShader) { // Index des uniformes dans le shader shaderUniforms = GetShaderUniforms(shader); // Couleur ambiante de la scène shader->SendColor(shaderUniforms->sceneAmbient, scene->GetAmbientColor()); // Position de la caméra shader->SendVector(shaderUniforms->eyePosition, viewer->GetEyePosition()); // On envoie les lumières directionnelles s'il y a (Les mêmes pour tous) if (shaderUniforms->hasLightUniforms) { lightCount = std::min(m_directionalLights.GetLightCount(), NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U)); for (unsigned int i = 0; i < lightCount; ++i) m_directionalLights.GetLight(i)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i); } lastShader = shader; } // Mesh const NzMatrix4f& matrix = modelData.transformMatrix; const NzMeshData& meshData = modelData.meshData; const NzIndexBuffer* indexBuffer = meshData.indexBuffer; const NzVertexBuffer* vertexBuffer = meshData.vertexBuffer; // Gestion du draw call avant la boucle de rendu NzRenderer::DrawCall drawFunc; unsigned int indexCount; if (indexBuffer) { drawFunc = NzRenderer::DrawIndexedPrimitives; indexCount = indexBuffer->GetIndexCount(); } else { drawFunc = NzRenderer::DrawPrimitives; indexCount = vertexBuffer->GetVertexCount(); } NzRenderer::SetIndexBuffer(indexBuffer); NzRenderer::SetVertexBuffer(vertexBuffer); if (shaderUniforms->hasLightUniforms) { // Calcul des lumières les plus proches if (lightCount < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS && !m_lights.IsEmpty()) { NzVector3f position = matrix.GetTranslation() + modelData.squaredBoundingSphere.GetPosition(); float radius = modelData.squaredBoundingSphere.radius; unsigned int closestLightCount = m_lights.ComputeClosestLights(position, radius, NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS); unsigned int count = std::min(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS - lightCount, closestLightCount); for (unsigned int i = 0; i < count; ++i) m_lights.GetResult(i)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*(lightCount++)); } for (unsigned int i = lightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) NzLight::Disable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i); } NzRenderer::SetMatrix(nzMatrixType_World, matrix); drawFunc(meshData.primitiveMode, 0, indexCount); } }
void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene) const { NzAbstractViewer* viewer = scene->GetViewer(); const NzShader* lastShader = nullptr; const ShaderUniforms* shaderUniforms = nullptr; for (auto& matIt : m_renderQueue.opaqueModels) { auto& matEntry = matIt.second; if (matEntry.enabled) { NzForwardRenderQueue::MeshInstanceContainer& meshInstances = matEntry.meshMap; if (!meshInstances.empty()) { const NzMaterial* material = matIt.first; // Nous utilisons de l'instancing que lorsqu'aucune lumière (autre que directionnelle) n'est active // Ceci car l'instancing n'est pas compatible avec la recherche des lumières les plus proches // (Le deferred shading n'a pas ce problème) bool instancing = m_instancingEnabled && (!material->IsLightingEnabled() || m_lights.IsEmpty()) && matEntry.instancingEnabled; // On commence par appliquer du matériau (et récupérer le shader ainsi activé) const NzShader* shader = material->Apply((instancing) ? nzShaderFlags_Instancing : 0); // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas if (shader != lastShader) { // Index des uniformes dans le shader shaderUniforms = GetShaderUniforms(shader); // Couleur ambiante de la scène shader->SendColor(shaderUniforms->sceneAmbient, scene->GetAmbientColor()); // Position de la caméra shader->SendVector(shaderUniforms->eyePosition, viewer->GetEyePosition()); lastShader = shader; } // Meshes for (auto& meshIt : meshInstances) { const NzMeshData& meshData = meshIt.first; auto& meshEntry = meshIt.second; const NzSpheref& squaredBoundingSphere = meshEntry.squaredBoundingSphere; std::vector<NzMatrix4f>& instances = meshEntry.instances; if (!instances.empty()) { const NzIndexBuffer* indexBuffer = meshData.indexBuffer; const NzVertexBuffer* vertexBuffer = meshData.vertexBuffer; // Gestion du draw call avant la boucle de rendu NzRenderer::DrawCall drawFunc; NzRenderer::DrawCallInstanced instancedDrawFunc; unsigned int indexCount; if (indexBuffer) { drawFunc = NzRenderer::DrawIndexedPrimitives; instancedDrawFunc = NzRenderer::DrawIndexedPrimitivesInstanced; indexCount = indexBuffer->GetIndexCount(); } else { drawFunc = NzRenderer::DrawPrimitives; instancedDrawFunc = NzRenderer::DrawPrimitivesInstanced; indexCount = vertexBuffer->GetVertexCount(); } NzRenderer::SetIndexBuffer(indexBuffer); NzRenderer::SetVertexBuffer(vertexBuffer); if (instancing) { // On calcule le nombre d'instances que l'on pourra afficher cette fois-ci (Selon la taille du buffer d'instancing) NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer(); instanceBuffer->SetVertexDeclaration(NzVertexDeclaration::Get(nzVertexLayout_Matrix4)); // Avec l'instancing, impossible de sélectionner les lumières pour chaque objet // Du coup, il n'est activé que pour les lumières directionnelles unsigned int lightCount = m_directionalLights.GetLightCount(); unsigned int lightIndex = 0; nzRendererComparison oldDepthFunc = NzRenderer::GetDepthFunc(); unsigned int passCount = (lightCount == 0) ? 1 : (lightCount-1)/NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1; for (unsigned int pass = 0; pass < passCount; ++pass) { if (shaderUniforms->hasLightUniforms) { unsigned int renderedLightCount = std::min(lightCount, NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U)); lightCount -= renderedLightCount; if (pass == 1) { // Pour additionner le résultat des calculs de lumière // Aucune chance d'interférer avec les paramètres du matériau car nous ne rendons que les objets opaques // (Autrement dit, sans blending) // Quant à la fonction de profondeur, elle ne doit être appliquée que la première fois NzRenderer::Enable(nzRendererParameter_Blend, true); NzRenderer::SetBlendFunc(nzBlendFunc_One, nzBlendFunc_One); NzRenderer::SetDepthFunc(nzRendererComparison_Equal); } for (unsigned int i = 0; i < renderedLightCount; ++i) m_directionalLights.GetLight(lightIndex++)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i); for (unsigned int i = renderedLightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) NzLight::Disable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i); } const NzMatrix4f* instanceMatrices = &instances[0]; unsigned int instanceCount = instances.size(); unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); // Le nombre maximum d'instances en une fois while (instanceCount > 0) { // On calcule le nombre d'instances que l'on pourra afficher cette fois-ci (Selon la taille du buffer d'instancing) unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount); instanceCount -= renderedInstanceCount; // On remplit l'instancing buffer avec nos matrices world instanceBuffer->Fill(instanceMatrices, 0, renderedInstanceCount, true); instanceMatrices += renderedInstanceCount; // Et on affiche instancedDrawFunc(renderedInstanceCount, meshData.primitiveMode, 0, indexCount); } } // On n'oublie pas de désactiver le blending pour ne pas interférer sur le reste du rendu NzRenderer::Enable(nzRendererParameter_Blend, false); NzRenderer::SetDepthFunc(oldDepthFunc); } else { if (shaderUniforms->hasLightUniforms) { for (const NzMatrix4f& matrix : instances) { unsigned int directionalLightCount = m_directionalLights.GetLightCount(); unsigned int otherLightCount = m_lights.ComputeClosestLights(matrix.GetTranslation() + squaredBoundingSphere.GetPosition(), squaredBoundingSphere.radius, m_maxLightPassPerObject*NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS - directionalLightCount); unsigned int lightCount = directionalLightCount + otherLightCount; NzRenderer::SetMatrix(nzMatrixType_World, matrix); unsigned int directionalLightIndex = 0; unsigned int otherLightIndex = 0; nzRendererComparison oldDepthFunc = NzRenderer::GetDepthFunc(); // Dans le cas où nous aurions à le changer unsigned int passCount = (lightCount == 0) ? 1 : (lightCount-1)/NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS + 1; for (unsigned int pass = 0; pass < passCount; ++pass) { unsigned int renderedLightCount = std::min(lightCount, NazaraSuffixMacro(NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS, U)); lightCount -= renderedLightCount; if (pass == 1) { // Pour additionner le résultat des calculs de lumière // Aucune chance d'interférer avec les paramètres du matériau car nous ne rendons que les objets opaques // (Autrement dit, sans blending) // Quant à la fonction de profondeur, elle ne doit être appliquée que la première fois NzRenderer::Enable(nzRendererParameter_Blend, true); NzRenderer::SetBlendFunc(nzBlendFunc_One, nzBlendFunc_One); NzRenderer::SetDepthFunc(nzRendererComparison_Equal); } // On active les lumières de cette passe-ci for (unsigned int i = 0; i < renderedLightCount; ++i) { if (directionalLightIndex >= directionalLightCount) m_lights.GetResult(otherLightIndex++)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i); else m_directionalLights.GetLight(directionalLightIndex++)->Enable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i); } // On désactive l'éventuel surplus for (unsigned int i = renderedLightCount; i < NAZARA_GRAPHICS_MAX_LIGHT_PER_PASS; ++i) NzLight::Disable(shader, shaderUniforms->lightUniforms, shaderUniforms->lightOffset*i); // Et on passe à l'affichage drawFunc(meshData.primitiveMode, 0, indexCount); } NzRenderer::Enable(nzRendererParameter_Blend, false); NzRenderer::SetDepthFunc(oldDepthFunc); } } else { // Sans instancing, on doit effectuer un draw call pour chaque instance // Cela reste néanmoins plus rapide que l'instancing en dessous d'un certain nombre d'instances // À cause du temps de modification du buffer d'instancing for (const NzMatrix4f& matrix : instances) { NzRenderer::SetMatrix(nzMatrixType_World, matrix); drawFunc(meshData.primitiveMode, 0, indexCount); } } } instances.clear(); } } } // Et on remet à zéro les données matEntry.enabled = false; matEntry.instancingEnabled = false; } } }