add scene render system, add proper HDR environments, try load texture from mesh file

This commit is contained in:
2025-11-30 20:12:57 +08:00
parent 4cdd405ba9
commit 0d4024be39
62 changed files with 2302 additions and 1037 deletions

View File

@ -3,9 +3,7 @@
//
#include "EditorLayer.h"
#include "ImGuizmo.h"
#include "Prism/Core/KeyCodes.h"
namespace Prism
{
@ -19,7 +17,6 @@ namespace Prism
ImGui::Text(name.c_str());
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
std::string id = "##" + name;
ImGui::Checkbox(id.c_str(), &value);
@ -88,6 +85,25 @@ namespace Prism
}
void Property(const std::string& name, glm::vec2& value, float min, float max, PropertyFlag flags)
{
ImGui::Text(name.c_str());
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
std::string id = "##" + name;
ImGui::SliderFloat2(id.c_str(), glm::value_ptr(value), min, max);
ImGui::PopItemWidth();
ImGui::NextColumn();
}
void Property(const std::string& name, glm::vec2& value, PropertyFlag flags)
{
Property(name, value, -1.0f, 1.0f, flags);
}
static void ImGuiShowHelpMarker(const char* desc)
{
ImGui::TextDisabled("(?)");
@ -102,8 +118,7 @@ namespace Prism
}
EditorLayer::EditorLayer()
: m_ClearColor{0.1f, 0.1f, 0.1f, 1.0f}, m_Scene(Scene::Model),
m_Camera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f))
: m_SceneType(SceneType::Model)
{
}
@ -113,114 +128,83 @@ namespace Prism
void EditorLayer::OnAttach()
{
// RenderPass
FramebufferSpecification geoFramebufferSpec;
geoFramebufferSpec.Width = 1280;
geoFramebufferSpec.Height = 720;
geoFramebufferSpec.Format = FramebufferFormat::RGBA16F;
geoFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
auto environment = Environment::Load("assets/env/birchwood_4k.hdr");
RenderPassSpecification geoRenderPassSpec;
geoRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(geoFramebufferSpec);
m_GeoPass = RenderPass::Create(geoRenderPassSpec);
// Model Scene
{
m_Scene = CreateRef<Scene>("Model Scene");
m_Scene->SetCamera(Camera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f)));
m_Scene->SetEnvironment(environment);
FramebufferSpecification compFramebufferSpec;
compFramebufferSpec.Width = 1280;
compFramebufferSpec.Height = 720;
compFramebufferSpec.Format = FramebufferFormat::RGBA8;
compFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
m_MeshEntity = m_Scene->CreateEntity();
RenderPassSpecification compRenderPassSpec;
compRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(compFramebufferSpec);
m_CompositePass = RenderPass::Create(compRenderPassSpec);
const auto mesh = CreateRef<Mesh>("assets/models/m1911/m1911.fbx");
//auto mesh = CreateRef<Mesh>("assets/meshes/cerberus/CerberusMaterials.fbx");
// auto mesh = CreateRef<Mesh>("assets/models/m1911/M1911Materials.fbx");
m_MeshEntity->SetMesh(mesh);
/*
m_Framebuffer.reset(FrameBuffer::Create(1280, 720, FramebufferFormat::RGBA16F));
m_FinalPresentBuffer.reset(FrameBuffer::Create(1280, 720, FramebufferFormat::RGBA8));
*/
m_MeshMaterial = mesh->GetMaterial();
}
m_QuadShader = Shader::Create("assets/shaders/quad.glsl");
m_HDRShader = Shader::Create("assets/shaders/hdr.glsl");
// Sphere Scene
{
m_SphereScene = CreateRef<Scene>("PBR Sphere Scene");
m_SphereScene->SetCamera(Camera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f)));
m_Mesh = CreateRef<Mesh>("assets/models/m1911/m1911.fbx");
m_MeshMaterial = CreateRef<MaterialInstance>(m_Mesh->GetMaterial());
m_SphereMesh = CreateRef<Mesh>("assets/models/Sphere1m.fbx");
m_PlaneMesh = CreateRef<Mesh>("assets/models/Plane1m.fbx");
m_GridShader = Shader::Create("assets/shaders/Grid.glsl");
m_GridMaterial = MaterialInstance::Create(Material::Create(m_GridShader));
m_GridMaterial->Set("u_Scale", m_GridScale);
m_GridMaterial->Set("u_Res", m_GridSize);
// Editor
m_CheckerboardTex.reset(Texture2D::Create("assets/editor/Checkerboard.tga"));
// Environment
m_EnvironmentCubeMap.reset(
TextureCube::Create("assets/textures/environments/Arches_E_PineTree_Radiance.tga"));
m_EnvironmentIrradiance.reset(
TextureCube::Create("assets/textures/environments/Arches_E_PineTree_Irradiance.tga"));
m_BRDFLUT.reset(Texture2D::Create("assets/textures/BRDF_LUT.tga"));
m_SphereScene->SetEnvironment(environment);
auto sphereMesh = CreateRef<Mesh>("assets/models/Sphere1m.fbx");
m_SphereBaseMaterial = sphereMesh->GetMaterial();
float x = -4.0f;
float roughness = 0.0f;
for (int i = 0; i < 8; i++)
{
Ref<MaterialInstance> mi = CreateRef<MaterialInstance>(
m_SphereMesh->GetMaterial());
auto sphereEntity = m_SphereScene->CreateEntity();
Ref<MaterialInstance> mi = CreateRef<MaterialInstance>(m_SphereBaseMaterial);
mi->Set("u_Metalness", 1.0f);
mi->Set("u_Roughness", roughness);
mi->Set("u_ModelMatrix", glm::translate(glm::mat4(1.0f), glm::vec3(x, 0.0f, 0.0f)));
x += 1.1f;
roughness += 0.125f;
roughness += 0.15f;
m_MetalSphereMaterialInstances.push_back(mi);
sphereEntity->SetMesh(sphereMesh);
sphereEntity->SetMaterial(mi);
sphereEntity->Transform() = glm::translate(glm::mat4(1.0f), glm::vec3(x, 0.0f, 0.0f));
}
x = -4.0f;
roughness = 0.0f;
for (int i = 0; i < 8; i++)
{
Ref<MaterialInstance> mi = CreateRef<MaterialInstance>(
m_SphereMesh->GetMaterial());
auto sphereEntity = m_SphereScene->CreateEntity();
Ref<MaterialInstance> mi(new MaterialInstance(m_SphereBaseMaterial));
mi->Set("u_Metalness", 0.0f);
mi->Set("u_Roughness", roughness);
mi->Set("u_ModelMatrix", translate(glm::mat4(1.0f), glm::vec3(x, 1.2f, 0.0f)));
x += 1.1f;
roughness += 0.125f;
roughness += 0.15f;
m_DielectricSphereMaterialInstances.push_back(mi);
sphereEntity->SetMesh(sphereMesh);
sphereEntity->SetMaterial(mi);
sphereEntity->Transform() = glm::translate(glm::mat4(1.0f), glm::vec3(x, 1.2f, 0.0f));
}
}
// Create Quad
static float QuadVertex[] = {
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
static uint32_t QuadIndices[] = {
0, 1, 2, 2, 3, 0
};
m_ActiveScene = m_Scene;
m_SceneHierarchyPanel = CreateScope<SceneHierarchyPanel>(m_ActiveScene);
m_FullscreenQuadVertexArray = VertexArray::Create();
auto quadVB = VertexBuffer::Create(QuadVertex, sizeof(float) * sizeof(QuadVertex));
quadVB->SetLayout({
{ ShaderDataType::Float3, "a_Position" },
{ ShaderDataType::Float2, "a_TexCoord" }
});
auto quadIB = IndexBuffer::Create(QuadIndices, sizeof(QuadIndices) * sizeof(uint32_t));
m_FullscreenQuadVertexArray->AddVertexBuffer(quadVB);
m_FullscreenQuadVertexArray->SetIndexBuffer(quadIB);
m_PlaneMesh.reset(new Mesh("assets/models/Plane1m.obj"));
// Editor
m_CheckerboardTex = Texture2D::Create("assets/editor/Checkerboard.tga");
// lights
m_Light.Direction = {-0.5f, -0.5f, 1.0f};
m_Light.Radiance = {1.0f, 1.0f, 1.0f};
m_Transform = glm::scale(glm::mat4(1.0f), glm::vec3(m_MeshScale));
}
void EditorLayer::OnDetach()
@ -232,62 +216,28 @@ namespace Prism
{
// THINGS TO LOOK AT:
// - BRDF LUT
// - Cubemap mips and filtering
// - Tonemapping and proper HDR pipeline
using namespace Prism;
using namespace glm;
m_Camera.Update(deltaTime);
auto viewProjection = m_Camera.GetProjectionMatrix() * m_Camera.GetViewMatrix();
// m_Framebuffer->Bind();
Renderer::BeginRenderPass(m_GeoPass);
Renderer::Clear(m_ClearColor[0], m_ClearColor[1], m_ClearColor[2], m_ClearColor[3]);
m_QuadShader->Bind();
m_QuadShader->SetMat4("u_InverseVP", inverse(viewProjection));
m_EnvironmentCubeMap->Bind(0);
/*
m_VertexBuffer->Bind();
m_IndexBuffer->Bind();
*/
m_FullscreenQuadVertexArray->Bind();
Renderer::DrawIndexed(m_FullscreenQuadVertexArray->GetIndexBuffer()->GetCount(), false);
m_MeshMaterial->Set("u_AlbedoColor", m_AlbedoInput.Color);
m_MeshMaterial->Set("u_Metalness", m_MetalnessInput.Value);
m_MeshMaterial->Set("u_Roughness", m_RoughnessInput.Value);
m_MeshMaterial->Set("u_ViewProjectionMatrix", viewProjection);
m_MeshMaterial->Set("u_ModelMatrix", scale(mat4(1.0f), vec3(m_MeshScale)));
m_MeshMaterial->Set("lights", m_Light);
m_MeshMaterial->Set("u_CameraPosition", m_Camera.GetPosition());
m_MeshMaterial->Set("u_RadiancePrefilter", m_RadiancePrefilter ? 1.0f : 0.0f);
m_MeshMaterial->Set("u_AlbedoTexToggle", m_AlbedoInput.UseTexture ? 1.0f : 0.0f);
m_MeshMaterial->Set("u_NormalTexToggle", m_NormalInput.UseTexture ? 1.0f : 0.0f);
m_MeshMaterial->Set("u_MetalnessTexToggle", m_MetalnessInput.UseTexture ? 1.0f : 0.0f);
m_MeshMaterial->Set("u_RoughnessTexToggle", m_RoughnessInput.UseTexture ? 1.0f : 0.0f);
m_MeshMaterial->Set("u_EnvMapRotation", m_EnvMapRotation);
m_MeshMaterial->Set("u_EnvRadianceTex", m_EnvironmentCubeMap);
m_MeshMaterial->Set("u_EnvIrradianceTex", m_EnvironmentIrradiance);
m_MeshMaterial->Set("u_BRDFLUTTexture", m_BRDFLUT);
m_SphereMesh->GetMaterial()->Set("u_AlbedoColor", m_AlbedoInput.Color);
m_SphereMesh->GetMaterial()->Set("u_Metalness", m_MetalnessInput.Value);
m_SphereMesh->GetMaterial()->Set("u_Roughness", m_RoughnessInput.Value);
m_SphereMesh->GetMaterial()->Set("u_ViewProjectionMatrix", viewProjection);
m_SphereMesh->GetMaterial()->Set("u_ModelMatrix", scale(mat4(1.0f), vec3(m_MeshScale)));
m_SphereMesh->GetMaterial()->Set("lights", m_Light);
m_SphereMesh->GetMaterial()->Set("u_CameraPosition", m_Camera.GetPosition());
m_SphereMesh->GetMaterial()->Set("u_RadiancePrefilter", m_RadiancePrefilter ? 1.0f : 0.0f);
m_SphereMesh->GetMaterial()->Set("u_AlbedoTexToggle", m_AlbedoInput.UseTexture ? 1.0f : 0.0f);
m_SphereMesh->GetMaterial()->Set("u_NormalTexToggle", m_NormalInput.UseTexture ? 1.0f : 0.0f);
m_SphereMesh->GetMaterial()->Set("u_MetalnessTexToggle", m_MetalnessInput.UseTexture ? 1.0f : 0.0f);
m_SphereMesh->GetMaterial()->Set("u_RoughnessTexToggle", m_RoughnessInput.UseTexture ? 1.0f : 0.0f);
m_SphereMesh->GetMaterial()->Set("u_EnvMapRotation", m_EnvMapRotation);
m_SphereMesh->GetMaterial()->Set("u_EnvRadianceTex", m_EnvironmentCubeMap);
m_SphereMesh->GetMaterial()->Set("u_EnvIrradianceTex", m_EnvironmentIrradiance);
m_SphereMesh->GetMaterial()->Set("u_BRDFLUTTexture", m_BRDFLUT);
m_SphereBaseMaterial->Set("u_AlbedoColor", m_AlbedoInput.Color);
m_SphereBaseMaterial->Set("lights", m_Light);
m_SphereBaseMaterial->Set("u_RadiancePrefilter", m_RadiancePrefilter ? 1.0f : 0.0f);
m_SphereBaseMaterial->Set("u_AlbedoTexToggle", m_AlbedoInput.UseTexture ? 1.0f : 0.0f);
m_SphereBaseMaterial->Set("u_NormalTexToggle", m_NormalInput.UseTexture ? 1.0f : 0.0f);
m_SphereBaseMaterial->Set("u_MetalnessTexToggle", m_MetalnessInput.UseTexture ? 1.0f : 0.0f);
m_SphereBaseMaterial->Set("u_RoughnessTexToggle", m_RoughnessInput.UseTexture ? 1.0f : 0.0f);
m_SphereBaseMaterial->Set("u_EnvMapRotation", m_EnvMapRotation);
if (m_AlbedoInput.TextureMap)
@ -299,56 +249,7 @@ namespace Prism
if (m_RoughnessInput.TextureMap)
m_MeshMaterial->Set("u_RoughnessTexture", m_RoughnessInput.TextureMap);
if (m_Scene == Scene::Spheres)
{
// Metals
for (int i = 0; i < 8; i++)
{
m_SphereMesh->Render(deltaTime, glm::mat4(1.0f), m_MetalSphereMaterialInstances[i]);
/*
m_MetalSphereMaterialInstances[i]->Bind();
m_SphereMesh->Render(deltaTime, m_SimplePBRShader.get());
*/
}
// Dielectrics
for (int i = 0; i < 8; i++)
{
m_SphereMesh->Render(deltaTime, glm::mat4(1.0f), m_DielectricSphereMaterialInstances[i]);
/*
m_DielectricSphereMaterialInstances[i]->Bind();
m_SphereMesh->Render(deltaTime, m_SimplePBRShader.get());
*/
}
}
else if (m_Scene == Scene::Model)
{
if (m_Mesh)
{
m_Mesh->Render(deltaTime, m_Transform, m_MeshMaterial);
}
}
m_GridMaterial->Set("u_MVP", viewProjection * glm::scale(glm::mat4(1.0f), glm::vec3(16.0f)));
m_PlaneMesh->Render(deltaTime, m_GridMaterial);
// m_Framebuffer->Unbind();
Renderer::EndRenderPass();
Renderer::BeginRenderPass(m_CompositePass);
// m_FinalPresentBuffer->Bind();
m_HDRShader->Bind();
m_HDRShader->SetFloat("u_Exposure", m_Exposure);
// m_Framebuffer->BindTexture();
m_GeoPass->GetSpecification().TargetFramebuffer->BindTexture();
/*
m_VertexBuffer->Bind();
m_IndexBuffer->Bind();
*/
m_FullscreenQuadVertexArray->Bind();
Renderer::DrawIndexed(m_FullscreenQuadVertexArray->GetIndexBuffer()->GetCount(), false);
// m_FinalPresentBuffer->Unbind();
Renderer::EndRenderPass();
m_ActiveScene->OnUpdate(deltaTime);
}
}
@ -493,25 +394,39 @@ namespace Prism
ImGui::EndMenuBar();
}
m_SceneHierarchyPanel->OnImGuiRender();
ImGui::End();
#endif
// Editor Panel ------------------------------------------------------------------------------
ImGui::Begin("Model");
ImGui::RadioButton("Spheres", (int*)&m_Scene, (int)Scene::Spheres);
if (ImGui::RadioButton("Spheres", (int*)&m_SceneType, (int)SceneType::Spheres))
m_ActiveScene = m_SphereScene;
ImGui::SameLine();
ImGui::RadioButton("Model", (int*)&m_Scene, (int)Scene::Model);
if (ImGui::RadioButton("Model", (int*)&m_SceneType, (int)SceneType::Model))
m_ActiveScene = m_Scene;
ImGui::Begin("Environment");
if (ImGui::Button("Load Environment Map"))
{
std::string filename = Application::Get().OpenFile("*.hdr");
if (!filename.empty())
m_ActiveScene->SetEnvironment(Environment::Load(filename));
}
ImGui::SliderFloat("Skybox LOD", &m_Scene->GetSkyboxLod(), 0.0f, 11.0f);
ImGui::Columns(2);
ImGui::AlignTextToFramePadding();
Property("Light Direction", m_Light.Direction);
Property("Light Radiance", m_Light.Radiance, PropertyFlag::ColorProperty);
Property("Light Multiplier", m_LightMultiplier, 0.0f, 5.0f);
Property("Exposure", m_Exposure, 0.0f, 5.0f);
Property("Exposure", m_ActiveScene->GetCamera().GetExposure(), 0.0f, 5.0f);
Property("Radiance Prefiltering", m_RadiancePrefilter);
Property("Env Map Rotation", m_EnvMapRotation, -360.0f, 360.0f);
@ -523,7 +438,8 @@ namespace Prism
ImGui::Separator();
{
ImGui::Text("Mesh");
std::string fullpath = m_Mesh ? m_Mesh->GetFilePath() : "None";
auto mesh = m_MeshEntity->GetMesh();
std::string fullpath = mesh ? mesh->GetFilePath() : "None";
size_t found = fullpath.find_last_of("/\\");
std::string path = found != std::string::npos ? fullpath.substr(found + 1) : fullpath;
ImGui::Text(path.c_str());
@ -531,8 +447,13 @@ namespace Prism
if (ImGui::Button("...##Mesh"))
{
std::string filename = Application::Get().OpenFile("");
if (filename != "")
m_Mesh.reset(new Mesh(filename));
if (!filename.empty())
{
auto newMesh = CreateRef<Mesh>(filename);
// m_MeshMaterial.reset(new MaterialInstance(newMesh->GetMaterial()));
// m_MeshEntity->SetMaterial(m_MeshMaterial);
m_MeshEntity->SetMesh(newMesh);
}
}
}
ImGui::Separator();
@ -561,8 +482,8 @@ namespace Prism
if (ImGui::IsItemClicked())
{
std::string filename = Application::Get().OpenFile("");
if (filename != "")
m_AlbedoInput.TextureMap.reset(Texture2D::Create(filename, m_AlbedoInput.SRGB));
if (!filename.empty())
m_AlbedoInput.TextureMap = Texture2D::Create(filename, m_AlbedoInput.SRGB);
}
}
ImGui::SameLine();
@ -571,8 +492,7 @@ namespace Prism
if (ImGui::Checkbox("sRGB##AlbedoMap", &m_AlbedoInput.SRGB))
{
if (m_AlbedoInput.TextureMap)
m_AlbedoInput.TextureMap.reset(
Texture2D::Create(m_AlbedoInput.TextureMap->GetPath(), m_AlbedoInput.SRGB));
m_AlbedoInput.TextureMap = Texture2D::Create(m_AlbedoInput.TextureMap->GetPath(), m_AlbedoInput.SRGB);
}
ImGui::EndGroup();
ImGui::SameLine();
@ -602,8 +522,8 @@ namespace Prism
if (ImGui::IsItemClicked())
{
std::string filename = Application::Get().OpenFile("");
if (filename != "")
m_NormalInput.TextureMap.reset(Texture2D::Create(filename));
if (!filename.empty())
m_NormalInput.TextureMap = Texture2D::Create(filename);
}
}
ImGui::SameLine();
@ -633,8 +553,8 @@ namespace Prism
if (ImGui::IsItemClicked())
{
std::string filename = Application::Get().OpenFile("");
if (filename != "")
m_MetalnessInput.TextureMap.reset(Texture2D::Create(filename));
if (!filename.empty())
m_MetalnessInput.TextureMap = Texture2D::Create(filename);
}
}
ImGui::SameLine();
@ -666,8 +586,8 @@ namespace Prism
if (ImGui::IsItemClicked())
{
std::string filename = Application::Get().OpenFile("");
if (filename != "")
m_RoughnessInput.TextureMap.reset(Texture2D::Create(filename));
if (!filename.empty())
m_RoughnessInput.TextureMap = Texture2D::Create(filename);
}
}
ImGui::SameLine();
@ -701,42 +621,25 @@ namespace Prism
ImGui::Begin("Viewport");
auto viewportSize = ImGui::GetContentRegionAvail();
m_GeoPass->GetSpecification().TargetFramebuffer->Resize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
m_CompositePass->GetSpecification().TargetFramebuffer->Resize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
// m_Framebuffer->Resize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
// m_FinalPresentBuffer->Resize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
m_Camera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f));
// ImGui::Image((ImTextureRef)m_FinalPresentBuffer->GetColorAttachmentRendererID(), viewportSize, {0, 1}, {1, 0});
ImGui::Image((ImTextureRef)m_CompositePass->GetSpecification().TargetFramebuffer->GetColorAttachmentRendererID(), viewportSize, { 0, 1 }, { 1, 0 });
SceneRenderer::SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
m_ActiveScene->GetCamera().SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f));
m_ActiveScene->GetCamera().SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
ImGui::Image((ImTextureRef)SceneRenderer::GetFinalColorBufferRendererID(), viewportSize, { 0, 1 }, { 1, 0 });
// ImGuizmo
if (m_GizmoType != -1)
{
const float rw = (float)ImGui::GetWindowWidth();
const float rh = (float)ImGui::GetWindowHeight();
const auto rw = (float)ImGui::GetWindowWidth();
const auto rh = (float)ImGui::GetWindowHeight();
ImGuizmo::SetOrthographic(false);
ImGuizmo::SetDrawlist();
ImGuizmo::SetRect(ImGui::GetWindowPos().x, ImGui::GetWindowPos().y, rw, rh);
ImGuizmo::Manipulate(glm::value_ptr(m_Camera.GetViewMatrix()), glm::value_ptr(m_Camera.GetProjectionMatrix()), (ImGuizmo::OPERATION)m_GizmoType, ImGuizmo::LOCAL, glm::value_ptr(m_Transform));
const auto& camera = m_ActiveScene->GetCamera();
ImGuizmo::Manipulate(glm::value_ptr(camera.GetViewMatrix()), glm::value_ptr(camera.GetProjectionMatrix()), (ImGuizmo::OPERATION)m_GizmoType, ImGuizmo::LOCAL, glm::value_ptr(m_MeshEntity->Transform()));
}
ImGui::End();
ImGui::PopStyleVar();
ImGui::Begin("Log");
const auto& cameraPos = m_Camera.GetPosition();
ImGui::Text("cameraPos: (%.2f, %.2f, %.2f)", cameraPos.x, cameraPos.y, cameraPos.z);
const auto& Direct = m_Camera.GetForwardDirection();
ImGui::Text("forward Vec: (%.2f, %.2f, %.2f)", Direct.x, Direct.y, Direct.z);
const auto& distance = m_Camera.GetDistance();
ImGui::Text("distance: %.3f", distance);
ImGui::End();
if (m_Mesh)
m_Mesh->OnImGuiRender();
}

View File

@ -6,12 +6,13 @@
#define EDITORLAYER_H
#include "Prism.h"
#include "Prism/Editor/SceneHierachyPanel.h"
namespace Prism
{
class EditorLayer : public Layer
{
public:
class EditorLayer : public Layer
{
public:
EditorLayer();
virtual ~EditorLayer();
@ -21,32 +22,26 @@ public:
virtual void OnImGuiRender() override;
virtual void OnEvent(Event& e) override;
private:
private:
bool OnKeyPressedEvent(KeyPressedEvent& e);
private:
float m_ClearColor[4];
private:
Scope<SceneHierarchyPanel> m_SceneHierarchyPanel;
Ref<Scene> m_Scene;
Ref<Scene> m_SphereScene;
Ref<Scene> m_ActiveScene;
Entity* m_MeshEntity = nullptr;
Ref<Shader> m_BrushShader;
Ref<Mesh> m_PlaneMesh;
Ref<Material> m_SphereBaseMaterial;
Ref<Material> m_MeshMaterial;
// Ref<FrameBuffer> m_Framebuffer, m_FinalPresentBuffer;
Ref<RenderPass> m_GeoPass, m_CompositePass;
/*
Ref<VertexBuffer> m_VertexBuffer;
Ref<IndexBuffer> m_IndexBuffer;
*/
Ref<VertexArray> m_FullscreenQuadVertexArray;
Ref<TextureCube> m_EnvironmentCubeMap, m_EnvironmentIrradiance;
Camera m_Camera;
Ref<Shader> m_QuadShader;
Ref<Shader> m_HDRShader;
Ref<Shader> m_GridShader;
Ref<Mesh> m_Mesh;
Ref<Mesh> m_SphereMesh, m_PlaneMesh;
Ref<Texture2D> m_BRDFLUT;
Ref<MaterialInstance> m_MeshMaterial;
Ref<MaterialInstance> m_GridMaterial;
std::vector<Ref<MaterialInstance>> m_MetalSphereMaterialInstances;
@ -55,12 +50,9 @@ private:
float m_GridScale = 16.025f, m_GridSize = 0.025f;
float m_MeshScale = 1.0f;
Ref<Shader> m_Shader;
Ref<Shader> m_PBRShader;
// Imguizmo
int m_GizmoType = -1; // -1 = no gizmo
glm::mat4 m_Transform;
struct AlbedoInput
@ -105,21 +97,19 @@ private:
float m_LightMultiplier = 0.3f;
// PBR params
float m_Exposure = 1.0f;
bool m_RadiancePrefilter = false;
float m_EnvMapRotation = 0.0f;
enum class Scene : uint32_t
enum class SceneType : uint32_t
{
Spheres = 0, Model = 1
};
Scene m_Scene;
SceneType m_SceneType;
// Editor resources
Ref<Texture2D> m_CheckerboardTex;
};
};
}

BIN
Editor/assets/env/birchwood_4k.hdr vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,71 @@
[Window][DockSpace Demo]
Pos=0,0
Size=2560,1387
Collapsed=0
[Window][Debug##Default]
Pos=55,90
Size=400,400
Collapsed=0
[Window][Renderer]
Pos=2047,1141
Size=285,134
Collapsed=0
[Window][Model]
Pos=1772,24
Size=788,577
Collapsed=0
DockId=0x00000001,0
[Window][Environment]
Pos=1772,603
Size=788,784
Collapsed=0
DockId=0x00000002,0
[Window][Viewport]
Pos=421,24
Size=1349,1363
Collapsed=0
DockId=0x00000003,0
[Window][Mesh Debug]
Pos=2055,471
Size=505,916
Collapsed=0
DockId=0x00000002,1
[Window][ImGui Demo]
ViewportPos=276,66
ViewportId=0x080FC883
Size=1923,1168
Collapsed=0
[Window][Mesh Hierarchy]
Pos=0,24
Size=419,1363
Collapsed=0
DockId=0x00000005,1
[Window][Scene Hierarchy]
Pos=0,24
Size=419,1363
Collapsed=0
DockId=0x00000005,0
[Window][Properties]
Pos=107,1086
Size=188,127
Collapsed=0
[Docking][Data]
DockSpace ID=0xFA06BC56 Window=0x4647B76E Pos=0,47 Size=2560,1363 Split=X
DockNode ID=0x00000005 Parent=0xFA06BC56 SizeRef=419,1363 Selected=0x9A68760C
DockNode ID=0x00000006 Parent=0xFA06BC56 SizeRef=2139,1363 Split=X
DockNode ID=0x00000003 Parent=0x00000006 SizeRef=1349,876 CentralNode=1 HiddenTabBar=1 Selected=0x995B0CF8
DockNode ID=0x00000004 Parent=0x00000006 SizeRef=788,876 Split=Y Selected=0x16545DDD
DockNode ID=0x00000001 Parent=0x00000004 SizeRef=705,577 Selected=0x16545DDD
DockNode ID=0x00000002 Parent=0x00000004 SizeRef=705,784 Selected=0xC0BA51F5

View File

@ -0,0 +1,103 @@
#type compute
#version 450 core
// Physically Based Rendering
// Copyright (c) 2017-2018 Michał Siejak
// Computes diffuse irradiance cubemap convolution for image-based lighting.
// Uses quasi Monte Carlo sampling with Hammersley sequence.
const float PI = 3.141592;
const float TwoPI = 2 * PI;
const float Epsilon = 0.00001;
const uint NumSamples = 64 * 1024;
const float InvNumSamples = 1.0 / float(NumSamples);
layout(binding=0) uniform samplerCube inputTexture;
layout(binding=0, rgba16f) restrict writeonly uniform imageCube outputTexture;
// Compute Van der Corput radical inverse
// See: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
float radicalInverse_VdC(uint bits)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
// Sample i-th point from Hammersley point set of NumSamples points total.
vec2 sampleHammersley(uint i)
{
return vec2(i * InvNumSamples, radicalInverse_VdC(i));
}
// Uniformly sample point on a hemisphere.
// Cosine-weighted sampling would be a better fit for Lambertian BRDF but since this
// compute shader runs only once as a pre-processing step performance is not *that* important.
// See: "Physically Based Rendering" 2nd ed., section 13.6.1.
vec3 sampleHemisphere(float u1, float u2)
{
const float u1p = sqrt(max(0.0, 1.0 - u1*u1));
return vec3(cos(TwoPI*u2) * u1p, sin(TwoPI*u2) * u1p, u1);
}
vec3 GetCubeMapTexCoord()
{
vec2 st = gl_GlobalInvocationID.xy / vec2(imageSize(outputTexture));
vec2 uv = 2.0 * vec2(st.x, 1.0 - st.y) - vec2(1.0);
vec3 ret;
if (gl_GlobalInvocationID.z == 0) ret = vec3( 1.0, uv.y, -uv.x);
else if (gl_GlobalInvocationID.z == 1) ret = vec3( -1.0, uv.y, uv.x);
else if (gl_GlobalInvocationID.z == 2) ret = vec3( uv.x, 1.0, -uv.y);
else if (gl_GlobalInvocationID.z == 3) ret = vec3( uv.x, -1.0, uv.y);
else if (gl_GlobalInvocationID.z == 4) ret = vec3( uv.x, uv.y, 1.0);
else if (gl_GlobalInvocationID.z == 5) ret = vec3(-uv.x, uv.y, -1.0);
return normalize(ret);
}
// Compute orthonormal basis for converting from tanget/shading space to world space.
void computeBasisVectors(const vec3 N, out vec3 S, out vec3 T)
{
// Branchless select non-degenerate T.
T = cross(N, vec3(0.0, 1.0, 0.0));
T = mix(cross(N, vec3(1.0, 0.0, 0.0)), T, step(Epsilon, dot(T, T)));
T = normalize(T);
S = normalize(cross(N, T));
}
// Convert point from tangent/shading space to world space.
vec3 tangentToWorld(const vec3 v, const vec3 N, const vec3 S, const vec3 T)
{
return S * v.x + T * v.y + N * v.z;
}
layout(local_size_x=32, local_size_y=32, local_size_z=1) in;
void main(void)
{
vec3 N = GetCubeMapTexCoord();
vec3 S, T;
computeBasisVectors(N, S, T);
// Monte Carlo integration of hemispherical irradiance.
// As a small optimization this also includes Lambertian BRDF assuming perfectly white surface (albedo of 1.0)
// so we don't need to normalize in PBR fragment shader (so technically it encodes exitant radiance rather than irradiance).
vec3 irradiance = vec3(0);
for(uint i = 0; i < NumSamples; i++)
{
vec2 u = sampleHammersley(i);
vec3 Li = tangentToWorld(sampleHemisphere(u.x, u.y), N, S, T);
float cosTheta = max(0.0, dot(Li, N));
// PIs here cancel out because of division by pdf.
irradiance += 2.0 * textureLod(inputTexture, Li, 0).rgb * cosTheta;
}
irradiance /= vec3(NumSamples);
imageStore(outputTexture, ivec3(gl_GlobalInvocationID), vec4(irradiance, 1.0));
}

View File

@ -0,0 +1,159 @@
#type compute
#version 450 core
// Physically Based Rendering
// Copyright (c) 2017-2018 Michał Siejak
// Pre-filters environment cube map using GGX NDF importance sampling.
// Part of specular IBL split-sum approximation.
const float PI = 3.141592;
const float TwoPI = 2 * PI;
const float Epsilon = 0.00001;
const uint NumSamples = 1024;
const float InvNumSamples = 1.0 / float(NumSamples);
const int NumMipLevels = 1;
layout(binding = 0) uniform samplerCube inputTexture;
layout(binding = 0, rgba16f) restrict writeonly uniform imageCube outputTexture[NumMipLevels];
// Roughness value to pre-filter for.
layout(location=0) uniform float roughness;
#define PARAM_LEVEL 0
#define PARAM_ROUGHNESS roughness
// Compute Van der Corput radical inverse
// See: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
float radicalInverse_VdC(uint bits)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
// Sample i-th point from Hammersley point set of NumSamples points total.
vec2 sampleHammersley(uint i)
{
return vec2(i * InvNumSamples, radicalInverse_VdC(i));
}
// Importance sample GGX normal distribution function for a fixed roughness value.
// This returns normalized half-vector between Li & Lo.
// For derivation see: http://blog.tobias-franke.eu/2014/03/30/notes_on_importance_sampling.html
vec3 sampleGGX(float u1, float u2, float roughness)
{
float alpha = roughness * roughness;
float cosTheta = sqrt((1.0 - u2) / (1.0 + (alpha*alpha - 1.0) * u2));
float sinTheta = sqrt(1.0 - cosTheta*cosTheta); // Trig. identity
float phi = TwoPI * u1;
// Convert to Cartesian upon return.
return vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
}
// GGX/Towbridge-Reitz normal distribution function.
// Uses Disney's reparametrization of alpha = roughness^2.
float ndfGGX(float cosLh, float roughness)
{
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
vec3 GetCubeMapTexCoord()
{
vec2 st = gl_GlobalInvocationID.xy / vec2(imageSize(outputTexture[PARAM_LEVEL]));
vec2 uv = 2.0 * vec2(st.x, 1.0 - st.y) - vec2(1.0);
vec3 ret;
if (gl_GlobalInvocationID.z == 0) ret = vec3( 1.0, uv.y, -uv.x);
else if (gl_GlobalInvocationID.z == 1) ret = vec3( -1.0, uv.y, uv.x);
else if (gl_GlobalInvocationID.z == 2) ret = vec3( uv.x, 1.0, -uv.y);
else if (gl_GlobalInvocationID.z == 3) ret = vec3( uv.x, -1.0, uv.y);
else if (gl_GlobalInvocationID.z == 4) ret = vec3( uv.x, uv.y, 1.0);
else if (gl_GlobalInvocationID.z == 5) ret = vec3(-uv.x, uv.y, -1.0);
return normalize(ret);
}
// Compute orthonormal basis for converting from tanget/shading space to world space.
void computeBasisVectors(const vec3 N, out vec3 S, out vec3 T)
{
// Branchless select non-degenerate T.
T = cross(N, vec3(0.0, 1.0, 0.0));
T = mix(cross(N, vec3(1.0, 0.0, 0.0)), T, step(Epsilon, dot(T, T)));
T = normalize(T);
S = normalize(cross(N, T));
}
// Convert point from tangent/shading space to world space.
vec3 tangentToWorld(const vec3 v, const vec3 N, const vec3 S, const vec3 T)
{
return S * v.x + T * v.y + N * v.z;
}
layout(local_size_x=32, local_size_y=32, local_size_z=1) in;
void main(void)
{
// Make sure we won't write past output when computing higher mipmap levels.
ivec2 outputSize = imageSize(outputTexture[PARAM_LEVEL]);
if(gl_GlobalInvocationID.x >= outputSize.x || gl_GlobalInvocationID.y >= outputSize.y) {
return;
}
// Solid angle associated with a single cubemap texel at zero mipmap level.
// This will come in handy for importance sampling below.
vec2 inputSize = vec2(textureSize(inputTexture, 0));
float wt = 4.0 * PI / (6 * inputSize.x * inputSize.y);
// Approximation: Assume zero viewing angle (isotropic reflections).
vec3 N = GetCubeMapTexCoord();
vec3 Lo = N;
vec3 S, T;
computeBasisVectors(N, S, T);
vec3 color = vec3(0);
float weight = 0;
// Convolve environment map using GGX NDF importance sampling.
// Weight by cosine term since Epic claims it generally improves quality.
for(uint i = 0; i < NumSamples; i++) {
vec2 u = sampleHammersley(i);
vec3 Lh = tangentToWorld(sampleGGX(u.x, u.y, PARAM_ROUGHNESS), N, S, T);
// Compute incident direction (Li) by reflecting viewing direction (Lo) around half-vector (Lh).
vec3 Li = 2.0 * dot(Lo, Lh) * Lh - Lo;
float cosLi = dot(N, Li);
if(cosLi > 0.0) {
// Use Mipmap Filtered Importance Sampling to improve convergence.
// See: https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html, section 20.4
float cosLh = max(dot(N, Lh), 0.0);
// GGX normal distribution function (D term) probability density function.
// Scaling by 1/4 is due to change of density in terms of Lh to Li (and since N=V, rest of the scaling factor cancels out).
float pdf = ndfGGX(cosLh, PARAM_ROUGHNESS) * 0.25;
// Solid angle associated with this sample.
float ws = 1.0 / (NumSamples * pdf);
// Mip level to sample from.
float mipLevel = max(0.5 * log2(ws / wt) + 1.0, 0.0);
color += textureLod(inputTexture, Li, mipLevel).rgb * cosLi;
weight += cosLi;
}
}
color /= weight;
imageStore(outputTexture[PARAM_LEVEL], ivec3(gl_GlobalInvocationID), vec4(color, 1.0));
}

View File

@ -0,0 +1,39 @@
#type compute
#version 450 core
// Converts equirectangular (lat-long) projection texture into a cubemap
const float PI = 3.141592;
layout(binding = 0) uniform sampler2D u_EquirectangularTex;
layout(binding = 0, rgba16f) restrict writeonly uniform imageCube o_CubeMap;
vec3 GetCubeMapTexCoord()
{
vec2 st = gl_GlobalInvocationID.xy / vec2(imageSize(o_CubeMap));
vec2 uv = 2.0 * vec2(st.x, 1.0 - st.y) - vec2(1.0);
vec3 ret;
if (gl_GlobalInvocationID.z == 0) ret = vec3( 1.0, uv.y, -uv.x);
else if (gl_GlobalInvocationID.z == 1) ret = vec3( -1.0, uv.y, uv.x);
else if (gl_GlobalInvocationID.z == 2) ret = vec3( uv.x, 1.0, -uv.y);
else if (gl_GlobalInvocationID.z == 3) ret = vec3( uv.x, -1.0, uv.y);
else if (gl_GlobalInvocationID.z == 4) ret = vec3( uv.x, uv.y, 1.0);
else if (gl_GlobalInvocationID.z == 5) ret = vec3(-uv.x, uv.y, -1.0);
return normalize(ret);
}
layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
void main()
{
vec3 cubeTC = GetCubeMapTexCoord();
// Calculate sampling coords for equirectangular texture
// https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates
float phi = atan(cubeTC.z, cubeTC.x);
float theta = acos(cubeTC.y);
vec2 uv = vec2(phi / (2.0 * PI) + 0.5, theta / PI);
vec4 color = texture(u_EquirectangularTex, uv);
imageStore(o_CubeMap, ivec3(gl_GlobalInvocationID), color);
}

View File

@ -1,18 +1,19 @@
// Simple Texture Shader
// Grid Shader
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 4) in vec2 a_TexCoord;
layout(location = 1) in vec2 a_TexCoord;
uniform mat4 u_MVP;
uniform mat4 u_ViewProjection;
uniform mat4 u_Transform;
out vec2 v_TexCoord;
void main()
{
vec4 position = u_MVP * vec4(a_Position, 1.0);
vec4 position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
gl_Position = position;
v_TexCoord = a_TexCoord;
@ -28,11 +29,6 @@ uniform float u_Res;
in vec2 v_TexCoord;
/*void main()
{
color = texture(u_Texture, v_TexCoord * 8.0);
}*/
float grid(vec2 st, float res)
{
vec2 grid = fract(st);

View File

@ -22,7 +22,7 @@ layout(location = 5) in ivec4 a_BoneIndices;
layout(location = 6) in vec4 a_BoneWeights;
uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Transform;
const int MAX_BONES = 100;
uniform mat4 u_BoneTransforms[100];
@ -45,14 +45,13 @@ void main()
vec4 localPosition = boneTransform * vec4(a_Position, 1.0);
vs_Output.WorldPosition = vec3(u_ModelMatrix * boneTransform * vec4(a_Position, 1.0));
vs_Output.WorldPosition = vec3(u_Transform * boneTransform * vec4(a_Position, 1.0));
vs_Output.Normal = mat3(boneTransform) * a_Normal;
vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y);
vs_Output.WorldNormals = mat3(u_ModelMatrix) * mat3(a_Tangent, a_Binormal, a_Normal);
vs_Output.WorldNormals = mat3(u_Transform) * mat3(a_Tangent, a_Binormal, a_Normal);
vs_Output.Binormal = mat3(boneTransform) * a_Binormal;
//gl_Position = u_ViewProjectionMatrix * u_ModelMatrix * vec4(a_Position, 1.0);
gl_Position = u_ViewProjectionMatrix * u_ModelMatrix * localPosition;
gl_Position = u_ViewProjectionMatrix * u_Transform * localPosition;
}
#type fragment
@ -63,7 +62,6 @@ const float Epsilon = 0.00001;
const int LightCount = 1;
// Constant normal incidence Fresnel factor for all dielectrics.
const vec3 Fdielectric = vec3(0.04);
@ -290,12 +288,7 @@ vec3 IBL(vec3 F0, vec3 Lr)
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
float NoV = clamp(m_Params.NdotV, 0.0, 1.0);
vec3 R = 2.0 * dot(m_Params.View, m_Params.Normal) * m_Params.Normal - m_Params.View;
vec3 specularIrradiance = vec3(0.0);
if (u_RadiancePrefilter > 0.5)
specularIrradiance = PrefilterEnvMap(m_Params.Roughness * m_Params.Roughness, R) * u_RadiancePrefilter;
else
specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), sqrt(m_Params.Roughness) * u_EnvRadianceTexLevels).rgb * (1.0 - u_RadiancePrefilter);
vec3 specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), (m_Params.Roughness * m_Params.Roughness) * u_EnvRadianceTexLevels).rgb;
// Sample BRDF Lut, 1.0 - roughness for y-coord because texture was generated (in Sparky) for gloss model
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
@ -329,7 +322,7 @@ void main()
// Fresnel reflectance, metals use albedo
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
vec3 lightContribution = Lighting(F0);
vec3 lightContribution = vec3(0.0);//Lighting(F0);
vec3 iblContribution = IBL(F0, Lr);
color = vec4(lightContribution + iblContribution, 1.0);

View File

@ -0,0 +1,319 @@
// -----------------------------
// -- Hazel Engine PBR shader --
// -----------------------------
// Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in.
// Currently heavily updated.
//
// References upon which this is based:
// - Unreal Engine 4 PBR notes (https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf)
// - Frostbite's SIGGRAPH 2014 paper (https://seblagarde.wordpress.com/2015/07/14/siggraph-2014-moving-frostbite-to-physically-based-rendering/)
// - Michał Siejak's PBR project (https://github.com/Nadrin)
// - My implementation from years ago in the Sparky engine (https://github.com/TheCherno/Sparky)
#type vertex
#version 430 core
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec3 a_Normal;
layout(location = 2) in vec3 a_Tangent;
layout(location = 3) in vec3 a_Binormal;
layout(location = 4) in vec2 a_TexCoord;
uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_Transform;
out VertexOutput
{
vec3 WorldPosition;
vec3 Normal;
vec2 TexCoord;
mat3 WorldNormals;
mat3 WorldTransform;
vec3 Binormal;
} vs_Output;
void main()
{
vs_Output.WorldPosition = vec3(u_Transform * vec4(a_Position, 1.0));
vs_Output.Normal = a_Normal;
vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y);
vs_Output.WorldNormals = mat3(u_Transform) * mat3(a_Tangent, a_Binormal, a_Normal);
vs_Output.WorldTransform = mat3(u_Transform);
vs_Output.Binormal = a_Binormal;
gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0);
}
#type fragment
#version 430 core
const float PI = 3.141592;
const float Epsilon = 0.00001;
const int LightCount = 1;
// Constant normal incidence Fresnel factor for all dielectrics.
const vec3 Fdielectric = vec3(0.04);
struct Light {
vec3 Direction;
vec3 Radiance;
};
in VertexOutput
{
vec3 WorldPosition;
vec3 Normal;
vec2 TexCoord;
mat3 WorldNormals;
mat3 WorldTransform;
vec3 Binormal;
} vs_Input;
layout(location = 0) out vec4 color;
uniform Light lights;
uniform vec3 u_CameraPosition;
// PBR texture inputs
uniform sampler2D u_AlbedoTexture;
uniform sampler2D u_NormalTexture;
uniform sampler2D u_MetalnessTexture;
uniform sampler2D u_RoughnessTexture;
// Environment maps
uniform samplerCube u_EnvRadianceTex;
uniform samplerCube u_EnvIrradianceTex;
// BRDF LUT
uniform sampler2D u_BRDFLUTTexture;
uniform vec3 u_AlbedoColor;
uniform float u_Metalness;
uniform float u_Roughness;
uniform float u_EnvMapRotation;
// Toggles
uniform float u_RadiancePrefilter;
uniform float u_AlbedoTexToggle;
uniform float u_NormalTexToggle;
uniform float u_MetalnessTexToggle;
uniform float u_RoughnessTexToggle;
struct PBRParameters
{
vec3 Albedo;
float Roughness;
float Metalness;
vec3 Normal;
vec3 View;
float NdotV;
};
PBRParameters m_Params;
// GGX/Towbridge-Reitz normal distribution function.
// Uses Disney's reparametrization of alpha = roughness^2
float ndfGGX(float cosLh, float roughness)
{
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
// Single term for separable Schlick-GGX below.
float gaSchlickG1(float cosTheta, float k)
{
return cosTheta / (cosTheta * (1.0 - k) + k);
}
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
float gaSchlickGGX(float cosLi, float NdotV, float roughness)
{
float r = roughness + 1.0;
float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
return gaSchlickG1(cosLi, k) * gaSchlickG1(NdotV, k);
}
float GeometrySchlickGGX(float NdotV, float roughness)
{
float r = (roughness + 1.0);
float k = (r*r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
// Shlick's approximation of the Fresnel factor.
vec3 fresnelSchlick(vec3 F0, float cosTheta)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness)
{
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
}
// ---------------------------------------------------------------------------------------------------
// The following code (from Unreal Engine 4's paper) shows how to filter the environment map
// for different roughnesses. This is mean to be computed offline and stored in cube map mips,
// so turning this on online will cause poor performance
float RadicalInverse_VdC(uint bits)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
vec2 Hammersley(uint i, uint N)
{
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
}
vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N)
{
float a = Roughness * Roughness;
float Phi = 2 * PI * Xi.x;
float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
float SinTheta = sqrt( 1 - CosTheta * CosTheta );
vec3 H;
H.x = SinTheta * cos( Phi );
H.y = SinTheta * sin( Phi );
H.z = CosTheta;
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0,0,1) : vec3(1,0,0);
vec3 TangentX = normalize( cross( UpVector, N ) );
vec3 TangentY = cross( N, TangentX );
// Tangent to world space
return TangentX * H.x + TangentY * H.y + N * H.z;
}
float TotalWeight = 0.0;
vec3 PrefilterEnvMap(float Roughness, vec3 R)
{
vec3 N = R;
vec3 V = R;
vec3 PrefilteredColor = vec3(0.0);
int NumSamples = 1024;
for(int i = 0; i < NumSamples; i++)
{
vec2 Xi = Hammersley(i, NumSamples);
vec3 H = ImportanceSampleGGX(Xi, Roughness, N);
vec3 L = 2 * dot(V, H) * H - V;
float NoL = clamp(dot(N, L), 0.0, 1.0);
if (NoL > 0)
{
PrefilteredColor += texture(u_EnvRadianceTex, L).rgb * NoL;
TotalWeight += NoL;
}
}
return PrefilteredColor / TotalWeight;
}
// ---------------------------------------------------------------------------------------------------
vec3 RotateVectorAboutY(float angle, vec3 vec)
{
angle = radians(angle);
mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)),
vec3(0.0,1.0,0.0),
vec3(-sin(angle),0.0,cos(angle))};
return rotationMatrix * vec;
}
vec3 Lighting(vec3 F0)
{
vec3 result = vec3(0.0);
for(int i = 0; i < LightCount; i++)
{
vec3 Li = -lights.Direction;
vec3 Lradiance = lights.Radiance;
vec3 Lh = normalize(Li + m_Params.View);
// Calculate angles between surface normal and various light vectors.
float cosLi = max(0.0, dot(m_Params.Normal, Li));
float cosLh = max(0.0, dot(m_Params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, m_Params.View)));
float D = ndfGGX(cosLh, m_Params.Roughness);
float G = gaSchlickGGX(cosLi, m_Params.NdotV, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseBRDF = kd * m_Params.Albedo;
// Cook-Torrance
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * m_Params.NdotV);
result += (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
}
return result;
}
vec3 IBL(vec3 F0, vec3 Lr)
{
vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb;
vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseIBL = m_Params.Albedo * irradiance;
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
float NoV = clamp(m_Params.NdotV, 0.0, 1.0);
vec3 R = 2.0 * dot(m_Params.View, m_Params.Normal) * m_Params.Normal - m_Params.View;
vec3 specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), (m_Params.Roughness * m_Params.Roughness) * u_EnvRadianceTexLevels).rgb;
// Sample BRDF Lut, 1.0 - roughness for y-coord because texture was generated (in Sparky) for gloss model
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y);
return kd * diffuseIBL + specularIBL;
}
void main()
{
// Standard PBR inputs
m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor;
m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness;
m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight
// Normals (either from vertex or map)
m_Params.Normal = normalize(vs_Input.Normal);
if (u_NormalTexToggle > 0.5)
{
m_Params.Normal = normalize(2.0 * texture(u_NormalTexture, vs_Input.TexCoord).rgb - 1.0);
m_Params.Normal = normalize(vs_Input.WorldNormals * m_Params.Normal);
}
m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition);
m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0);
// Specular reflection vector
vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View;
// Fresnel reflectance, metals use albedo
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
vec3 lightContribution = Lighting(F0);
vec3 iblContribution = IBL(F0, Lr);
color = vec4(lightContribution + iblContribution, 1.0);
}

View File

@ -0,0 +1,33 @@
// Skybox shader
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
uniform mat4 u_InverseVP;
out vec3 v_Position;
void main()
{
vec4 position = vec4(a_Position.xy, 1.0, 1.0);
gl_Position = position;
v_Position = (u_InverseVP * position).xyz;
}
#type fragment
#version 430
layout(location = 0) out vec4 finalColor;
uniform samplerCube u_Texture;
uniform float u_TextureLod;
in vec3 v_Position;
void main()
{
finalColor = textureLod(u_Texture, v_Position, u_TextureLod);
}

View File

@ -8,7 +8,7 @@ out vec2 v_TexCoord;
void main()
{
vec4 position = vec4(a_Position.xy, 1.0, 1.0);
vec4 position = vec4(a_Position.xy, 0.0, 1.0);
v_TexCoord = a_TexCoord;
gl_Position = position;
}

View File

@ -22,11 +22,14 @@ void main()
layout(location = 0) out vec4 finalColor;
//uniform vec4 u_Color;
uniform vec4 u_Color;
in vec3 v_Normal;
void main()
{
finalColor = vec4(0.8, 0.0, 0.8, 1.0);
finalColor = vec4((v_Normal * 0.5 + 0.5), 1.0);// * u_Color.xyz, 1.0);
}

View File

@ -1,321 +0,0 @@
// -----------------------------
// -- Hazel Engine PBR shader --
// -----------------------------
// Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in.
// Currently heavily updated.
//
// References upon which this is based:
// - Unreal Engine 4 PBR notes (https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf)
// - Frostbite's SIGGRAPH 2014 paper (https://seblagarde.wordpress.com/2015/07/14/siggraph-2014-moving-frostbite-to-physically-based-rendering/)
// - Michał Siejak's PBR project (https://github.com/Nadrin)
// - My implementation from years ago in the Sparky engine (https://github.com/TheCherno/Sparky)
#type vertex
#version 430 core
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec3 a_Normal;
layout(location = 2) in vec3 a_Tangent;
layout(location = 3) in vec3 a_Binormal;
layout(location = 4) in vec2 a_TexCoord;
uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_ModelMatrix;
out VertexOutput
{
vec3 WorldPosition;
vec3 Normal;
vec2 TexCoord;
mat3 WorldNormals;
vec3 Binormal;
} vs_Output;
void main()
{
vs_Output.WorldPosition = vec3(u_ModelMatrix * vec4(a_Position, 1.0));
vs_Output.Normal = a_Normal;
vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y);
vs_Output.WorldNormals = mat3(u_ModelMatrix) * mat3(a_Tangent, a_Binormal, a_Normal);
vs_Output.Binormal = a_Binormal;
gl_Position = u_ViewProjectionMatrix * u_ModelMatrix * vec4(a_Position, 1.0);
}
#type fragment
#version 430 core
const float PI = 3.141592;
const float Epsilon = 0.00001;
const int LightCount = 1;
// Constant normal incidence Fresnel factor for all dielectrics.
const vec3 Fdielectric = vec3(0.04);
struct Light {
vec3 Direction;
vec3 Radiance;
};
in VertexOutput
{
vec3 WorldPosition;
vec3 Normal;
vec2 TexCoord;
mat3 WorldNormals;
vec3 Binormal;
} vs_Input;
layout(location = 0) out vec4 color;
uniform Light lights;
uniform vec3 u_CameraPosition;
// PBR texture inputs
uniform sampler2D u_AlbedoTexture;
uniform sampler2D u_NormalTexture;
uniform sampler2D u_MetalnessTexture;
uniform sampler2D u_RoughnessTexture;
// Environment maps
uniform samplerCube u_EnvRadianceTex;
uniform samplerCube u_EnvIrradianceTex;
// BRDF LUT
uniform sampler2D u_BRDFLUTTexture;
uniform vec3 u_AlbedoColor;
uniform float u_Metalness;
uniform float u_Roughness;
uniform float u_EnvMapRotation;
// Toggles
uniform float u_RadiancePrefilter;
uniform float u_AlbedoTexToggle;
uniform float u_NormalTexToggle;
uniform float u_MetalnessTexToggle;
uniform float u_RoughnessTexToggle;
struct PBRParameters
{
vec3 Albedo;
float Roughness;
float Metalness;
vec3 Normal;
vec3 View;
float NdotV;
};
PBRParameters m_Params;
// GGX/Towbridge-Reitz normal distribution function.
// Uses Disney's reparametrization of alpha = roughness^2
float ndfGGX(float cosLh, float roughness)
{
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
// Single term for separable Schlick-GGX below.
float gaSchlickG1(float cosTheta, float k)
{
return cosTheta / (cosTheta * (1.0 - k) + k);
}
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
float gaSchlickGGX(float cosLi, float NdotV, float roughness)
{
float r = roughness + 1.0;
float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
return gaSchlickG1(cosLi, k) * gaSchlickG1(NdotV, k);
}
float GeometrySchlickGGX(float NdotV, float roughness)
{
float r = (roughness + 1.0);
float k = (r*r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
// Shlick's approximation of the Fresnel factor.
vec3 fresnelSchlick(vec3 F0, float cosTheta)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness)
{
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
}
// ---------------------------------------------------------------------------------------------------
// The following code (from Unreal Engine 4's paper) shows how to filter the environment map
// for different roughnesses. This is mean to be computed offline and stored in cube map mips,
// so turning this on online will cause poor performance
float RadicalInverse_VdC(uint bits)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
vec2 Hammersley(uint i, uint N)
{
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
}
vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N)
{
float a = Roughness * Roughness;
float Phi = 2 * PI * Xi.x;
float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
float SinTheta = sqrt( 1 - CosTheta * CosTheta );
vec3 H;
H.x = SinTheta * cos( Phi );
H.y = SinTheta * sin( Phi );
H.z = CosTheta;
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0,0,1) : vec3(1,0,0);
vec3 TangentX = normalize( cross( UpVector, N ) );
vec3 TangentY = cross( N, TangentX );
// Tangent to world space
return TangentX * H.x + TangentY * H.y + N * H.z;
}
float TotalWeight = 0.0;
vec3 PrefilterEnvMap(float Roughness, vec3 R)
{
vec3 N = R;
vec3 V = R;
vec3 PrefilteredColor = vec3(0.0);
int NumSamples = 1024;
for(int i = 0; i < NumSamples; i++)
{
vec2 Xi = Hammersley(i, NumSamples);
vec3 H = ImportanceSampleGGX(Xi, Roughness, N);
vec3 L = 2 * dot(V, H) * H - V;
float NoL = clamp(dot(N, L), 0.0, 1.0);
if (NoL > 0)
{
PrefilteredColor += texture(u_EnvRadianceTex, L).rgb * NoL;
TotalWeight += NoL;
}
}
return PrefilteredColor / TotalWeight;
}
// ---------------------------------------------------------------------------------------------------
vec3 RotateVectorAboutY(float angle, vec3 vec)
{
angle = radians(angle);
mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)),
vec3(0.0,1.0,0.0),
vec3(-sin(angle),0.0,cos(angle))};
return rotationMatrix * vec;
}
vec3 Lighting(vec3 F0)
{
vec3 result = vec3(0.0);
for(int i = 0; i < LightCount; i++)
{
vec3 Li = -lights.Direction;
vec3 Lradiance = lights.Radiance;
vec3 Lh = normalize(Li + m_Params.View);
// Calculate angles between surface normal and various light vectors.
float cosLi = max(0.0, dot(m_Params.Normal, Li));
float cosLh = max(0.0, dot(m_Params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, m_Params.View)));
float D = ndfGGX(cosLh, m_Params.Roughness);
float G = gaSchlickGGX(cosLi, m_Params.NdotV, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseBRDF = kd * m_Params.Albedo;
// Cook-Torrance
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * m_Params.NdotV);
result += (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
}
return result;
}
vec3 IBL(vec3 F0, vec3 Lr)
{
vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb;
vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseIBL = m_Params.Albedo * irradiance;
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
float NoV = clamp(m_Params.NdotV, 0.0, 1.0);
vec3 R = 2.0 * dot(m_Params.View, m_Params.Normal) * m_Params.Normal - m_Params.View;
vec3 specularIrradiance = vec3(0.0);
if (u_RadiancePrefilter > 0.5)
specularIrradiance = PrefilterEnvMap(m_Params.Roughness * m_Params.Roughness, R) * u_RadiancePrefilter;
else
specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), sqrt(m_Params.Roughness) * u_EnvRadianceTexLevels).rgb * (1.0 - u_RadiancePrefilter);
// Sample BRDF Lut, 1.0 - roughness for y-coord because texture was generated (in Sparky) for gloss model
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y);
return kd * diffuseIBL + specularIBL;
}
void main()
{
// Standard PBR inputs
m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor;
m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness;
m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight
// Normals (either from vertex or map)
m_Params.Normal = normalize(vs_Input.Normal);
if (u_NormalTexToggle > 0.5)
{
m_Params.Normal = normalize(2.0 * texture(u_NormalTexture, vs_Input.TexCoord).rgb - 1.0);
m_Params.Normal = normalize(vs_Input.WorldNormals * m_Params.Normal);
}
m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition);
m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0);
// Specular reflection vector
vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View;
// Fresnel reflectance, metals use albedo
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
vec3 lightContribution = Lighting(F0);
vec3 iblContribution = IBL(F0, Lr);
color = vec4(lightContribution + iblContribution, 1.0);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 MiB

View File

@ -43,7 +43,6 @@ list(APPEND SRC_SOURCE ${IMGUIZMO_SOURCE})
# ------------- link libraries -------------
set(LINK_LIBRARIES_PRIVATE
spdlog
glfw
glad
assimp
@ -52,6 +51,7 @@ set(LINK_LIBRARIES_PRIVATE
)
set(LINK_LIBRARIES_PUBLIC
spdlog
glm
)

View File

@ -8,6 +8,7 @@
#include "Prism/Core/Application.h"
#include "Prism/Core/TimeStep.h"
#include "Prism/Core/KeyCodes.h"
#include "Prism/Core/Events/Event.h"
#include "Prism/Core/Events/ApplicationEvent.h"
#include "Prism/Core/Events/KeyEvent.h"
@ -18,6 +19,7 @@
#include "Prism/Renderer/Buffer.h"
#include "Prism/Renderer/Renderer.h"
#include "Prism/Renderer/SceneRenderer.h"
#include "Prism/Renderer/RenderPass.h"
#include "Prism/Renderer/Shader.h"
#include "Prism/Renderer/Texture.h"
@ -27,4 +29,8 @@
#include "Prism/Renderer/Camera.h"
#include "Prism/Renderer/Material.h"
// Scene
#include "Prism/Scene/Entity.h"
#include "Prism/Scene/Scene.h"
#endif //PRISM_H

View File

@ -36,7 +36,7 @@ namespace Prism
PushOverlay(m_ImGuiLayer);
Renderer::Init();
Renderer::Get().WaitAndRender();
Renderer::WaitAndRender();
}
Application::~Application()
@ -56,7 +56,7 @@ namespace Prism
Renderer::Submit([this](){ this->RenderImGui(); });
Renderer::Get().WaitAndRender();
Renderer::WaitAndRender();
}
@ -87,13 +87,15 @@ namespace Prism
{
m_ImGuiLayer->Begin();
/*
ImGui::Begin("Renderer");
auto& caps = RendererAPI::GetCapabilities();
const auto& caps = RendererAPI::GetCapabilities();
ImGui::Text("Vendor: %s", caps.Vendor.c_str());
ImGui::Text("Renderer: %s", caps.Renderer.c_str());
ImGui::Text("Version: %s", caps.Version.c_str());
ImGui::Text("Frame Time: %.2fms\n", m_TimeStep.GetMilliseconds());
ImGui::End();
*/
for (Layer* layer : m_LayerStack)
layer->OnImGuiRender();

View File

@ -61,6 +61,13 @@ namespace Prism
template<typename T>
using Scope = std::unique_ptr<T>;
template<typename T, typename ... Args>
constexpr Scope<T> CreateScope(Args&& ... args)
{
return std::make_unique<T>(std::forward<Args>(args)...);
}
template<typename T>
using Ref = std::shared_ptr<T>;

View File

@ -5,23 +5,4 @@
namespace Prism
{
bool Input::IsKeyPressed(const int keycode)
{
return s_Instance->IsKeyPressedImpl(keycode);
}
bool Input::IsMouseButtonPressed(const int button)
{
return s_Instance->IsMouseButtonPressedImpl(button);
}
float Input::GetMouseX()
{
return s_Instance->GetMouseXImpl();
}
float Input::GetMouseY()
{
return s_Instance->GetMouseYImpl();
}
}

View File

@ -10,19 +10,11 @@ namespace Prism
class PRISM_API Input
{
public:
static bool IsKeyPressed(const int keycode);
static bool IsKeyPressed(int keycode);
static bool IsMouseButtonPressed(const int button);
static bool IsMouseButtonPressed(int button);
static float GetMouseX();
static float GetMouseY();
protected:
virtual bool IsKeyPressedImpl(int keycode) = 0;
virtual bool IsMouseButtonPressedImpl(int button) = 0;
virtual float GetMouseXImpl() = 0;
virtual float GetMouseYImpl() = 0;
private:
static Input* s_Instance;
};
}

View File

@ -27,4 +27,14 @@ namespace Prism
s_ClientLogger = spdlog::stdout_color_mt("APP");
s_ClientLogger->set_level(LOG_LEVEL);
}
std::shared_ptr<spdlog::logger>& Log::GetCoreLogger()
{
return s_CoreLogger;
}
std::shared_ptr<spdlog::logger>& Log::GetClientLogger()
{
return s_ClientLogger;
}
}

View File

@ -5,7 +5,7 @@
#ifndef LOG_H
#define LOG_H
#include "spdlog/spdlog.h"
#include <spdlog/spdlog.h>
namespace Prism
@ -15,8 +15,9 @@ namespace Prism
public:
static void Init();
inline static std::shared_ptr<spdlog::logger>& GetCoreLogger() { return s_CoreLogger; }
inline static std::shared_ptr<spdlog::logger>& GetClientLogger() { return s_ClientLogger; }
PRISM_API static std::shared_ptr<spdlog::logger>& GetCoreLogger();
PRISM_API static std::shared_ptr<spdlog::logger>& GetClientLogger();
private:
static std::shared_ptr<spdlog::logger> s_CoreLogger;
static std::shared_ptr<spdlog::logger> s_ClientLogger;

View File

@ -0,0 +1,141 @@
//
// Created by sfd on 25-11-30.
//
#include "SceneHierachyPanel.h"
#include "imgui.h"
#include "assimp/matrix4x4.h"
#define GLM_ENABLE_EXPERIMENTAL
#include "assimp/scene.h"
#include "glm/gtx/matrix_decompose.hpp"
namespace Prism
{
glm::mat4 Mat4FromAssimpMat4(const aiMatrix4x4& matrix);
SceneHierarchyPanel::SceneHierarchyPanel(const Ref<Scene>& context)
: m_Context(context)
{
}
void SceneHierarchyPanel::SetContext(const Ref<Scene>& scene)
{
m_Context = scene;
}
void SceneHierarchyPanel::OnImGuiRender()
{
ImGui::Begin("Scene Hierarchy");
auto& sceneEntities = m_Context->m_Entities;
for (Entity* entity : sceneEntities)
{
auto mesh = entity->GetMesh();
auto material = entity->GetMaterial();
const auto& transform = entity->GetTransform();
if (mesh)
{
DrawMeshNode(mesh);
}
}
ImGui::End();
ImGui::Begin("Properties");
if (m_SelectionContext)
{
/*auto mesh = m_SelectionContext;
{
auto [translation, rotation, scale] = GetTransformDecomposition(transform);
ImGui::Text("World Transform");
ImGui::Text(" Translation: %.2f, %.2f, %.2f", translation.x, translation.y, translation.z);
ImGui::Text(" Scale: %.2f, %.2f, %.2f", scale.x, scale.y, scale.z);
}
{
auto [translation, rotation, scale] = GetTransformDecomposition(localTransform);
ImGui::Text("Local Transform");
ImGui::Text(" Translation: %.2f, %.2f, %.2f", translation.x, translation.y, translation.z);
ImGui::Text(" Scale: %.2f, %.2f, %.2f", scale.x, scale.y, scale.z);
}*/
}
ImGui::End();
#if TODO
ImGui::Begin("Mesh Debug");
if (ImGui::CollapsingHeader(mesh->m_FilePath.c_str()))
{
if (mesh->m_IsAnimated)
{
if (ImGui::CollapsingHeader("Animation"))
{
if (ImGui::Button(mesh->m_AnimationPlaying ? "Pause" : "Play"))
mesh->m_AnimationPlaying = !mesh->m_AnimationPlaying;
ImGui::SliderFloat("##AnimationTime", &mesh->m_AnimationTime, 0.0f, (float)mesh->m_Scene->mAnimations[0]->mDuration);
ImGui::DragFloat("Time Scale", &mesh->m_TimeMultiplier, 0.05f, 0.0f, 10.0f);
}
}
}
ImGui::End();
#endif
}
static std::tuple<glm::vec3, glm::quat, glm::vec3> GetTransformDecomposition(const glm::mat4& transform)
{
glm::vec3 scale, translation, skew;
glm::vec4 perspective;
glm::quat orientation;
glm::decompose(transform, scale, orientation, translation, skew, perspective);
return { translation, orientation, scale };
}
void SceneHierarchyPanel::DrawMeshNode(const Ref<Mesh>& mesh)
{
auto rootNode = mesh->m_Scene->mRootNode;
MeshNodeHierarchy(mesh, rootNode);
}
void SceneHierarchyPanel::MeshNodeHierarchy(const Ref<Mesh>& mesh, aiNode* node, const glm::mat4& parentTransform, uint32_t level)
{
glm::mat4 localTransform = Mat4FromAssimpMat4(node->mTransformation);
glm::mat4 transform = parentTransform * localTransform;
for (uint32_t i = 0; i < node->mNumMeshes; i++)
{
uint32_t meshIndex = node->mMeshes[i];
mesh->m_Submeshes[meshIndex].Transform = transform;
}
if (ImGui::TreeNode(node->mName.C_Str()))
{
{
auto [translation, rotation, scale] = GetTransformDecomposition(transform);
ImGui::Text("World Transform");
ImGui::Text(" Translation: %.2f, %.2f, %.2f", translation.x, translation.y, translation.z);
ImGui::Text(" Scale: %.2f, %.2f, %.2f", scale.x, scale.y, scale.z);
}
{
auto [translation, rotation, scale] = GetTransformDecomposition(localTransform);
ImGui::Text("Local Transform");
ImGui::Text(" Translation: %.2f, %.2f, %.2f", translation.x, translation.y, translation.z);
ImGui::Text(" Scale: %.2f, %.2f, %.2f", scale.x, scale.y, scale.z);
}
for (uint32_t i = 0; i < node->mNumChildren; i++)
MeshNodeHierarchy(mesh, node->mChildren[i], transform, level + 1);
ImGui::TreePop();
}
}
}

View File

@ -0,0 +1,30 @@
//
// Created by sfd on 25-11-30.
//
#ifndef SCENEHIERACHYPANEL_H
#define SCENEHIERACHYPANEL_H
#include "Prism/Scene/Scene.h"
namespace Prism
{
class PRISM_API SceneHierarchyPanel
{
public:
SceneHierarchyPanel(const Ref<Scene>& context);
void SetContext(const Ref<Scene>& scene);
void OnImGuiRender();
private:
void DrawMeshNode(const Ref<Mesh>& mesh);
void MeshNodeHierarchy(const Ref<Mesh>& mesh, aiNode* node, const glm::mat4& parentTransform = glm::mat4(1.0f), uint32_t level = 0);
private:
Ref<Scene> m_Context;
Ref<Mesh> m_SelectionContext;
};
}
#endif //SCENEHIERACHYPANEL_H

View File

@ -19,7 +19,7 @@ namespace Prism
OpenGLFrameBuffer::~OpenGLFrameBuffer()
{
Renderer::Submit([this](){
Renderer::Submit([=](){
glDeleteFramebuffers(1, &m_RendererID);
});
}

View File

@ -37,6 +37,7 @@ namespace Prism
void OpenGLShader::Load(const std::string& source)
{
m_ShaderSource = PreProcess(source);
if (!m_IsCompute)
Parse();
Renderer::Submit([this](){
@ -45,8 +46,11 @@ namespace Prism
glDeleteProgram(m_RendererID);
CompileAndUploadShader();
if (!m_IsCompute)
{
ResolveUniforms();
ValidateUniforms();
}
if (m_Loaded)
{
@ -60,7 +64,7 @@ namespace Prism
void OpenGLShader::Bind()
{
Renderer::Submit([this](){
Renderer::Submit([=](){
glUseProgram(m_RendererID);
});
}
@ -179,7 +183,7 @@ namespace Prism
}
else
{
PM_CORE_WARN("Could not read shader file {0}", filepath);
PM_CORE_ASSERT(false, "Could not load shader file");
}
return result;
@ -206,11 +210,20 @@ namespace Prism
PM_CORE_ASSERT(eol != std::string::npos, "Syntax error");
size_t begin = pos + typeTokenLength + 1;
std::string type = source.substr(begin, eol - begin);
PM_CORE_ASSERT(type == "vertex" || type == "fragment" || type == "pixel", "Invalid shader type");
PM_CORE_ASSERT(type == "vertex" || type == "fragment" || type == "pixel" || type == "compute", "Invalid shader type");
size_t nextLinePos = source.find_first_not_of("\r\n", eol);
pos = source.find(typeToken, nextLinePos);
shaderSources[ShaderTypeFromString(type)] = source.substr(nextLinePos, pos - (nextLinePos == std::string::npos ? m_ShaderSource.size() - 1 : nextLinePos));
auto shaderType = ShaderTypeFromString(type);
shaderSources[shaderType] = source.substr(nextLinePos, pos - (nextLinePos == std::string::npos ? m_ShaderSource.size() - 1 : nextLinePos));
// Compute shaders cannot contain other types
if (shaderType == GL_COMPUTE_SHADER)
{
m_IsCompute = true;
break;
}
}
return shaderSources;
@ -678,6 +691,8 @@ namespace Prism
return GL_VERTEX_SHADER;
if (type == "fragment" || type == "pixel")
return GL_FRAGMENT_SHADER;
if (type == "compute")
return GL_COMPUTE_SHADER;
return GL_NONE;
}

View File

@ -19,6 +19,8 @@ namespace Prism
OpenGLShader(const std::string& filepath);
static Ref<OpenGLShader> CreateFromString(const std::string& source);
virtual RendererID GetRendererID() const override { return m_RendererID; }
virtual void Reload();
virtual void Bind() override;
@ -81,15 +83,18 @@ namespace Prism
void UploadUniformStruct(const OpenGLShaderUniformDeclaration* uniform, byte* buffer, uint32_t offset);
void UploadUniformIntArray(const std::string& name, const int32_t* values, int32_t count) const;
inline const ShaderUniformBufferList& GetVSRendererUniforms() const override { return m_VSRendererUniformBuffers; }
inline const ShaderUniformBufferList& GetPSRendererUniforms() const override { return m_PSRendererUniformBuffers; }
inline const ShaderUniformBufferDeclaration& GetVSMaterialUniformBuffer() const override { return *m_VSMaterialUniformBuffer; }
inline const ShaderUniformBufferDeclaration& GetPSMaterialUniformBuffer() const override { return *m_PSMaterialUniformBuffer; }
inline const ShaderResourceList& GetResources() const override { return m_Resources; }
virtual const ShaderUniformBufferList& GetVSRendererUniforms() const override { return m_VSRendererUniformBuffers; }
virtual const ShaderUniformBufferList& GetPSRendererUniforms() const override { return m_PSRendererUniformBuffers; }
virtual bool HasVSMaterialUniformBuffer() const override { return (bool)m_VSMaterialUniformBuffer; }
virtual bool HasPSMaterialUniformBuffer() const override { return (bool)m_PSMaterialUniformBuffer; }
virtual const ShaderUniformBufferDeclaration& GetVSMaterialUniformBuffer() const override { return *m_VSMaterialUniformBuffer; }
virtual const ShaderUniformBufferDeclaration& GetPSMaterialUniformBuffer() const override { return *m_PSMaterialUniformBuffer; }
virtual const ShaderResourceList& GetResources() const override { return m_Resources; }
private:
RendererID m_RendererID = 0;
bool m_Loaded = false;
bool m_IsCompute = false;
std::string m_Name, m_AssetPath;
std::unordered_map<GLenum, std::string> m_ShaderSource;

View File

@ -11,29 +11,23 @@
namespace Prism
{
static GLenum PrismToOpenGLTextureFormat(TextureFormat format)
static GLint PrismToOpenGLTextureFormat(const TextureFormat format)
{
switch (format)
{
case TextureFormat::RGB: return GL_RGB;
case TextureFormat::RGBA: return GL_RGBA;
case TextureFormat::Float16: return GL_RGBA16F;
}
return 0;
}
static int CalculateMipMapCount(const int width, const int height)
{
int levels = 1;
while ((width | height) >> levels) {
levels++;
}
return levels;
}
// ******************************************
// Texture2D
// ******************************************
OpenGLTexture2D::OpenGLTexture2D(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap)
OpenGLTexture2D::OpenGLTexture2D(TextureFormat format, uint32_t width, uint32_t height, TextureWrap wrap)
: m_Format(format), m_Width(width), m_Height(height), m_Wrap(wrap)
{
Renderer::Submit([this]() {
@ -42,12 +36,12 @@ namespace Prism
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLenum wrap = m_Wrap == TextureWrap::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT;
const GLint wrap = m_Wrap == TextureWrap::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
glTextureParameterf(m_RendererID, GL_TEXTURE_MAX_ANISOTROPY, RendererAPI::GetCapabilities().MaxAnisotropy);
glTexImage2D(GL_TEXTURE_2D, 0, Prism::PrismToOpenGLTextureFormat(m_Format), m_Width, m_Height, 0, Prism::PrismToOpenGLTextureFormat(m_Format), GL_UNSIGNED_BYTE, nullptr);
glTexImage2D(GL_TEXTURE_2D, 0, PrismToOpenGLTextureFormat(m_Format), (GLint)m_Width, (GLint)m_Height, 0, Prism::PrismToOpenGLTextureFormat(m_Format), GL_UNSIGNED_BYTE, nullptr);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
@ -60,25 +54,39 @@ namespace Prism
: m_FilePath(path)
{
int width, height, channels;
if (stbi_is_hdr(path.c_str()))
{
PM_CORE_INFO("Loading HDR texture {0}, srgb={1}", path, srgb);
m_ImageData.Data = (byte*)stbi_loadf(path.c_str(), &width, &height, &channels, 0);
m_IsHDR = true;
m_Format = TextureFormat::Float16;
}
else
{
PM_CORE_INFO("Loading texture {0}, srgb={1}", path, srgb);
m_ImageData.Data = stbi_load(path.c_str(), &width, &height, &channels, srgb ? STBI_rgb : STBI_rgb_alpha);
PM_CORE_ASSERT(m_ImageData.Data, "Could not read image!");
m_Format = TextureFormat::RGBA;
}
if (!m_ImageData.Data)
return;
m_Loaded = true;
m_Width = width;
m_Height = height;
m_Format = TextureFormat::RGBA;
Renderer::Submit([this, srgb]() {
Renderer::Submit([=]() {
// TODO: Consolidate properly
if (srgb)
{
glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID);
int levels = CalculateMipMapCount(m_Width, m_Height);
PM_CORE_INFO("Creating srgb texture width {0} mips", levels);
glTextureStorage2D(m_RendererID, levels, GL_SRGB8, m_Width, m_Height);
const auto levels = (GLint)CalculateMipMapCount(m_Width, m_Height);
glTextureStorage2D(m_RendererID, levels, GL_SRGB8, (GLint)m_Width, (GLint)m_Height);
glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, levels > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTextureSubImage2D(m_RendererID, 0, 0, 0, m_Width, m_Height, GL_RGB, GL_UNSIGNED_BYTE, m_ImageData.Data);
glTextureSubImage2D(m_RendererID, 0, 0, 0, (GLint)m_Width, (GLint)m_Height, GL_RGB, GL_UNSIGNED_BYTE, m_ImageData.Data);
glGenerateTextureMipmap(m_RendererID);
}
else
@ -90,8 +98,13 @@ namespace Prism
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
GLenum internalFormat = PrismToOpenGLTextureFormat(m_Format);
GLenum format = srgb ? GL_SRGB8 : (m_IsHDR ? GL_RGB : PrismToOpenGLTextureFormat(m_Format)); // HDR = GL_RGB for now
GLenum type = internalFormat == GL_RGBA16F ? GL_FLOAT : GL_UNSIGNED_BYTE;
glTexImage2D(GL_TEXTURE_2D, 0, (GLint)internalFormat, (GLint)m_Width, (GLint)m_Height, 0, format, type, m_ImageData.Data);
glTexImage2D(GL_TEXTURE_2D, 0, PrismToOpenGLTextureFormat(m_Format), m_Width, m_Height, 0, srgb ? GL_SRGB8 : PrismToOpenGLTextureFormat(m_Format), GL_UNSIGNED_BYTE, m_ImageData.Data);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
@ -114,6 +127,11 @@ namespace Prism
});
}
uint32_t OpenGLTexture2D::GetMipLevelCount() const
{
return CalculateMipMapCount(m_Width, m_Height);
}
void OpenGLTexture2D::Lock()
{
m_Locked = true;
@ -132,9 +150,6 @@ namespace Prism
PM_CORE_ASSERT(m_Locked, "Texture must be locked!");
m_ImageData.Allocate(width * height * Texture::GetBPP(m_Format));
#if PM_DEBUG
m_ImageData.ZeroInitialize();
#endif
}
Buffer OpenGLTexture2D::GetWriteableBuffer()
@ -148,6 +163,26 @@ namespace Prism
// TextureCube
// ******************************************
OpenGLTextureCube::OpenGLTextureCube(TextureFormat format, uint32_t width, uint32_t height)
{
m_Width = width;
m_Height = height;
m_Format = format;
const GLint levels = CalculateMipMapCount(width, height);
Renderer::Submit([=]() {
glCreateTextures(GL_TEXTURE_CUBE_MAP, 1, &m_RendererID);
glTextureStorage2D(m_RendererID, levels, PrismToOpenGLTextureFormat(m_Format), width, height);
glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, levels > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
});
}
OpenGLTextureCube::OpenGLTextureCube(const std::string& path)
: m_FilePath(path)
{
@ -159,8 +194,8 @@ namespace Prism
m_Height = height;
m_Format = TextureFormat::RGB;
unsigned int faceWidth = m_Width / 4;
unsigned int faceHeight = m_Height / 3;
const uint32_t faceWidth = m_Width / 4;
const uint32_t faceHeight = m_Height / 3;
PM_CORE_ASSERT(faceWidth == faceHeight, "Non-square faces!");
std::array<unsigned char*, 6> faces;
@ -212,6 +247,8 @@ namespace Prism
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTextureParameterf(m_RendererID, GL_TEXTURE_MAX_ANISOTROPY, RendererAPI::GetCapabilities().MaxAnisotropy);
auto format = PrismToOpenGLTextureFormat(m_Format);
@ -249,4 +286,8 @@ namespace Prism
});
}
uint32_t OpenGLTextureCube::GetMipLevelCount() const
{
return CalculateMipMapCount(m_Width, m_Height);
}
}

View File

@ -13,7 +13,7 @@ namespace Prism
class OpenGLTexture2D : public Texture2D
{
public:
OpenGLTexture2D(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap);
OpenGLTexture2D(TextureFormat format, uint32_t width, uint32_t height, TextureWrap wrap);
OpenGLTexture2D(const std::string& path, bool srgb);
virtual ~OpenGLTexture2D();
@ -23,12 +23,17 @@ namespace Prism
virtual uint32_t GetWidth() const override { return m_Width; }
virtual uint32_t GetHeight() const override { return m_Height; }
// This function currently returns the expected number of mips based on image size,
// not present mips in data
virtual uint32_t GetMipLevelCount() const override;
virtual void Lock() override;
virtual void Unlock() override;
virtual void Resize(uint32_t width, uint32_t height) override;
virtual Buffer GetWriteableBuffer() override;
virtual bool Loaded() const override { return m_Loaded; }
virtual const std::string& GetPath() const override { return m_FilePath; }
@ -39,6 +44,8 @@ namespace Prism
unsigned int m_Width, m_Height;
TextureWrap m_Wrap = TextureWrap::Clamp;
bool m_IsHDR = false;
bool m_Loaded = false;
bool m_Locked = false;
Buffer m_ImageData;
@ -49,14 +56,18 @@ namespace Prism
class OpenGLTextureCube : public TextureCube
{
public:
OpenGLTextureCube(TextureFormat format, uint32_t width, uint32_t height);
OpenGLTextureCube(const std::string& path);
virtual ~OpenGLTextureCube();
virtual void Bind(unsigned int slot = 0) const override;
virtual TextureFormat GetFormat() const override { return m_Format; }
virtual unsigned int GetWidth() const override { return m_Width; }
virtual unsigned int GetHeight() const override { return m_Height; }
virtual uint32_t GetWidth() const override { return m_Width; }
virtual uint32_t GetHeight() const override { return m_Height; }
// This function currently returns the expected number of mips based on image size,
// not present mips in data
virtual uint32_t GetMipLevelCount() const override;
virtual const std::string& GetPath() const override { return m_FilePath; }
@ -65,9 +76,9 @@ namespace Prism
private:
RendererID m_RendererID;
TextureFormat m_Format;
unsigned int m_Width, m_Height;
uint32_t m_Width, m_Height;
unsigned char* m_ImageData;
uint8_t* m_ImageData;
std::string m_FilePath;
};

View File

@ -2,50 +2,47 @@
// Created by sfd on 25-11-20.
//
#include "WindowsInput.h"
#include "WindowsWindow.h"
#include "Prism/Core/Application.h"
#include "GLFW/glfw3.h"
#include "Prism/Core/Input.h"
namespace Prism
{
Input* Input::s_Instance = new WindowsInput;
bool WindowsInput::IsKeyPressedImpl(const int keycode)
bool Input::IsKeyPressed(int keycode)
{
auto& window = static_cast<WindowsWindow&>(Application::Get().GetWindow());
auto state = glfwGetKey((GLFWwindow*)window.GetNativeWindow(), keycode);
const auto& window = dynamic_cast<WindowsWindow&>(Application::Get().GetWindow());
const auto state = glfwGetKey(static_cast<GLFWwindow*>(window.GetNativeWindow()), keycode);
return state == GLFW_PRESS || state == GLFW_REPEAT;
}
bool WindowsInput::IsMouseButtonPressedImpl(const int button)
bool Input::IsMouseButtonPressed(int button)
{
auto& window = static_cast<WindowsWindow&>(Application::Get().GetWindow());
const auto& window = dynamic_cast<WindowsWindow&>(Application::Get().GetWindow());
auto state = glfwGetMouseButton((GLFWwindow*)window.GetNativeWindow(), button);
const auto state = glfwGetMouseButton(static_cast<GLFWwindow*>(window.GetNativeWindow()), button);
return state == GLFW_PRESS;
}
float WindowsInput::GetMouseXImpl()
float Input::GetMouseX()
{
auto& window = static_cast<WindowsWindow&>(Application::Get().GetWindow());
const auto& window = dynamic_cast<WindowsWindow&>(Application::Get().GetWindow());
double xpos, ypos;
glfwGetCursorPos((GLFWwindow*)window.GetNativeWindow(), &xpos, &ypos);
glfwGetCursorPos(static_cast<GLFWwindow*>(window.GetNativeWindow()), &xpos, &ypos);
return (float)xpos;
return static_cast<float>(xpos);
}
float WindowsInput::GetMouseYImpl()
float Input::GetMouseY()
{
auto& window = static_cast<WindowsWindow&>(Application::Get().GetWindow());
const auto& window = dynamic_cast<WindowsWindow&>(Application::Get().GetWindow());
double xpos, ypos;
glfwGetCursorPos((GLFWwindow*)window.GetNativeWindow(), &xpos, &ypos);
glfwGetCursorPos(static_cast<GLFWwindow*>(window.GetNativeWindow()), &xpos, &ypos);
return (float)ypos;
return static_cast<float>(ypos);
}
}

View File

@ -1,26 +0,0 @@
//
// Created by sfd on 25-11-20.
//
#ifndef WINDOWSINPUT_H
#define WINDOWSINPUT_H
#include "Prism/Core/Input.h"
namespace Prism
{
class WindowsInput : public Input
{
protected:
virtual bool IsKeyPressedImpl(int keycode);
virtual bool IsMouseButtonPressedImpl(int button);
virtual float GetMouseXImpl();
virtual float GetMouseYImpl();
};
}
#endif //WINDOWSINPUT_H

View File

@ -87,6 +87,7 @@ namespace Prism
m_Window = glfwCreateWindow((int)props.Width, (int)props.Height, m_Data.Title.c_str(), nullptr, nullptr);
glfwMakeContextCurrent(m_Window);
// glfwMaximizeWindow(m_Window);
// init glad
int status = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);

View File

@ -15,6 +15,7 @@ namespace Prism
class PRISM_API Camera
{
public:
Camera() = default;
Camera(const glm::mat4& projectionMatrix);
void Focus();
@ -32,6 +33,10 @@ namespace Prism
glm::vec3 GetRightDirection();
glm::vec3 GetForwardDirection();
const glm::vec3& GetPosition() const { return m_Position; }
float GetExposure() const { return m_Exposure; }
float& GetExposure() { return m_Exposure; }
public:
inline void SetViewportSize(const uint32_t width, const uint32_t height) { m_ViewportWidth = width; m_ViewportHeight = height; }
private:
@ -57,6 +62,8 @@ namespace Prism
float m_Distance;
float m_Exposure = 0.8f;
float m_Pitch, m_Yaw;
};
}

View File

@ -11,6 +11,9 @@ namespace Prism
{
m_Shader->AddShaderReloadedCallback(std::bind(&Material::OnShaderReloaded, this));
AllocateStorage();
m_MaterialFlags |= (uint32_t)MaterialFlag::DepthTest;
m_MaterialFlags |= (uint32_t)MaterialFlag::Blend;
}
Material::~Material()
@ -24,15 +27,21 @@ namespace Prism
void Material::AllocateStorage()
{
if (m_Shader->HasVSMaterialUniformBuffer())
{
const auto& vsBuffer = m_Shader->GetVSMaterialUniformBuffer();
m_VSUniformStorageBuffer.Allocate(vsBuffer.GetSize());
m_VSUniformStorageBuffer.ZeroInitialize();
}
if (m_Shader->HasPSMaterialUniformBuffer())
{
const auto& psBuffer = m_Shader->GetPSMaterialUniformBuffer();
m_PSUniformStorageBuffer.Allocate(psBuffer.GetSize());
m_PSUniformStorageBuffer.ZeroInitialize();
}
}
void Material::OnShaderReloaded()
{
@ -135,16 +144,30 @@ namespace Prism
m_OverriddenValues.clear();
}
void MaterialInstance::SetFlag(MaterialFlag flag, bool value)
{
if (value)
m_Material->m_MaterialFlags |= (uint32_t)flag;
else
m_Material->m_MaterialFlags &= ~(uint32_t)flag;
}
void MaterialInstance::AllocateStorage()
{
if (m_Material->m_Shader->HasVSMaterialUniformBuffer())
{
const auto& vsBuffer = m_Material->m_Shader->GetVSMaterialUniformBuffer();
m_VSUniformStorageBuffer.Allocate(vsBuffer.GetSize());
memcpy(m_VSUniformStorageBuffer.Data, m_Material->m_VSUniformStorageBuffer.Data, vsBuffer.GetSize());
}
if (m_Material->m_Shader->HasPSMaterialUniformBuffer())
{
const auto& psBuffer = m_Material->m_Shader->GetPSMaterialUniformBuffer();
m_PSUniformStorageBuffer.Allocate(psBuffer.GetSize());
memcpy(m_PSUniformStorageBuffer.Data, m_Material->m_PSUniformStorageBuffer.Data, psBuffer.GetSize());
}
}
void MaterialInstance::OnMaterialValueUpdated(ShaderUniformDeclaration* decl)
{

View File

@ -8,6 +8,7 @@
#include "Shader.h"
#include "Texture.h"
#include "Prism/Core/Log.h"
namespace Prism
{
@ -16,6 +17,13 @@ namespace Prism
class MaterialInstance;
#endif
enum class MaterialFlag
{
None = BIT(0),
DepthTest = BIT(1),
Blend = BIT(2)
};
class PRISM_API Material
{
friend class MaterialInstance;
@ -27,6 +35,9 @@ namespace Prism
void Bind() const;
uint32_t GetFlags() const { return m_MaterialFlags; }
void SetFlag(MaterialFlag flag) { m_MaterialFlags |= (uint32_t)flag; }
template <typename T>
void Set(const std::string& name, const T& value);
@ -67,7 +78,7 @@ namespace Prism
Buffer m_PSUniformStorageBuffer;
std::vector<Ref<Texture>> m_Textures;
int32_t m_RenderFlags = 0;
int32_t m_MaterialFlags = 0;
};
@ -84,9 +95,8 @@ namespace Prism
template <typename T>
void Set(const std::string& name, const T& value)
{
auto decl = m_Material->FindUniformDeclaration(name);
// HZ_CORE_ASSERT(decl, "Could not find uniform with name '{0}'", name);
PM_CORE_ASSERT(decl, "Could not find uniform with name 'x'");
const auto decl = m_Material->FindUniformDeclaration(name);
if (!decl) PM_CORE_WARN("Could not find uniform with name {0}", name);
auto& buffer = GetUniformBufferTarget(decl);
buffer.Write((byte*)& value, decl->GetSize(), decl->GetOffset());
@ -113,6 +123,12 @@ namespace Prism
}
void Bind() const;
uint32_t GetFlags() const { return m_Material->m_MaterialFlags; }
bool GetFlag(MaterialFlag flag) const { return (uint32_t)flag & m_Material->m_MaterialFlags; }
void SetFlag(MaterialFlag flag, bool value = true);
Ref<Shader >GetShader() { return m_Material->m_Shader; }
private:
void AllocateStorage();
void OnShaderReloaded();

View File

@ -13,6 +13,8 @@
#include "glad/glad.h"
#define GLM_ENABLE_EXPERIMENTAL
#include <filesystem>
#include "glm/gtx/quaternion.hpp"
#include "Prism/Core/Log.h"
@ -45,15 +47,15 @@ namespace Prism
}
};
static glm::mat4 aiMatrix4x4ToGlm(const aiMatrix4x4 &from)
glm::mat4 Mat4FromAssimpMat4(const aiMatrix4x4& matrix)
{
glm::mat4 to;
glm::mat4 result;
//the a,b,c,d in assimp is the row ; the 1,2,3,4 is the column
to[0][0] = from.a1; to[1][0] = from.a2; to[2][0] = from.a3; to[3][0] = from.a4;
to[0][1] = from.b1; to[1][1] = from.b2; to[2][1] = from.b3; to[3][1] = from.b4;
to[0][2] = from.c1; to[1][2] = from.c2; to[2][2] = from.c3; to[3][2] = from.c4;
to[0][3] = from.d1; to[1][3] = from.d2; to[2][3] = from.d3; to[3][3] = from.d4;
return to;
result[0][0] = matrix.a1; result[1][0] = matrix.a2; result[2][0] = matrix.a3; result[3][0] = matrix.a4;
result[0][1] = matrix.b1; result[1][1] = matrix.b2; result[2][1] = matrix.b3; result[3][1] = matrix.b4;
result[0][2] = matrix.c1; result[1][2] = matrix.c2; result[2][2] = matrix.c3; result[3][2] = matrix.c4;
result[0][3] = matrix.d1; result[1][3] = matrix.d2; result[2][3] = matrix.d3; result[3][3] = matrix.d4;
return result;
}
@ -102,11 +104,14 @@ namespace Prism
if (!scene || !scene->HasMeshes())
PM_CORE_ERROR("Failed to load mesh file: {0}", filename);
//double factor;
//scene->mMetaData->Get("UnitScaleFactor", factor);
//PM_CORE_INFO("FBX Scene Scale: {0}", factor);
m_IsAnimated = scene->mAnimations != nullptr;
m_MeshShader = m_IsAnimated ? Renderer::GetShaderLibrary()->Get("simplepbr_Anim") : Renderer::GetShaderLibrary()->Get("simplepbr_Static");
m_Material.reset(new Prism::Material(m_MeshShader));
m_InverseTransform = glm::inverse(aiMatrix4x4ToGlm(scene->mRootNode->mTransformation));
m_MeshShader = m_IsAnimated ? Renderer::GetShaderLibrary()->Get("PBRShader_Anim") : Renderer::GetShaderLibrary()->Get("PBRShader_Static");
m_BaseMaterial = CreateRef<Material>(m_MeshShader);
m_InverseTransform = glm::inverse(Mat4FromAssimpMat4(scene->mRootNode->mTransformation));
uint32_t vertexCount = 0;
uint32_t indexCount = 0;
@ -200,7 +205,7 @@ namespace Prism
m_BoneCount++;
BoneInfo bi;
m_BoneInfo.push_back(bi);
m_BoneInfo[boneIndex].BoneOffset = aiMatrix4x4ToGlm(bone->mOffsetMatrix);
m_BoneInfo[boneIndex].BoneOffset = Mat4FromAssimpMat4(bone->mOffsetMatrix);
m_BoneMapping[boneName] = boneIndex;
}
else
@ -219,6 +224,213 @@ namespace Prism
}
}
// Material
if (scene->HasMaterials())
{
m_Textures.resize(scene->mNumMaterials);
m_Materials.resize(scene->mNumMaterials);
for (uint32_t i = 0; i < scene->mNumMaterials; i++)
{
auto aiMaterial = scene->mMaterials[i];
auto aiMaterialName = aiMaterial->GetName();
auto mi = CreateRef<MaterialInstance>(m_BaseMaterial);
m_Materials[i] = mi;
PM_CORE_INFO("Material Name = {0}; Index = {1}", aiMaterialName.data, i);
aiString aiTexPath;
uint32_t textureCount = aiMaterial->GetTextureCount(aiTextureType_DIFFUSE);
PM_CORE_TRACE(" TextureCount = {0}", textureCount);
aiColor3D aiColor;
aiMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, aiColor);
PM_CORE_TRACE("COLOR = {0}, {1}, {2}", aiColor.r, aiColor.g, aiColor.b);
if (aiMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &aiTexPath) == AI_SUCCESS)
{
// TODO: Temp - this should be handled by Hazel's filesystem
std::filesystem::path path = filename;
auto parentPath = path.parent_path();
parentPath /= std::string(aiTexPath.data);
std::string texturePath = parentPath.string();
auto texture = Texture2D::Create(texturePath, true);
if (texture->Loaded())
{
m_Textures[i] = texture;
PM_CORE_TRACE(" Texture Path = {0}", texturePath);
mi->Set("u_AlbedoTexture", m_Textures[i]);
mi->Set("u_AlbedoTexToggle", 1.0f);
}
else
{
PM_CORE_ERROR("Could not load texture: {0}", texturePath);
//mi->Set("u_AlbedoTexToggle", 0.0f);
mi->Set("u_AlbedoColor", glm::vec3{ aiColor.r, aiColor.g, aiColor.b });
}
}
else
{
mi->Set("u_AlbedoTexToggle", 0.0f);
mi->Set("u_AlbedoColor", glm::vec3 { aiColor.r, aiColor.g, aiColor.b });
}
for (uint32_t i = 0; i < aiMaterial->mNumProperties; i++)
{
auto prop = aiMaterial->mProperties[i];
PM_CORE_TRACE("Material Property:");
PM_CORE_TRACE(" Name = {0}", prop->mKey.data);
switch (prop->mSemantic)
{
case aiTextureType_NONE:
PM_CORE_TRACE(" Semantic = aiTextureType_NONE");
break;
case aiTextureType_DIFFUSE:
PM_CORE_TRACE(" Semantic = aiTextureType_DIFFUSE");
break;
case aiTextureType_SPECULAR:
PM_CORE_TRACE(" Semantic = aiTextureType_SPECULAR");
break;
case aiTextureType_AMBIENT:
PM_CORE_TRACE(" Semantic = aiTextureType_AMBIENT");
break;
case aiTextureType_EMISSIVE:
PM_CORE_TRACE(" Semantic = aiTextureType_EMISSIVE");
break;
case aiTextureType_HEIGHT:
PM_CORE_TRACE(" Semantic = aiTextureType_HEIGHT");
break;
case aiTextureType_NORMALS:
PM_CORE_TRACE(" Semantic = aiTextureType_NORMALS");
break;
case aiTextureType_SHININESS:
PM_CORE_TRACE(" Semantic = aiTextureType_SHININESS");
break;
case aiTextureType_OPACITY:
PM_CORE_TRACE(" Semantic = aiTextureType_OPACITY");
break;
case aiTextureType_DISPLACEMENT:
PM_CORE_TRACE(" Semantic = aiTextureType_DISPLACEMENT");
break;
case aiTextureType_LIGHTMAP:
PM_CORE_TRACE(" Semantic = aiTextureType_LIGHTMAP");
break;
case aiTextureType_REFLECTION:
PM_CORE_TRACE(" Semantic = aiTextureType_REFLECTION");
break;
case aiTextureType_UNKNOWN:
PM_CORE_TRACE(" Semantic = aiTextureType_UNKNOWN");
break;
}
if (prop->mType == aiPTI_String)
{
uint32_t strLength = *(uint32_t*)prop->mData;
std::string str(prop->mData + 4, strLength);
PM_CORE_TRACE(" Value = {0}", str);
std::string key = prop->mKey.data;
if (key == "$raw.ReflectionFactor|file")
{
// TODO: Temp - this should be handled by Hazel's filesystem
std::filesystem::path path = filename;
auto parentPath = path.parent_path();
parentPath /= str;
std::string texturePath = parentPath.string();
auto texture = Texture2D::Create(texturePath);
if (texture->Loaded())
{
PM_CORE_TRACE(" Metalness map path = {0}", texturePath);
mi->Set("u_MetalnessTexture", texture);
mi->Set("u_MetalnessTexToggle", 1.0f);
}
else
{
PM_CORE_ERROR("Could not load texture: {0}", texturePath);
mi->Set("u_Metalness", 0.5f);
mi->Set("u_MetalnessTexToggle", 1.0f);
}
}
}
}
// Normal maps
if (aiMaterial->GetTexture(aiTextureType_NORMALS, 0, &aiTexPath) == AI_SUCCESS)
{
// TODO: Temp - this should be handled by Hazel's filesystem
std::filesystem::path path = filename;
auto parentPath = path.parent_path();
parentPath /= std::string(aiTexPath.data);
std::string texturePath = parentPath.string();
auto texture = Texture2D::Create(texturePath);
if (texture->Loaded())
{
PM_CORE_TRACE(" Normal map path = {0}", texturePath);
mi->Set("u_NormalTexture", texture);
mi->Set("u_NormalTexToggle", 1.0f);
}
else
{
PM_CORE_ERROR("Could not load texture: {0}", texturePath);
//mi->Set("u_AlbedoTexToggle", 0.0f);
// mi->Set("u_AlbedoColor", glm::vec3{ color.r, color.g, color.b });
}
}
// Roughness map
if (aiMaterial->GetTexture(aiTextureType_SHININESS, 0, &aiTexPath) == AI_SUCCESS)
{
// TODO: Temp - this should be handled by Hazel's filesystem
std::filesystem::path path = filename;
auto parentPath = path.parent_path();
parentPath /= std::string(aiTexPath.data);
std::string texturePath = parentPath.string();
auto texture = Texture2D::Create(texturePath);
if (texture->Loaded())
{
PM_CORE_TRACE(" Roughness map path = {0}", texturePath);
mi->Set("u_RoughnessTexture", texture);
mi->Set("u_RoughnessTexToggle", 1.0f);
}
else
{
PM_CORE_ERROR("Could not load texture: {0}", texturePath);
mi->Set("u_RoughnessTexToggle", 1.0f);
mi->Set("u_Roughness", 0.5f);
}
}
// Metalness map
if (aiMaterial->Get("$raw.ReflectionFactor|file", aiPTI_String, 0, aiTexPath) == AI_SUCCESS)
{
// TODO: Temp - this should be handled by Hazel's filesystem
std::filesystem::path path = filename;
auto parentPath = path.parent_path();
parentPath /= std::string(aiTexPath.data);
std::string texturePath = parentPath.string();
auto texture = Texture2D::Create(texturePath);
if (texture->Loaded())
{
PM_CORE_TRACE(" Metalness map path = {0}", texturePath);
mi->Set("u_MetalnessTexture", texture);
mi->Set("u_MetalnessTexToggle", 1.0f);
}
else
{
PM_CORE_ERROR("Could not load texture: {0}", texturePath);
mi->Set("u_Metalness", 0.5f);
mi->Set("u_MetalnessTexToggle", 1.0f);
}
}
}
}
m_VertexArray = VertexArray::Create();
if (m_IsAnimated)
{
@ -247,17 +459,6 @@ namespace Prism
}
/*
m_VertexBuffer.reset(VertexBuffer::Create());
if (m_IsAnimated)
m_VertexBuffer->SetData(m_AnimatedVertices.data(), m_AnimatedVertices.size() * sizeof(AnimatedVertex));
else
m_VertexBuffer->SetData(m_StaticVertices.data(), m_StaticVertices.size() * sizeof(Vertex));
m_IndexBuffer.reset(IndexBuffer::Create());
m_IndexBuffer->SetData(m_Indices.data(), m_Indices.size() * sizeof(Index));
*/
auto ib = IndexBuffer::Create(m_Indices.data(), (uint32_t)m_Indices.size() * sizeof(Index));
m_VertexArray->SetIndexBuffer(ib);
m_Scene = scene;
@ -265,12 +466,7 @@ namespace Prism
Mesh::~Mesh() = default;
void Mesh::Render(const TimeStep deltaTime, const Ref<MaterialInstance>& materialInstance)
{
Render(deltaTime, glm::mat4(1.0f), materialInstance);
}
void Mesh::Render(TimeStep deltaTime, const glm::mat4& transform, const Ref<MaterialInstance>& materialInstance)
void Mesh::OnUpdate(TimeStep deltaTime)
{
if (m_IsAnimated)
{
@ -278,66 +474,16 @@ namespace Prism
{
m_WorldTime += deltaTime;
float ticksPerSecond = (float)(m_Scene->mAnimations[0]->mTicksPerSecond != 0 ? m_Scene->mAnimations[0]->mTicksPerSecond : 25.0f) * m_TimeMultiplier;
const float ticksPerSecond = (float)(m_Scene->mAnimations[0]->mTicksPerSecond != 0 ? m_Scene->mAnimations[0]->mTicksPerSecond : 25.0f) * m_TimeMultiplier;
m_AnimationTime += deltaTime * ticksPerSecond;
m_AnimationTime = fmod(m_AnimationTime, (float)m_Scene->mAnimations[0]->mDuration);
}
// TODO: We only need to recalc bones if rendering has been requested at the current animation frame
BoneTransform(m_AnimationTime);
}
if (materialInstance)
materialInstance->Bind();
// TODO: Sort this out
/*
m_VertexBuffer->Bind();
m_IndexBuffer->Bind();
*/
m_VertexArray->Bind();
bool materialOverride = !!materialInstance;
// TODO: replace with render API calls
Renderer::Submit([=](){
for (const Submesh& submesh : m_Submeshes)
{
if (m_IsAnimated)
{
for (size_t i = 0; i < m_BoneTransforms.size(); i++)
{
std::string uniformName = std::string("u_BoneTransforms[") + std::to_string(i) + std::string("]");
m_MeshShader->SetMat4FromRenderThread(uniformName, m_BoneTransforms[i]);
}
}
// if (!materialOverride)
// self->m_MeshShader->SetMat4FromRenderThread("u_ModelMatrix", transform * submesh.Transform);
glDrawElementsBaseVertex(GL_TRIANGLES, submesh.IndexCount, GL_UNSIGNED_INT, (void*)(sizeof(uint32_t) * submesh.BaseIndex), submesh.BaseVertex);
}
});
}
void Mesh::OnImGuiRender()
{
ImGui::Begin("Mesh Debug");
if (ImGui::CollapsingHeader(m_FilePath.c_str()))
{
if (m_IsAnimated)
{
if (ImGui::CollapsingHeader("Animation"))
{
if (ImGui::Button(m_AnimationPlaying ? "Pause" : "Play"))
m_AnimationPlaying = !m_AnimationPlaying;
ImGui::SliderFloat("##AnimationTime", &m_AnimationTime, 0.0f, (float)m_Scene->mAnimations[0]->mDuration);
ImGui::DragFloat("Time Scale", &m_TimeMultiplier, 0.05f, 0.0f, 10.0f);
}
}
}
ImGui::End();
}
void Mesh::DumpVertexBuffer()
{
@ -387,7 +533,7 @@ namespace Prism
{
const std::string name(pNode->mName.data);
const aiAnimation* animation = m_Scene->mAnimations[0];
glm::mat4 nodeTransform(aiMatrix4x4ToGlm(pNode->mTransformation));
glm::mat4 nodeTransform(Mat4FromAssimpMat4(pNode->mTransformation));
const aiNodeAnim* nodeAnim = FindNodeAnim(animation, name);
if (nodeAnim)
@ -416,23 +562,26 @@ namespace Prism
ReadNodeHierarchy(AnimationTime, pNode->mChildren[i], transform);
}
void Mesh::TraverseNodes(const aiNode* node)
static std::string LevelToSpaces(uint32_t level)
{
/*
std::string levelText;
for (int i = 0; i < level; i++)
levelText += "-";
PM_CORE_TRACE("{0}Node name: {1}", levelText, std::string(node->mName.data));
*/
std::string result;
for (uint32_t i = 0; i < level; i++)
result += "--";
return result;
}
void Mesh::TraverseNodes(const aiNode* node, const glm::mat4& parentTransform, uint32_t level)
{
const glm::mat4 transform = parentTransform * Mat4FromAssimpMat4(node->mTransformation);
for (uint32_t i = 0; i < node->mNumMeshes; i++)
{
uint32_t mesh = node->mMeshes[i];
m_Submeshes[mesh].Transform = aiMatrix4x4ToGlm(node->mTransformation);
const uint32_t mesh = node->mMeshes[i];
m_Submeshes[mesh].Transform = transform;
}
for (uint32_t i = 0; i < node->mNumChildren; i++)
{
TraverseNodes(node->mChildren[i]);
TraverseNodes(node->mChildren[i], transform, level + 1);
}
}

View File

@ -97,18 +97,18 @@ namespace Prism
Mesh(const std::string& filename);
~Mesh();
void Render(TimeStep deltaTime, const Ref<MaterialInstance>& materialInstance = Ref<MaterialInstance>());
void Render(TimeStep deltaTime, const glm::mat4& transform = glm::mat4(1.0f), const Ref<MaterialInstance>& materialInstance = Ref<MaterialInstance>());
void OnImGuiRender();
void OnUpdate(TimeStep deltaTime);
void DumpVertexBuffer();
inline Ref<Shader> GetMeshShader() { return m_MeshShader; }
inline Ref<Material> GetMaterial() { return m_Material; }
inline const std::string& GetFilePath() const { return m_FilePath; }
inline Ref<Material> GetMaterial() { return m_BaseMaterial; }
std::vector<Ref<MaterialInstance>> GetMaterials() { return m_Materials; }
const std::vector<Ref<Texture2D>>& GetTextures() const { return m_Textures; }
const std::string& GetFilePath() const { return m_FilePath; }
private:
void BoneTransform(float time);
void ReadNodeHierarchy(float AnimationTime, const aiNode* pNode, const glm::mat4& ParentTransform);
void TraverseNodes(const aiNode* node);
void TraverseNodes(const aiNode* node, const glm::mat4& parentTransform = glm::mat4(1.0f), uint32_t level = 0);
const aiNodeAnim* FindNodeAnim(const aiAnimation* animation, const std::string& nodeName);
uint32_t FindPosition(float AnimationTime, const aiNodeAnim* pNodeAnim);
@ -130,11 +130,6 @@ namespace Prism
Ref<VertexArray> m_VertexArray;
/*
std::unique_ptr<VertexBuffer> m_VertexBuffer;
std::unique_ptr<IndexBuffer> m_IndexBuffer;
*/
std::vector<Vertex> m_StaticVertices;
std::vector<AnimatedVertex> m_AnimatedVertices;
@ -145,7 +140,10 @@ namespace Prism
// Material
Ref<Shader> m_MeshShader;
Ref<Material> m_Material;
Ref<Material> m_BaseMaterial;
std::vector<Ref<Texture2D>> m_Textures;
std::vector<Ref<Texture2D>> m_NormalMaps;
std::vector<Ref<MaterialInstance>> m_Materials;
// Animation
bool m_IsAnimated = false;
@ -157,6 +155,7 @@ namespace Prism
std::string m_FilePath;
private:
friend class Renderer;
friend class SceneHierarchyPanel;
};
}

View File

@ -5,14 +5,58 @@
#include "Renderer.h"
#include "RendererAPI.h"
#include "SceneRenderer.h"
#include "glad/glad.h"
namespace Prism
{
Renderer* Renderer::s_Instance = new Renderer();
RendererAPIType RendererAPI::s_CurrentRendererAPI = RendererAPIType::OpenGL;
struct RendererData
{
Ref<RenderPass> m_ActiveRenderPass;
RenderCommandQueue m_CommandQueue;
Scope<ShaderLibrary> m_ShaderLibrary;
Ref<VertexArray> m_FullscreenQuadVertexArray;
};
static RendererData s_Data;
void Renderer::Init()
{
s_Data.m_ShaderLibrary = std::make_unique<ShaderLibrary>();
Submit([](){ RendererAPI::Init(); });
GetShaderLibrary()->Load("assets/shaders/PBRShader_Static.glsl");
GetShaderLibrary()->Load("assets/shaders/PBRShader_Anim.glsl");
SceneRenderer::Init();
// FullScreen Quad
static float fullScreenQuadVertex[] = {
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
static uint32_t fullScreenQuadIndices[] = {
0, 1, 2, 2, 3, 0
};
s_Data.m_FullscreenQuadVertexArray = VertexArray::Create();
const auto quadVB = VertexBuffer::Create(fullScreenQuadVertex, sizeof(float) * sizeof(fullScreenQuadVertex));
quadVB->SetLayout({
{ ShaderDataType::Float3, "a_Position" },
{ ShaderDataType::Float2, "a_TexCoord" }
});
const auto quadIB = IndexBuffer::Create(fullScreenQuadIndices, sizeof(fullScreenQuadIndices) * sizeof(uint32_t));
s_Data.m_FullscreenQuadVertexArray->AddVertexBuffer(quadVB);
s_Data.m_FullscreenQuadVertexArray->SetIndexBuffer(quadIB);
}
void Renderer::Clear()
{
Submit([]()
@ -39,90 +83,107 @@ namespace Prism
});
}
void Renderer::ClearMagenta()
{
Clear(1, 0, 1);
}
void Renderer::Init()
{
s_Instance->m_ShaderLibrary = std::make_unique<ShaderLibrary>();
Submit([](){ RendererAPI::Init(); });
GetShaderLibrary()->Load("assets/shaders/simplepbr_Static.glsl");
GetShaderLibrary()->Load("assets/shaders/simplepbr_Anim.glsl");
const Scope<ShaderLibrary>& Renderer::GetShaderLibrary()
{
return s_Data.m_ShaderLibrary;
}
void Renderer::WaitAndRender()
{
s_Instance->m_CommandQueue.Execute();
s_Data.m_CommandQueue.Execute();
}
void Renderer::BeginRenderPass(const Ref<RenderPass>& renderPass)
{
s_Instance->IBeginRenderPass(renderPass);
}
PM_CORE_ASSERT(renderPass, "Render pass cannot be null!");
void Renderer::EndRenderPass()
{
s_Instance->IEndRenderPass();
}
void Renderer::SubmitMesh(const Ref<Mesh>& mesh, const glm::mat4& transform,
const Ref<MaterialInstance>& overrideMaterial)
{
s_Instance->SubmitMeshI(mesh, transform, overrideMaterial);
}
void Renderer::IBeginRenderPass(const Ref<RenderPass>& renderPass)
{
// TODO: Convert all of this into a render command buffer
m_ActiveRenderPass = renderPass;
s_Data.m_ActiveRenderPass = renderPass;
renderPass->GetSpecification().TargetFramebuffer->Bind();
const glm::vec4& clearColor = renderPass->GetSpecification().TargetFramebuffer->GetSpecification().ClearColor;
Submit([=]() {
Renderer::Submit([=]() {
RendererAPI::Clear(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
});
}
void Renderer::IEndRenderPass()
void Renderer::EndRenderPass()
{
PM_CORE_ASSERT(m_ActiveRenderPass, "No active render pass! Have you called Renderer::EndRenderPass twice?");
m_ActiveRenderPass->GetSpecification().TargetFramebuffer->Unbind();
m_ActiveRenderPass = nullptr;
PM_CORE_ASSERT(s_Data.m_ActiveRenderPass, "No active render pass! Have you called Renderer::EndRenderPass twice?");
s_Data.m_ActiveRenderPass->GetSpecification().TargetFramebuffer->Unbind();
s_Data.m_ActiveRenderPass = nullptr;
}
void Renderer::SubmitMeshI(const Ref<Mesh>& mesh, const glm::mat4& transform,
const Ref<MaterialInstance>& overrideMaterial)
void Renderer::SubmitQuad(const Ref<MaterialInstance>& material, const glm::mat4& transform)
{
if (overrideMaterial)
bool depthTest = true;
if (material)
{
overrideMaterial->Bind();
}else
{
// bind mesh material here
material->Bind();
depthTest = material->GetFlag(MaterialFlag::DepthTest);
auto shader = material->GetShader();
shader->SetMat4("u_Transform", transform);
}
s_Data.m_FullscreenQuadVertexArray->Bind();
Renderer::DrawIndexed(6, depthTest);
}
void Renderer::SubmitFullscreenQuad(const Ref<MaterialInstance>& material)
{
bool depthTest = true;
if (material)
{
material->Bind();
depthTest = material->GetFlag(MaterialFlag::DepthTest);
}
s_Data.m_FullscreenQuadVertexArray->Bind();
Renderer::DrawIndexed(6, depthTest);
}
void Renderer::SubmitMesh(const Ref<Mesh>& mesh, const glm::mat4& transform, const Ref<MaterialInstance>& overrideMaterial)
{
// auto material = overrideMaterial ? overrideMaterial : mesh->GetMaterialInstance();
// auto shader = material->GetShader();
// TODO: Sort this out
mesh->m_VertexArray->Bind();
// TODO: temp test , use RenderAPI replace this
Submit([=]()
{
for (const Submesh& submesh : mesh->m_Submeshes)
const auto& materials = mesh->GetMaterials();
for (Submesh& submesh : mesh->m_Submeshes)
{
// Material
auto material = materials[submesh.MaterialIndex];
auto shader = material->GetShader();
material->Bind();
if (mesh->m_IsAnimated)
{
for (size_t i = 0; i < mesh->m_BoneTransforms.size(); i++)
{
std::string uniformName = std::string("u_BoneTransforms[") + std::to_string(i) + std::string("]");
mesh->m_MeshShader->SetMat4FromRenderThread(uniformName, mesh->m_BoneTransforms[i]);
mesh->m_MeshShader->SetMat4(uniformName, mesh->m_BoneTransforms[i]);
}
}
shader->SetMat4("u_Transform", transform * submesh.Transform);
Renderer::Submit([submesh, material]() {
if (material->GetFlag(MaterialFlag::DepthTest))
glEnable(GL_DEPTH_TEST);
else
glDisable(GL_DEPTH_TEST);
glDrawElementsBaseVertex(GL_TRIANGLES, submesh.IndexCount, GL_UNSIGNED_INT, (void*)(sizeof(uint32_t) * submesh.BaseIndex), submesh.BaseVertex);
});
}
}
glDrawElementsBaseVertex(GL_TRIANGLES, submesh.IndexCount, GL_UNSIGNED_INT, (void*)(sizeof(uint32_t) * submesh.BaseIndex), submesh.BaseVertex);
}
});
RenderCommandQueue& Renderer::GetRenderCommandQueue()
{
return s_Data.m_CommandQueue;
}
}

View File

@ -17,18 +17,15 @@ namespace Prism
{
public:
typedef void(*RenderCommandFn)(void*);
static void Init();
static void Clear();
static void Clear(float r, float g, float b, float a = 1.0f);
static void SetClearColor(float r, float g, float b, float a);
static void DrawIndexed(uint32_t count, bool depthTest = true);
// test
static void ClearMagenta();
static void Init();
static const Scope<ShaderLibrary>& GetShaderLibrary() { return Get().m_ShaderLibrary; }
static const Scope<ShaderLibrary>& GetShaderLibrary();
template<typename FuncT>
static void Submit(FuncT&& func)
@ -42,39 +39,23 @@ namespace Prism
// static_assert(std::is_trivially_destructible_v<FuncT>, "FuncT must be trivially destructible");
pFunc->~FuncT();
};
auto storageBuffer = s_Instance->m_CommandQueue.Allocate(renderCmd, sizeof(func));
auto storageBuffer = GetRenderCommandQueue().Allocate(renderCmd, sizeof(func));
new (storageBuffer) FuncT(std::forward<FuncT>(func));
}
/*
static void* Submit(const RenderCommandFn func, const unsigned int size)
{
return s_Instance->m_CommandQueue.Allocate(func, size);
}
*/
void WaitAndRender();
inline static Renderer& Get() { return *s_Instance; }
static void WaitAndRender();
public:
static void BeginRenderPass(const Ref<RenderPass>& renderPass);
static void EndRenderPass();
static void SubmitQuad(const Ref<MaterialInstance>& material, const glm::mat4& transform = glm::mat4(1.0f));
static void SubmitFullscreenQuad(const Ref<MaterialInstance>& material);
static void SubmitMesh(const Ref<Mesh>& mesh, const glm::mat4& transform, const Ref<MaterialInstance>& overrideMaterial = nullptr);
private:
void IBeginRenderPass(const Ref<RenderPass>& renderPass);
void IEndRenderPass();
void SubmitMeshI(const Ref<Mesh>& mesh, const glm::mat4& transform, const Ref<MaterialInstance>& overrideMaterial = nullptr);
private:
static Renderer* s_Instance;
private:
Ref<RenderPass> m_ActiveRenderPass;
Scope<ShaderLibrary> m_ShaderLibrary;
RenderCommandQueue m_CommandQueue;
static RenderCommandQueue& GetRenderCommandQueue();
};
#if 0

View File

@ -0,0 +1,252 @@
//
// Created by sfd on 25-11-30.
//
#include "SceneRenderer.h"
#include "Camera.h"
#include "FrameBuffer.h"
#include "Material.h"
#include "Mesh.h"
#include "Renderer.h"
#include "RenderPass.h"
#include "glad/glad.h"
namespace Prism
{
struct SceneRendererData
{
const Scene* ActiveScene = nullptr;
struct SceneInfo
{
Camera SceneCamera;
// Resources
Ref<MaterialInstance> SkyboxMaterial;
Environment SceneEnvironment;
} SceneData;
Ref<Texture2D> BRDFLUT;
Ref<Shader> CompositeShader;
Ref<RenderPass> GeoPass;
Ref<RenderPass> CompositePass;
struct DrawCommand
{
Ref<Mesh> mesh;
Ref<MaterialInstance> Material;
glm::mat4 Transform;
};
std::vector<DrawCommand> DrawList;
// Grid
Ref<MaterialInstance> GridMaterial;
};
static SceneRendererData s_Data;
void SceneRenderer::Init()
{
FramebufferSpecification geoFramebufferSpec;
geoFramebufferSpec.Width = 1280;
geoFramebufferSpec.Height = 720;
geoFramebufferSpec.Format = FramebufferFormat::RGBA16F;
geoFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
RenderPassSpecification geoRenderPassSpec;
geoRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(geoFramebufferSpec);
s_Data.GeoPass = RenderPass::Create(geoRenderPassSpec);
FramebufferSpecification compFramebufferSpec;
compFramebufferSpec.Width = 1280;
compFramebufferSpec.Height = 720;
compFramebufferSpec.Format = FramebufferFormat::RGBA8;
compFramebufferSpec.ClearColor = { 0.5f, 0.1f, 0.1f, 1.0f };
RenderPassSpecification compRenderPassSpec;
compRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(compFramebufferSpec);
s_Data.CompositePass = RenderPass::Create(compRenderPassSpec);
s_Data.CompositeShader = Shader::Create("assets/shaders/hdr.glsl");
s_Data.BRDFLUT = Texture2D::Create("assets/textures/BRDF_LUT.tga");
// Grid
const auto gridShader = Shader::Create("assets/shaders/Grid.glsl");
s_Data.GridMaterial = MaterialInstance::Create(Material::Create(gridShader));
constexpr float gridScale = 16.025f;
constexpr float gridSize = 0.025f;
s_Data.GridMaterial->Set("u_Scale", gridScale);
s_Data.GridMaterial->Set("u_Res", gridSize);
}
void SceneRenderer::SetViewportSize(uint32_t width, uint32_t height)
{
s_Data.GeoPass->GetSpecification().TargetFramebuffer->Resize(width, height);
s_Data.CompositePass->GetSpecification().TargetFramebuffer->Resize(width, height);
}
void SceneRenderer::BeginScene(const Scene* scene)
{
PM_CORE_ASSERT(!s_Data.ActiveScene);
s_Data.ActiveScene = scene;
s_Data.SceneData.SceneCamera = scene->m_Camera;
s_Data.SceneData.SkyboxMaterial = scene->m_SkyboxMaterial;
s_Data.SceneData.SceneEnvironment = scene->m_Environment;
}
void SceneRenderer::EndScene()
{
PM_CORE_ASSERT(s_Data.ActiveScene);
s_Data.ActiveScene = nullptr;
FlushDrawList();
}
void SceneRenderer::SubmitEntity(Entity* entity)
{
// TODO: Culling, sorting, etc.
auto mesh = entity->GetMesh();
if (!mesh)
return;
s_Data.DrawList.push_back({ mesh, entity->GetMaterial(), entity->GetTransform() });
}
static Ref<Shader> equirectangularConversionShader, envFilteringShader, envIrradianceShader;
std::pair<Ref<TextureCube>, Ref<TextureCube>> SceneRenderer::CreateEnvironmentMap(const std::string& filepath)
{
const uint32_t cubemapSize = 2048;
const uint32_t irradianceMapSize = 32;
Ref<TextureCube> envUnfiltered = TextureCube::Create(TextureFormat::Float16, cubemapSize, cubemapSize);
if (!equirectangularConversionShader)
equirectangularConversionShader = Shader::Create("assets/shaders/EquirectangularToCubeMap.glsl");
Ref<Texture2D> envEquirect = Texture2D::Create(filepath);
PM_CORE_ASSERT(envEquirect->GetFormat() == TextureFormat::Float16, "Texture is not HDR!");
equirectangularConversionShader->Bind();
envEquirect->Bind();
Renderer::Submit([envUnfiltered, cubemapSize, envEquirect]()
{
glBindImageTexture(0, envUnfiltered->GetRendererID(), 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA16F);
glDispatchCompute(cubemapSize / 32, cubemapSize / 32, 6);
glGenerateTextureMipmap(envUnfiltered->GetRendererID());
});
if (!envFilteringShader)
envFilteringShader = Shader::Create("assets/shaders/EnvironmentMipFilter.glsl");
Ref<TextureCube> envFiltered = TextureCube::Create(TextureFormat::Float16, cubemapSize, cubemapSize);
Renderer::Submit([envUnfiltered, envFiltered]()
{
glCopyImageSubData(envUnfiltered->GetRendererID(), GL_TEXTURE_CUBE_MAP, 0, 0, 0, 0,
envFiltered->GetRendererID(), GL_TEXTURE_CUBE_MAP, 0, 0, 0, 0,
envFiltered->GetWidth(), envFiltered->GetHeight(), 6);
});
envFilteringShader->Bind();
envUnfiltered->Bind();
Renderer::Submit([envUnfiltered, envFiltered, cubemapSize]() {
const float deltaRoughness = 1.0f / glm::max((float)(envFiltered->GetMipLevelCount() - 1.0f), 1.0f);
for (int level = 1, size = cubemapSize / 2; level < (int)envFiltered->GetMipLevelCount(); level++, size /= 2) // <= ?
{
const GLuint numGroups = glm::max(1, size / 32);
glBindImageTexture(0, envFiltered->GetRendererID(), level, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA16F);
glProgramUniform1f(envFilteringShader->GetRendererID(), 0, level * deltaRoughness);
glDispatchCompute(numGroups, numGroups, 6);
}
});
if (!envIrradianceShader)
envIrradianceShader = Shader::Create("assets/shaders/EnvironmentIrradiance.glsl");
Ref<TextureCube> irradianceMap = TextureCube::Create(TextureFormat::Float16, irradianceMapSize, irradianceMapSize);
envIrradianceShader->Bind();
envFiltered->Bind();
Renderer::Submit([irradianceMap]()
{
glBindImageTexture(0, irradianceMap->GetRendererID(), 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA16F);
glDispatchCompute(irradianceMap->GetWidth() / 32, irradianceMap->GetHeight() / 32, 6);
});
return { envFiltered, irradianceMap };
}
Ref<Texture2D> SceneRenderer::GetFinalColorBuffer()
{
PM_CORE_ASSERT(false, "Not implemented");
return nullptr;
}
uint32_t SceneRenderer::GetFinalColorBufferRendererID()
{
return s_Data.CompositePass->GetSpecification().TargetFramebuffer->GetColorAttachmentRendererID();
}
void SceneRenderer::FlushDrawList()
{
PM_CORE_ASSERT(!s_Data.ActiveScene);
GeometryPass();
CompositePass();
s_Data.DrawList.clear();
s_Data.SceneData = {};
}
void SceneRenderer::GeometryPass()
{
Renderer::BeginRenderPass(s_Data.GeoPass);
auto viewProjection = s_Data.SceneData.SceneCamera.GetProjectionMatrix() * s_Data.SceneData.SceneCamera.GetViewMatrix();
// Skybox
auto skyboxShader = s_Data.SceneData.SkyboxMaterial->GetShader();
s_Data.SceneData.SkyboxMaterial->Set("u_InverseVP", glm::inverse(viewProjection));
// s_Data.SceneInfo.EnvironmentIrradianceMap->Bind(0);
Renderer::SubmitFullscreenQuad(s_Data.SceneData.SkyboxMaterial);
// Render entities
for (auto& dc : s_Data.DrawList)
{
auto baseMaterial = dc.mesh->GetMaterial();
baseMaterial->Set("u_ViewProjectionMatrix", viewProjection);
baseMaterial->Set("u_CameraPosition", s_Data.SceneData.SceneCamera.GetPosition());
// Environment (TODO: don't do this per mesh)
baseMaterial->Set("u_EnvRadianceTex", s_Data.SceneData.SceneEnvironment.RadianceMap);
baseMaterial->Set("u_EnvIrradianceTex", s_Data.SceneData.SceneEnvironment.IrradianceMap);
baseMaterial->Set("u_BRDFLUTTexture", s_Data.BRDFLUT);
auto overrideMaterial = nullptr; // dc.Material;
Renderer::SubmitMesh(dc.mesh, dc.Transform, overrideMaterial);
}
// Grid
s_Data.GridMaterial->Set("u_ViewProjection", viewProjection);
Renderer::SubmitQuad(s_Data.GridMaterial, glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)) * glm::scale(glm::mat4(1.0f), glm::vec3(16.0f)));
Renderer::EndRenderPass();
}
void SceneRenderer::CompositePass()
{
Renderer::BeginRenderPass(s_Data.CompositePass);
s_Data.CompositeShader->Bind();
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.GetExposure());
s_Data.GeoPass->GetSpecification().TargetFramebuffer->BindTexture();
Renderer::SubmitFullscreenQuad(nullptr);
Renderer::EndRenderPass();
}
}

View File

@ -0,0 +1,40 @@
//
// Created by sfd on 25-11-30.
//
#ifndef SCENERENDERER_H
#define SCENERENDERER_H
#include "Texture.h"
#include "Prism/Scene/Scene.h"
namespace Prism
{
class PRISM_API SceneRenderer
{
public:
static void Init();
static void SetViewportSize(uint32_t width, uint32_t height);
static void BeginScene(const Scene* scene);
static void EndScene();
static void SubmitEntity(Entity* entity);
static std::pair<Ref<TextureCube>, Ref<TextureCube>> CreateEnvironmentMap(const std::string& filepath);
static Ref<Texture2D> GetFinalColorBuffer();
// TODO: Temp
static uint32_t GetFinalColorBufferRendererID();
private:
static void FlushDrawList();
static void GeometryPass();
static void CompositePass();
};
}
#endif //SCENERENDERER_H

View File

@ -33,7 +33,7 @@ namespace Prism
switch (RendererAPI::Current())
{
case RendererAPIType::None: return nullptr;
case RendererAPIType::OpenGL: result = OpenGLShader::CreateFromSring(source);
case RendererAPIType::OpenGL: result = CreateFromSring(source);
}
s_AllShaders.push_back(result);

View File

@ -7,6 +7,7 @@
#include <glm/glm.hpp>
#include "RendererAPI.h"
#include "glm/gtc/type_ptr.hpp"
#include "Prism/Core/Buffer.h"
#include "ShaderUniform.h"
@ -102,6 +103,8 @@ namespace Prism
static Ref<Shader> Create(const std::string& filepath);
static Ref<Shader> CreateFromSring(const std::string& source);
virtual RendererID GetRendererID() const = 0;
virtual void Reload() = 0;
virtual void Bind() = 0;
virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) = 0;
@ -117,6 +120,8 @@ namespace Prism
virtual const ShaderUniformBufferList& GetVSRendererUniforms() const = 0;
virtual const ShaderUniformBufferList& GetPSRendererUniforms() const = 0;
virtual bool HasVSMaterialUniformBuffer() const = 0;
virtual bool HasPSMaterialUniformBuffer() const = 0;
virtual const ShaderUniformBufferDeclaration& GetVSMaterialUniformBuffer() const = 0;
virtual const ShaderUniformBufferDeclaration& GetPSMaterialUniformBuffer() const = 0;

View File

@ -19,33 +19,52 @@ namespace Prism
return 0;
}
Texture2D* Texture2D::Create(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap)
uint32_t Texture::CalculateMipMapCount(const uint32_t width, const uint32_t height)
{
uint32_t levels = 1;
while ((width | height) >> levels)
levels++;
return levels;
}
Ref<Texture2D> Texture2D::Create(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap)
{
switch (RendererAPI::Current())
{
case RendererAPIType::None: return nullptr;
case RendererAPIType::OpenGL: return new OpenGLTexture2D(format, width, height, wrap);
case RendererAPIType::OpenGL: return CreateRef<OpenGLTexture2D>(format, width, height, wrap);
}
return nullptr;
}
Texture2D* Texture2D::Create(const std::string& path, bool srgb)
Ref<Texture2D> Texture2D::Create(const std::string& path, bool srgb)
{
switch (RendererAPI::Current())
{
case RendererAPIType::None: return nullptr;
case RendererAPIType::OpenGL: return new OpenGLTexture2D(path, srgb);
case RendererAPIType::OpenGL: return CreateRef<OpenGLTexture2D>(path, srgb);
}
return nullptr;
}
TextureCube* TextureCube::Create(const std::string& path)
Ref<TextureCube> TextureCube::Create(TextureFormat format, uint32_t width, uint32_t height)
{
switch (RendererAPI::Current())
{
case RendererAPIType::None: return nullptr;
case RendererAPIType::OpenGL: return new OpenGLTextureCube(path);
case RendererAPIType::OpenGL: return CreateRef<OpenGLTextureCube>(format, width, height);
}
return nullptr;
}
Ref<TextureCube> TextureCube::Create(const std::string& path)
{
switch (RendererAPI::Current())
{
case RendererAPIType::None: return nullptr;
case RendererAPIType::OpenGL: return CreateRef<OpenGLTextureCube>(path);
}
return nullptr;
}

View File

@ -16,6 +16,7 @@ namespace Prism
None = 0,
RGB = 1,
RGBA = 2,
Float16 = 3,
};
enum class TextureWrap
@ -32,16 +33,23 @@ namespace Prism
virtual void Bind(uint32_t slot = 0) const = 0;
virtual TextureFormat GetFormat() const = 0;
virtual uint32_t GetWidth() const = 0;
virtual uint32_t GetHeight() const = 0;
virtual uint32_t GetMipLevelCount() const = 0;
virtual RendererID GetRendererID() const = 0;
static uint32_t GetBPP(TextureFormat format);
static uint32_t CalculateMipMapCount(uint32_t width, uint32_t height);
};
class PRISM_API Texture2D : public Texture
{
public:
static Texture2D* Create(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap = TextureWrap::Clamp);
static Texture2D* Create(const std::string& path, bool srgb = false);
static Ref<Texture2D> Create(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap = TextureWrap::Clamp);
static Ref<Texture2D> Create(const std::string& path, bool srgb = false);
virtual TextureFormat GetFormat() const = 0;
@ -53,17 +61,15 @@ namespace Prism
virtual void Resize(uint32_t width, uint32_t height) = 0;
virtual Buffer GetWriteableBuffer() = 0;
virtual bool Loaded() const = 0;
virtual const std::string& GetPath() const = 0;
};
class PRISM_API TextureCube : public Texture
{
public:
static TextureCube* Create(const std::string& path);
virtual TextureFormat GetFormat() const = 0;
virtual uint32_t GetWidth() const = 0;
virtual uint32_t GetHeight() const = 0;
static Ref<TextureCube> Create(TextureFormat format, uint32_t width, uint32_t height);
static Ref<TextureCube> Create(const std::string& path);
virtual const std::string& GetPath() const = 0;
};

View File

@ -0,0 +1,17 @@
//
// Created by sfd on 25-11-30.
//
#include "Entity.h"
namespace Prism
{
Entity::Entity()
: m_Transform(glm::mat4(1.0f))
{
}
Entity::~Entity()
{
}
}

View File

@ -0,0 +1,38 @@
//
// Created by sfd on 25-11-30.
//
#ifndef ENTITY_H
#define ENTITY_H
#include "glm/glm.hpp"
#include "Prism/Renderer/Mesh.h"
namespace Prism
{
class PRISM_API Entity
{
public:
Entity();
~Entity();
// TODO: Move to Component
void SetMesh(const Ref<Mesh>& mesh) { m_Mesh = mesh; }
Ref<Mesh> GetMesh() { return m_Mesh; }
void SetMaterial(const Ref<MaterialInstance>& material) { m_Material = material; }
Ref<MaterialInstance> GetMaterial() { return m_Material; }
const glm::mat4& GetTransform() const { return m_Transform; }
glm::mat4& Transform() { return m_Transform; }
private:
glm::mat4 m_Transform;
// TODO: Temp
Ref<Mesh> m_Mesh;
Ref<MaterialInstance> m_Material;
};
}
#endif //ENTITY_H

View File

@ -0,0 +1,90 @@
//
// Created by sfd on 25-11-30.
//
#include "Scene.h"
#include "Prism/Renderer/SceneRenderer.h"
namespace Prism
{
Environment Environment::Load(const std::string& filepath)
{
auto [radiance, irradiance] = SceneRenderer::CreateEnvironmentMap(filepath);
return { radiance, irradiance };
}
Scene::Scene(const std::string& debugName)
: m_DebugName(debugName)
{
Init();
}
Scene::~Scene()
{
for (const Entity* entity : m_Entities)
delete entity;
}
void Scene::Init()
{
auto skyboxShader = Shader::Create("assets/shaders/Skybox.glsl");
m_SkyboxMaterial = MaterialInstance::Create(Material::Create(skyboxShader));
m_SkyboxMaterial->SetFlag(MaterialFlag::DepthTest, false);
}
void Scene::OnUpdate(TimeStep ts)
{
m_Camera.Update(ts);
m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod);
// Update all entities
for (auto entity : m_Entities)
{
auto mesh = entity->GetMesh();
if (mesh)
mesh->OnUpdate(ts);
}
SceneRenderer::BeginScene(this);
// Render entities
for (auto entity : m_Entities)
{
// TODO: Should we render (logically)
SceneRenderer::SubmitEntity(entity);
}
SceneRenderer::EndScene();
}
void Scene::SetCamera(const Camera& camera)
{
m_Camera = camera;
}
void Scene::SetEnvironment(const Environment& environment)
{
m_Environment = environment;
SetSkybox(environment.RadianceMap);
}
void Scene::SetSkybox(const Ref<TextureCube>& skybox)
{
m_SkyboxTexture = skybox;
m_SkyboxMaterial->Set("u_Texture", skybox);
}
void Scene::AddEntity(Entity* entity)
{
m_Entities.push_back(entity);
}
Entity* Scene::CreateEntity()
{
const auto entity = new Entity();
AddEntity(entity);
return entity;
}
}

View File

@ -0,0 +1,60 @@
//
// Created by sfd on 25-11-30.
//
#ifndef SCENE_H
#define SCENE_H
#include "Entity.h"
#include "Prism/Renderer/Camera.h"
#include "Prism/Renderer/Material.h"
namespace Prism
{
struct PRISM_API Environment
{
Ref<TextureCube> RadianceMap;
Ref<TextureCube> IrradianceMap;
static Environment Load(const std::string& filepath);
};
class PRISM_API Scene{
public:
Scene(const std::string& debugName = "Scene");
~Scene();
void Init();
void OnUpdate(TimeStep ts);
void SetCamera(const Camera& camera);
Camera& GetCamera() { return m_Camera; }
void SetEnvironment(const Environment& environment);
void SetSkybox(const Ref<TextureCube>& skybox);
float& GetSkyboxLod() { return m_SkyboxLod; }
void AddEntity(Entity* entity);
Entity* CreateEntity();
private:
std::string m_DebugName;
std::vector<Entity*> m_Entities;
Camera m_Camera;
Environment m_Environment;
Ref<TextureCube> m_SkyboxTexture;
Ref<MaterialInstance> m_SkyboxMaterial;
float m_SkyboxLod = 1.0f;
friend class SceneRenderer;
friend class SceneHierarchyPanel;
};
}
#endif //SCENE_H