/* * Copyright (C) 2017 Christopher J. Howard * * This file is part of Antkeeper Source Code. * * Antkeeper Source Code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Antkeeper Source Code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Antkeeper Source Code. If not, see . */ #include "render-passes.hpp" #include "configuration.hpp" #include "ui/ui.hpp" #include #include ClearRenderPass::ClearRenderPass(): clearColor(true), clearDepth(true), clearStencil(true), color(0.0f), depth(1.0f), index(0) {} bool ClearRenderPass::load(const RenderContext* renderContext) { return true; } void ClearRenderPass::unload() {} void ClearRenderPass::render(RenderContext* renderContext) { glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->framebuffer); GLbitfield mask = 0; if (clearColor) { mask |= GL_COLOR_BUFFER_BIT; glClearColor(color[0], color[1], color[2], color[3]); } if (clearDepth) { mask |= GL_DEPTH_BUFFER_BIT; glClearDepth(depth); } if (clearStencil) { mask |= GL_STENCIL_BUFFER_BIT; glClearStencil(index); } glClear(mask); } void ClearRenderPass::setClear(bool color, bool depth, bool stencil) { clearColor = color; clearDepth = depth; clearStencil = stencil; } void ClearRenderPass::setClearColor(const Vector4& color) { this->color = color; } void ClearRenderPass::setClearDepth(float depth) { this->depth = depth; } void ClearRenderPass::setClearStencil(int index) { this->index = index; } BlurRenderPass::BlurRenderPass(): gammaCorrect(false) {} bool BlurRenderPass::load(const RenderContext* renderContext) { permutation = (gammaCorrect) ? 1 : 0; // Load shader source if (!shader.loadSource("data/shaders/blur.glsl")) { std::cerr << std::string("BlurRenderPass: failed to load shader source.") << std::endl; return false; } // Generate permutation if (!shader.generatePermutation(permutation)) { std::cerr << std::string("BlurRenderPass: failed to generate shader permutation.") << std::endl; return false; } // Connect shader variables textureParam.connect(shader.getInput("blurTexture")); resolutionParam.connect(shader.getInput("resolution")); directionParam.connect(shader.getInput("direction")); if (!textureParam.isConnected() || !resolutionParam.isConnected() || !directionParam.isConnected()) { std::cerr << std::string("BlurRenderPass: one or more shader variables were not connected to shader inputs.") << std::endl; return false; } // Initialize shader variables resolutionParam.setValue(Vector2(renderTarget->width, renderTarget->height)); const float quadVertexData[] = { -1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f }; const std::uint32_t quadIndexData[] = { 0, 1, 3, 3, 1, 2 }; quadVertexCount = 4; quadIndexCount = 6; // Create AABB geometry glGenVertexArrays(1, &quadVAO); glBindVertexArray(quadVAO); glGenBuffers(1, &quadVBO); glBindBuffer(GL_ARRAY_BUFFER, quadVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * quadVertexCount, quadVertexData, GL_STATIC_DRAW); glEnableVertexAttribArray(EMERGENT_VERTEX_POSITION); glVertexAttribPointer(EMERGENT_VERTEX_POSITION, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (char*)0 + 0*sizeof(float)); glGenBuffers(1, &quadIBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadIBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(std::uint32_t) * quadIndexCount, quadIndexData, GL_STATIC_DRAW); return true; } void BlurRenderPass::unload() { textureParam.disconnect(); resolutionParam.disconnect(); directionParam.disconnect(); shader.deleteAllPermutations(); glDeleteBuffers(1, &quadIBO); glDeleteBuffers(1, &quadVBO); glDeleteVertexArrays(1, &quadVAO); } void BlurRenderPass::render(RenderContext* renderContext) { // Bind framebuffer and set up viewport glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->framebuffer); glViewport(0, 0, renderTarget->width, renderTarget->height); // Set up OpenGL state glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glDisable(GL_BLEND); glDisable(GL_CULL_FACE); // Activate shader permutation shader.activate(permutation); // Upload shader variables to shader permutation textureParam.upload(); resolutionParam.upload(); directionParam.upload(); // Render quad glBindVertexArray(quadVAO); glDrawElementsBaseVertex(GL_TRIANGLES, quadIndexCount, GL_UNSIGNED_INT, (void*)0, 0); // Unbind texture //glBindTexture(GL_TEXTURE_2D, 0); } ShadowMapRenderPass::ShadowMapRenderPass(): croppedShadowMapViewports(nullptr), viewCamera(nullptr), splitViewFrustum(nullptr), cropMatrices(nullptr), tileMatrices(nullptr) {} bool ShadowMapRenderPass::load(const RenderContext* renderContext) { // Set maximum number of bones for skinned meshes maxBoneCount = 64; // Create split view frustum splitViewFrustum = new SplitViewFrustum(4); splitViewFrustum->setSplitSchemeWeight(0.85f); // Determine resolution of shadow maps shadowMapResolution = 4096; croppedShadowMapResolution = shadowMapResolution >> 1; // Allocate viewports croppedShadowMapViewports = new Vector4[splitViewFrustum->getSubfrustumCount()]; // Setup viewports for (int i = 0; i < splitViewFrustum->getSubfrustumCount(); ++i) { int x = i % 2; int y = i / 2; Vector4* viewport = &croppedShadowMapViewports[i]; (*viewport)[0] = static_cast(x * croppedShadowMapResolution); (*viewport)[1] = static_cast(y * croppedShadowMapResolution); (*viewport)[2] = static_cast(croppedShadowMapResolution); (*viewport)[3] = static_cast(croppedShadowMapResolution); } // Allocate matrices cropMatrices = new Matrix4[splitViewFrustum->getSubfrustumCount()]; tileMatrices = new Matrix4[splitViewFrustum->getSubfrustumCount()]; // Setup tile matrices Matrix4 tileScale = glm::scale(Vector3(0.5f, 0.5f, 1.0f)); for (int i = 0; i < splitViewFrustum->getSubfrustumCount(); ++i) { float x = static_cast(i % 2) * 0.5f; float y = static_cast(i / 2) * 0.5f; tileMatrices[i] = glm::translate(Vector3(x, y, 0.0f)) * tileScale; } // Setup permutation values unskinnedPermutation = 0; skinnedPermutation = 1; // Load shader source if (!shader.loadSource("data/shaders/depth-pass.glsl")) { std::cerr << std::string("ShadowMapRenderPass: failed to load shader source.") << std::endl; return false; } // Generate unskinned and skinned permutations if (!shader.generatePermutation(unskinnedPermutation) || !shader.generatePermutation(skinnedPermutation)) { std::cerr << std::string("ShadowMapRenderPass: failed to generate shader permutation.") << std::endl; return false; } // Allocate bone palette parameter matrixPaletteParam = new ShaderMatrix4(maxBoneCount); // Connect shader variables modelViewProjectionParam.connect(shader.getInput("modelViewProjectionMatrix")); matrixPaletteParam->connect(shader.getInput("matrixPalette")); if (!modelViewProjectionParam.isConnected() || !matrixPaletteParam->isConnected()) { std::cerr << std::string("ShadowMapRenderPass: one or more shader variables were not connected to shader inputs.") << std::endl; return false; } return true; } void ShadowMapRenderPass::unload() { modelViewProjectionParam.disconnect(); matrixPaletteParam->disconnect(); delete matrixPaletteParam; shader.deleteAllPermutations(); delete[] croppedShadowMapViewports; croppedShadowMapViewports = nullptr; delete splitViewFrustum; splitViewFrustum = nullptr; delete[] cropMatrices; cropMatrices = nullptr; delete[] tileMatrices; tileMatrices = nullptr; } void ShadowMapRenderPass::render(RenderContext* renderContext) { // Bind framebuffer and setup viewport glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->framebuffer); glViewport(0, 0, renderTarget->width, renderTarget->height); // Enable depth testing glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glDepthFunc(GL_LESS); // Clear the framebuffer depth glClear(GL_DEPTH_BUFFER_BIT); // Draw front and back faces glEnable(GL_CULL_FACE); glCullFace(GL_BACK); // Disable alpha blending glDisable(GL_BLEND); //const Camera& lightCamera = *(renderContext->camera); std::list* operations = renderContext->queue->getOperations(); GLuint boundVAO = 0; splitViewFrustum->setMatrices(viewCamera->getView(), viewCamera->getProjection()); // Sort operations operations->sort(RenderOpCompare()); std::uint32_t permutation = 0xDEADBEEF; // For each frustum split for (int i = 0; i < splitViewFrustum->getSubfrustumCount(); ++i) { // Calculate crop matrix { const ViewFrustum& subfrustum = splitViewFrustum->getSubfrustum(i); // Create AABB containing the subfrustum corners AABB subfrustumBounds(subfrustum.getCorner(0), subfrustum.getCorner(0)); for (std::size_t j = 1; j < 8; ++j) { subfrustumBounds.add(subfrustum.getCorner(j)); } // Transform subfrustum bounds into light's clip space AABB croppingBounds = subfrustumBounds.transformed(lightCamera->getViewProjection()); Vector3 cropMax = croppingBounds.getMax(); Vector3 cropMin = croppingBounds.getMin(); // Calculate scale Vector3 scale; scale.x = 2.0f / (cropMax.x - cropMin.x); scale.y = 2.0f / (cropMax.y - cropMin.y); scale.z = 1.0f / (cropMax.z - cropMin.z); // Quantize scale float scaleQuantizer = 64.0f; scale.x = 1.0f / std::ceil(1.0f / scale.x * scaleQuantizer) * scaleQuantizer; scale.y = 1.0f / std::ceil(1.0f / scale.y * scaleQuantizer) * scaleQuantizer; // Calculate offset Vector3 offset; offset.x = (cropMax.x + cropMin.x) * scale.x * -0.5f; offset.y = (cropMax.y + cropMin.y) * scale.y * -0.5f; offset.z = -cropMin.z * scale.z; // Quantize offset float halfTextureSize = static_cast(croppedShadowMapResolution) * 0.5f; offset.x = std::ceil(offset.x * halfTextureSize) / halfTextureSize; offset.y = std::ceil(offset.y * halfTextureSize) / halfTextureSize; cropMatrices[i] = glm::translate(offset) * glm::scale(scale); } Matrix4 croppedViewProjection = cropMatrices[i] * lightCamera->getViewProjection(); // Activate viewport for corresponding cropped shadow map const Vector4& viewport = croppedShadowMapViewports[i]; glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); // Render operations for (const RenderOperation& operation: *operations) { // Skip operations with no materials and materials with no shadows if (operation.material == nullptr || (operation.material->getFlags() & MATERIAL_FLAG_DISABLE_SHADOW_CASTING)) { continue; } // TODO: Perform culling for subfrustums // Select permutation std::uint32_t targetPermutation = (operation.pose != nullptr) ? skinnedPermutation : unskinnedPermutation; if (permutation != targetPermutation) { permutation = targetPermutation; shader.activate(permutation); } // Pass matrix palette if (operation.pose != nullptr) { matrixPaletteParam->getConnectedInput()->upload(0, operation.pose->getMatrixPalette(), operation.pose->getSkeleton()->getBoneCount()); } const Matrix4& modelMatrix = operation.transform; Matrix4 modelViewProjectionMatrix = croppedViewProjection * modelMatrix; modelViewProjectionParam.setValue(modelViewProjectionMatrix); modelViewProjectionParam.upload(); if (boundVAO != operation.vao) { glBindVertexArray(operation.vao); boundVAO = operation.vao; } glDrawElementsBaseVertex(GL_TRIANGLES, operation.triangleCount * 3, GL_UNSIGNED_INT, (void*)0, operation.indexOffset); } } glBindFramebuffer(GL_FRAMEBUFFER, 0); } bool ShadowMapRenderPass::RenderOpCompare::operator()(const RenderOperation& opA, const RenderOperation& opB) const { // If A is rigged if (opA.pose != nullptr) { // And B is rigged if (opB.pose != nullptr) { // Sort by VAO ID return (opA.vao <= opB.vao); } else { // Render A first return true; } } // Sort by VAO ID return (opA.vao <= opB.vao); } LightingRenderPass::LightingRenderPass(): shadowMap(0), shadowCamera(nullptr), diffuseCubemap(nullptr), specularCubemap(nullptr), shadowMapPass(nullptr) { // Initialize bias matrix for calculating the model-view-projection-bias matrix (used for shadow map texture coordinate calculation) biasMatrix = glm::translate(Vector3(0.5f)) * glm::scale(Vector3(0.5f)); maxBoneCount = 64; maxDirectionalLightCount = 1; maxSpotlightCount = 1; } bool LightingRenderPass::load(const RenderContext* renderContext) { // Load shaders for each material // Register parameter sets for each unique shader if (!shader.loadSource("data/shaders/standard.glsl")) { std::cerr << std::string("LightingRenderPass: Failed to load shader source.") << std::endl; return false; } /* if (renderContext != nullptr) { std::list* operations = renderContext->queue->getOperations(); for (RenderOperation& op: *operations) { } } */ // Set permutation values std::uint32_t IS_SKINNED = 1; std::uint32_t HAS_AMBIENT_CUBE = 1 << 7; unskinnedPermutation = HAS_AMBIENT_CUBE; skinnedPermutation = IS_SKINNED | HAS_AMBIENT_CUBE; // Generate shader permutations if (!shader.generatePermutation(unskinnedPermutation) || !shader.generatePermutation(skinnedPermutation)) { std::cerr << std::string("LightingRenderPass: failed to generate shader permutation.") << std::endl; return false; } // Allocate shader array parameters parameters.matrixPalette = new ShaderMatrix4(maxBoneCount); parameters.lightViewProjectionMatrices = new ShaderMatrix4(4); // Connect shader parameters parameters.matrixPalette->connect(shader.getInput("matrixPalette")); parameters.modelMatrix.connect(shader.getInput("modelMatrix")); parameters.modelViewMatrix.connect(shader.getInput("modelViewMatrix")); parameters.modelViewProjectionMatrix.connect(shader.getInput("modelViewProjectionMatrix")); parameters.normalModelViewMatrix.connect(shader.getInput("normalModelViewMatrix")); parameters.normalModelMatrix.connect(shader.getInput("normalModelMatrix")); parameters.lightViewProjectionMatrices->connect(shader.getInput("lightViewProjectionMatrices")); parameters.splitDistances.connect(shader.getInput("splitDistances")); parameters.shadowMap.connect(shader.getInput("shadowMap")); parameters.cameraPosition.connect(shader.getInput("cameraPosition")); parameters.diffuseCubemap.connect(shader.getInput("diffuseCubemap")); parameters.specularCubemap.connect(shader.getInput("specularCubemap")); parameters.albedoOpacityMap.connect(shader.getInput("albedoOpacityMap")); parameters.metalnessRoughnessMap.connect(shader.getInput("metalnessRoughnessMap")); parameters.normalOcclusionMap.connect(shader.getInput("normalOcclusionMap")); if (!parameters.matrixPalette->isConnected() || !parameters.modelMatrix.isConnected() || !parameters.modelViewMatrix.isConnected() || !parameters.modelViewProjectionMatrix.isConnected() || !parameters.normalModelViewMatrix.isConnected() || !parameters.normalModelMatrix.isConnected() || !parameters.lightViewProjectionMatrices->isConnected() || !parameters.splitDistances.isConnected() || !parameters.shadowMap.isConnected() || !parameters.cameraPosition.isConnected() || !parameters.diffuseCubemap.isConnected() || !parameters.specularCubemap.isConnected()) { std::cerr << std::string("LightingRenderPass: one or more shader variables were not connected to shader inputs.") << std::endl; } return true; } void LightingRenderPass::unload() { // Free shader array parameters delete parameters.matrixPalette; delete parameters.lightViewProjectionMatrices; parameters.matrixPalette = nullptr; parameters.lightViewProjectionMatrices = nullptr; shader.deleteAllPermutations(); } void LightingRenderPass::render(RenderContext* renderContext) { const Camera& camera = *(renderContext->camera); std::list* operations = renderContext->queue->getOperations(); // Bind framebuffer and setup viewport glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->framebuffer); glViewport(0, 0, renderTarget->width, renderTarget->height); // Enable depth testing glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glDepthFunc(GL_LEQUAL); // Enable backface culling glEnable(GL_CULL_FACE); glCullFace(GL_BACK); // Enable alpha blending //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); Vector4 splitDistances; for (int i = 0; i < 4; ++i) { splitDistances[i] = shadowMapPass->getSplitViewFrustum().getSplitDistance(i + 1); } Vector3 directionalLightColors[3]; Vector3 directionalLightDirections[3]; Vector3 spotlightColors[3]; Vector3 spotlightPositions[3]; Vector3 spotlightAttenuations[3]; Vector3 spotlightDirections[3]; float spotlightCutoffs[3]; float spotlightExponents[3]; // Add directional light int directionalLightCount = 1; directionalLightColors[0] = Vector3(1); directionalLightDirections[0] = glm::normalize(Vector3(camera.getView() * -Vector4(0, -2, -1, 0))); // Add spotlights int spotlightCount = 0; const std::list* lights = renderContext->layer->getObjects(SceneObjectType::LIGHT); if (lights != nullptr) { for (auto object: *lights) { const Light* light = static_cast(object); LightType lightType = light->getLightType(); if (lightType == LightType::SPOTLIGHT && light->isActive()) { const Spotlight* spotlight = static_cast(light); spotlightColors[spotlightCount] = spotlight->getScaledColor(); spotlightPositions[spotlightCount] = Vector3(camera.getView() * Vector4(spotlight->getTranslation(), 1.0f)); spotlightAttenuations[spotlightCount] = spotlight->getAttenuation(); spotlightDirections[spotlightCount] = glm::normalize(Vector3(camera.getView() * Vector4(-spotlight->getDirection(), 0.0f))); spotlightCutoffs[spotlightCount] = spotlight->getCutoff(); spotlightExponents[spotlightCount] = spotlight->getExponent(); ++spotlightCount; } } } // Calculate the (light-space) view-projection matrices Matrix4 lightViewProjectionMatrices[4]; for (int i = 0; i < 4; ++i) { lightViewProjectionMatrices[i] = shadowMapPass->getTileMatrix(i) * biasMatrix * shadowMapPass->getCropMatrix(i) * shadowCamera->getViewProjection(); } // Set shader parameter values parameters.lightViewProjectionMatrices->setValues(0, &lightViewProjectionMatrices[0], 4); parameters.splitDistances.setValue(splitDistances); parameters.cameraPosition.setValue(camera.getTranslation()); parameters.diffuseCubemap.setValue(diffuseCubemap); parameters.specularCubemap.setValue(specularCubemap); parameters.shadowMap.setValue(shadowMap); parameters.directionalLightCount.setValue(0); parameters.spotlightCount.setValue(0); std::uint32_t permutation = 0xDEADBEEF; bool blending = false; GLuint boundVAO = 0; // Sort operations operations->sort(RenderOpCompare()); // Render operations for (const RenderOperation& operation: *operations) { // Skip operations without materials if (!operation.material) { continue; } bool hasTranslucency = operation.material->getFlags() & MATERIAL_FLAG_TRANSLUCENT; if (hasTranslucency && !blending) { glEnable(GL_BLEND); blending = true; } // Select permutation std::uint32_t targetPermutation = (operation.pose != nullptr) ? skinnedPermutation : unskinnedPermutation; if (permutation != targetPermutation) { permutation = targetPermutation; shader.activate(permutation); // Pass static params parameters.lightViewProjectionMatrices->upload(); parameters.splitDistances.upload(); parameters.cameraPosition.upload(); parameters.diffuseCubemap.upload(); parameters.specularCubemap.upload(); parameters.shadowMap.upload(); parameters.directionalLightCount.upload(); parameters.spotlightCount.upload(); /* shader->setParameter(directionalLightCountParam, directionalLightCount); shader->setParameter(directionalLightColorsParam, 0, &directionalLightColors[0], directionalLightCount); shader->setParameter(directionalLightDirectionsParam, 0, &directionalLightDirections[0], directionalLightCount); shader->setParameter(spotlightCountParam, spotlightCount); shader->setParameter(spotlightColorsParam, 0, &spotlightColors[0], spotlightCount); shader->setParameter(spotlightPositionsParam, 0, &spotlightPositions[0], spotlightCount); shader->setParameter(spotlightAttenuationsParam, 0, &spotlightAttenuations[0], spotlightCount); shader->setParameter(spotlightDirectionsParam, 0, &spotlightDirections[0], spotlightCount); shader->setParameter(spotlightCutoffsParam, 0, &spotlightCutoffs[0], spotlightCount); shader->setParameter(spotlightExponentsParam, 0, &spotlightExponents[0], spotlightCount); */ } const Matrix4& modelMatrix = operation.transform; Matrix4 modelViewMatrix = camera.getView() * modelMatrix; Matrix4 modelViewProjectionMatrix = camera.getViewProjection() * modelMatrix; Matrix3 normalModelViewMatrix = glm::transpose(glm::inverse(Matrix3(modelViewMatrix))); Matrix3 normalModelMatrix = glm::transpose(glm::inverse(Matrix3(modelMatrix))); parameters.modelMatrix.setValue(modelMatrix); parameters.modelViewMatrix.setValue(modelViewMatrix); parameters.modelViewProjectionMatrix.setValue(modelViewProjectionMatrix); parameters.normalModelViewMatrix.setValue(normalModelViewMatrix); parameters.normalModelMatrix.setValue(normalModelMatrix); // Upload matrix parameters parameters.modelMatrix.upload(); parameters.modelViewMatrix.upload(); parameters.modelViewProjectionMatrix.upload(); parameters.normalModelViewMatrix.upload(); parameters.normalModelMatrix.upload(); // Upload pose matrix palette if (operation.pose != nullptr && parameters.matrixPalette->isConnected()) { parameters.matrixPalette->getConnectedInput()->upload(0, operation.pose->getMatrixPalette(), operation.pose->getSkeleton()->getBoneCount()); } // Upload material parameters if (operation.material != nullptr) { if (operation.material->getShader() != &shader) { ((Material*)operation.material)->setShader(&shader); } operation.material->upload(); } if (boundVAO != operation.vao) { glBindVertexArray(operation.vao); boundVAO = operation.vao; } glDrawElementsBaseVertex(GL_TRIANGLES, operation.triangleCount * 3, GL_UNSIGNED_INT, (void*)0, operation.indexOffset); } glActiveTexture(GL_TEXTURE5); glBindTexture(GL_TEXTURE_2D, 0); } bool LightingRenderPass::RenderOpCompare::operator()(const RenderOperation& opA, const RenderOperation& opB) const { if (!opA.material) return false; else if (!opB.material); return true; // Determine transparency bool transparentA = opA.material->getFlags() & MATERIAL_FLAG_TRANSLUCENT; bool transparentB = opB.material->getFlags() & MATERIAL_FLAG_TRANSLUCENT; if (transparentA) { if (transparentB) { // A and B are both transparent, render back to front return (opA.depth >= opB.depth); } else { // A is transparent, B is opaque. Render B first return false; } } else { if (transparentB) { // A is opaque, B is transparent. Render A first return true; } else { // A and B are both opaque if (opA.material->getShader() == opB.material->getShader()) { // A and B have the same shader if (opA.vao == opB.vao) { // A and B have the same VAO, sort by depth return (opA.depth < opB.depth); } else { // Sort by VAO return (opA.vao < opB.vao); } } } } // A and B are both opaque and have different shaders, sort by shader return (opA.material->getShader() < opB.material->getShader()); } DebugRenderPass::DebugRenderPass() {} bool DebugRenderPass::load(const RenderContext* renderContext) { if (!shader.loadSource("data/shaders/unlit-solid.glsl")) { std::cerr << std::string("DebugRenderPass: Failed to load shader source.") << std::endl; return false; } // Set permutation values permutation = 0; // Generate shader permutations if (!shader.generatePermutation(permutation)) { std::cerr << std::string("DebugRenderPass: failed to generate shader permutation.") << std::endl; return false; } // Connect shader variables modelViewProjectionMatrixParam.connect(shader.getInput("modelViewProjectionMatrix")); if (!modelViewProjectionMatrixParam.isConnected()) { std::cerr << std::string("DebugRenderPass: one or more shader variables were not connected to shader inputs.") << std::endl; return false; } const float aabbVertexData[] = { -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, }; const std::uint32_t aabbIndexData[] = { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 }; aabbVertexCount = 8; aabbIndexCount = 24; // Create AABB geometry glGenVertexArrays(1, &aabbVAO); glBindVertexArray(aabbVAO); glGenBuffers(1, &aabbVBO); glBindBuffer(GL_ARRAY_BUFFER, aabbVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * aabbVertexCount, aabbVertexData, GL_STATIC_DRAW); glEnableVertexAttribArray(EMERGENT_VERTEX_POSITION); glVertexAttribPointer(EMERGENT_VERTEX_POSITION, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (char*)0 + 0*sizeof(float)); glGenBuffers(1, &aabbIBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, aabbIBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(std::uint32_t) * aabbIndexCount, aabbIndexData, GL_STATIC_DRAW); return true; } void DebugRenderPass::unload() { modelViewProjectionMatrixParam.disconnect(); shader.deleteAllPermutations(); glDeleteBuffers(1, &aabbIBO); glDeleteBuffers(1, &aabbVBO); glDeleteVertexArrays(1, &aabbVAO); } void DebugRenderPass::render(RenderContext* renderContext) { // Bind framebuffer and setup viewport glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->framebuffer); glViewport(0, 0, renderTarget->width, renderTarget->height); const Camera& camera = *(renderContext->camera); /* // Bind framebuffer and setup viewport glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->framebuffer); glViewport(0, 0, renderTarget->width, renderTarget->height); // Clear the framebuffer depth glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearDepth(1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Disable depth testing glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glDepthFunc(GL_LESS); */ // Disable backface culling glDisable(GL_CULL_FACE); // Disable alpha blending glDisable(GL_BLEND); // Bind unlit solid shader shader.activate(permutation); // Bind AABB geometry glBindVertexArray(aabbVAO); const std::list* objects = renderContext->layer->getObjects(); for (auto object: *objects) { if (!camera.getCullingMask()->intersects(object->getBounds())) continue; const AABB& bounds = object->getBounds(); const Vector3& min = bounds.getMin(); const Vector3& max = bounds.getMax(); Vector3 scale = max - min; Vector3 center = (min + max) * 0.5f; const Vector3& translation = center; Matrix4 modelMatrix = glm::translate(translation) * glm::scale(scale); Matrix4 modelViewProjectionMatrix = camera.getViewProjection() * modelMatrix; modelViewProjectionMatrixParam.setValue(modelViewProjectionMatrix); modelViewProjectionMatrixParam.upload(); glDrawElements(GL_LINES, aabbIndexCount, GL_UNSIGNED_INT, (void*)0); } } UIRenderPass::UIRenderPass() {} bool UIRenderPass::load(const RenderContext* renderContext) { if (!shader.loadSource("data/shaders/ui.glsl")) { std::cerr << std::string("UIRenderPass: Failed to load shader source.") << std::endl; return false; } // Set permutation values untexturedPermutation = 0; texturedPermutation = 1; // Generate shader permutations if (!shader.generatePermutation(untexturedPermutation) || !shader.generatePermutation(texturedPermutation)) { std::cerr << std::string("UIRenderPass: failed to generate shader permutation.") << std::endl; return false; } // Connect shader variables modelViewProjectionMatrixParam.connect(shader.getInput("modelViewProjectionMatrix")); textureParam.connect(shader.getInput("tex")); textureOffsetParam.connect(shader.getInput("texcoordOffset")); textureScaleParam.connect(shader.getInput("texcoordScale")); if (!modelViewProjectionMatrixParam.isConnected() || !textureParam.isConnected() || !textureOffsetParam.isConnected() || !textureScaleParam.isConnected()) { std::cerr << std::string("UIRenderPass: one or more shader variables were not connected to shader inputs.") << std::endl; return false; } return true; } void UIRenderPass::unload() { modelViewProjectionMatrixParam.disconnect(); textureParam.disconnect(); textureOffsetParam.disconnect(); textureScaleParam.disconnect(); shader.deleteAllPermutations(); } void UIRenderPass::render(RenderContext* renderContext) { const Camera& camera = *(renderContext->camera); // Bind framebuffer and setup viewport glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->framebuffer); glViewport(0, 0, renderTarget->width, renderTarget->height); // Disable depth testing glDisable(GL_DEPTH_TEST); //glDepthMask(GL_FALSE); //glDepthFunc(GL_LESS); // Disable backface culling glEnable(GL_CULL_FACE); glCullFace(GL_BACK); // Enable alpha blending glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Render operations const std::list* operations = renderContext->queue->getOperations(); for (const RenderOperation& operation: *operations) { // Skip render operations with unsupported materials /* if (operation.material->getMaterialFormatID() != static_cast(MaterialFormat::UI)) { continue; } */ const UIMaterial* material = static_cast(operation.material); if (material->texture->getValue() != nullptr) { shader.activate(texturedPermutation); textureParam.setValue(material->texture->getValue()); textureOffsetParam.setValue(material->textureOffset->getValue()); textureScaleParam.setValue(material->textureScale->getValue()); textureParam.upload(); textureOffsetParam.upload(); textureScaleParam.upload(); } else { shader.activate(untexturedPermutation); } const Matrix4& modelMatrix = operation.transform; Matrix4 modelViewProjectionMatrix = camera.getViewProjection() * modelMatrix; // Upload matrix parameters modelViewProjectionMatrixParam.setValue(modelViewProjectionMatrix); modelViewProjectionMatrixParam.upload(); // Upload material parameters //operation.material->upload(); // Draw geometry glBindVertexArray(operation.vao); glDrawElementsBaseVertex(GL_TRIANGLES, operation.triangleCount * 3, GL_UNSIGNED_INT, (void*)0, operation.indexOffset); } } /* VignetteRenderPass::VignetteRenderPass(): shader(nullptr) { bayerTextureParam = parameterSet.addParameter("bayerTexture", ShaderParameter::Type::INT, 1); modelViewProjectionParam = parameterSet.addParameter("modelViewProjectionMatrix", ShaderParameter::Type::MATRIX_4, 1); } bool VignetteRenderPass::load(const RenderContext* renderContext) { shaderLoader.undefine(); shaderLoader.define("VERTEX_POSITION", EMERGENT_VERTEX_POSITION); shaderLoader.define("VERTEX_COLOR", EMERGENT_VERTEX_COLOR); shaderLoader.define("TEXTURE_COUNT", 1); shader = shaderLoader.load("data/shaders/vignette.glsl", ¶meterSet); if (!shader) { return false; } /// @see http://www.anisopteragames.com/how-to-fix-color-banding-with-dithering/ static const char pattern[] = { 0, 32, 8, 40, 2, 34, 10, 42, 48, 16, 56, 24, 50, 18, 58, 26, 12, 44, 4, 36, 14, 46, 6, 38, 60, 28, 52, 20, 62, 30, 54, 22, 3, 35, 11, 43, 1, 33, 9, 41, 51, 19, 59, 27, 49, 17, 57, 25, 15, 47, 7, 39, 13, 45, 5, 37, 63, 31, 55, 23, 61, 29, 53, 21 }; glGenTextures(1, &bayerTextureID); glBindTexture(GL_TEXTURE_2D, bayerTextureID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 8, 8, 0, GL_RED, GL_UNSIGNED_BYTE, pattern); return true; } void VignetteRenderPass::unload() { delete shader; shader = nullptr; glDeleteTextures(1, &bayerTextureID); } void VignetteRenderPass::render(RenderContext* renderContext) { glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); // Bind shader shader->bind(); // Bind texture glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, bayerTextureID); // Pass texture unit to shader shader->setParameter(bayerTextureParam, 0); const Camera& camera = *(renderContext->camera); const std::list* operations = renderContext->queue->getOperations(); // Render operations for (const RenderOperation& operation: *operations) { const Material* material = operation.material; const Matrix4& modelMatrix = operation.transform; Matrix4 modelViewProjectionMatrix = camera.getViewProjection() * modelMatrix; shader->setParameter(modelViewProjectionParam, modelViewProjectionMatrix); glBindVertexArray(operation.vao); glDrawElementsBaseVertex(GL_TRIANGLES, operation.triangleCount * 3, GL_UNSIGNED_INT, (void*)0, operation.indexOffset); } } */ SkyboxRenderPass::SkyboxRenderPass(): cubemap(nullptr) {} bool SkyboxRenderPass::load(const RenderContext* renderContext) { if (!shader.loadSource("data/shaders/skybox.glsl")) { std::cerr << std::string("SkyboxRenderPass: Failed to load shader source.") << std::endl; return false; } // Set permutation values permutation = 0; // Generate shader permutations if (!shader.generatePermutation(permutation)) { std::cerr << std::string("SkyboxRenderPass: failed to generate shader permutation.") << std::endl; return false; } // Connect shader variables matrixParam.connect(shader.getInput("matrix")); cubemapParam.connect(shader.getInput("cubemap")); if (!matrixParam.isConnected() || !cubemapParam.isConnected()) { std::cerr << std::string("SkyboxRenderPass: one or more shader variables were not connected to shader inputs.") << std::endl; return false; } const float quadVertexData[] = { -1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f }; const std::uint32_t quadIndexData[] = { 0, 1, 3, 3, 1, 2 }; quadVertexCount = 4; quadIndexCount = 6; // Create AABB geometry glGenVertexArrays(1, &quadVAO); glBindVertexArray(quadVAO); glGenBuffers(1, &quadVBO); glBindBuffer(GL_ARRAY_BUFFER, quadVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * quadVertexCount, quadVertexData, GL_STATIC_DRAW); glEnableVertexAttribArray(EMERGENT_VERTEX_POSITION); glVertexAttribPointer(EMERGENT_VERTEX_POSITION, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (char*)0 + 0*sizeof(float)); glGenBuffers(1, &quadIBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadIBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(std::uint32_t) * quadIndexCount, quadIndexData, GL_STATIC_DRAW); return true; } void SkyboxRenderPass::unload() { matrixParam.disconnect(); cubemapParam.disconnect(); shader.deleteAllPermutations(); glDeleteBuffers(1, &quadIBO); glDeleteBuffers(1, &quadVBO); glDeleteVertexArrays(1, &quadVAO); } void SkyboxRenderPass::render(RenderContext* renderContext) { if (!cubemap) { return; } glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->framebuffer); glViewport(0, 0, renderTarget->width, renderTarget->height); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); //glDisable(GL_CULL_FACE); //glCullFace(GL_BACK); // Bind shader shader.activate(permutation); // Bind cubemap texture cubemapParam.setValue(cubemap); cubemapParam.upload(); // Calculate matrix const Camera& camera = *(renderContext->camera); Matrix4 modelView = Matrix4(Matrix3(camera.getView())); Matrix4 matrix = glm::inverse(modelView) * glm::inverse(camera.getProjection()); // Upload matrix to shader matrixParam.setValue(matrix); matrixParam.upload(); // Render quad glBindVertexArray(quadVAO); glDrawElementsBaseVertex(GL_TRIANGLES, quadIndexCount, GL_UNSIGNED_INT, (void*)0, 0); }