add directional light component and skylight component, add PCSS and hard shadow

This commit is contained in:
2026-01-02 22:46:29 +08:00
parent abf0a65bd6
commit 9e1474e643
55 changed files with 3032 additions and 1121 deletions

View File

@ -161,7 +161,7 @@ namespace Prism
} }
EditorLayer::EditorLayer() EditorLayer::EditorLayer()
: m_SceneType(SceneType::Model), m_EditorCamera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f)) : m_SceneType(SceneType::Model), m_EditorCamera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 1000.0f))
{ {
} }
@ -175,12 +175,12 @@ namespace Prism
m_CheckerboardTex = Texture2D::Create("assets/editor/Checkerboard.tga"); m_CheckerboardTex = Texture2D::Create("assets/editor/Checkerboard.tga");
m_PlayButtonTex = Texture2D::Create("assets/editor/PlayButton.png"); m_PlayButtonTex = Texture2D::Create("assets/editor/PlayButton.png");
m_EditorScene = Ref<Scene>::Create("untitled Scene", true);
ScriptEngine::SetSceneContext(m_EditorScene);
m_SceneHierarchyPanel = CreateScope<SceneHierarchyPanel>(m_EditorScene); m_SceneHierarchyPanel = CreateScope<SceneHierarchyPanel>(m_EditorScene);
m_SceneHierarchyPanel->SetSelectionChangedCallback(std::bind(&EditorLayer::SelectEntity, this, std::placeholders::_1)); m_SceneHierarchyPanel->SetSelectionChangedCallback(std::bind(&EditorLayer::SelectEntity, this, std::placeholders::_1));
m_SceneHierarchyPanel->SetEntityDeletedCallback(std::bind(&EditorLayer::OnEntityDeleted, this, std::placeholders::_1)); m_SceneHierarchyPanel->SetEntityDeletedCallback(std::bind(&EditorLayer::OnEntityDeleted, this, std::placeholders::_1));
UpdateWindowTitle("untitled Scene"); UpdateWindowTitle("untitled Scene");
OpenScene("assets/scenes/FPSDemo.scene");
} }
void EditorLayer::OnDetach() void EditorLayer::OnDetach()
@ -190,6 +190,10 @@ namespace Prism
void EditorLayer::OnUpdate(const TimeStep deltaTime) void EditorLayer::OnUpdate(const TimeStep deltaTime)
{ {
auto [x, y] = GetMouseViewportSpace();
SceneRenderer::SetFocusPoint({ x * 0.5f + 0.5f, y * 0.5f + 0.5f });
switch (m_SceneState) switch (m_SceneState)
{ {
case SceneState::Edit: case SceneState::Edit:
@ -330,9 +334,11 @@ namespace Prism
{ {
if (ImGui::BeginMenu("file")) if (ImGui::BeginMenu("file"))
{ {
if (ImGui::MenuItem("New Scene")) if (ImGui::MenuItem("New Scene", "Ctrl+N"))
{ {
NewScene();
} }
if (ImGui::MenuItem("Open Scene...", "Ctrl+O")) if (ImGui::MenuItem("Open Scene...", "Ctrl+O"))
{ {
OpenScene(); OpenScene();
@ -390,6 +396,8 @@ namespace Prism
m_SceneHierarchyPanel->OnImGuiRender(); m_SceneHierarchyPanel->OnImGuiRender();
PhysicsSettingsWindow::OnImGuiRender(m_ShowPhysicsSettings); PhysicsSettingsWindow::OnImGuiRender(m_ShowPhysicsSettings);
SceneRenderer::OnImGuiRender();
// m_EditorCamera.OnImGuiRender();
ImGui::Begin("Materials"); ImGui::Begin("Materials");
@ -475,6 +483,7 @@ namespace Prism
} }
{ {
// Normals // Normals
if (ImGui::CollapsingHeader("Normals", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) if (ImGui::CollapsingHeader("Normals", nullptr, ImGuiTreeNodeFlags_DefaultOpen))
{ {
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10));
@ -606,13 +615,6 @@ namespace Prism
ImGui::Begin("Model"); ImGui::Begin("Model");
ImGui::Begin("Environment"); ImGui::Begin("Environment");
if (ImGui::Button("Load Environment Map"))
{
std::string filename = Application::Get().OpenFile("*.hdr");
if (!filename.empty())
m_EditorScene->SetEnvironment(Environment::Load(filename));
}
ImGui::SliderFloat("Skybox LOD", &m_EditorScene->GetSkyboxLod(), 0.0f, 11.0f); ImGui::SliderFloat("Skybox LOD", &m_EditorScene->GetSkyboxLod(), 0.0f, 11.0f);
@ -623,7 +625,6 @@ namespace Prism
Property("Light Direction", light.Direction, PropertyFlag::SliderProperty); Property("Light Direction", light.Direction, PropertyFlag::SliderProperty);
Property("Light Radiance", light.Radiance, PropertyFlag::ColorProperty); Property("Light Radiance", light.Radiance, PropertyFlag::ColorProperty);
Property("Light Multiplier", light.Multiplier, 0.0f, 5.0f, PropertyFlag::SliderProperty); Property("Light Multiplier", light.Multiplier, 0.0f, 5.0f, PropertyFlag::SliderProperty);
Property("Exposure", m_EditorCamera.GetExposure(), 0.0f, 5.0f, PropertyFlag::SliderProperty);
Property("Radiance Prefiltering", m_RadiancePrefilter); Property("Radiance Prefiltering", m_RadiancePrefilter);
Property("Env Map Rotation", m_EnvMapRotation, -360.0f, 360.0f, PropertyFlag::SliderProperty); Property("Env Map Rotation", m_EnvMapRotation, -360.0f, 360.0f, PropertyFlag::SliderProperty);
@ -758,7 +759,7 @@ namespace Prism
m_EditorScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); m_EditorScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
if (m_RuntimeScene) if (m_RuntimeScene)
m_RuntimeScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); m_RuntimeScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
m_EditorCamera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f)); m_EditorCamera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 1000.0f));
m_EditorCamera.SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); m_EditorCamera.SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
ImGui::Image((ImTextureRef)SceneRenderer::GetFinalColorBufferRendererID(), viewportSize, { 0, 1 }, { 1, 0 }); ImGui::Image((ImTextureRef)SceneRenderer::GetFinalColorBufferRendererID(), viewportSize, { 0, 1 }, { 1, 0 });
@ -846,7 +847,7 @@ namespace Prism
bool EditorLayer::OnKeyPressedEvent(KeyPressedEvent& e) bool EditorLayer::OnKeyPressedEvent(KeyPressedEvent& e)
{ {
if (m_ViewportPanelFocused) if (m_ViewportPanelHovered)
{ {
switch (e.GetKeyCode()) switch (e.GetKeyCode())
{ {
@ -862,19 +863,23 @@ namespace Prism
case KeyCode::R: case KeyCode::R:
m_GizmoType = ImGuizmo::OPERATION::SCALE; m_GizmoType = ImGuizmo::OPERATION::SCALE;
break; break;
case KeyCode::DELETE:
if (m_SelectionContext.size())
{
const Entity selectedEntity = m_SelectionContext[0].Entity;
m_EditorScene->DestroyEntity(selectedEntity);
m_SelectionContext.clear();
m_EditorScene->SetSelectedEntity({});
m_SceneHierarchyPanel->SetSelected({});
}
break;
} }
} }
switch (e.GetKeyCode())
{
case KeyCode::DELETE:
if (m_SelectionContext.size())
{
const Entity selectedEntity = m_SelectionContext[0].Entity;
m_EditorScene->DestroyEntity(selectedEntity);
m_SelectionContext.clear();
m_EditorScene->SetSelectedEntity({});
m_SceneHierarchyPanel->SetSelected({});
}
break;
}
if (Input::IsKeyPressed(KeyCode::LEFT_CONTROL)) if (Input::IsKeyPressed(KeyCode::LEFT_CONTROL))
{ {
const bool IsShiftPressed = Input::IsKeyPressed(KeyCode::LEFT_SHIFT); const bool IsShiftPressed = Input::IsKeyPressed(KeyCode::LEFT_SHIFT);
@ -888,7 +893,9 @@ namespace Prism
case KeyCode::S: case KeyCode::S:
SaveScene(); SaveScene();
break; break;
case KeyCode::N:
NewScene();
break;
case KeyCode::B: case KeyCode::B:
// Toggle bounding boxes // Toggle bounding boxes
m_UIShowBoundingBoxes = !m_UIShowBoundingBoxes; m_UIShowBoundingBoxes = !m_UIShowBoundingBoxes;
@ -998,10 +1005,8 @@ namespace Prism
SelectedSubmesh selection; SelectedSubmesh selection;
if (entity.HasComponent<MeshComponent>()) if (entity.HasComponent<MeshComponent>())
{ {
if (auto& meshComp = entity.GetComponent<MeshComponent>(); meshComp.Mesh) if (auto mesh = entity.GetComponent<MeshComponent>().Mesh)
{ selection.Mesh = &mesh->GetSubmeshes()[0];
selection.Mesh = &meshComp.Mesh->GetSubmeshes()[0];
}
} }
selection.Entity = entity; selection.Entity = entity;
m_SelectionContext.clear(); m_SelectionContext.clear();
@ -1068,33 +1073,56 @@ namespace Prism
return Ray::Zero(); return Ray::Zero();
} }
void EditorLayer::NewScene()
{
m_EditorScene = Ref<Scene>::Create("Untitled Scene", true);
m_SceneHierarchyPanel->SetContext(m_EditorScene);
ScriptEngine::SetSceneContext(m_EditorScene);
UpdateWindowTitle("Untitled Scene");
m_SceneFilePath = std::string();
m_EditorCamera = EditorCamera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 1000.0f));
m_SelectionContext.clear();
}
void EditorLayer::OpenScene() void EditorLayer::OpenScene()
{ {
const auto& app = Application::Get(); const auto& app = Application::Get();
const std::string filepath = app.OpenFile("Prism Scene (*.hsc)\0*.hsc\0"); const std::string filepath = app.OpenFile("Hazel Scene (*.hsc)\0*.hsc\0");
if (!filepath.empty()) if (!filepath.empty())
{ OpenScene(filepath);
const Ref<Scene> newScene = Ref<Scene>::Create("EditorScene", true); }
SceneSerializer serializer(newScene);
serializer.Deserialize(filepath);
m_EditorScene = newScene;
const std::filesystem::path path = filepath;
UpdateWindowTitle(path.filename().string());
m_SceneHierarchyPanel->SetContext(m_EditorScene);
ScriptEngine::SetSceneContext(m_EditorScene);
m_EditorScene->SetSelectedEntity({}); void EditorLayer::OpenScene(const std::string& filepath)
m_SelectionContext.clear(); {
const std::string fileName = std::filesystem::path(filepath).filename().string();
UpdateWindowTitle(fileName);
m_SceneFilePath = filepath; const Ref<Scene> newScene = Ref<Scene>::Create(fileName, true);
} SceneSerializer serializer(newScene);
serializer.Deserialize(filepath);
m_EditorScene = newScene;
m_SceneFilePath = filepath;
m_SceneHierarchyPanel->SetContext(m_EditorScene);
ScriptEngine::SetSceneContext(m_EditorScene);
m_EditorScene->SetSelectedEntity({});
m_SelectionContext.clear();
} }
void EditorLayer::SaveScene() void EditorLayer::SaveScene()
{ {
PM_CLIENT_INFO("Saving scene to: {0}", m_SceneFilePath); if (!m_SceneFilePath.empty())
SceneSerializer serializer(m_EditorScene); {
serializer.Serialize(m_SceneFilePath); PM_CLIENT_INFO("Saving scene to: {0}", m_SceneFilePath);
SceneSerializer serializer(m_EditorScene);
serializer.Serialize(m_SceneFilePath);
}
else
{
SaveSceneAs();
}
} }
void EditorLayer::SaveSceneAs() void EditorLayer::SaveSceneAs()
@ -1110,6 +1138,9 @@ namespace Prism
std::filesystem::path path = filepath; std::filesystem::path path = filepath;
UpdateWindowTitle(path.filename().string()); UpdateWindowTitle(path.filename().string());
m_SceneFilePath = filepath; m_SceneFilePath = filepath;
}else
{
PM_CLIENT_INFO("cancelled");
} }
} }
@ -1140,8 +1171,6 @@ namespace Prism
m_SelectionContext.clear(); m_SelectionContext.clear();
ScriptEngine::SetSceneContext(m_EditorScene); ScriptEngine::SetSceneContext(m_EditorScene);
m_SceneHierarchyPanel->SetContext(m_EditorScene); m_SceneHierarchyPanel->SetContext(m_EditorScene);
Input::SetCursorMode(CursorMode::Normal);
} }
float EditorLayer::GetSnapValue() float EditorLayer::GetSnapValue()

View File

@ -44,7 +44,9 @@ namespace Prism
void OnEntityDeleted(Entity e); void OnEntityDeleted(Entity e);
Ray CastMouseRay(); Ray CastMouseRay();
void NewScene();
void OpenScene(); void OpenScene();
void OpenScene(const std::string& filepath);
void SaveScene(); void SaveScene();
void SaveSceneAs(); void SaveSceneAs();

View File

@ -1,155 +0,0 @@
Scene: Scene Name
Environment:
AssetPath: assets/env/pink_sunrise_4k.hdr
Light:
Direction: [-0.787, -0.73299998, 1]
Radiance: [1, 1, 1]
Multiplier: 0.514999986
Entities:
- Entity: 12498244675852797835
TagComponent:
Tag: Box
TransformComponent:
Position: [-12.0348625, 6.59647179, 9.60061925e-07]
Rotation: [1, 0, 0, 0]
Scale: [3.00000024, 0.300000012, 1]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 0
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [1.5, 0.150000006]
Density: 1
Friction: 1
- Entity: 5178862374589434728
TagComponent:
Tag: Camera
TransformComponent:
Position: [-21.7406311, 9.70659542, 15]
Rotation: [0.999910355, -0.0133911213, 0, 0]
Scale: [1, 1, 1]
ScriptComponent:
ModuleName: Example.BasicController
StoredFields:
- Name: Speed
Type: 1
Data: 12
CameraComponent:
Camera: some camera data...
Primary: true
- Entity: 1289165777996378215
TagComponent:
Tag: Cube
TransformComponent:
Position: [500, 0, 0]
Rotation: [1, 0, 0, 0]
Scale: [1200, 1, 5]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 0
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [600, 0.5]
Density: 1
Friction: 2
- Entity: 14057422478420564497
TagComponent:
Tag: Player
TransformComponent:
Position: [-23.6932545, 1.59184527, -1.96369365e-06]
Rotation: [1, 0, 0, 0]
Scale: [1, 1, 1]
ScriptComponent:
ModuleName: Example.PlayerCube
StoredFields:
- Name: HorizontalForce
Type: 1
Data: 0.5
- Name: MaxSpeed
Type: 5
Data: [7, 10]
- Name: JumpForce
Type: 1
Data: 3
MeshComponent:
AssetPath: assets/meshes/Sphere1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 29.2000008
CircleCollider2DComponent:
Offset: [0, 0]
Radius: 0.5
Density: 1
Friction: 1
- Entity: 1352995477042327524
TagComponent:
Tag: Box
TransformComponent:
Position: [-29.6808929, 29.7597198, 0]
Rotation: [0.707106769, 0, 0, 0.707106769]
Scale: [58.4179001, 4.47999144, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 0
Mass: 3
BoxCollider2DComponent:
Offset: [0, 0]
Size: [29.7000008, 2.24000001]
Density: 1
Friction: 1
- Entity: 15223077898852293773
TagComponent:
Tag: Box
TransformComponent:
Position: [6.12674046, 45.5617676, 0]
Rotation: [0.977883637, 0, 0, -0.209149584]
Scale: [4.47999668, 4.47999668, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [2.24000001, 2.24000001]
Density: 1
Friction: 1
- Entity: 5421735812495444456
TagComponent:
Tag: Box
TransformComponent:
Position: [-20.766222, 2.29431438, 0]
Rotation: [1, 0, 0, 0]
Scale: [3.00000024, 0.300000012, 1]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 0
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [1.5, 0.150000006]
Density: 1
Friction: 1
- Entity: 2842299641876190180
TagComponent:
Tag: Box
TransformComponent:
Position: [-16.6143265, 4.39151001, 6.43359499e-09]
Rotation: [1, 0, 0, 0]
Scale: [3.00000024, 0.300000012, 1]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 0
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [1.5, 0.150000006]
Density: 1
Friction: 1

View File

@ -1,6 +1,6 @@
Scene: Scene Name Scene: Scene Name
Environment: Environment:
AssetPath: assets/env/pink_sunrise_4k.hdr AssetPath: assets\env\birchwood_4k.hdr
Light: Light:
Direction: [-0.787, -0.733, 1] Direction: [-0.787, -0.733, 1]
Radiance: [1, 1, 1] Radiance: [1, 1, 1]
@ -51,6 +51,7 @@ Entities:
Bounciness: 0.1 Bounciness: 0.1
MeshColliderComponent: MeshColliderComponent:
AssetPath: assets/meshes/Capsule.fbx AssetPath: assets/meshes/Capsule.fbx
IsConvex: true
IsTrigger: false IsTrigger: false
- Entity: 11149966982516343187 - Entity: 11149966982516343187
TagComponent: TagComponent:
@ -79,6 +80,7 @@ Entities:
Bounciness: 0.1 Bounciness: 0.1
MeshColliderComponent: MeshColliderComponent:
AssetPath: assets/meshes/Sphere1m.fbx AssetPath: assets/meshes/Sphere1m.fbx
IsConvex: true
IsTrigger: false IsTrigger: false
- Entity: 10169503531257462571 - Entity: 10169503531257462571
TagComponent: TagComponent:
@ -176,6 +178,17 @@ Entities:
Offset: [0, 0, 0] Offset: [0, 0, 0]
Size: [1, 1, 1] Size: [1, 1, 1]
IsTrigger: false IsTrigger: false
- Entity: 2025484417758554619
TagComponent:
Tag: Sky Light
TransformComponent:
Position: [0, 0, 0]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
SkyLightComponent:
EnvironmentAssetPath: assets\env\birchwood_4k.hdr
Intensity: 1
Angle: 0
PhysicsLayers: PhysicsLayers:
- Name: Box - Name: Box
CollidesWith: CollidesWith:

View File

@ -1,174 +0,0 @@
Scene: Scene Name
Environment:
AssetPath: assets/env/pink_sunrise_4k.hdr
Light:
Direction: [-0.787, -0.73299998, 1]
Radiance: [1, 1, 1]
Multiplier: 0.514999986
Entities:
- Entity: 15861629587505754
TagComponent:
Tag: Box
TransformComponent:
Position: [-18.2095661, 39.2518234, 0]
Rotation: [0.967056513, 0, 0, -0.254561812]
Scale: [4.47999525, 4.47999525, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [2.24000001, 2.24000001]
- Entity: 15223077898852293773
TagComponent:
Tag: Box
TransformComponent:
Position: [5.37119865, 43.8762894, 0]
Rotation: [0.977883637, 0, 0, -0.209149718]
Scale: [4.47999668, 4.47999668, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [2.24000001, 2.24000001]
- Entity: 2157107598622182863
TagComponent:
Tag: Box
TransformComponent:
Position: [-7.60411549, 44.1442184, 0]
Rotation: [0.989285827, 0, 0, 0.145991713]
Scale: [4.47999287, 4.47999287, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 0.5
BoxCollider2DComponent:
Offset: [0, 0]
Size: [2.24000001, 2.24000001]
- Entity: 8080964283681139153
TagComponent:
Tag: Box
TransformComponent:
Position: [-0.739211679, 37.7653275, 0]
Rotation: [0.956475914, 0, 0, -0.291811317]
Scale: [5, 2, 2]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 0.25
BoxCollider2DComponent:
Offset: [0, 0]
Size: [2.5, 1]
- Entity: 1352995477042327524
TagComponent:
Tag: Box
TransformComponent:
Position: [-8.32969856, 30.4078159, 0]
Rotation: [0.781595349, 0, 0, 0.623785794]
Scale: [14.000001, 4.47999334, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 3
BoxCollider2DComponent:
Offset: [0, 0]
Size: [7, 2.24000001]
- Entity: 935615878363259513
TagComponent:
Tag: Box
TransformComponent:
Position: [6.88031197, 31.942337, 0]
Rotation: [0.986578286, 0, 0, 0.163288936]
Scale: [4.47999954, 4.47999954, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [2.24000001, 2.24000001]
- Entity: 14057422478420564497
TagComponent:
Tag: Player
TransformComponent:
Position: [0, 22.774044, 0]
Rotation: [0.942591429, 0, 0, -0.333948225]
Scale: [6.00000048, 6.00000048, 4.48000002]
ScriptComponent:
ModuleName: Example.PlayerCube
StoredFields:
- Name: HorizontalForce
Type: 1
Data: 10
- Name: VerticalForce
Type: 1
Data: 10
MeshComponent:
AssetPath: assets/meshes/Sphere1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 1
CircleCollider2DComponent:
Offset: [0, 0]
Radius: 3
- Entity: 1289165777996378215
TagComponent:
Tag: Cube
TransformComponent:
Position: [0, 0, 0]
Rotation: [1, 0, 0, 0]
Scale: [50, 1, 50]
ScriptComponent:
ModuleName: Example.Sink
StoredFields:
- Name: SinkSpeed
Type: 1
Data: 0
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 0
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [25, 0.5]
- Entity: 5178862374589434728
TagComponent:
Tag: Camera
TransformComponent:
Position: [0, 25, 79.75]
Rotation: [0.995602965, -0.0936739072, 0, 0]
Scale: [1, 0.999999821, 0.999999821]
ScriptComponent:
ModuleName: Example.BasicController
StoredFields:
- Name: Speed
Type: 1
Data: 12
CameraComponent:
Camera: some camera data...
Primary: true
- Entity: 3948844418381294888
TagComponent:
Tag: Box
TransformComponent:
Position: [-1.48028564, 49.5945244, -2.38418579e-07]
Rotation: [0.977883637, 0, 0, -0.209149733]
Scale: [1.99999976, 1.99999976, 2]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [1, 1]

View File

@ -1,119 +0,0 @@
Scene: Scene Name
Environment:
AssetPath: assets/env/pink_sunrise_4k.hdr
Light:
Direction: [-0.787, -0.73299998, 1]
Radiance: [1, 1, 1]
Multiplier: 0.514999986
Entities:
- Entity: 10169503531257462571
TagComponent:
Tag: Box
TransformComponent:
Position: [0, 1.5, 0]
Rotation: [1, 0, 0, 0]
Scale: [2, 2, 2]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBodyComponent:
BodyType: 1
Mass: 0.5
IsKinematic: false
Constraints:
LockPositionX: false
LockPositionY: false
LockPositionZ: false
LockRotationX: true
LockRotationY: true
LockRotationZ: true
PhysicsMaterialComponent:
StaticFriction: 1
DynamicFriction: 1
Bounciness: 0
BoxColliderComponent:
Offset: [0, 0, 0]
Size: [2, 2, 2]
- Entity: 14057422478420564497
TagComponent:
Tag: Player
TransformComponent:
Position: [-19.43363, 4.50874043, -1.96695328e-06]
Rotation: [1, 0, 0, 0]
Scale: [1, 1, 1]
ScriptComponent:
ModuleName: Example.PlayerSphere
StoredFields:
- Name: HorizontalForce
Type: 1
Data: 10
- Name: MaxSpeed
Type: 6
Data: [10, 10, 10]
- Name: JumpForce
Type: 1
Data: 200
MeshComponent:
AssetPath: assets/meshes/Sphere1m.fbx
RigidBodyComponent:
BodyType: 1
Mass: 1
IsKinematic: false
Constraints:
LockPositionX: false
LockPositionY: false
LockPositionZ: false
LockRotationX: true
LockRotationY: true
LockRotationZ: true
PhysicsMaterialComponent:
StaticFriction: 1
DynamicFriction: 1
Bounciness: 0
SphereColliderComponent:
Radius: 0.5
- Entity: 5178862374589434728
TagComponent:
Tag: Camera
TransformComponent:
Position: [-21.7406311, 9.70659542, 15]
Rotation: [0.999910355, -0.0133911213, 0, 0]
Scale: [1, 1, 1]
ScriptComponent:
ModuleName: Example.BasicController
StoredFields:
- Name: Speed
Type: 1
Data: 12
- Name: DistanceFromPlayer
Type: 1
Data: 15
CameraComponent:
Camera: some camera data...
Primary: true
- Entity: 18306113171518048249
TagComponent:
Tag: Box
TransformComponent:
Position: [0, 0, 0]
Rotation: [1, 0, 0, 0]
Scale: [50, 1, 50]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBodyComponent:
BodyType: 0
Mass: 1
IsKinematic: false
Constraints:
LockPositionX: false
LockPositionY: false
LockPositionZ: false
LockRotationX: true
LockRotationY: true
LockRotationZ: true
PhysicsMaterialComponent:
StaticFriction: 1
DynamicFriction: 1
Bounciness: 0
BoxColliderComponent:
Offset: [0, 0, 0]
Size: [50, 1, 50]

View File

@ -1,66 +0,0 @@
Scene: Scene Name
Environment:
AssetPath: assets/env/birchwood_4k.hdr
Light:
Direction: [-0.5, -0.5, 1]
Radiance: [1, 1, 1]
Multiplier: 1
Entities:
- Entity: 1289165777996378215
TagComponent:
Tag: Sphere
TransformComponent:
Position: [0, 21.9805069, -1.64006281]
Rotation: [1, 0, 0, 0]
Scale: [0.100000024, 0.100000024, 0.100000024]
ScriptComponent:
ModuleName: Example.Sink
StoredFields:
- Name: SinkSpeed
Type: 1
Data: 5
MeshComponent:
AssetPath: assets/meshes/Sphere1m.fbx
- Entity: 5178862374589434728
TagComponent:
Tag: Camera
TransformComponent:
Position: [0, 14.75, 79.75]
Rotation: [0.995602965, -0.0936739072, 0, 0]
Scale: [1, 0.999999821, 0.999999821]
ScriptComponent:
ModuleName: Example.BasicController
StoredFields:
- Name: Speed
Type: 1
Data: 12
CameraComponent:
Camera: some camera data...
Primary: true
- Entity: 9095450049242347594
TagComponent:
Tag: Test Entity
TransformComponent:
Position: [0.248109579, -1.90734863e-06, -0.268640995]
Rotation: [1, 0, 0, 0]
Scale: [1, 1, 1]
ScriptComponent:
ModuleName: Example.Script
StoredFields:
- Name: VerticalSpeed
Type: 1
Data: 0
- Name: SinkRate
Type: 1
Data: 0
- Name: Speed
Type: 1
Data: 1
- Name: Rotation
Type: 1
Data: 0
- Name: Velocity
Type: 6
Data: [0, 0, 0]
MeshComponent:
AssetPath: assets/meshes/TestScene.fbx

View File

@ -0,0 +1,63 @@
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
out vec2 v_TexCoord;
void main()
{
vec4 position = vec4(a_Position.xy, 0.0, 1.0);
v_TexCoord = a_TexCoord;
gl_Position = position;
}
#type fragment
#version 430
layout(location = 0) out vec4 o_Color;
in vec2 v_TexCoord;
uniform sampler2D u_SceneTexture;
uniform sampler2D u_BloomTexture;
uniform float u_Exposure;
uniform bool u_EnableBloom;
void main()
{
#if 1
const float gamma = 2.2;
const float pureWhite = 1.0;
// Tonemapping
vec3 color = texture(u_SceneTexture, v_TexCoord).rgb;
if (u_EnableBloom)
{
vec3 bloomColor = texture(u_BloomTexture, v_TexCoord).rgb;
color += bloomColor;
}
// Reinhard tonemapping
float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
float mappedLuminance = (luminance * (1.0 + luminance / (pureWhite * pureWhite))) / (1.0 + luminance);
// Scale color by ratio of average luminances.
vec3 mappedColor = (mappedLuminance / luminance) * color* u_Exposure;
// Gamma correction.
o_Color = vec4(color, 1.0);
#else
const float gamma = 2.2;
vec3 hdrColor = texture(u_SceneTexture, v_TexCoord).rgb;
vec3 bloomColor = texture(u_BloomTexture, v_TexCoord).rgb;
hdrColor += bloomColor; // additive blending
// tone mapping
vec3 result = vec3(1.0) - exp(-hdrColor * u_Exposure);
// also gamma correct while we're at it
result = pow(result, vec3(1.0 / gamma));
o_Color = vec4(result, 1.0);
#endif
}

View File

@ -0,0 +1,79 @@
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
out vec2 v_TexCoord;
void main()
{
vec4 position = vec4(a_Position.xy, 0.0, 1.0);
v_TexCoord = a_TexCoord;
gl_Position = position;
}
#type fragment
#version 430
layout(location = 0) out vec4 o_Color;
in vec2 v_TexCoord;
uniform sampler2D u_Texture;
uniform bool u_Horizontal;
void main()
{
#if 1
// From learnopengl.com
float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
vec2 tex_offset = 1.0 / textureSize(u_Texture, 0); // gets size of single texel
vec3 result = texture(u_Texture, v_TexCoord).rgb * weight[0]; // current fragment's contribution
if (u_Horizontal)
{
for(int i = 1; i < 5; ++i)
{
result += texture(u_Texture, v_TexCoord + vec2(tex_offset.x * i, 0.0)).rgb * weight[i];
result += texture(u_Texture, v_TexCoord - vec2(tex_offset.x * i, 0.0)).rgb * weight[i];
}
}
else
{
for(int i = 1; i < 5; ++i)
{
result += texture(u_Texture, v_TexCoord + vec2(0.0, tex_offset.y * i)).rgb * weight[i];
result += texture(u_Texture, v_TexCoord - vec2(0.0, tex_offset.y * i)).rgb * weight[i];
}
}
o_Color = vec4(result, 1.0);
#else
// From https://www.shadertoy.com/view/Xltfzj
float Pi = 6.28318530718; // Pi*2
// GAUSSIAN BLUR SETTINGS {{{
float Directions =32.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower)
float Quality = 6.0; // BLUR QUALITY (Default 4.0 - More is better but slower)
float Size = 16.0; // BLUR SIZE (Radius)
// GAUSSIAN BLUR SETTINGS }}}
vec2 Radius = Size/textureSize(u_Texture, 0);
vec3 result = texture(u_Texture, v_TexCoord).rgb;
vec2 uv = v_TexCoord;
// Blur calculations
for( float d=0.0; d<Pi; d+=Pi/Directions)
{
for(float i=1.0/Quality; i<=1.0; i+=1.0/Quality)
{
result += texture( u_Texture, uv+vec2(cos(d),sin(d))*Radius*i).rgb;
}
}
// Output to screen
result /= Quality * Directions - 15.0;
o_Color = vec4(result, 1.0);
#endif
}

View File

@ -4,13 +4,10 @@
#version 430 #version 430
layout(location = 0) in vec3 a_Position; layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
uniform mat4 u_ViewProjection; uniform mat4 u_ViewProjection;
uniform mat4 u_Transform; uniform mat4 u_Transform;
out vec2 v_TexCoord;
void main() void main()
{ {
gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0); gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);

View File

@ -0,0 +1,36 @@
// Outline Shader
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 5) in ivec4 a_BoneIndices;
layout(location = 6) in vec4 a_BoneWeights;
uniform mat4 u_ViewProjection;
uniform mat4 u_Transform;
const int MAX_BONES = 100;
uniform mat4 u_BoneTransforms[100];
void main()
{
mat4 boneTransform = u_BoneTransforms[a_BoneIndices[0]] * a_BoneWeights[0];
boneTransform += u_BoneTransforms[a_BoneIndices[1]] * a_BoneWeights[1];
boneTransform += u_BoneTransforms[a_BoneIndices[2]] * a_BoneWeights[2];
boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3];
vec4 localPosition = boneTransform * vec4(a_Position, 1.0);
gl_Position = u_ViewProjection * u_Transform * localPosition;
}
#type fragment
#version 430
layout(location = 0) out vec4 color;
void main()
{
color = vec4(1.0, 0.5, 0.0, 1.0);
}

View File

@ -1,8 +1,8 @@
// ----------------------------- // -----------------------------
// -- From Hazel Engine PBR shader -- // -- 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. // Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in.
// Currently heavily updated. // Currently heavily updated.
// //
// References upon which this is based: // 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) // - Unreal Engine 4 PBR notes (https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf)
@ -22,34 +22,50 @@ layout(location = 5) in ivec4 a_BoneIndices;
layout(location = 6) in vec4 a_BoneWeights; layout(location = 6) in vec4 a_BoneWeights;
uniform mat4 u_ViewProjectionMatrix; uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_Transform; uniform mat4 u_Transform;
uniform mat4 u_LightMatrixCascade0;
uniform mat4 u_LightMatrixCascade1;
uniform mat4 u_LightMatrixCascade2;
uniform mat4 u_LightMatrixCascade3;
const int MAX_BONES = 100; const int MAX_BONES = 100;
uniform mat4 u_BoneTransforms[100]; uniform mat4 u_BoneTransforms[100];
out VertexOutput out VertexOutput
{ {
vec3 WorldPosition; vec3 WorldPosition;
vec3 Normal; vec3 Normal;
vec2 TexCoord; vec2 TexCoord;
mat3 WorldNormals; mat3 WorldNormals;
mat3 WorldTransform;
vec3 Binormal; vec3 Binormal;
vec4 ShadowMapCoords[4];
vec3 ViewPosition;
} vs_Output; } vs_Output;
void main() void main()
{ {
mat4 boneTransform = u_BoneTransforms[a_BoneIndices[0]] * a_BoneWeights[0]; mat4 boneTransform = u_BoneTransforms[a_BoneIndices[0]] * a_BoneWeights[0];
boneTransform += u_BoneTransforms[a_BoneIndices[1]] * a_BoneWeights[1]; boneTransform += u_BoneTransforms[a_BoneIndices[1]] * a_BoneWeights[1];
boneTransform += u_BoneTransforms[a_BoneIndices[2]] * a_BoneWeights[2]; boneTransform += u_BoneTransforms[a_BoneIndices[2]] * a_BoneWeights[2];
boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3]; boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3];
vec4 localPosition = boneTransform * vec4(a_Position, 1.0); vec4 localPosition = boneTransform * vec4(a_Position, 1.0);
vs_Output.WorldPosition = vec3(u_Transform * boneTransform * vec4(a_Position, 1.0)); vs_Output.WorldPosition = vec3(u_Transform * boneTransform * vec4(a_Position, 1.0));
vs_Output.Normal = mat3(u_Transform) * mat3(boneTransform) * a_Normal; vs_Output.Normal = mat3(u_Transform) * mat3(boneTransform) * a_Normal;
vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y); 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.WorldNormals = mat3(u_Transform) * mat3(a_Tangent, a_Binormal, a_Normal);
vs_Output.Binormal = mat3(boneTransform) * a_Binormal; vs_Output.WorldTransform = mat3(u_Transform);
vs_Output.Binormal = a_Binormal;
vs_Output.ShadowMapCoords[0] = u_LightMatrixCascade0 * vec4(vs_Output.WorldPosition, 1.0);
vs_Output.ShadowMapCoords[1] = u_LightMatrixCascade1 * vec4(vs_Output.WorldPosition, 1.0);
vs_Output.ShadowMapCoords[2] = u_LightMatrixCascade2 * vec4(vs_Output.WorldPosition, 1.0);
vs_Output.ShadowMapCoords[3] = u_LightMatrixCascade3 * vec4(vs_Output.WorldPosition, 1.0);
vs_Output.ViewPosition = vec3(u_ViewMatrix * vec4(vs_Output.WorldPosition, 1.0));
gl_Position = u_ViewProjectionMatrix * u_Transform * localPosition; gl_Position = u_ViewProjectionMatrix * u_Transform * localPosition;
} }
@ -65,7 +81,8 @@ const int LightCount = 1;
// Constant normal incidence Fresnel factor for all dielectrics. // Constant normal incidence Fresnel factor for all dielectrics.
const vec3 Fdielectric = vec3(0.04); const vec3 Fdielectric = vec3(0.04);
struct Light { struct DirectionalLight
{
vec3 Direction; vec3 Direction;
vec3 Radiance; vec3 Radiance;
float Multiplier; float Multiplier;
@ -74,15 +91,19 @@ struct Light {
in VertexOutput in VertexOutput
{ {
vec3 WorldPosition; vec3 WorldPosition;
vec3 Normal; vec3 Normal;
vec2 TexCoord; vec2 TexCoord;
mat3 WorldNormals; mat3 WorldNormals;
mat3 WorldTransform;
vec3 Binormal; vec3 Binormal;
vec4 ShadowMapCoords[4];
vec3 ViewPosition;
} vs_Input; } vs_Input;
layout(location=0) out vec4 color; layout(location = 0) out vec4 color;
layout(location = 1) out vec4 o_BloomColor;
uniform Light lights; uniform DirectionalLight u_DirectionalLights;
uniform vec3 u_CameraPosition; uniform vec3 u_CameraPosition;
// PBR texture inputs // PBR texture inputs
@ -98,6 +119,25 @@ uniform samplerCube u_EnvIrradianceTex;
// BRDF LUT // BRDF LUT
uniform sampler2D u_BRDFLUTTexture; uniform sampler2D u_BRDFLUTTexture;
// PCSS
uniform sampler2D u_ShadowMapTexture[4];
uniform mat4 u_LightView;
uniform bool u_ShowCascades;
uniform bool u_SoftShadows;
uniform float u_LightSize;
uniform float u_MaxShadowDistance;
uniform float u_ShadowFade;
uniform bool u_CascadeFading;
uniform float u_CascadeTransitionFade;
uniform vec4 u_CascadeSplits;
uniform float u_IBLContribution;
uniform float u_BloomThreshold;
////////////////////////////////////////
uniform vec3 u_AlbedoColor; uniform vec3 u_AlbedoColor;
uniform float u_Metalness; uniform float u_Metalness;
uniform float u_Roughness; uniform float u_Roughness;
@ -105,7 +145,6 @@ uniform float u_Roughness;
uniform float u_EnvMapRotation; uniform float u_EnvMapRotation;
// Toggles // Toggles
uniform float u_RadiancePrefilter;
uniform float u_AlbedoTexToggle; uniform float u_AlbedoTexToggle;
uniform float u_NormalTexToggle; uniform float u_NormalTexToggle;
uniform float u_MetalnessTexToggle; uniform float u_MetalnessTexToggle;
@ -151,23 +190,23 @@ float gaSchlickGGX(float cosLi, float NdotV, float roughness)
float GeometrySchlickGGX(float NdotV, float roughness) float GeometrySchlickGGX(float NdotV, float roughness)
{ {
float r = (roughness + 1.0); float r = (roughness + 1.0);
float k = (r*r) / 8.0; float k = (r*r) / 8.0;
float nom = NdotV; float nom = NdotV;
float denom = NdotV * (1.0 - k) + k; float denom = NdotV * (1.0 - k) + k;
return nom / denom; return nom / denom;
} }
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{ {
float NdotV = max(dot(N, V), 0.0); float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0); float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness); float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness); float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2; return ggx1 * ggx2;
} }
// Shlick's approximation of the Fresnel factor. // Shlick's approximation of the Fresnel factor.
@ -178,26 +217,26 @@ vec3 fresnelSchlick(vec3 F0, float cosTheta)
vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness) vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness)
{ {
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); 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 // 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, // 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 // so turning this on online will cause poor performance
float RadicalInverse_VdC(uint bits) float RadicalInverse_VdC(uint bits)
{ {
bits = (bits << 16u) | (bits >> 16u); bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000 return float(bits) * 2.3283064365386963e-10; // / 0x100000000
} }
vec2 Hammersley(uint i, uint N) vec2 Hammersley(uint i, uint N)
{ {
return vec2(float(i)/float(N), RadicalInverse_VdC(i)); return vec2(float(i)/float(N), RadicalInverse_VdC(i));
} }
vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N)
@ -244,11 +283,11 @@ vec3 PrefilterEnvMap(float Roughness, vec3 R)
vec3 RotateVectorAboutY(float angle, vec3 vec) vec3 RotateVectorAboutY(float angle, vec3 vec)
{ {
angle = radians(angle); angle = radians(angle);
mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)), mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)),
vec3(0.0,1.0,0.0), vec3(0.0,1.0,0.0),
vec3(-sin(angle),0.0,cos(angle))}; vec3(-sin(angle),0.0,cos(angle))};
return rotationMatrix * vec; return rotationMatrix * vec;
} }
vec3 Lighting(vec3 F0) vec3 Lighting(vec3 F0)
@ -256,8 +295,8 @@ vec3 Lighting(vec3 F0)
vec3 result = vec3(0.0); vec3 result = vec3(0.0);
for(int i = 0; i < LightCount; i++) for(int i = 0; i < LightCount; i++)
{ {
vec3 Li = -lights.Direction; vec3 Li = u_DirectionalLights.Direction;
vec3 Lradiance = lights.Radiance * lights.Multiplier; vec3 Lradiance = u_DirectionalLights.Radiance * u_DirectionalLights.Multiplier;
vec3 Lh = normalize(Li + m_Params.View); vec3 Lh = normalize(Li + m_Params.View);
// Calculate angles between surface normal and various light vectors. // Calculate angles between surface normal and various light vectors.
@ -298,13 +337,188 @@ vec3 IBL(vec3 F0, vec3 Lr)
return kd * diffuseIBL + specularIBL; return kd * diffuseIBL + specularIBL;
} }
/////////////////////////////////////////////
// PCSS
/////////////////////////////////////////////
uint CascadeIndex = 0;
float ShadowFade = 1.0;
float GetShadowBias()
{
const float MINIMUM_SHADOW_BIAS = 0.002;
float bias = max(MINIMUM_SHADOW_BIAS * (1.0 - dot(m_Params.Normal, u_DirectionalLights.Direction)), MINIMUM_SHADOW_BIAS);
return bias;
}
float HardShadows_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords)
{
float bias = GetShadowBias();
float z = texture(shadowMap, shadowCoords.xy).x;
return 1.0 - step(z + bias, shadowCoords.z) * ShadowFade;
}
// Penumbra
// this search area estimation comes from the following article:
// http://developer.download.nvidia.com/whitepapers/2008/PCSS_DirectionalLight_Integration.pdf
float SearchWidth(float uvLightSize, float receiverDistance)
{
const float NEAR = 0.1;
return uvLightSize * (receiverDistance - NEAR) / u_CameraPosition.z;
}
float u_light_zNear = 0.0; // 0.01 gives artifacts? maybe because of ortho proj?
float u_light_zFar = 10000.0;
vec2 u_lightRadiusUV = vec2(0.05);
vec2 searchRegionRadiusUV(float zWorld)
{
return u_lightRadiusUV * (zWorld - u_light_zNear) / zWorld;
}
const vec2 PoissonDistribution[64] = vec2[](
vec2(-0.884081, 0.124488),
vec2(-0.714377, 0.027940),
vec2(-0.747945, 0.227922),
vec2(-0.939609, 0.243634),
vec2(-0.985465, 0.045534),
vec2(-0.861367, -0.136222),
vec2(-0.881934, 0.396908),
vec2(-0.466938, 0.014526),
vec2(-0.558207, 0.212662),
vec2(-0.578447, -0.095822),
vec2(-0.740266, -0.095631),
vec2(-0.751681, 0.472604),
vec2(-0.553147, -0.243177),
vec2(-0.674762, -0.330730),
vec2(-0.402765, -0.122087),
vec2(-0.319776, -0.312166),
vec2(-0.413923, -0.439757),
vec2(-0.979153, -0.201245),
vec2(-0.865579, -0.288695),
vec2(-0.243704, -0.186378),
vec2(-0.294920, -0.055748),
vec2(-0.604452, -0.544251),
vec2(-0.418056, -0.587679),
vec2(-0.549156, -0.415877),
vec2(-0.238080, -0.611761),
vec2(-0.267004, -0.459702),
vec2(-0.100006, -0.229116),
vec2(-0.101928, -0.380382),
vec2(-0.681467, -0.700773),
vec2(-0.763488, -0.543386),
vec2(-0.549030, -0.750749),
vec2(-0.809045, -0.408738),
vec2(-0.388134, -0.773448),
vec2(-0.429392, -0.894892),
vec2(-0.131597, 0.065058),
vec2(-0.275002, 0.102922),
vec2(-0.106117, -0.068327),
vec2(-0.294586, -0.891515),
vec2(-0.629418, 0.379387),
vec2(-0.407257, 0.339748),
vec2(0.071650, -0.384284),
vec2(0.022018, -0.263793),
vec2(0.003879, -0.136073),
vec2(-0.137533, -0.767844),
vec2(-0.050874, -0.906068),
vec2(0.114133, -0.070053),
vec2(0.163314, -0.217231),
vec2(-0.100262, -0.587992),
vec2(-0.004942, 0.125368),
vec2(0.035302, -0.619310),
vec2(0.195646, -0.459022),
vec2(0.303969, -0.346362),
vec2(-0.678118, 0.685099),
vec2(-0.628418, 0.507978),
vec2(-0.508473, 0.458753),
vec2(0.032134, -0.782030),
vec2(0.122595, 0.280353),
vec2(-0.043643, 0.312119),
vec2(0.132993, 0.085170),
vec2(-0.192106, 0.285848),
vec2(0.183621, -0.713242),
vec2(0.265220, -0.596716),
vec2(-0.009628, -0.483058),
vec2(-0.018516, 0.435703)
);
vec2 SamplePoisson(int index)
{
return PoissonDistribution[index % 64];
}
float FindBlockerDistance_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize)
{
float bias = GetShadowBias();
int numBlockerSearchSamples = 64;
int blockers = 0;
float avgBlockerDistance = 0;
float zEye = -(u_LightView * vec4(vs_Input.WorldPosition, 1.0)).z;
vec2 searchWidth = searchRegionRadiusUV(zEye);
for (int i = 0; i < numBlockerSearchSamples; i++)
{
float z = texture(shadowMap, shadowCoords.xy + SamplePoisson(i) * searchWidth).r;
if (z < (shadowCoords.z - bias))
{
blockers++;
avgBlockerDistance += z;
}
}
if (blockers > 0)
return avgBlockerDistance / float(blockers);
return -1;
}
float PenumbraWidth(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize)
{
float blockerDistance = FindBlockerDistance_DirectionalLight(shadowMap, shadowCoords, uvLightSize);
if (blockerDistance == -1)
return -1;
return (shadowCoords.z - blockerDistance) / blockerDistance;
}
float PCF_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvRadius)
{
float bias = GetShadowBias();
int numPCFSamples = 64;
float sum = 0;
for (int i = 0; i < numPCFSamples; i++)
{
float z = texture(shadowMap, shadowCoords.xy + SamplePoisson(i) * uvRadius).r;
sum += (z < (shadowCoords.z - bias)) ? 1 : 0;
}
return sum / numPCFSamples;
}
float PCSS_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize)
{
float blockerDistance = FindBlockerDistance_DirectionalLight(shadowMap, shadowCoords, uvLightSize);
if (blockerDistance == -1)
return 1;
float penumbraWidth = (shadowCoords.z - blockerDistance) / blockerDistance;
float NEAR = 0.01; // Should this value be tweakable?
float uvRadius = penumbraWidth * uvLightSize * NEAR / shadowCoords.z;
return 1.0 - PCF_DirectionalLight(shadowMap, shadowCoords, uvRadius) * ShadowFade;
}
/////////////////////////////////////////////
void main() void main()
{ {
// Standard PBR inputs // Standard PBR inputs
m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor; 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.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 = 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 m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight
// Normals (either from vertex or map) // Normals (either from vertex or map)
m_Params.Normal = normalize(vs_Input.Normal); m_Params.Normal = normalize(vs_Input.Normal);
@ -316,15 +530,109 @@ void main()
m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition); m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition);
m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0); m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0);
// Specular reflection vector // Specular reflection vector
vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View; vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View;
// Fresnel reflectance, metals use albedo // Fresnel reflectance, metals use albedo
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness); vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
vec3 lightContribution = Lighting(F0); const uint SHADOW_MAP_CASCADE_COUNT = 4;
vec3 iblContribution = IBL(F0, Lr); for(uint i = 0; i < SHADOW_MAP_CASCADE_COUNT - 1; i++)
{
if(vs_Input.ViewPosition.z < u_CascadeSplits[i])
CascadeIndex = i + 1;
}
float shadowDistance = u_MaxShadowDistance;//u_CascadeSplits[3];
float transitionDistance = u_ShadowFade;
float distance = length(vs_Input.ViewPosition);
ShadowFade = distance - (shadowDistance - transitionDistance);
ShadowFade /= transitionDistance;
ShadowFade = clamp(1.0 - ShadowFade, 0.0, 1.0);
bool fadeCascades = u_CascadeFading;
float shadowAmount = 1.0;
if (fadeCascades)
{
float cascadeTransitionFade = u_CascadeTransitionFade;
float c0 = smoothstep(u_CascadeSplits[0] + cascadeTransitionFade * 0.5f, u_CascadeSplits[0] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z);
float c1 = smoothstep(u_CascadeSplits[1] + cascadeTransitionFade * 0.5f, u_CascadeSplits[1] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z);
float c2 = smoothstep(u_CascadeSplits[2] + cascadeTransitionFade * 0.5f, u_CascadeSplits[2] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z);
if (c0 > 0.0 && c0 < 1.0)
{
// Sample 0 & 1
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[0].xyz / vs_Input.ShadowMapCoords[0].w);
float shadowAmount0 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[0], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[0], shadowMapCoords);
shadowMapCoords = (vs_Input.ShadowMapCoords[1].xyz / vs_Input.ShadowMapCoords[1].w);
float shadowAmount1 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords);
shadowAmount = mix(shadowAmount0, shadowAmount1, c0);
}
else if (c1 > 0.0 && c1 < 1.0)
{
// Sample 1 & 2
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[1].xyz / vs_Input.ShadowMapCoords[1].w);
float shadowAmount1 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords);
shadowMapCoords = (vs_Input.ShadowMapCoords[2].xyz / vs_Input.ShadowMapCoords[2].w);
float shadowAmount2 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords);
shadowAmount = mix(shadowAmount1, shadowAmount2, c1);
}
else if (c2 > 0.0 && c2 < 1.0)
{
// Sample 2 & 3
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[2].xyz / vs_Input.ShadowMapCoords[2].w);
float shadowAmount2 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords);
shadowMapCoords = (vs_Input.ShadowMapCoords[3].xyz / vs_Input.ShadowMapCoords[3].w);
float shadowAmount3 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[3], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[3], shadowMapCoords);
shadowAmount = mix(shadowAmount2, shadowAmount3, c2);
}
else
{
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[CascadeIndex].xyz / vs_Input.ShadowMapCoords[CascadeIndex].w);
shadowAmount = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords);
}
}
else
{
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[CascadeIndex].xyz / vs_Input.ShadowMapCoords[CascadeIndex].w);
shadowAmount = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords);
}
float NdotL = dot(m_Params.Normal, u_DirectionalLights.Direction);
NdotL = smoothstep(0.0, 0.4, NdotL + 0.2);
shadowAmount *= (NdotL * 1.0);
vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution;
vec3 lightContribution = u_DirectionalLights.Multiplier > 0.0f ? (Lighting(F0) * shadowAmount) : vec3(0.0f);
color = vec4(lightContribution + iblContribution, 1.0); color = vec4(lightContribution + iblContribution, 1.0);
// Bloom
float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
o_BloomColor = vec4(0.0, 0.0, 0.0, 1.0);
if (brightness > u_BloomThreshold)
o_BloomColor = color;
if (u_ShowCascades)
{
switch(CascadeIndex)
{
case 0:
color.rgb *= vec3(1.0f, 0.25f, 0.25f);
break;
case 1:
color.rgb *= vec3(0.25f, 1.0f, 0.25f);
break;
case 2:
color.rgb *= vec3(0.25f, 0.25f, 1.0f);
break;
case 3:
color.rgb *= vec3(1.0f, 1.0f, 0.25f);
break;
}
}
} }

View File

@ -1,8 +1,8 @@
// ----------------------------- // -----------------------------
// -- From Hazel Engine PBR shader -- // -- 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. // Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in.
// Currently heavily updated. // Currently heavily updated.
// //
// References upon which this is based: // 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) // - Unreal Engine 4 PBR notes (https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf)
@ -19,27 +19,41 @@ layout(location = 3) in vec3 a_Binormal;
layout(location = 4) in vec2 a_TexCoord; layout(location = 4) in vec2 a_TexCoord;
uniform mat4 u_ViewProjectionMatrix; uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_Transform; uniform mat4 u_Transform;
uniform mat4 u_LightMatrixCascade0;
uniform mat4 u_LightMatrixCascade1;
uniform mat4 u_LightMatrixCascade2;
uniform mat4 u_LightMatrixCascade3;
out VertexOutput out VertexOutput
{ {
vec3 WorldPosition; vec3 WorldPosition;
vec3 Normal; vec3 Normal;
vec2 TexCoord; vec2 TexCoord;
mat3 WorldNormals; mat3 WorldNormals;
mat3 WorldTransform; mat3 WorldTransform;
vec3 Binormal; vec3 Binormal;
vec4 ShadowMapCoords[4];
vec3 ViewPosition;
} vs_Output; } vs_Output;
void main() void main()
{ {
vs_Output.WorldPosition = vec3(u_Transform * vec4(a_Position, 1.0)); vs_Output.WorldPosition = vec3(u_Transform * vec4(a_Position, 1.0));
vs_Output.Normal = mat3(u_Transform) * a_Normal; vs_Output.Normal = mat3(u_Transform) * a_Normal;
vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y); 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.WorldNormals = mat3(u_Transform) * mat3(a_Tangent, a_Binormal, a_Normal);
vs_Output.WorldTransform = mat3(u_Transform); vs_Output.WorldTransform = mat3(u_Transform);
vs_Output.Binormal = a_Binormal; vs_Output.Binormal = a_Binormal;
vs_Output.ShadowMapCoords[0] = u_LightMatrixCascade0 * vec4(vs_Output.WorldPosition, 1.0);
vs_Output.ShadowMapCoords[1] = u_LightMatrixCascade1 * vec4(vs_Output.WorldPosition, 1.0);
vs_Output.ShadowMapCoords[2] = u_LightMatrixCascade2 * vec4(vs_Output.WorldPosition, 1.0);
vs_Output.ShadowMapCoords[3] = u_LightMatrixCascade3 * vec4(vs_Output.WorldPosition, 1.0);
vs_Output.ViewPosition = vec3(u_ViewMatrix * vec4(vs_Output.WorldPosition, 1.0));
gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0); gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0);
} }
@ -54,7 +68,8 @@ const int LightCount = 1;
// Constant normal incidence Fresnel factor for all dielectrics. // Constant normal incidence Fresnel factor for all dielectrics.
const vec3 Fdielectric = vec3(0.04); const vec3 Fdielectric = vec3(0.04);
struct Light { struct DirectionalLight
{
vec3 Direction; vec3 Direction;
vec3 Radiance; vec3 Radiance;
float Multiplier; float Multiplier;
@ -63,16 +78,19 @@ struct Light {
in VertexOutput in VertexOutput
{ {
vec3 WorldPosition; vec3 WorldPosition;
vec3 Normal; vec3 Normal;
vec2 TexCoord; vec2 TexCoord;
mat3 WorldNormals; mat3 WorldNormals;
mat3 WorldTransform; mat3 WorldTransform;
vec3 Binormal; vec3 Binormal;
vec4 ShadowMapCoords[4];
vec3 ViewPosition;
} vs_Input; } vs_Input;
layout(location = 0) out vec4 color; layout(location = 0) out vec4 color;
layout(location = 1) out vec4 o_BloomColor;
uniform Light lights; uniform DirectionalLight u_DirectionalLights;
uniform vec3 u_CameraPosition; uniform vec3 u_CameraPosition;
// PBR texture inputs // PBR texture inputs
@ -88,6 +106,25 @@ uniform samplerCube u_EnvIrradianceTex;
// BRDF LUT // BRDF LUT
uniform sampler2D u_BRDFLUTTexture; uniform sampler2D u_BRDFLUTTexture;
// PCSS
uniform sampler2D u_ShadowMapTexture[4];
uniform mat4 u_LightView;
uniform bool u_ShowCascades;
uniform bool u_SoftShadows;
uniform float u_LightSize;
uniform float u_MaxShadowDistance;
uniform float u_ShadowFade;
uniform bool u_CascadeFading;
uniform float u_CascadeTransitionFade;
uniform vec4 u_CascadeSplits;
uniform float u_IBLContribution;
uniform float u_BloomThreshold;
////////////////////////////////////////
uniform vec3 u_AlbedoColor; uniform vec3 u_AlbedoColor;
uniform float u_Metalness; uniform float u_Metalness;
uniform float u_Roughness; uniform float u_Roughness;
@ -95,7 +132,6 @@ uniform float u_Roughness;
uniform float u_EnvMapRotation; uniform float u_EnvMapRotation;
// Toggles // Toggles
uniform float u_RadiancePrefilter;
uniform float u_AlbedoTexToggle; uniform float u_AlbedoTexToggle;
uniform float u_NormalTexToggle; uniform float u_NormalTexToggle;
uniform float u_MetalnessTexToggle; uniform float u_MetalnessTexToggle;
@ -141,23 +177,23 @@ float gaSchlickGGX(float cosLi, float NdotV, float roughness)
float GeometrySchlickGGX(float NdotV, float roughness) float GeometrySchlickGGX(float NdotV, float roughness)
{ {
float r = (roughness + 1.0); float r = (roughness + 1.0);
float k = (r*r) / 8.0; float k = (r*r) / 8.0;
float nom = NdotV; float nom = NdotV;
float denom = NdotV * (1.0 - k) + k; float denom = NdotV * (1.0 - k) + k;
return nom / denom; return nom / denom;
} }
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{ {
float NdotV = max(dot(N, V), 0.0); float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0); float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness); float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness); float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2; return ggx1 * ggx2;
} }
// Shlick's approximation of the Fresnel factor. // Shlick's approximation of the Fresnel factor.
@ -168,26 +204,26 @@ vec3 fresnelSchlick(vec3 F0, float cosTheta)
vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness) vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness)
{ {
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); 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 // 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, // 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 // so turning this on online will cause poor performance
float RadicalInverse_VdC(uint bits) float RadicalInverse_VdC(uint bits)
{ {
bits = (bits << 16u) | (bits >> 16u); bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000 return float(bits) * 2.3283064365386963e-10; // / 0x100000000
} }
vec2 Hammersley(uint i, uint N) vec2 Hammersley(uint i, uint N)
{ {
return vec2(float(i)/float(N), RadicalInverse_VdC(i)); return vec2(float(i)/float(N), RadicalInverse_VdC(i));
} }
vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N)
@ -234,11 +270,11 @@ vec3 PrefilterEnvMap(float Roughness, vec3 R)
vec3 RotateVectorAboutY(float angle, vec3 vec) vec3 RotateVectorAboutY(float angle, vec3 vec)
{ {
angle = radians(angle); angle = radians(angle);
mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)), mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)),
vec3(0.0,1.0,0.0), vec3(0.0,1.0,0.0),
vec3(-sin(angle),0.0,cos(angle))}; vec3(-sin(angle),0.0,cos(angle))};
return rotationMatrix * vec; return rotationMatrix * vec;
} }
vec3 Lighting(vec3 F0) vec3 Lighting(vec3 F0)
@ -246,8 +282,8 @@ vec3 Lighting(vec3 F0)
vec3 result = vec3(0.0); vec3 result = vec3(0.0);
for(int i = 0; i < LightCount; i++) for(int i = 0; i < LightCount; i++)
{ {
vec3 Li = -lights.Direction; vec3 Li = u_DirectionalLights.Direction;
vec3 Lradiance = lights.Radiance * lights.Multiplier; vec3 Lradiance = u_DirectionalLights.Radiance * u_DirectionalLights.Multiplier;
vec3 Lh = normalize(Li + m_Params.View); vec3 Lh = normalize(Li + m_Params.View);
// Calculate angles between surface normal and various light vectors. // Calculate angles between surface normal and various light vectors.
@ -273,7 +309,6 @@ vec3 IBL(vec3 F0, vec3 Lr)
{ {
vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb; vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb;
vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness); vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness);
// vec3 F = fresnelSchlickR(F0, m_Params.NdotV);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness); vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseIBL = m_Params.Albedo * irradiance; vec3 diffuseIBL = m_Params.Albedo * irradiance;
@ -289,13 +324,188 @@ vec3 IBL(vec3 F0, vec3 Lr)
return kd * diffuseIBL + specularIBL; return kd * diffuseIBL + specularIBL;
} }
/////////////////////////////////////////////
// PCSS
/////////////////////////////////////////////
uint CascadeIndex = 0;
float ShadowFade = 1.0;
float GetShadowBias()
{
const float MINIMUM_SHADOW_BIAS = 0.002;
float bias = max(MINIMUM_SHADOW_BIAS * (1.0 - dot(m_Params.Normal, u_DirectionalLights.Direction)), MINIMUM_SHADOW_BIAS);
return bias;
}
float HardShadows_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords)
{
float bias = GetShadowBias();
float z = texture(shadowMap, shadowCoords.xy).x;
return 1.0 - step(z + bias, shadowCoords.z) * ShadowFade;
}
// Penumbra
// this search area estimation comes from the following article:
// http://developer.download.nvidia.com/whitepapers/2008/PCSS_DirectionalLight_Integration.pdf
float SearchWidth(float uvLightSize, float receiverDistance)
{
const float NEAR = 0.1;
return uvLightSize * (receiverDistance - NEAR) / u_CameraPosition.z;
}
float u_light_zNear = 0.0; // 0.01 gives artifacts? maybe because of ortho proj?
float u_light_zFar = 10000.0;
vec2 u_lightRadiusUV = vec2(0.05);
vec2 searchRegionRadiusUV(float zWorld)
{
return u_lightRadiusUV * (zWorld - u_light_zNear) / zWorld;
}
const vec2 PoissonDistribution[64] = vec2[](
vec2(-0.884081, 0.124488),
vec2(-0.714377, 0.027940),
vec2(-0.747945, 0.227922),
vec2(-0.939609, 0.243634),
vec2(-0.985465, 0.045534),
vec2(-0.861367, -0.136222),
vec2(-0.881934, 0.396908),
vec2(-0.466938, 0.014526),
vec2(-0.558207, 0.212662),
vec2(-0.578447, -0.095822),
vec2(-0.740266, -0.095631),
vec2(-0.751681, 0.472604),
vec2(-0.553147, -0.243177),
vec2(-0.674762, -0.330730),
vec2(-0.402765, -0.122087),
vec2(-0.319776, -0.312166),
vec2(-0.413923, -0.439757),
vec2(-0.979153, -0.201245),
vec2(-0.865579, -0.288695),
vec2(-0.243704, -0.186378),
vec2(-0.294920, -0.055748),
vec2(-0.604452, -0.544251),
vec2(-0.418056, -0.587679),
vec2(-0.549156, -0.415877),
vec2(-0.238080, -0.611761),
vec2(-0.267004, -0.459702),
vec2(-0.100006, -0.229116),
vec2(-0.101928, -0.380382),
vec2(-0.681467, -0.700773),
vec2(-0.763488, -0.543386),
vec2(-0.549030, -0.750749),
vec2(-0.809045, -0.408738),
vec2(-0.388134, -0.773448),
vec2(-0.429392, -0.894892),
vec2(-0.131597, 0.065058),
vec2(-0.275002, 0.102922),
vec2(-0.106117, -0.068327),
vec2(-0.294586, -0.891515),
vec2(-0.629418, 0.379387),
vec2(-0.407257, 0.339748),
vec2(0.071650, -0.384284),
vec2(0.022018, -0.263793),
vec2(0.003879, -0.136073),
vec2(-0.137533, -0.767844),
vec2(-0.050874, -0.906068),
vec2(0.114133, -0.070053),
vec2(0.163314, -0.217231),
vec2(-0.100262, -0.587992),
vec2(-0.004942, 0.125368),
vec2(0.035302, -0.619310),
vec2(0.195646, -0.459022),
vec2(0.303969, -0.346362),
vec2(-0.678118, 0.685099),
vec2(-0.628418, 0.507978),
vec2(-0.508473, 0.458753),
vec2(0.032134, -0.782030),
vec2(0.122595, 0.280353),
vec2(-0.043643, 0.312119),
vec2(0.132993, 0.085170),
vec2(-0.192106, 0.285848),
vec2(0.183621, -0.713242),
vec2(0.265220, -0.596716),
vec2(-0.009628, -0.483058),
vec2(-0.018516, 0.435703)
);
vec2 SamplePoisson(int index)
{
return PoissonDistribution[index % 64];
}
float FindBlockerDistance_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize)
{
float bias = GetShadowBias();
int numBlockerSearchSamples = 64;
int blockers = 0;
float avgBlockerDistance = 0;
float zEye = -(u_LightView * vec4(vs_Input.WorldPosition, 1.0)).z;
vec2 searchWidth = searchRegionRadiusUV(zEye);
for (int i = 0; i < numBlockerSearchSamples; i++)
{
float z = texture(shadowMap, shadowCoords.xy + SamplePoisson(i) * searchWidth).r;
if (z < (shadowCoords.z - bias))
{
blockers++;
avgBlockerDistance += z;
}
}
if (blockers > 0)
return avgBlockerDistance / float(blockers);
return -1;
}
float PenumbraWidth(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize)
{
float blockerDistance = FindBlockerDistance_DirectionalLight(shadowMap, shadowCoords, uvLightSize);
if (blockerDistance == -1)
return -1;
return (shadowCoords.z - blockerDistance) / blockerDistance;
}
float PCF_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvRadius)
{
float bias = GetShadowBias();
int numPCFSamples = 64;
float sum = 0;
for (int i = 0; i < numPCFSamples; i++)
{
float z = texture(shadowMap, shadowCoords.xy + SamplePoisson(i) * uvRadius).r;
sum += (z < (shadowCoords.z - bias)) ? 1 : 0;
}
return sum / numPCFSamples;
}
float PCSS_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize)
{
float blockerDistance = FindBlockerDistance_DirectionalLight(shadowMap, shadowCoords, uvLightSize);
if (blockerDistance == -1)
return 1;
float penumbraWidth = (shadowCoords.z - blockerDistance) / blockerDistance;
float NEAR = 0.01; // Should this value be tweakable?
float uvRadius = penumbraWidth * uvLightSize * NEAR / shadowCoords.z;
return 1.0 - PCF_DirectionalLight(shadowMap, shadowCoords, uvRadius) * ShadowFade;
}
/////////////////////////////////////////////
void main() void main()
{ {
// Standard PBR inputs // Standard PBR inputs
m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor; 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.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 = 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 m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight
// Normals (either from vertex or map) // Normals (either from vertex or map)
m_Params.Normal = normalize(vs_Input.Normal); m_Params.Normal = normalize(vs_Input.Normal);
@ -307,16 +517,109 @@ void main()
m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition); m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition);
m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0); m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0);
// Specular reflection vector // Specular reflection vector
vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View; vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View;
// Fresnel reflectance, metals use albedo // Fresnel reflectance, metals use albedo
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness); vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
vec3 lightContribution = Lighting(F0); const uint SHADOW_MAP_CASCADE_COUNT = 4;
vec3 iblContribution = IBL(F0, Lr); for(uint i = 0; i < SHADOW_MAP_CASCADE_COUNT - 1; i++)
{
if(vs_Input.ViewPosition.z < u_CascadeSplits[i])
CascadeIndex = i + 1;
}
float shadowDistance = u_MaxShadowDistance;//u_CascadeSplits[3];
float transitionDistance = u_ShadowFade;
float distance = length(vs_Input.ViewPosition);
ShadowFade = distance - (shadowDistance - transitionDistance);
ShadowFade /= transitionDistance;
ShadowFade = clamp(1.0 - ShadowFade, 0.0, 1.0);
bool fadeCascades = u_CascadeFading;
float shadowAmount = 1.0;
if (fadeCascades)
{
float cascadeTransitionFade = u_CascadeTransitionFade;
float c0 = smoothstep(u_CascadeSplits[0] + cascadeTransitionFade * 0.5f, u_CascadeSplits[0] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z);
float c1 = smoothstep(u_CascadeSplits[1] + cascadeTransitionFade * 0.5f, u_CascadeSplits[1] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z);
float c2 = smoothstep(u_CascadeSplits[2] + cascadeTransitionFade * 0.5f, u_CascadeSplits[2] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z);
if (c0 > 0.0 && c0 < 1.0)
{
// Sample 0 & 1
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[0].xyz / vs_Input.ShadowMapCoords[0].w);
float shadowAmount0 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[0], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[0], shadowMapCoords);
shadowMapCoords = (vs_Input.ShadowMapCoords[1].xyz / vs_Input.ShadowMapCoords[1].w);
float shadowAmount1 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords);
shadowAmount = mix(shadowAmount0, shadowAmount1, c0);
}
else if (c1 > 0.0 && c1 < 1.0)
{
// Sample 1 & 2
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[1].xyz / vs_Input.ShadowMapCoords[1].w);
float shadowAmount1 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords);
shadowMapCoords = (vs_Input.ShadowMapCoords[2].xyz / vs_Input.ShadowMapCoords[2].w);
float shadowAmount2 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords);
shadowAmount = mix(shadowAmount1, shadowAmount2, c1);
}
else if (c2 > 0.0 && c2 < 1.0)
{
// Sample 2 & 3
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[2].xyz / vs_Input.ShadowMapCoords[2].w);
float shadowAmount2 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords);
shadowMapCoords = (vs_Input.ShadowMapCoords[3].xyz / vs_Input.ShadowMapCoords[3].w);
float shadowAmount3 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[3], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[3], shadowMapCoords);
shadowAmount = mix(shadowAmount2, shadowAmount3, c2);
}
else
{
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[CascadeIndex].xyz / vs_Input.ShadowMapCoords[CascadeIndex].w);
shadowAmount = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords);
}
}
else
{
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[CascadeIndex].xyz / vs_Input.ShadowMapCoords[CascadeIndex].w);
shadowAmount = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords);
}
float NdotL = dot(m_Params.Normal, u_DirectionalLights.Direction);
NdotL = smoothstep(0.0, 0.4, NdotL + 0.2);
shadowAmount *= (NdotL * 1.0);
vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution;
vec3 lightContribution = u_DirectionalLights.Multiplier > 0.0f ? (Lighting(F0) * shadowAmount) : vec3(0.0f);
color = vec4(lightContribution + iblContribution, 1.0); color = vec4(lightContribution + iblContribution, 1.0);
// color = vec4(iblContribution, 1.0);
// Bloom
float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
o_BloomColor = vec4(0.0, 0.0, 0.0, 1.0);
if (brightness > u_BloomThreshold)
o_BloomColor = color;
if (u_ShowCascades)
{
switch(CascadeIndex)
{
case 0:
color.rgb *= vec3(1.0f, 0.25f, 0.25f);
break;
case 1:
color.rgb *= vec3(0.25f, 1.0f, 0.25f);
break;
case 2:
color.rgb *= vec3(0.25f, 0.25f, 1.0f);
break;
case 3:
color.rgb *= vec3(1.0f, 1.0f, 0.25f);
break;
}
}
} }

View File

@ -17,20 +17,34 @@ void main()
#version 430 #version 430
layout(location = 0) out vec4 o_Color; layout(location = 0) out vec4 o_Color;
layout(location = 1) out vec4 o_BloomTexture;
in vec2 v_TexCoord; in vec2 v_TexCoord;
uniform sampler2DMS u_Texture; uniform sampler2DMS u_Texture;
uniform float u_Exposure; uniform float u_Exposure;
uniform int u_TextureSamples; uniform int u_TextureSamples;
vec4 MultiSampleTexture(sampler2DMS tex, ivec2 texCoord, int samples) uniform bool u_EnableBloom;
uniform float u_BloomThreshold;
const float uFar = 1.0;
vec4 SampleTexture(sampler2D tex, vec2 texCoord)
{ {
return texture(tex, texCoord);
}
vec4 MultiSampleTexture(sampler2DMS tex, vec2 tc)
{
ivec2 texSize = textureSize(tex);
ivec2 texCoord = ivec2(tc * texSize);
vec4 result = vec4(0.0); vec4 result = vec4(0.0);
for (int i = 0; i < samples; i++) for (int i = 0; i < u_TextureSamples; i++)
result += texelFetch(tex, texCoord, i); result += texelFetch(tex, texCoord, i);
result /= float(samples); result /= float(u_TextureSamples);
return result; return result;
} }
@ -39,10 +53,18 @@ void main()
const float gamma = 2.2; const float gamma = 2.2;
const float pureWhite = 1.0; const float pureWhite = 1.0;
ivec2 texSize = textureSize(u_Texture); // Tonemapping
ivec2 texCoord = ivec2(v_TexCoord * texSize); vec4 msColor = MultiSampleTexture(u_Texture, v_TexCoord);
vec4 msColor = MultiSampleTexture(u_Texture, texCoord, u_TextureSamples);
vec3 color = msColor.rgb * u_Exposure;//texture(u_Texture, v_TexCoord).rgb * u_Exposure; vec3 color = msColor.rgb;
if (u_EnableBloom)
{
vec3 bloomColor = MultiSampleTexture(u_Texture, v_TexCoord).rgb;
color += bloomColor;
}
color *= u_Exposure;
// Reinhard tonemapping operator. // Reinhard tonemapping operator.
// see: "Photographic Tone Reproduction for Digital Images", eq. 4 // see: "Photographic Tone Reproduction for Digital Images", eq. 4
@ -54,4 +76,8 @@ void main()
// Gamma correction. // Gamma correction.
o_Color = vec4(pow(mappedColor, vec3(1.0 / gamma)), 1.0); o_Color = vec4(pow(mappedColor, vec3(1.0 / gamma)), 1.0);
}
// Show over-exposed areas
// if (o_Color.r > 1.0 || o_Color.g > 1.0 || o_Color.b > 1.0)
// o_Color.rgb *= vec3(1.0, 0.25, 0.25);
}

View File

@ -0,0 +1,23 @@
// Shadow Map shader
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
uniform mat4 u_ViewProjection;
uniform mat4 u_Transform;
void main()
{
gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
}
#type fragment
#version 430
layout(location = 0) out vec4 o_Color;
void main()
{
}

View File

@ -0,0 +1,35 @@
// Shadow Map shader
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 5) in ivec4 a_BoneIndices;
layout(location = 6) in vec4 a_BoneWeights;
uniform mat4 u_ViewProjection;
uniform mat4 u_Transform;
const int MAX_BONES = 100;
uniform mat4 u_BoneTransforms[100];
void main()
{
mat4 boneTransform = u_BoneTransforms[a_BoneIndices[0]] * a_BoneWeights[0];
boneTransform += u_BoneTransforms[a_BoneIndices[1]] * a_BoneWeights[1];
boneTransform += u_BoneTransforms[a_BoneIndices[2]] * a_BoneWeights[2];
boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3];
vec4 localPosition = boneTransform * vec4(a_Position, 1.0);
gl_Position = u_ViewProjection * u_Transform * localPosition;
}
#type fragment
#version 430
layout(location = 0) out vec4 o_Color;
void main()
{
}

View File

@ -24,10 +24,13 @@ layout(location = 0) out vec4 finalColor;
uniform samplerCube u_Texture; uniform samplerCube u_Texture;
uniform float u_TextureLod; uniform float u_TextureLod;
uniform float u_SkyIntensity;
in vec3 v_Position; in vec3 v_Position;
void main() void main()
{ {
finalColor = textureLod(u_Texture, v_Position, u_TextureLod); vec3 color = textureLod(u_Texture, v_Position, u_TextureLod).rgb * u_SkyIntensity;
finalColor = vec4(color, 1.0);
} }

View File

@ -121,7 +121,7 @@ namespace FPSExample
private void UpdateCameraTransform(){ private void UpdateCameraTransform(){
Vec3 position = m_Transform.Position; Vec3 position = m_Transform.Position;
position.Y += 1.5f; position.Y += m_Transform.Position.Y + 1.5f;
m_CameraTransform.Position = position; m_CameraTransform.Position = position;
} }
} }

View File

@ -31,6 +31,8 @@ namespace Prism
m_Window = std::unique_ptr<Window>(Window::Create(WindowProps{props.Name, props.Width, props.Height})); m_Window = std::unique_ptr<Window>(Window::Create(WindowProps{props.Name, props.Width, props.Height}));
m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent)); m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent));
m_Window->SetVSync(true);
m_Window->Maximize();
m_ImGuiLayer = new ImGuiLayer("ImGui Layer"); m_ImGuiLayer = new ImGuiLayer("ImGui Layer");
PushOverlay(m_ImGuiLayer); PushOverlay(m_ImGuiLayer);

View File

@ -0,0 +1,273 @@
//
// Created by sfd on 26-1-1.
//
#ifndef IMGUI_H
#define IMGUI_H
#include <cstdint>
#include <imgui.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
namespace Prism::UI {
static int s_UIContextID = 0;
static uint32_t s_Counter = 0;
static char s_IDBuffer[16];
static void PushID()
{
ImGui::PushID(s_UIContextID++);
s_Counter = 0;
}
static void PopID()
{
ImGui::PopID();
s_UIContextID--;
}
static void BeginPropertyGrid()
{
PushID();
ImGui::Columns(2);
}
static bool Property(const char* label, std::string& value, const bool error = false)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
char buffer[256];
snprintf(buffer, sizeof(buffer), "%s", value.c_str());
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (error)
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.9f, 0.2f, 0.2f, 1.0f));
if (ImGui::InputText(s_IDBuffer, buffer, 256))
{
value = buffer;
modified = true;
}
if (error)
ImGui::PopStyleColor();
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static void Property(const char* label, const char* value)
{
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
ImGui::InputText(s_IDBuffer, const_cast<char*>(value), 256, ImGuiInputTextFlags_ReadOnly);
ImGui::PopItemWidth();
ImGui::NextColumn();
}
static bool Property(const char* label, bool& value)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::Checkbox(s_IDBuffer, &value))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool Property(const char* label, int& value)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::DragInt(s_IDBuffer, &value))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool PropertySlider(const char* label, int& value, const int min, const int max)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::SliderInt(s_IDBuffer, &value, min, max))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool Property(const char* label, float& value, const float delta = 0.1f, const float min = 0.0f, const float max = 0.0f)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::DragFloat(s_IDBuffer, &value, delta, min, max))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool Property(const char* label, glm::vec2& value, const float delta = 0.1f)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::DragFloat3(s_IDBuffer, glm::value_ptr(value), delta))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool PropertyColor(const char* label, glm::vec3& value)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::ColorEdit3(s_IDBuffer, glm::value_ptr(value)))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool Property(const char* label, glm::vec3& value, const float delta = 0.1f)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::DragFloat3(s_IDBuffer, glm::value_ptr(value), delta))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool Property(const char* label, glm::vec4& value, const float delta = 0.1f)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::DragFloat4(s_IDBuffer, glm::value_ptr(value), delta))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static void EndPropertyGrid()
{
ImGui::Columns(1);
PopID();
}
static bool BeginTreeNode(const char* name, const bool defaultOpen = true)
{
ImGuiTreeNodeFlags treeNodeFlags = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_FramePadding;
if (defaultOpen)
treeNodeFlags |= ImGuiTreeNodeFlags_DefaultOpen;
return ImGui::TreeNodeEx(name, treeNodeFlags);
}
static void EndTreeNode()
{
ImGui::TreePop();
}
}
#endif //IMGUI_H

View File

@ -45,6 +45,8 @@ namespace Prism
virtual void SetVSync(bool enable) = 0; virtual void SetVSync(bool enable) = 0;
virtual bool const IsVSync() const = 0; virtual bool const IsVSync() const = 0;
virtual void Maximize() = 0;
static Window* Create(const WindowProps& props); static Window* Create(const WindowProps& props);
virtual const std::string& GetTitle() const = 0; virtual const std::string& GetTitle() const = 0;

View File

@ -44,13 +44,15 @@ namespace Prism
const glm::vec2& mouse{ Input::GetMouseX(), Input::GetMouseY() }; const glm::vec2& mouse{ Input::GetMouseX(), Input::GetMouseY() };
const glm::vec2 delta = (mouse - m_InitialMousePosition) * 0.003f; const glm::vec2 delta = (mouse - m_InitialMousePosition) * 0.003f;
m_InitialMousePosition = mouse; m_InitialMousePosition = mouse;
if (delta.x != 0.0f || delta.y != 0.0f)
if (Input::IsMouseButtonPressed(MouseButton::Middle)) {
MousePan(delta); if (Input::IsMouseButtonPressed(MouseButton::Middle))
else if (Input::IsMouseButtonPressed(MouseButton::Left)) MousePan(delta);
MouseRotate(delta); else if (Input::IsMouseButtonPressed(MouseButton::Left))
else if (Input::IsMouseButtonPressed(MouseButton::Right)) MouseRotate(delta);
MouseZoom(delta.y); else if (Input::IsMouseButtonPressed(MouseButton::Right))
MouseZoom(delta.y);
}
} }
UpdateCameraView(); UpdateCameraView();
@ -77,6 +79,19 @@ namespace Prism
return glm::rotate(GetOrientation(), glm::vec3(0.0f, 0.0f, -1.0f)); return glm::rotate(GetOrientation(), glm::vec3(0.0f, 0.0f, -1.0f));
} }
void EditorCamera::OnImGuiRender()
{
ImGui::Begin("Camera Info");
UI::Property("yaw", m_Yaw);
UI::Property("pitch", m_Pitch);
UI::Property("focus", m_FocalPoint);
UI::Property("distance", m_Distance);
UI::Property("rotation", m_Rotation);
UI::Property("focus", m_FocalPoint);
UI::Property("position", m_Position);
ImGui::End();
}
void EditorCamera::UpdateCameraView() void EditorCamera::UpdateCameraView()
{ {
m_Position = CalculatePosition(); m_Position = CalculatePosition();

View File

@ -40,12 +40,11 @@ namespace Prism
const glm::vec3& GetPosition() const { return m_Position; } const glm::vec3& GetPosition() const { return m_Position; }
glm::quat GetOrientation() const; glm::quat GetOrientation() const;
float GetExposure() const { return m_Exposure; }
float& GetExposure() { return m_Exposure; }
float GetPitch() const { return m_Pitch; } float GetPitch() const { return m_Pitch; }
float GetYaw() const { return m_Yaw; } float GetYaw() const { return m_Yaw; }
void OnImGuiRender();
public: public:
inline void SetViewportSize(const uint32_t width, const uint32_t height) { m_ViewportWidth = width; m_ViewportHeight = height; } inline void SetViewportSize(const uint32_t width, const uint32_t height) { m_ViewportWidth = width; m_ViewportHeight = height; }
private: private:

View File

@ -6,6 +6,7 @@
#include "imgui.h" #include "imgui.h"
#include "imgui_internal.h" #include "imgui_internal.h"
#include "Prism/Core/ImGui/imgui.h"
#include "Prism/Physics/Physics3D.h" #include "Prism/Physics/Physics3D.h"
#include "Prism/Physics/PhysicsLayer.h" #include "Prism/Physics/PhysicsLayer.h"

View File

@ -167,9 +167,34 @@ namespace Prism
if (ImGui::BeginPopupContextWindow()) if (ImGui::BeginPopupContextWindow())
{ {
if (ImGui::MenuItem("Create Empty Entity"))
if (ImGui::BeginMenu("Create"))
{ {
m_Context->CreateEntity("Empty Entity"); if (ImGui::MenuItem("Empty Entity"))
{
auto newEntity = m_Context->CreateEntity("Empty Entity");
SetSelected(newEntity);
}
if (ImGui::MenuItem("Mesh"))
{
auto newEntity = m_Context->CreateEntity("Mesh");
newEntity.AddComponent<MeshComponent>();
SetSelected(newEntity);
}
ImGui::Separator();
if (ImGui::MenuItem("Directional Light"))
{
auto newEntity = m_Context->CreateEntity("Directional Light");
newEntity.AddComponent<DirectionalLightComponent>();
SetSelected(newEntity);
}
if (ImGui::MenuItem("Sky Light"))
{
auto newEntity = m_Context->CreateEntity("Sky Light");
newEntity.AddComponent<SkyLightComponent>();
SetSelected(newEntity);
}
ImGui::EndMenu();
} }
ImGui::EndPopup(); ImGui::EndPopup();
} }
@ -322,224 +347,6 @@ namespace Prism
} }
static int s_UIContextID = 0;
static uint32_t s_Counter = 0;
static char s_IDBuffer[16];
static void PushID()
{
ImGui::PushID(s_UIContextID++);
s_Counter = 0;
}
static void PopID()
{
ImGui::PopID();
s_UIContextID--;
}
static void BeginPropertyGrid()
{
PushID();
ImGui::Columns(2);
ImGui::SetColumnWidth(0, 100);
}
static bool Property(const char* label, std::string& value, bool error = false)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
char buffer[256];
snprintf(buffer, sizeof(buffer), "%s", value.c_str());
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
// itoa(s_Counter++, s_IDBuffer + 2, 16);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (error)
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.9f, 0.2f, 0.2f, 1.0f));
if (ImGui::InputText(s_IDBuffer, buffer, 256))
{
value = buffer;
modified = true;
}
if (error)
ImGui::PopStyleColor();
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static void Property(const char* label, const char* value)
{
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
// itoa(s_Counter++, s_IDBuffer + 2, 16);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
ImGui::InputText(s_IDBuffer, (char*)value, 256, ImGuiInputTextFlags_ReadOnly);
ImGui::PopItemWidth();
ImGui::NextColumn();
}
static bool Property(const char* label, bool& value)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
// itoa(s_Counter++, s_IDBuffer + 2, 16);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::Checkbox(s_IDBuffer, &value))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool Property(const char* label, int& value)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
// itoa(s_Counter++, s_IDBuffer + 2, 16);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::DragInt(s_IDBuffer, &value))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool Property(const char* label, float& value, float delta = 0.1f, float min = 0.0f, float max = 0.0f)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
// itoa(s_Counter++, s_IDBuffer + 2, 16);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::DragFloat(s_IDBuffer, &value, delta, min, max))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool Property(const char* label, glm::vec2& value, float delta = 0.1f)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
// itoa(s_Counter++, s_IDBuffer + 2, 16);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::DragFloat2(s_IDBuffer, glm::value_ptr(value), delta))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool Property(const char* label, glm::vec3& value, float delta = 0.1f)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
// itoa(s_Counter++, s_IDBuffer + 2, 16);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::DragFloat3(s_IDBuffer, glm::value_ptr(value), delta))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool Property(const char* label, glm::vec4& value, float delta = 0.1f)
{
bool modified = false;
ImGui::Text(label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
// itoa(s_Counter++, s_IDBuffer + 2, 16);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::DragFloat4(s_IDBuffer, glm::value_ptr(value), delta))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static void EndPropertyGrid()
{
ImGui::Columns(1);
PopID();
}
template<typename T, typename UIFunction> template<typename T, typename UIFunction>
static void DrawComponent(const std::string& name, Entity entity, UIFunction uiFunction) static void DrawComponent(const std::string& name, Entity entity, UIFunction uiFunction)
@ -622,6 +429,8 @@ namespace Prism
{ {
AddComponentPopup<CameraComponent>("Camera"); AddComponentPopup<CameraComponent>("Camera");
AddComponentPopup<MeshComponent>("Mesh"); AddComponentPopup<MeshComponent>("Mesh");
AddComponentPopup<DirectionalLightComponent>("Directional Light");
AddComponentPopup<SkyLightComponent>("sky Light");
AddComponentPopup<ScriptComponent>("Script"); AddComponentPopup<ScriptComponent>("Script");
AddComponentPopup<SpriteRendererComponent>("SpriteRenderer"); AddComponentPopup<SpriteRendererComponent>("SpriteRenderer");
AddComponentPopup<RigidBody2DComponent>("RigidBody2D"); AddComponentPopup<RigidBody2DComponent>("RigidBody2D");
@ -682,6 +491,7 @@ namespace Prism
if (!file.empty()) if (!file.empty())
meshComponent.Mesh = Ref<Mesh>::Create(file); meshComponent.Mesh = Ref<Mesh>::Create(file);
} }
ImGui::Columns(1);
}); });
DrawComponent<CameraComponent>("Camera", entity, [](CameraComponent& cameraComponent) { DrawComponent<CameraComponent>("Camera", entity, [](CameraComponent& cameraComponent) {
@ -704,20 +514,20 @@ namespace Prism
ImGui::EndCombo(); ImGui::EndCombo();
} }
BeginPropertyGrid(); UI::BeginPropertyGrid();
// Perspective parameters // Perspective parameters
if (cameraComponent.Camera.GetProjectionType() == SceneCamera::ProjectionType::Perspective) if (cameraComponent.Camera.GetProjectionType() == SceneCamera::ProjectionType::Perspective)
{ {
float verticalFOV = cameraComponent.Camera.GetPerspectiveVerticalFOV(); float verticalFOV = cameraComponent.Camera.GetPerspectiveVerticalFOV();
if (Property("Vertical FOV", verticalFOV)) if (UI::Property("Vertical FOV", verticalFOV))
cameraComponent.Camera.SetPerspectiveVerticalFOV(verticalFOV); cameraComponent.Camera.SetPerspectiveVerticalFOV(verticalFOV);
float nearClip = cameraComponent.Camera.GetPerspectiveNearClip(); float nearClip = cameraComponent.Camera.GetPerspectiveNearClip();
if (Property("Near Clip", nearClip)) if (UI::Property("Near Clip", nearClip))
cameraComponent.Camera.SetPerspectiveNearClip(nearClip); cameraComponent.Camera.SetPerspectiveNearClip(nearClip);
ImGui::SameLine(); ImGui::SameLine();
float farClip = cameraComponent.Camera.GetPerspectiveFarClip(); float farClip = cameraComponent.Camera.GetPerspectiveFarClip();
if (Property("Far Clip", farClip)) if (UI::Property("Far Clip", farClip))
cameraComponent.Camera.SetPerspectiveFarClip(farClip); cameraComponent.Camera.SetPerspectiveFarClip(farClip);
} }
@ -725,18 +535,57 @@ namespace Prism
else if (cameraComponent.Camera.GetProjectionType() == SceneCamera::ProjectionType::Orthographic) else if (cameraComponent.Camera.GetProjectionType() == SceneCamera::ProjectionType::Orthographic)
{ {
float orthoSize = cameraComponent.Camera.GetOrthographicSize(); float orthoSize = cameraComponent.Camera.GetOrthographicSize();
if (Property("Size", orthoSize)) if (UI::Property("Size", orthoSize))
cameraComponent.Camera.SetOrthographicSize(orthoSize); cameraComponent.Camera.SetOrthographicSize(orthoSize);
float nearClip = cameraComponent.Camera.GetOrthographicNearClip(); float nearClip = cameraComponent.Camera.GetOrthographicNearClip();
if (Property("Near Clip", nearClip)) if (UI::Property("Near Clip", nearClip))
cameraComponent.Camera.SetOrthographicNearClip(nearClip); cameraComponent.Camera.SetOrthographicNearClip(nearClip);
ImGui::SameLine(); ImGui::SameLine();
float farClip = cameraComponent.Camera.GetOrthographicFarClip(); float farClip = cameraComponent.Camera.GetOrthographicFarClip();
if (Property("Far Clip", farClip)) if (UI::Property("Far Clip", farClip))
cameraComponent.Camera.SetOrthographicFarClip(farClip); cameraComponent.Camera.SetOrthographicFarClip(farClip);
} }
EndPropertyGrid(); UI::EndPropertyGrid();
});
DrawComponent<DirectionalLightComponent>("Directional Light", entity, [](DirectionalLightComponent& dlc)
{
UI::BeginPropertyGrid();
UI::PropertyColor("Radiance", dlc.Radiance);
UI::Property("Intensity", dlc.Intensity);
UI::Property("Cast Shadows", dlc.CastShadows);
UI::Property("Soft Shadows", dlc.SoftShadows);
UI::Property("Source Size", dlc.LightSize);
UI::EndPropertyGrid();
});
DrawComponent<SkyLightComponent>("Sky Light", entity, [](SkyLightComponent& slc)
{
ImGui::Columns(3);
ImGui::SetColumnWidth(0, 100);
ImGui::SetColumnWidth(1, 300);
ImGui::SetColumnWidth(2, 40);
ImGui::Text("File Path");
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
if (!slc.SceneEnvironment.FilePath.empty())
ImGui::InputText("##envfilepath", (char*)slc.SceneEnvironment.FilePath.c_str(), 256, ImGuiInputTextFlags_ReadOnly);
else
ImGui::InputText("##envfilepath", (char*)"Empty", 256, ImGuiInputTextFlags_ReadOnly);
ImGui::PopItemWidth();
ImGui::NextColumn();
if (ImGui::Button("...##openenv"))
{
std::string file = Application::Get().OpenFile("*.hdr");
if (!file.empty())
slc.SceneEnvironment = Environment::Load(file);
}
ImGui::Columns(1);
UI::BeginPropertyGrid();
UI::Property("Intensity", slc.Intensity, 0.01f, 0.0f, 5.0f);
UI::EndPropertyGrid();
}); });
DrawComponent<SpriteRendererComponent>("Sprite Renderer", entity, [](SpriteRendererComponent& mc) DrawComponent<SpriteRendererComponent>("Sprite Renderer", entity, [](SpriteRendererComponent& mc)
@ -744,9 +593,9 @@ namespace Prism
}); });
DrawComponent<ScriptComponent>("Script", entity, [=](ScriptComponent& scriptComponent) mutable { DrawComponent<ScriptComponent>("Script", entity, [=](ScriptComponent& scriptComponent) mutable {
BeginPropertyGrid(); UI::BeginPropertyGrid();
const std::string oldName = scriptComponent.ModuleName; const std::string oldName = scriptComponent.ModuleName;
if (Property("Module Name", scriptComponent.ModuleName, !ScriptEngine::ModuleExists(scriptComponent.ModuleName))) // TODO: no live edit if (UI::Property("Module Name", scriptComponent.ModuleName, !ScriptEngine::ModuleExists(scriptComponent.ModuleName))) // TODO: no live edit
{ {
// Shutdown old script // Shutdown old script
if (ScriptEngine::ModuleExists(oldName)) if (ScriptEngine::ModuleExists(oldName))
@ -772,7 +621,7 @@ namespace Prism
case FieldType::Int: case FieldType::Int:
{ {
int value = isRuntime ? field.GetRuntimeValue<int>() : field.GetStoredValue<int>(); int value = isRuntime ? field.GetRuntimeValue<int>() : field.GetStoredValue<int>();
if (Property(field.Name.c_str(), value)) if (UI::Property(field.Name.c_str(), value))
{ {
if (isRuntime) if (isRuntime)
field.SetRuntimeValue(value); field.SetRuntimeValue(value);
@ -784,7 +633,7 @@ namespace Prism
case FieldType::Float: case FieldType::Float:
{ {
float value = isRuntime ? field.GetRuntimeValue<float>() : field.GetStoredValue<float>(); float value = isRuntime ? field.GetRuntimeValue<float>() : field.GetStoredValue<float>();
if (Property(field.Name.c_str(), value, 0.2f)) if (UI::Property(field.Name.c_str(), value, 0.2f))
{ {
if (isRuntime) if (isRuntime)
field.SetRuntimeValue(value); field.SetRuntimeValue(value);
@ -796,7 +645,7 @@ namespace Prism
case FieldType::Vec2: case FieldType::Vec2:
{ {
glm::vec2 value = isRuntime ? field.GetRuntimeValue<glm::vec2>() : field.GetStoredValue<glm::vec2>(); glm::vec2 value = isRuntime ? field.GetRuntimeValue<glm::vec2>() : field.GetStoredValue<glm::vec2>();
if (Property(field.Name.c_str(), value, 0.2f)) if (UI::Property(field.Name.c_str(), value, 0.2f))
{ {
if (isRuntime) if (isRuntime)
field.SetRuntimeValue(value); field.SetRuntimeValue(value);
@ -808,7 +657,7 @@ namespace Prism
case FieldType::Vec3: case FieldType::Vec3:
{ {
glm::vec3 value = isRuntime ? field.GetRuntimeValue<glm::vec3>() : field.GetStoredValue<glm::vec3>(); glm::vec3 value = isRuntime ? field.GetRuntimeValue<glm::vec3>() : field.GetStoredValue<glm::vec3>();
if (Property(field.Name.c_str(), value, 0.2f)) if (UI::Property(field.Name.c_str(), value, 0.2f))
{ {
if (isRuntime) if (isRuntime)
field.SetRuntimeValue(value); field.SetRuntimeValue(value);
@ -820,7 +669,7 @@ namespace Prism
case FieldType::Vec4: case FieldType::Vec4:
{ {
glm::vec4 value = isRuntime ? field.GetRuntimeValue<glm::vec4>() : field.GetStoredValue<glm::vec4>(); glm::vec4 value = isRuntime ? field.GetRuntimeValue<glm::vec4>() : field.GetStoredValue<glm::vec4>();
if (Property(field.Name.c_str(), value, 0.2f)) if (UI::Property(field.Name.c_str(), value, 0.2f))
{ {
if (isRuntime) if (isRuntime)
field.SetRuntimeValue(value); field.SetRuntimeValue(value);
@ -834,7 +683,7 @@ namespace Prism
} }
} }
EndPropertyGrid(); UI::EndPropertyGrid();
#if TODO #if TODO
if (ImGui::Button("Run Script")) if (ImGui::Button("Run Script"))
{ {
@ -866,34 +715,34 @@ namespace Prism
if (rb2dComponent.BodyType == RigidBody2DComponent::Type::Dynamic) if (rb2dComponent.BodyType == RigidBody2DComponent::Type::Dynamic)
{ {
BeginPropertyGrid(); UI::BeginPropertyGrid();
Property("Fixed Rotation", rb2dComponent.FixedRotation); UI::Property("Fixed Rotation", rb2dComponent.FixedRotation);
EndPropertyGrid(); UI::EndPropertyGrid();
} }
}); });
DrawComponent<BoxCollider2DComponent>("Box Collider 2D", entity, [](BoxCollider2DComponent& bc2dComponent) DrawComponent<BoxCollider2DComponent>("Box Collider 2D", entity, [](BoxCollider2DComponent& bc2dComponent)
{ {
BeginPropertyGrid(); UI::BeginPropertyGrid();
Property("Offset", bc2dComponent.Offset); UI::Property("Offset", bc2dComponent.Offset);
Property("Size", bc2dComponent.Size); UI::Property("Size", bc2dComponent.Size);
Property("Density", bc2dComponent.Density); UI::Property("Density", bc2dComponent.Density);
Property("Friction", bc2dComponent.Friction); UI::Property("Friction", bc2dComponent.Friction);
EndPropertyGrid(); UI::EndPropertyGrid();
}); });
DrawComponent<CircleCollider2DComponent>("Circle Collider 2D", entity, [](CircleCollider2DComponent& cc2dComponent) DrawComponent<CircleCollider2DComponent>("Circle Collider 2D", entity, [](CircleCollider2DComponent& cc2dComponent)
{ {
BeginPropertyGrid(); UI::BeginPropertyGrid();
Property("Offset", cc2dComponent.Offset); UI::Property("Offset", cc2dComponent.Offset);
Property("Radius", cc2dComponent.Radius); UI::Property("Radius", cc2dComponent.Radius);
Property("Density", cc2dComponent.Density); UI::Property("Density", cc2dComponent.Density);
Property("Friction", cc2dComponent.Friction); UI::Property("Friction", cc2dComponent.Friction);
EndPropertyGrid(); UI::EndPropertyGrid();
}); });
DrawComponent<RigidBodyComponent>("Rigidbody", entity, [](RigidBodyComponent& rbc) DrawComponent<RigidBodyComponent>("Rigidbody", entity, [](RigidBodyComponent& rbc)
@ -948,21 +797,21 @@ namespace Prism
if (rbc.BodyType == RigidBodyComponent::Type::Dynamic) if (rbc.BodyType == RigidBodyComponent::Type::Dynamic)
{ {
BeginPropertyGrid(); UI::BeginPropertyGrid();
Property("Mass", rbc.Mass); UI::Property("Mass", rbc.Mass);
Property("Is Kinematic", rbc.IsKinematic); UI::Property("Is Kinematic", rbc.IsKinematic);
EndPropertyGrid(); UI::EndPropertyGrid();
if (ImGui::TreeNode("RigidBodyConstraints", "Constraints")) if (ImGui::TreeNode("RigidBodyConstraints", "Constraints"))
{ {
BeginPropertyGrid(); UI::BeginPropertyGrid();
Property("Position: X", rbc.LockPositionX); UI::Property("Position: X", rbc.LockPositionX);
Property("Position: Y", rbc.LockPositionY); UI::Property("Position: Y", rbc.LockPositionY);
Property("Position: Z", rbc.LockPositionZ); UI::Property("Position: Z", rbc.LockPositionZ);
Property("Rotation: X", rbc.LockRotationX); UI::Property("Rotation: X", rbc.LockRotationX);
Property("Rotation: Y", rbc.LockRotationY); UI::Property("Rotation: Y", rbc.LockRotationY);
Property("Rotation: Z", rbc.LockRotationZ); UI::Property("Rotation: Z", rbc.LockRotationZ);
EndPropertyGrid(); UI::EndPropertyGrid();
ImGui::TreePop(); ImGui::TreePop();
} }
@ -972,60 +821,60 @@ namespace Prism
DrawComponent<PhysicsMaterialComponent>("Physics Material", entity, [](PhysicsMaterialComponent& pmc) DrawComponent<PhysicsMaterialComponent>("Physics Material", entity, [](PhysicsMaterialComponent& pmc)
{ {
BeginPropertyGrid(); UI::BeginPropertyGrid();
Property("Static Friction", pmc.StaticFriction, 0.01f, 0.0f, 1.0f); UI::Property("Static Friction", pmc.StaticFriction, 0.01f, 0.0f, 1.0f);
Property("Dynamic Friction", pmc.DynamicFriction, 0.01f, 0.0f, 1.0f); UI::Property("Dynamic Friction", pmc.DynamicFriction, 0.01f, 0.0f, 1.0f);
Property("Bounciness", pmc.Bounciness, 0.01f, 0.0f, 1.0f); UI::Property("Bounciness", pmc.Bounciness, 0.01f, 0.0f, 1.0f);
EndPropertyGrid(); UI::EndPropertyGrid();
}); });
DrawComponent<BoxColliderComponent>("Box Collider", entity, [](BoxColliderComponent& bcc) DrawComponent<BoxColliderComponent>("Box Collider", entity, [](BoxColliderComponent& bcc)
{ {
BeginPropertyGrid(); UI::BeginPropertyGrid();
if (Property("Size", bcc.Size)) if (UI::Property("Size", bcc.Size))
{ {
bcc.DebugMesh = MeshFactory::CreateBox(bcc.Size); bcc.DebugMesh = MeshFactory::CreateBox(bcc.Size);
} }
Property("IsTrigger", bcc.IsTrigger); UI::Property("IsTrigger", bcc.IsTrigger);
EndPropertyGrid(); UI::EndPropertyGrid();
}); });
DrawComponent<SphereColliderComponent>("Sphere Collider", entity, [](SphereColliderComponent& scc) DrawComponent<SphereColliderComponent>("Sphere Collider", entity, [](SphereColliderComponent& scc)
{ {
BeginPropertyGrid(); UI::BeginPropertyGrid();
if (Property("Radius", scc.Radius)) if (UI::Property("Radius", scc.Radius))
{ {
scc.DebugMesh = MeshFactory::CreateSphere(scc.Radius); scc.DebugMesh = MeshFactory::CreateSphere(scc.Radius);
} }
Property("IsTrigger", scc.IsTrigger); UI::Property("IsTrigger", scc.IsTrigger);
EndPropertyGrid(); UI::EndPropertyGrid();
}); });
DrawComponent<CapsuleColliderComponent>("Capsule Collider", entity, [](CapsuleColliderComponent& ccc) DrawComponent<CapsuleColliderComponent>("Capsule Collider", entity, [](CapsuleColliderComponent& ccc)
{ {
BeginPropertyGrid(); UI::BeginPropertyGrid();
bool changed = false; bool changed = false;
if (Property("Radius", ccc.Radius)) changed = true; if (UI::Property("Radius", ccc.Radius)) changed = true;
if (Property("Height", ccc.Height)) changed = true; if (UI::Property("Height", ccc.Height)) changed = true;
Property("Is Trigger", ccc.IsTrigger); UI::Property("Is Trigger", ccc.IsTrigger);
if (changed) if (changed)
{ {
ccc.DebugMesh = MeshFactory::CreateCapsule(ccc.Radius, ccc.Height); ccc.DebugMesh = MeshFactory::CreateCapsule(ccc.Radius, ccc.Height);
} }
EndPropertyGrid(); UI::EndPropertyGrid();
}); });
DrawComponent<MeshColliderComponent>("Mesh Collider", entity, [](MeshColliderComponent& mcc) DrawComponent<MeshColliderComponent>("Mesh Collider", entity, [](MeshColliderComponent& mcc)
@ -1049,14 +898,28 @@ namespace Prism
if (!file.empty()) if (!file.empty())
{ {
mcc.CollisionMesh = Ref<Mesh>::Create(file); mcc.CollisionMesh = Ref<Mesh>::Create(file);
PhysicsWrappers::CreateConvexMesh(mcc); if (mcc.IsConvex)
PhysicsWrappers::CreateConvexMesh(mcc, true);
else
PhysicsWrappers::CreateTriangleMesh(mcc, true);
} }
} }
ImGui::EndColumns(); ImGui::EndColumns();
BeginPropertyGrid(); UI::BeginPropertyGrid();
Property("Is Trigger", mcc.IsTrigger); if (UI::Property("Is Convex", mcc.IsConvex))
EndPropertyGrid(); {
if (mcc.CollisionMesh)
{
if (mcc.IsConvex)
PhysicsWrappers::CreateConvexMesh(mcc, true);
else
PhysicsWrappers::CreateTriangleMesh(mcc, true);
}
}
UI::Property("Is Trigger", mcc.IsTrigger);
UI::EndPropertyGrid();
}); });
} }

View File

@ -146,7 +146,7 @@ namespace Prism
s_SimulationTime -= s_Settings.FixedTimestep; s_SimulationTime -= s_Settings.FixedTimestep;
for (Entity& e : s_SimulatedEntities) for (const Entity& e : s_SimulatedEntities)
{ {
if (ScriptEngine::IsEntityModuleValid(e)) if (ScriptEngine::IsEntityModuleValid(e))
ScriptEngine::OnPhysicsUpdateEntity(e, s_Settings.FixedTimestep); ScriptEngine::OnPhysicsUpdateEntity(e, s_Settings.FixedTimestep);

View File

@ -10,6 +10,12 @@
namespace Prism namespace Prism
{ {
physx::PxTransform ToPhysXTransform(const TransformComponent& transform)
{
const physx::PxQuat r = ToPhysXQuat(glm::normalize(glm::quat(transform.Rotation)));
const physx::PxVec3 p = ToPhysXVector(transform.Translation);
return physx::PxTransform(p, r);
}
physx::PxTransform ToPhysXTransform(const glm::mat4& matrix) physx::PxTransform ToPhysXTransform(const glm::mat4& matrix)
{ {

View File

@ -9,10 +9,12 @@
#include <PxPhysicsAPI.h> #include <PxPhysicsAPI.h>
#include "glm/glm.hpp" #include "glm/glm.hpp"
#include "Prism/Scene/Components.h"
namespace Prism namespace Prism
{ {
physx::PxTransform ToPhysXTransform(const TransformComponent& transform);
physx::PxTransform ToPhysXTransform(const glm::mat4& matrix); physx::PxTransform ToPhysXTransform(const glm::mat4& matrix);
physx::PxMat44 ToPhysXMatrix(const glm::mat4& matrix); physx::PxMat44 ToPhysXMatrix(const glm::mat4& matrix);
physx::PxVec3 ToPhysXVector(const glm::vec3& vector); physx::PxVec3 ToPhysXVector(const glm::vec3& vector);

View File

@ -284,21 +284,278 @@ namespace Prism
shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger); shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger);
shape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, collider.IsTrigger); shape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, collider.IsTrigger);
shape->setLocalPose(physx::PxTransform(physx::PxQuat(physx::PxHalfPi, physx::PxVec3(0, 0, 1)))); // shape->setLocalPose(physx::PxTransform(physx::PxQuat(physx::PxHalfPi, physx::PxVec3(0, 0, 1))));
} }
void PhysicsWrappers::AddMeshCollider(physx::PxRigidActor& actor, const physx::PxMaterial& material,MeshColliderComponent& collider, const glm::vec3& scale) void PhysicsWrappers::AddMeshCollider(physx::PxRigidActor& actor, const physx::PxMaterial& material,MeshColliderComponent& collider, const glm::vec3& scale)
{ {
physx::PxConvexMeshGeometry convexGeometry = physx::PxConvexMeshGeometry(CreateConvexMesh(collider)); if (collider.IsConvex)
convexGeometry.meshFlags = physx::PxConvexMeshGeometryFlag::eTIGHT_BOUNDS; {
physx::PxShape* shape = physx::PxRigidActorExt::createExclusiveShape(actor, convexGeometry, material); std::vector<physx::PxConvexMesh*> meshes = CreateConvexMesh(collider, true);
shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger);
shape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, collider.IsTrigger); for (const auto mesh : meshes)
{
physx::PxConvexMeshGeometry convexGeometry = physx::PxConvexMeshGeometry(mesh, physx::PxMeshScale(ToPhysXVector(scale)));
convexGeometry.meshFlags = physx::PxConvexMeshGeometryFlag::eTIGHT_BOUNDS;
physx::PxShape* shape = physx::PxRigidActorExt::createExclusiveShape(actor, convexGeometry, material);
shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger);
shape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, collider.IsTrigger);
}
}
else
{
std::vector<physx::PxTriangleMesh*> meshes = CreateTriangleMesh(collider, true);
for (const auto mesh : meshes)
{
physx::PxTriangleMeshGeometry convexGeometry = physx::PxTriangleMeshGeometry(mesh, physx::PxMeshScale(ToPhysXVector(scale)));
physx::PxShape* shape = physx::PxRigidActorExt::createExclusiveShape(actor, convexGeometry, material);
shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger);
shape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, collider.IsTrigger);
}
}
} }
physx::PxConvexMesh* PhysicsWrappers::CreateConvexMesh(MeshColliderComponent& collider) std::vector<physx::PxTriangleMesh*> PhysicsWrappers::CreateTriangleMesh(MeshColliderComponent& collider, bool invalidateOld)
{ {
std::vector<physx::PxTriangleMesh*> meshes;
// 设置烹饪参数,可以根据需要调整
physx::PxCookingParams cookingParams(s_Physics->getTolerancesScale());
// 可以添加额外的烹饪参数设置,如:
// cookingParams.meshPreprocessParams = physx::PxMeshPreprocessingFlag::eWELD_VERTICES;
// cookingParams.meshWeldTolerance = 0.01f;
// 获取顶点和索引数据
const std::vector<Vertex>& vertices = collider.CollisionMesh->GetStaticVertices();
const std::vector<Index>& indices = collider.CollisionMesh->GetIndices();
// 提取顶点位置(如果需要)
std::vector<glm::vec3> vertexPositions;
if (vertices[0].Position != glm::vec3()) {
vertexPositions.reserve(vertices.size());
for (const auto& vertex : vertices) {
vertexPositions.push_back(vertex.Position);
}
}
// 清空旧的处理网格(如果需要)
if (invalidateOld) {
collider.ProcessedMeshes.clear();
}
// 处理每个子网格
for (const auto& submesh : collider.CollisionMesh->GetSubmeshes())
{
// 准备三角形网格描述
physx::PxTriangleMeshDesc triangleDesc;
triangleDesc.points.count = submesh.VertexCount;
triangleDesc.points.stride = sizeof(glm::vec3);
// 使用顶点位置数组或原始顶点数据
if (!vertexPositions.empty()) {
triangleDesc.points.data = &vertexPositions[submesh.BaseVertex];
} else {
triangleDesc.points.stride = sizeof(Vertex);
triangleDesc.points.data = &vertices[submesh.BaseVertex];
}
triangleDesc.triangles.count = submesh.IndexCount / 3;
triangleDesc.triangles.data = &indices[submesh.BaseIndex / 3];
triangleDesc.triangles.stride = sizeof(Index); // 假设Index包含三个值
// 如果需要,设置三角形标志
// triangleDesc.flags = physx::PxMeshFlag::e16_BIT_INDICES; // 如果使用16位索引
// 创建三角形网格
physx::PxTriangleMeshCookingResult::Enum result;
physx::PxTriangleMesh* triangleMesh = PxCreateTriangleMesh(
cookingParams,
triangleDesc,
*PxGetStandaloneInsertionCallback(),
&result
);
if (!triangleMesh) {
PM_CORE_ERROR("Failed to create triangle mesh for submesh: {0}", submesh.MeshName);
continue;
}
meshes.push_back(triangleMesh);
// 为可视化生成渲染网格(如果需要)
if (collider.ProcessedMeshes.empty() || invalidateOld)
{
const uint32_t nbVerts = triangleMesh->getNbVertices();
const physx::PxVec3* meshVertices = triangleMesh->getVertices();
const uint32_t nbTriangles = triangleMesh->getNbTriangles();
std::vector<Vertex> processedVertices;
std::vector<Index> processedIndices;
// 获取顶点数据
processedVertices.reserve(nbVerts);
for (uint32_t v = 0; v < nbVerts; v++)
{
Vertex vertex;
vertex.Position = FromPhysXVector(meshVertices[v]);
processedVertices.push_back(vertex);
}
// 获取三角形索引
processedIndices.reserve(nbTriangles);
// 检查索引格式16位或32位
if (triangleMesh->getTriangleMeshFlags() & physx::PxTriangleMeshFlag::e16_BIT_INDICES)
{
const physx::PxU16* tris = static_cast<const physx::PxU16*>(triangleMesh->getTriangles());
for (uint32_t tri = 0; tri < nbTriangles; tri++)
{
Index index;
index.V1 = tris[3 * tri + 0];
index.V2 = tris[3 * tri + 1];
index.V3 = tris[3 * tri + 2];
processedIndices.push_back(index);
}
}
else
{
const physx::PxU32* tris = static_cast<const physx::PxU32*>(triangleMesh->getTriangles());
for (uint32_t tri = 0; tri < nbTriangles; tri++)
{
Index index;
index.V1 = static_cast<uint32_t>(tris[3 * tri + 0]);
index.V2 = static_cast<uint32_t>(tris[3 * tri + 1]);
index.V3 = static_cast<uint32_t>(tris[3 * tri + 2]);
processedIndices.push_back(index);
}
}
// 为当前子网格创建渲染网格
collider.ProcessedMeshes.push_back(Ref<Mesh>::Create(processedVertices, processedIndices));
}
}
return meshes;
}
std::vector<physx::PxConvexMesh*> PhysicsWrappers::CreateConvexMesh(MeshColliderComponent& collider, bool invalidateOld)
{
std::vector<physx::PxConvexMesh*> meshes;
// 设置烹饪参数,保持与原始版本相同的配置
physx::PxCookingParams cookingParams(s_Physics->getTolerancesScale());
cookingParams.planeTolerance = 0.0F;
cookingParams.meshPreprocessParams = physx::PxMeshPreprocessingFlags(physx::PxMeshPreprocessingFlag::eWELD_VERTICES);
cookingParams.meshWeldTolerance = 0.01f;
// 获取顶点数据
const std::vector<Vertex>& vertices = collider.CollisionMesh->GetStaticVertices();
const std::vector<Index>& indices = collider.CollisionMesh->GetIndices();
// 准备顶点位置数组(如果需要的话)
std::vector<glm::vec3> vertexPositions;
if (vertices[0].Position != glm::vec3()) {
vertexPositions.reserve(vertices.size());
for (const auto& vertex : vertices) {
vertexPositions.push_back(vertex.Position);
}
}
if (invalidateOld) {
collider.ProcessedMeshes.clear();
}
// 处理每个子网格
for (const auto& submesh : collider.CollisionMesh->GetSubmeshes())
{
// 准备凸包描述
physx::PxConvexMeshDesc convexDesc;
convexDesc.points.count = submesh.VertexCount;
convexDesc.points.stride = sizeof(glm::vec3);
// 使用顶点位置数组或原始顶点数据
if (!vertexPositions.empty()) {
convexDesc.points.data = &vertexPositions[submesh.BaseVertex];
} else {
convexDesc.points.stride = sizeof(Vertex);
convexDesc.points.data = &vertices[submesh.BaseVertex];
}
convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX |
physx::PxConvexFlag::eCHECK_ZERO_AREA_TRIANGLES |
physx::PxConvexFlag::eSHIFT_VERTICES;
// 创建凸包网格
physx::PxConvexMeshCookingResult::Enum result;
physx::PxConvexMesh* convexMesh = PxCreateConvexMesh(
cookingParams,
convexDesc,
*PxGetStandaloneInsertionCallback(),
&result
);
if (!convexMesh) {
PM_CORE_ERROR("Failed to create convex mesh for submesh: {0}", submesh.MeshName);
continue;
}
meshes.push_back(convexMesh);
// 为可视化生成渲染网格(如果需要)
if (collider.ProcessedMeshes.empty() || invalidateOld)
{
const uint32_t nbPolygons = convexMesh->getNbPolygons();
const physx::PxVec3* convexVertices = convexMesh->getVertices();
const physx::PxU8* convexIndices = convexMesh->getIndexBuffer();
std::vector<Vertex> collisionVertices;
std::vector<Index> collisionIndices;
uint32_t vertCounter = 0;
uint32_t indexCounter = 0;
for (uint32_t i = 0; i < nbPolygons; i++)
{
physx::PxHullPolygon polygon;
convexMesh->getPolygonData(i, polygon);
const uint32_t vI0 = vertCounter;
// 收集当前多边形的所有顶点
for (uint32_t vI = 0; vI < polygon.mNbVerts; vI++)
{
Vertex vertex;
vertex.Position = FromPhysXVector(
convexVertices[convexIndices[polygon.mIndexBase + vI]]
);
// 如果需要坐标系转换,可以在这里添加
// vertex.Position = glm::rotate(vertex.Position, glm::radians(90.0f), {1.0f, 0.0f, 0.0f});
collisionVertices.push_back(vertex);
vertCounter++;
}
// 为当前多边形生成三角形索引(扇形三角剖分)
for (auto vI = 1; vI < polygon.mNbVerts - 1; vI++)
{
Index index;
index.V1 = vI0;
index.V2 = vI0 + vI + 1;
index.V3 = vI0 + vI;
collisionIndices.push_back(index);
indexCounter++;
}
}
// 为当前子网格创建渲染网格
collider.ProcessedMeshes.push_back(Ref<Mesh>::Create(collisionVertices, collisionIndices));
}
}
return meshes;
#if 0
const auto& vertices = collider.CollisionMesh->GetStaticVertices(); const auto& vertices = collider.CollisionMesh->GetStaticVertices();
@ -373,6 +630,7 @@ namespace Prism
} }
return mesh; return mesh;
#endif
} }
physx::PxMaterial* PhysicsWrappers::CreateMaterial(const PhysicsMaterialComponent& material) physx::PxMaterial* PhysicsWrappers::CreateMaterial(const PhysicsMaterialComponent& material)
@ -498,7 +756,8 @@ namespace Prism
PM_CORE_WARN("PVD is already connected, reconnecting"); PM_CORE_WARN("PVD is already connected, reconnecting");
s_VisualDebugger->disconnect(); s_VisualDebugger->disconnect();
} }
isConnect = s_VisualDebugger->connect(*transport, physx::PxPvdInstrumentationFlag::eALL); s_VisualDebugger->connect(*transport, physx::PxPvdInstrumentationFlag::eALL);
isConnect = s_VisualDebugger->isConnected();
#endif #endif
return isConnect; return isConnect;
} }

View File

@ -45,7 +45,8 @@ namespace Prism
static void AddMeshCollider(::physx::PxRigidActor& actor, const ::physx::PxMaterial& material, ::Prism::MeshColliderComponent& collider, const glm::vec3& scale = glm::vec3(0.0f)); static void AddMeshCollider(::physx::PxRigidActor& actor, const ::physx::PxMaterial& material, ::Prism::MeshColliderComponent& collider, const glm::vec3& scale = glm::vec3(0.0f));
static physx::PxConvexMesh* CreateConvexMesh(MeshColliderComponent& collider); static std::vector<physx::PxTriangleMesh*> CreateTriangleMesh(MeshColliderComponent& collider, bool invalidateOld = false);
static std::vector<physx::PxConvexMesh*> CreateConvexMesh(MeshColliderComponent& collider, bool invalidateOld = false);
static physx::PxMaterial* CreateMaterial(const PhysicsMaterialComponent& material); static physx::PxMaterial* CreateMaterial(const PhysicsMaterialComponent& material);

View File

@ -10,74 +10,231 @@
namespace Prism namespace Prism
{ {
OpenGLFrameBuffer::OpenGLFrameBuffer(const FramebufferSpecification& spec) namespace Utils
: m_Specification(spec) {
{ static GLenum TextureTarget(bool multisampled)
OpenGLFrameBuffer::Resize(spec.Width, spec.Height, true); {
} return multisampled ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
}
static void CreateTextures(bool multisampled, RendererID* outID, uint32_t count)
{
glCreateTextures(TextureTarget(multisampled), count, outID);
}
static void BindTexture(bool multisampled, RendererID id)
{
glBindTexture(TextureTarget(multisampled), id);
}
static GLenum DataType(GLenum format)
{
switch (format)
{
case GL_RGBA8: return GL_UNSIGNED_BYTE;
case GL_RG16F:
case GL_RG32F:
case GL_RGBA16F:
case GL_RGBA32F: return GL_FLOAT;
case GL_DEPTH24_STENCIL8: return GL_UNSIGNED_INT_24_8;
}
PM_CORE_ASSERT(false, "Unknown format!");
return 0;
}
static void AttachColorTexture(RendererID id, int samples, GLenum format, uint32_t width, uint32_t height, int index)
{
const bool multisampled = samples > 1;
if (multisampled)
{
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, format, width, height, GL_FALSE);
}
else
{
// Only RGBA access for now
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, DataType(format), nullptr);
glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, TextureTarget(multisampled), id, 0);
}
static void AttachDepthTexture(RendererID id, int samples, GLenum format, GLenum attachmentType, uint32_t width, uint32_t height)
{
bool multisampled = samples > 1;
if (multisampled)
{
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, format, width, height, GL_FALSE);
}
else
{
glTexStorage2D(GL_TEXTURE_2D, 1, format, width, height);
glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
glFramebufferTexture2D(GL_FRAMEBUFFER, attachmentType, TextureTarget(multisampled), id, 0);
}
static bool IsDepthFormat(FramebufferTextureFormat format)
{
switch (format)
{
case FramebufferTextureFormat::DEPTH24STENCIL8:
case FramebufferTextureFormat::DEPTH32F:
return true;
}
return false;
}
}
OpenGLFrameBuffer::OpenGLFrameBuffer(const FramebufferSpecification& spec)
: m_Specification(spec), m_Width(spec.Width), m_Height(spec.Height)
{
for (auto format : m_Specification.Attachments.Attachments)
{
if (!Utils::IsDepthFormat(format.TextureFormat))
m_ColorAttachmentFormats.emplace_back(format.TextureFormat);
else
m_DepthAttachmentFormat = format.TextureFormat;
}
Resize(spec.Width, spec.Height, true);
}
OpenGLFrameBuffer::~OpenGLFrameBuffer() OpenGLFrameBuffer::~OpenGLFrameBuffer()
{ {
GLuint rendererID = m_RendererID; Ref<OpenGLFrameBuffer> instance = this;
Renderer::Submit([rendererID](){ Renderer::Submit([instance]() {
glDeleteFramebuffers(1, &rendererID); glDeleteFramebuffers(1, &instance->m_RendererID);
}); });
} }
void OpenGLFrameBuffer::Bind() const void OpenGLFrameBuffer::Bind() const
{ {
Renderer::Submit([=](){ Ref<const OpenGLFrameBuffer> instance = this;
glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID); Renderer::Submit([instance]() {
glViewport(0, 0, m_Specification.Width, m_Specification.Height); glBindFramebuffer(GL_FRAMEBUFFER, instance->m_RendererID);
}); glViewport(0, 0, instance->m_Width, instance->m_Height);
});
} }
void OpenGLFrameBuffer::Unbind() const void OpenGLFrameBuffer::Unbind() const
{ {
Renderer::Submit([=](){ Renderer::Submit([](){
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
}); });
} }
void OpenGLFrameBuffer::Resize(const uint32_t width, const uint32_t height, const bool forceReCreate) void OpenGLFrameBuffer::Resize(const uint32_t width, const uint32_t height, const bool forceReCreate)
{ {
if (!forceReCreate && (m_Specification.Width == width && m_Specification.Height == height)) if (!forceReCreate && (m_Width == width && m_Height == height))
return; return;
m_Specification.Width = width; m_Width = width;
m_Specification.Height = height; m_Height = height;
Renderer::Submit([this]()
Ref<OpenGLFrameBuffer> instance = this;
Renderer::Submit([instance]() mutable
{ {
if (m_RendererID) if (instance->m_RendererID)
{ {
glDeleteFramebuffers(1, &m_RendererID); glDeleteFramebuffers(1, &instance->m_RendererID);
glDeleteTextures(1, &m_ColorAttachment); glDeleteTextures(static_cast<GLsizei>(instance->m_ColorAttachments.size()), instance->m_ColorAttachments.data());
glDeleteTextures(1, &m_DepthAttachment); glDeleteTextures(1, &instance->m_DepthAttachment);
instance->m_ColorAttachments.clear();
instance->m_DepthAttachment = 0;
} }
glGenFramebuffers(1, &m_RendererID); glGenFramebuffers(1, &instance->m_RendererID);
glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID); glBindFramebuffer(GL_FRAMEBUFFER, instance->m_RendererID);
bool multisample = instance->m_Specification.Samples > 1;
if (instance->m_ColorAttachmentFormats.size())
{
instance->m_ColorAttachments.resize(instance->m_ColorAttachmentFormats.size());
Utils::CreateTextures(multisample, instance->m_ColorAttachments.data(), static_cast<uint32_t>(instance->m_ColorAttachments.size()));
// Create color attachments
for (int i = 0; i < instance->m_ColorAttachments.size(); i++)
{
Utils::BindTexture(multisample, instance->m_ColorAttachments[i]);
switch (instance->m_ColorAttachmentFormats[i])
{
case FramebufferTextureFormat::RGBA8:
Utils::AttachColorTexture(instance->m_ColorAttachments[i], instance->m_Specification.Samples, GL_RGBA8, instance->m_Width, instance->m_Height, i);
break;
case FramebufferTextureFormat::RGBA16F:
Utils::AttachColorTexture(instance->m_ColorAttachments[i], instance->m_Specification.Samples, GL_RGBA16F, instance->m_Width, instance->m_Height, i);
break;
case FramebufferTextureFormat::RGBA32F:
Utils::AttachColorTexture(instance->m_ColorAttachments[i], instance->m_Specification.Samples, GL_RGBA32F, instance->m_Width, instance->m_Height, i);
break;
case FramebufferTextureFormat::RG32F:
Utils::AttachColorTexture(instance->m_ColorAttachments[i], instance->m_Specification.Samples, GL_RG32F, instance->m_Width, instance->m_Height, i);
break;
}
}
}
if (instance->m_DepthAttachmentFormat != FramebufferTextureFormat::None)
{
Utils::CreateTextures(multisample, &instance->m_DepthAttachment, 1);
Utils::BindTexture(multisample, instance->m_DepthAttachment);
switch (instance->m_DepthAttachmentFormat)
{
case FramebufferTextureFormat::DEPTH24STENCIL8:
Utils::AttachDepthTexture(instance->m_DepthAttachment, instance->m_Specification.Samples, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL_ATTACHMENT, instance->m_Width, instance->m_Height);
break;
case FramebufferTextureFormat::DEPTH32F:
Utils::AttachDepthTexture(instance->m_DepthAttachment, instance->m_Specification.Samples, GL_DEPTH_COMPONENT32F, GL_DEPTH_ATTACHMENT, instance->m_Width, instance->m_Height);
break;
}
}
if (instance->m_ColorAttachments.size() > 1)
{
PM_CORE_ASSERT(instance->m_ColorAttachments.size() <= 4);
GLenum buffers[4] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
glDrawBuffers(static_cast<GLsizei>(instance->m_ColorAttachments.size()), buffers);
}
else if (instance->m_ColorAttachments.size() == 0)
{
// Only depth-pass
glDrawBuffer(GL_NONE);
}
#if 0
bool multisample = m_Specification.Samples > 1;
if (multisample) if (multisample)
{ {
glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &m_ColorAttachment); glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &m_ColorAttachment);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment);
// TODO: Create Prism texture object based on format here // TODO: Create Hazel texture object based on format here
if (m_Specification.Format == FramebufferFormat::RGBA16F) if (m_Specification.Format == FramebufferFormat::RGBA16F)
{ {
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_RGBA16F, m_Specification.Width, m_Specification.Height, GL_TRUE); glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_RGBA16F, m_Specification.Width, m_Specification.Height, GL_FALSE);
} }
else if (m_Specification.Format == FramebufferFormat::RGBA8) else if (m_Specification.Format == FramebufferFormat::RGBA8)
{ {
// glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 8, GL_RGBA8, m_Specification.Width, m_Specification.Height, GL_TRUE); glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_RGBA8, m_Specification.Width, m_Specification.Height, GL_FALSE);
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_RGBA8, m_Specification.Width, m_Specification.Height, GL_TRUE);
} }
// glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment, 0);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
} }
else else
@ -85,30 +242,51 @@ namespace Prism
glCreateTextures(GL_TEXTURE_2D, 1, &m_ColorAttachment); glCreateTextures(GL_TEXTURE_2D, 1, &m_ColorAttachment);
glBindTexture(GL_TEXTURE_2D, m_ColorAttachment); glBindTexture(GL_TEXTURE_2D, m_ColorAttachment);
// TODO: Create texture object based on format here // TODO: Create Hazel texture object based on format here
if (m_Specification.Format == FramebufferFormat::RGBA16F) if (m_Specification.Format == FramebufferFormat::RGBA16F)
{ {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_FLOAT, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_FLOAT, nullptr);
} }
else if (m_Specification.Format == FramebufferFormat::RG32F) // "Shadow" for now
{
glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT32F, m_Specification.Width, m_Specification.Height);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_ColorAttachment, 0);
glDrawBuffer(GL_NONE);
}
else if (m_Specification.Format == FramebufferFormat::RGBA8) else if (m_Specification.Format == FramebufferFormat::RGBA8)
{ {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
} }
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); else if (m_Specification.Format == FramebufferFormat::COMP)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_ColorAttachment, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glCreateTextures(GL_TEXTURE_2D, 1, &m_ColorAttachment2);
glBindTexture(GL_TEXTURE_2D, m_ColorAttachment2);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
if (m_Specification.Format != FramebufferFormat::RG32F)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_ColorAttachment, 0);
}
} }
if (multisample) if (multisample)
{ {
glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &m_DepthAttachment); glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &m_DepthAttachment);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_DepthAttachment); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_DepthAttachment);
// glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 8, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height, GL_TRUE); glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height, GL_FALSE);
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height, GL_TRUE);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
// glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, m_DepthAttachment, 0);
} }
else else if (m_Specification.Format != FramebufferFormat::RG32F)
{ {
glCreateTextures(GL_TEXTURE_2D, 1, &m_DepthAttachment); glCreateTextures(GL_TEXTURE_2D, 1, &m_DepthAttachment);
glBindTexture(GL_TEXTURE_2D, m_DepthAttachment); glBindTexture(GL_TEXTURE_2D, m_DepthAttachment);
@ -116,15 +294,31 @@ namespace Prism
GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height, 0, GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height, 0,
GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL
); );
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_DepthAttachment, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_DepthAttachment, 0);
} }
// glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_ColorAttachment, 0); if (m_Specification.Format != FramebufferFormat::RG32F)
if (multisample) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment, 0); if (multisample)
else {
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_ColorAttachment, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, m_DepthAttachment, 0); }
else
{
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_ColorAttachment, 0);
if (m_Specification.Format == FramebufferFormat::COMP)
{
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, m_ColorAttachment2, 0);
const GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(2, buffers);
}
}
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, m_DepthAttachment, 0);
}
#endif
PM_CORE_ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Framebuffer is incomplete!"); PM_CORE_ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Framebuffer is incomplete!");
@ -132,18 +326,11 @@ namespace Prism
}); });
} }
void OpenGLFrameBuffer::BindTexture(const uint32_t slot) const void OpenGLFrameBuffer::BindTexture(uint32_t attachmentIndex, uint32_t slot) const
{ {
Renderer::Submit([=](){ Ref<const OpenGLFrameBuffer> instance = this;
glActiveTexture(GL_TEXTURE0 + slot); Renderer::Submit([instance, attachmentIndex, slot]() {
glBindTextureUnit(slot, instance->m_ColorAttachments[attachmentIndex]);
if (m_Specification.Samples > 1) { });
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment);
} else {
glBindTexture(GL_TEXTURE_2D, m_ColorAttachment);
}
// glBindTexture(GL_TEXTURE_2D, m_ColorAttachment);
});
} }
} }

View File

@ -20,19 +20,28 @@ namespace Prism
void Resize(uint32_t width, uint32_t height, bool forceReCreate) override; void Resize(uint32_t width, uint32_t height, bool forceReCreate) override;
void BindTexture(uint32_t slot) const override; void BindTexture(uint32_t attachmentIndex, uint32_t slot) const override;
uint32_t GetWidth() const override { return m_Specification.Width; }
uint32_t GetHeight() const override { return m_Specification.Height; }
RendererID GetRendererID() const override { return m_RendererID; } RendererID GetRendererID() const override { return m_RendererID; }
RendererID GetColorAttachmentRendererID() const override { return m_ColorAttachment; } RendererID GetColorAttachmentRendererID(const int index = 0) const override { return m_ColorAttachments[index]; }
RendererID GetDepthAttachmentRendererID() const override { return m_DepthAttachment; } RendererID GetDepthAttachmentRendererID() const override { return m_DepthAttachment; }
virtual const FramebufferSpecification& GetSpecification() const override { return m_Specification; } virtual const FramebufferSpecification& GetSpecification() const override { return m_Specification; }
private: private:
FramebufferSpecification m_Specification;
FramebufferSpecification m_Specification;
RendererID m_RendererID = 0; RendererID m_RendererID = 0;
RendererID m_ColorAttachment = 0, m_DepthAttachment = 0; std::vector<RendererID> m_ColorAttachments;
RendererID m_DepthAttachment;
std::vector<FramebufferTextureFormat> m_ColorAttachmentFormats;
FramebufferTextureFormat m_DepthAttachmentFormat = FramebufferTextureFormat::None;
uint32_t m_Width = 0, m_Height = 0;
}; };
} }

View File

@ -118,6 +118,13 @@ namespace Prism
m_ShaderReloadedCallbacks.push_back(callback); m_ShaderReloadedCallbacks.push_back(callback);
} }
void OpenGLShader::SetBool(const std::string& name, bool value)
{
Renderer::Submit([=]() {
UploadUniformInt(name, value);
});
}
void OpenGLShader::SetFloat(const std::string& name, float value) void OpenGLShader::SetFloat(const std::string& name, float value)
{ {
Renderer::Submit([=]() { Renderer::Submit([=]() {
@ -125,7 +132,14 @@ namespace Prism
}); });
} }
void OpenGLShader::SetFloat3(const std::string& name, const glm::vec3& value) void OpenGLShader::SetFloat2(const std::string& name, const glm::vec2& value)
{
Renderer::Submit([=]() {
UploadUniformFloat2(name, value);
});
}
void OpenGLShader::SetFloat3(const std::string& name, const glm::vec3& value)
{ {
Renderer::Submit([=]() Renderer::Submit([=]()
{ {
@ -299,7 +313,7 @@ namespace Prism
std::vector<std::string> Tokenize(const std::string& string) std::vector<std::string> Tokenize(const std::string& string)
{ {
return SplitString(string, " \t\n"); return SplitString(string, " \t\n\r");
} }
std::vector<std::string> GetLines(const std::string& string) std::vector<std::string> GetLines(const std::string& string)
@ -372,6 +386,7 @@ namespace Prism
static bool IsTypeStringResource(const std::string& type) static bool IsTypeStringResource(const std::string& type)
{ {
if (type == "sampler1D") return true;
if (type == "sampler2D") return true; if (type == "sampler2D") return true;
if (type == "sampler2DMS") return true; if (type == "sampler2DMS") return true;
if (type == "samplerCube") return true; if (type == "samplerCube") return true;
@ -630,11 +645,11 @@ namespace Prism
} }
else if (resource->GetCount() > 1) else if (resource->GetCount() > 1)
{ {
resource->m_Register = 0; resource->m_Register = sampler;
uint32_t count = resource->GetCount(); uint32_t count = resource->GetCount();
int* samplers = new int[count]; int* samplers = new int[count];
for (uint32_t s = 0; s < count; s++) for (uint32_t s = 0; s < count; s++)
samplers[s] = s; samplers[s] = sampler++;
UploadUniformIntArray(resource->GetName(), samplers, count); UploadUniformIntArray(resource->GetName(), samplers, count);
delete[] samplers; delete[] samplers;
} }
@ -671,7 +686,7 @@ namespace Prism
std::vector<GLchar> infoLog(maxLength); std::vector<GLchar> infoLog(maxLength);
glGetShaderInfoLog(shaderRendererID, maxLength, &maxLength, &infoLog[0]); glGetShaderInfoLog(shaderRendererID, maxLength, &maxLength, &infoLog[0]);
PM_CORE_ERROR("Shader compile failed: \n{0}", &infoLog[0]); PM_CORE_ERROR("Shader compilation failed ({0}):\n{1}", m_AssetPath, &infoLog[0]);
glDeleteShader(shaderRendererID); glDeleteShader(shaderRendererID);
@ -692,7 +707,7 @@ namespace Prism
std::vector<GLchar> infoLog(maxLength); std::vector<GLchar> infoLog(maxLength);
glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]); glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);
PM_CORE_ERROR("Shader compilation failed:\n{0}", &infoLog[0]); PM_CORE_ERROR("Shader link failed ({0}):\n{1}", m_AssetPath, &infoLog[0]);
glDeleteProgram(program); glDeleteProgram(program);
@ -727,7 +742,7 @@ namespace Prism
if (location != -1) if (location != -1)
glUniform1i(location, value); glUniform1i(location, value);
else else
PM_CORE_WARN("Uniform '{0}' not found!", name); PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name);
} }
void OpenGLShader::UploadUniformFloat(const std::string& name, const float value) const void OpenGLShader::UploadUniformFloat(const std::string& name, const float value) const
@ -737,7 +752,7 @@ namespace Prism
if (location != -1) if (location != -1)
glUniform1f(location, value); glUniform1f(location, value);
else else
PM_CORE_WARN("Uniform '{0}' not found!", name); PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name);
} }
void OpenGLShader::UploadUniformFloat2(const std::string& name, const glm::vec2& values) const void OpenGLShader::UploadUniformFloat2(const std::string& name, const glm::vec2& values) const
@ -747,7 +762,7 @@ namespace Prism
if (location != -1) if (location != -1)
glUniform2fv(location,1, glm::value_ptr(values)); glUniform2fv(location,1, glm::value_ptr(values));
else else
PM_CORE_WARN("Uniform '{0}' not found!", name); PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name);
} }
void OpenGLShader::UploadUniformFloat3(const std::string& name, const glm::vec3& values) const void OpenGLShader::UploadUniformFloat3(const std::string& name, const glm::vec3& values) const
@ -757,7 +772,7 @@ namespace Prism
if (location != -1) if (location != -1)
glUniform3fv(location,1, glm::value_ptr(values)); glUniform3fv(location,1, glm::value_ptr(values));
else else
PM_CORE_WARN("Uniform '{0}' not found!", name); PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name);
} }
void OpenGLShader::UploadUniformFloat4(const std::string& name, const glm::vec4& values) const void OpenGLShader::UploadUniformFloat4(const std::string& name, const glm::vec4& values) const
@ -767,7 +782,7 @@ namespace Prism
if (location != -1) if (location != -1)
glUniform4fv(location,1, glm::value_ptr(values)); glUniform4fv(location,1, glm::value_ptr(values));
else else
PM_CORE_WARN("Uniform '{0}' not found!", name); PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name);
} }
void OpenGLShader::UploadUniformMat4(const std::string& name, const glm::mat4& values) const void OpenGLShader::UploadUniformMat4(const std::string& name, const glm::mat4& values) const
@ -777,7 +792,7 @@ namespace Prism
if (location != -1) if (location != -1)
glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(values)); glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(values));
else else
PM_CORE_WARN("Uniform '{0}' not found!", name); PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name);
} }
void OpenGLShader::ResolveAndSetUniforms(const Ref<OpenGLShaderUniformBufferDeclaration>& decl, Buffer buffer) void OpenGLShader::ResolveAndSetUniforms(const Ref<OpenGLShaderUniformBufferDeclaration>& decl, Buffer buffer)
@ -803,6 +818,9 @@ namespace Prism
uint32_t offset = uniform->GetOffset(); uint32_t offset = uniform->GetOffset();
switch (uniform->GetType()) switch (uniform->GetType())
{ {
case OpenGLShaderUniformDeclaration::Type::BOOL:
UploadUniformFloat(uniform->GetLocation(), *(bool*)&buffer.Data[offset]);
break;
case OpenGLShaderUniformDeclaration::Type::FLOAT32: case OpenGLShaderUniformDeclaration::Type::FLOAT32:
UploadUniformFloat(uniform->GetLocation(), *(float*)&buffer.Data[offset]); UploadUniformFloat(uniform->GetLocation(), *(float*)&buffer.Data[offset]);
break; break;
@ -839,6 +857,9 @@ namespace Prism
uint32_t offset = uniform->GetOffset(); uint32_t offset = uniform->GetOffset();
switch (uniform->GetType()) switch (uniform->GetType())
{ {
case OpenGLShaderUniformDeclaration::Type::BOOL:
UploadUniformFloat(uniform->GetLocation(), *(bool*)&buffer.Data[offset]);
break;
case OpenGLShaderUniformDeclaration::Type::FLOAT32: case OpenGLShaderUniformDeclaration::Type::FLOAT32:
UploadUniformFloat(uniform->GetLocation(), *(float*)&buffer.Data[offset]); UploadUniformFloat(uniform->GetLocation(), *(float*)&buffer.Data[offset]);
break; break;
@ -872,6 +893,9 @@ namespace Prism
{ {
switch (field.GetType()) switch (field.GetType())
{ {
case OpenGLShaderUniformDeclaration::Type::BOOL:
UploadUniformFloat(field.GetLocation(), *(bool*)&data[offset]);
break;
case OpenGLShaderUniformDeclaration::Type::FLOAT32: case OpenGLShaderUniformDeclaration::Type::FLOAT32:
UploadUniformFloat(field.GetLocation(), *(float*)&data[offset]); UploadUniformFloat(field.GetLocation(), *(float*)&data[offset]);
break; break;

View File

@ -27,7 +27,9 @@ namespace Prism
virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) override; virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) override;
virtual void AddShaderReloadedCallback(const ShaderReloadedCallback& callback) override; virtual void AddShaderReloadedCallback(const ShaderReloadedCallback& callback) override;
virtual void SetBool(const std::string& name, bool value) override;
virtual void SetFloat(const std::string& name, float value) override; virtual void SetFloat(const std::string& name, float value) override;
virtual void SetFloat2(const std::string& name, const glm::vec2& value) override;
virtual void SetFloat3(const std::string& name, const glm::vec3& value) override; virtual void SetFloat3(const std::string& name, const glm::vec3& value) override;
virtual void SetInt(const std::string& name, int value) override; virtual void SetInt(const std::string& name, int value) override;
virtual void SetMat4(const std::string& name, const glm::mat4& value) override; virtual void SetMat4(const std::string& name, const glm::mat4& value) override;

View File

@ -35,6 +35,7 @@ namespace Prism
{ {
switch (type) switch (type)
{ {
case OpenGLShaderUniformDeclaration::Type::BOOL: return 1;
case OpenGLShaderUniformDeclaration::Type::INT32: return 4; case OpenGLShaderUniformDeclaration::Type::INT32: return 4;
case OpenGLShaderUniformDeclaration::Type::FLOAT32: return 4; case OpenGLShaderUniformDeclaration::Type::FLOAT32: return 4;
case OpenGLShaderUniformDeclaration::Type::VEC2: return 4 * 2; case OpenGLShaderUniformDeclaration::Type::VEC2: return 4 * 2;
@ -48,14 +49,15 @@ namespace Prism
OpenGLShaderUniformDeclaration::Type OpenGLShaderUniformDeclaration::StringToType(const std::string& type) OpenGLShaderUniformDeclaration::Type OpenGLShaderUniformDeclaration::StringToType(const std::string& type)
{ {
if (type == "int") return Type::INT32; if (type == "bool") return OpenGLShaderUniformDeclaration::Type::BOOL;
if (type == "int32") return Type::INT32; if (type == "int") return OpenGLShaderUniformDeclaration::Type::INT32;
if (type == "float") return Type::FLOAT32; if (type == "int32") return OpenGLShaderUniformDeclaration::Type::INT32;
if (type == "vec2") return Type::VEC2; if (type == "float") return OpenGLShaderUniformDeclaration::Type::FLOAT32;
if (type == "vec3") return Type::VEC3; if (type == "vec2") return OpenGLShaderUniformDeclaration::Type::VEC2;
if (type == "vec4") return Type::VEC4; if (type == "vec3") return OpenGLShaderUniformDeclaration::Type::VEC3;
if (type == "mat3") return Type::MAT3; if (type == "vec4") return OpenGLShaderUniformDeclaration::Type::VEC4;
if (type == "mat4") return Type::MAT4; if (type == "mat3") return OpenGLShaderUniformDeclaration::Type::MAT3;
if (type == "mat4") return OpenGLShaderUniformDeclaration::Type::MAT4;
return Type::NONE; return Type::NONE;
} }

View File

@ -44,7 +44,7 @@ namespace Prism
public: public:
enum class Type enum class Type
{ {
NONE, FLOAT32, VEC2, VEC3, VEC4, MAT3, MAT4, INT32, STRUCT NONE, FLOAT32, VEC2, VEC3, VEC4, MAT3, MAT4, INT32, STRUCT, BOOL
}; };
private: private:
std::string m_Name; std::string m_Name;

View File

@ -67,7 +67,7 @@ namespace Prism
{ {
PM_CORE_INFO("Loading texture {0}, srgb={1}", path, srgb); 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); 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!"); if (!m_ImageData.Data) PM_CORE_ERROR("Could not read image file: {0}", path);
m_Format = TextureFormat::RGBA; m_Format = TextureFormat::RGBA;
} }

View File

@ -47,7 +47,12 @@ namespace Prism
glfwSetCursor(m_Window, m_ImGuiMouseCursors[imgui_cursor] ? m_ImGuiMouseCursors[imgui_cursor] : m_ImGuiMouseCursors[ImGuiMouseCursor_Arrow]); glfwSetCursor(m_Window, m_ImGuiMouseCursors[imgui_cursor] ? m_ImGuiMouseCursors[imgui_cursor] : m_ImGuiMouseCursors[ImGuiMouseCursor_Arrow]);
} }
void WindowsWindow::SetVSync(bool enable) void WindowsWindow::Maximize()
{
glfwMaximizeWindow(m_Window);
}
void WindowsWindow::SetVSync(const bool enable)
{ {
if (enable) if (enable)
glfwSwapInterval(1); glfwSwapInterval(1);

View File

@ -22,6 +22,9 @@ namespace Prism
inline uint32_t GetHeight() const override { return m_Data.Height; } inline uint32_t GetHeight() const override { return m_Data.Height; }
inline void SetEventCallback(const EventCallbackFn& callback) override { m_Data.EventCallback = callback; } inline void SetEventCallback(const EventCallbackFn& callback) override { m_Data.EventCallback = callback; }
virtual void Maximize() override;
bool const IsVSync() const override { return m_Data.VSync; } bool const IsVSync() const override { return m_Data.VSync; }
void SetVSync(bool enable) override; void SetVSync(bool enable) override;
virtual std::pair<uint32_t, uint32_t> GetSize() const override { return { m_Data.Width, m_Data.Height }; } virtual std::pair<uint32_t, uint32_t> GetSize() const override { return { m_Data.Width, m_Data.Height }; }

View File

@ -11,11 +11,40 @@
namespace Prism namespace Prism
{ {
enum class FramebufferFormat enum class FramebufferTextureFormat
{ {
None = 0, None = 0,
// Color
RGBA8 = 1, RGBA8 = 1,
RGBA16F = 2 RGBA16F = 2,
RGBA32F = 3,
RG32F = 4,
// Depth/stencil
DEPTH32F = 5,
DEPTH24STENCIL8 = 6,
// Defaults
Depth = DEPTH24STENCIL8
};
struct FramebufferTextureSpecification
{
FramebufferTextureSpecification() = default;
FramebufferTextureSpecification(FramebufferTextureFormat format) : TextureFormat(format) {}
FramebufferTextureFormat TextureFormat;
// TODO: filtering/wrap
};
struct FramebufferAttachmentSpecification
{
FramebufferAttachmentSpecification() = default;
FramebufferAttachmentSpecification(const std::initializer_list<FramebufferTextureSpecification>& attachments)
: Attachments(attachments) {}
std::vector<FramebufferTextureSpecification> Attachments;
}; };
struct FramebufferSpecification struct FramebufferSpecification
@ -23,13 +52,17 @@ namespace Prism
uint32_t Width = 1280; uint32_t Width = 1280;
uint32_t Height = 720; uint32_t Height = 720;
glm::vec4 ClearColor; glm::vec4 ClearColor;
FramebufferFormat Format; FramebufferAttachmentSpecification Attachments;
uint32_t Samples = 1; uint32_t Samples = 1; // multisampling
// TODO: Temp, needs scale
bool NoResize = false;
// SwapChainTarget = screen buffer (i.e. no framebuffer) // SwapChainTarget = screen buffer (i.e. no framebuffer)
bool SwapChainTarget = false; bool SwapChainTarget = false;
}; };
class PRISM_API FrameBuffer : public RefCounted class PRISM_API FrameBuffer : public RefCounted
{ {
public: public:
@ -44,10 +77,13 @@ namespace Prism
virtual void Resize(uint32_t width, uint32_t height, bool forceReCreate = false) = 0; virtual void Resize(uint32_t width, uint32_t height, bool forceReCreate = false) = 0;
virtual void BindTexture(uint32_t slot = 0) const = 0; virtual void BindTexture(uint32_t attachmentIndex = 0, uint32_t slot = 0) const = 0;
virtual uint32_t GetWidth() const = 0;
virtual uint32_t GetHeight() const = 0;
virtual RendererID GetRendererID() const = 0; virtual RendererID GetRendererID() const = 0;
virtual RendererID GetColorAttachmentRendererID() const = 0; virtual RendererID GetColorAttachmentRendererID(int index = 0) const = 0;
virtual RendererID GetDepthAttachmentRendererID() const = 0; virtual RendererID GetDepthAttachmentRendererID() const = 0;
}; };

View File

@ -21,7 +21,8 @@ namespace Prism
{ {
None = BIT(0), None = BIT(0),
DepthTest = BIT(1), DepthTest = BIT(1),
Blend = BIT(2) Blend = BIT(2),
TwoSided = BIT(3),
}; };
class PRISM_API Material : public RefCounted class PRISM_API Material : public RefCounted
@ -38,6 +39,9 @@ namespace Prism
uint32_t GetFlags() const { return m_MaterialFlags; } uint32_t GetFlags() const { return m_MaterialFlags; }
void SetFlag(MaterialFlag flag) { m_MaterialFlags |= (uint32_t)flag; } void SetFlag(MaterialFlag flag) { m_MaterialFlags |= (uint32_t)flag; }
Ref<Shader> GetShader() { return m_Shader; }
template <typename T> template <typename T>
T& Get(const std::string& name) T& Get(const std::string& name)
{ {
@ -83,13 +87,14 @@ namespace Prism
{ {
Set(name, (const Ref<Texture>&)texture); Set(name, (const Ref<Texture>&)texture);
} }
ShaderResourceDeclaration* FindResourceDeclaration(const std::string& name);
private: private:
void AllocateStorage(); void AllocateStorage();
void OnShaderReloaded(); void OnShaderReloaded();
void BindTextures() const; void BindTextures() const;
ShaderUniformDeclaration* FindUniformDeclaration(const std::string& name); ShaderUniformDeclaration* FindUniformDeclaration(const std::string& name);
ShaderResourceDeclaration* FindResourceDeclaration(const std::string& name);
Buffer& GetUniformBufferTarget(ShaderUniformDeclaration* uniformDeclaration); Buffer& GetUniformBufferTarget(ShaderUniformDeclaration* uniformDeclaration);
private: private:
@ -153,8 +158,8 @@ namespace Prism
void Set(const std::string& name, const T& value) void Set(const std::string& name, const T& value)
{ {
const auto decl = m_Material->FindUniformDeclaration(name); const auto decl = m_Material->FindUniformDeclaration(name);
if (!decl) PM_CORE_WARN("Could not find uniform with name {0}", name); if (!decl) PM_CORE_ERROR("Could not find uniform with name {0}", name);
auto& buffer = GetUniformBufferTarget(decl); const auto& buffer = GetUniformBufferTarget(decl);
buffer.Write(&value, decl->GetSize(), decl->GetOffset()); buffer.Write(&value, decl->GetSize(), decl->GetOffset());
m_OverriddenValues.insert(name); m_OverriddenValues.insert(name);
@ -207,6 +212,7 @@ namespace Prism
template <typename T> template <typename T>
void Material::Set(const std::string& name, const T& value) void Material::Set(const std::string& name, const T& value)
{ {
auto decl = FindUniformDeclaration(name); auto decl = FindUniformDeclaration(name);
PM_CORE_ASSERT(decl, "Could not find uniform with name x"); PM_CORE_ASSERT(decl, "Could not find uniform with name x");
const auto& buffer = GetUniformBufferTarget(decl); const auto& buffer = GetUniformBufferTarget(decl);

View File

@ -50,7 +50,7 @@ namespace Prism
virtual void write(const char* message) override virtual void write(const char* message) override
{ {
PM_CORE_ERROR("Assimp: {0}", message); PM_CORE_WARN("Assimp: {0}", message);
} }
}; };
@ -131,10 +131,11 @@ namespace Prism
submesh.BaseIndex = indexCount; submesh.BaseIndex = indexCount;
submesh.MaterialIndex = mesh->mMaterialIndex; submesh.MaterialIndex = mesh->mMaterialIndex;
submesh.IndexCount = mesh->mNumFaces * 3; submesh.IndexCount = mesh->mNumFaces * 3;
submesh.VertexCount = mesh->mNumVertices;
// m_Submeshes.push_back(submesh); // m_Submeshes.push_back(submesh);
submesh.MeshName = mesh->mName.C_Str(); submesh.MeshName = mesh->mName.C_Str();
vertexCount += mesh->mNumVertices; vertexCount += submesh.VertexCount;
indexCount += submesh.IndexCount; indexCount += submesh.IndexCount;
PM_CORE_ASSERT(mesh->HasPositions(), "Meshes require positions."); PM_CORE_ASSERT(mesh->HasPositions(), "Meshes require positions.");

View File

@ -94,6 +94,7 @@ namespace Prism
uint32_t BaseIndex; uint32_t BaseIndex;
uint32_t MaterialIndex; uint32_t MaterialIndex;
uint32_t IndexCount; uint32_t IndexCount;
uint32_t VertexCount;
glm::mat4 Transform; glm::mat4 Transform;
AABB BoundingBox; AABB BoundingBox;
@ -126,6 +127,8 @@ namespace Prism
const std::vector<Ref<Texture2D>>& GetTextures() const { return m_Textures; } const std::vector<Ref<Texture2D>>& GetTextures() const { return m_Textures; }
const std::string& GetFilePath() const { return m_FilePath; } const std::string& GetFilePath() const { return m_FilePath; }
bool IsAnimated() const { return m_IsAnimated; }
std::vector<Triangle> GetTriangleCache(const uint32_t index) const { return m_TriangleCache.at(index); } std::vector<Triangle> GetTriangleCache(const uint32_t index) const { return m_TriangleCache.at(index); }
private: private:
void BoneTransform(float time); void BoneTransform(float time);

View File

@ -135,31 +135,46 @@ namespace Prism
void Renderer::SubmitQuad(Ref<MaterialInstance>& material, const glm::mat4& transform) void Renderer::SubmitQuad(Ref<MaterialInstance>& material, const glm::mat4& transform)
{ {
bool depthTest = true; bool depthTest = true;
bool cullFace = true;
if (material) if (material)
{ {
material->Bind(); material->Bind();
depthTest = material->GetFlag(MaterialFlag::DepthTest); depthTest = material->GetFlag(MaterialFlag::DepthTest);
cullFace = !material->GetFlag(MaterialFlag::TwoSided);
auto shader = material->GetShader(); auto shader = material->GetShader();
shader->SetMat4("u_Transform", transform); shader->SetMat4("u_Transform", transform);
} }
if (cullFace)
Submit([]() { glEnable(GL_CULL_FACE); });
else
Submit([]() { glDisable(GL_CULL_FACE); });
s_Data.m_FullscreenQuadVertexBuffer->Bind(); s_Data.m_FullscreenQuadVertexBuffer->Bind();
s_Data.m_FullscreenQuadPipeline->Bind(); s_Data.m_FullscreenQuadPipeline->Bind();
s_Data.m_FullscreenQuadIndexBuffer->Bind(); s_Data.m_FullscreenQuadIndexBuffer->Bind();
Renderer::DrawIndexed(6, PrimitiveType::Triangles, depthTest); DrawIndexed(6, PrimitiveType::Triangles, depthTest);
} }
void Renderer::SubmitFullscreenQuad(Ref<MaterialInstance> material) void Renderer::SubmitFullscreenQuad(Ref<MaterialInstance> material)
{ {
bool depthTest = true; bool depthTest = true;
bool cullFace = true;
if (material) if (material)
{ {
material->Bind(); material->Bind();
depthTest = material->GetFlag(MaterialFlag::DepthTest); depthTest = material->GetFlag(MaterialFlag::DepthTest);
cullFace = !material->GetFlag(MaterialFlag::TwoSided);
} }
if (cullFace)
Submit([]() { glEnable(GL_CULL_FACE); });
else
Submit([]() { glDisable(GL_CULL_FACE); });
s_Data.m_FullscreenQuadVertexBuffer->Bind(); s_Data.m_FullscreenQuadVertexBuffer->Bind();
s_Data.m_FullscreenQuadPipeline->Bind(); s_Data.m_FullscreenQuadPipeline->Bind();
s_Data.m_FullscreenQuadIndexBuffer->Bind(); s_Data.m_FullscreenQuadIndexBuffer->Bind();
@ -201,6 +216,36 @@ namespace Prism
else else
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
if (!material->GetFlag(MaterialFlag::TwoSided))
glEnable(GL_CULL_FACE);
else
glDisable(GL_CULL_FACE);
glDrawElementsBaseVertex(GL_TRIANGLES, submesh.IndexCount, GL_UNSIGNED_INT, (void*)(sizeof(uint32_t) * submesh.BaseIndex), submesh.BaseVertex);
});
}
}
void Renderer::SubmitMeshWithShader(Ref<Mesh> mesh, const glm::mat4& transform, Ref<Shader> shader)
{
mesh->m_VertexBuffer->Bind();
mesh->m_Pipeline->Bind();
mesh->m_IndexBuffer->Bind();
for (Submesh& submesh : mesh->m_Submeshes)
{
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("]");
shader->SetMat4(uniformName, mesh->m_BoneTransforms[i]);
}
}
shader->SetMat4("u_Transform", transform * submesh.Transform);
Submit([submesh]() {
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);
}); });
} }

View File

@ -55,6 +55,7 @@ namespace Prism
static void SubmitQuad(Ref<MaterialInstance>& material, const glm::mat4& transform = glm::mat4(1.0f)); static void SubmitQuad(Ref<MaterialInstance>& material, const glm::mat4& transform = glm::mat4(1.0f));
static void SubmitFullscreenQuad(Ref<MaterialInstance> material); static void SubmitFullscreenQuad(Ref<MaterialInstance> material);
static void SubmitMesh(Ref<Mesh>& mesh, const glm::mat4& transform, const Ref<MaterialInstance>& overrideMaterial = nullptr); static void SubmitMesh(Ref<Mesh>& mesh, const glm::mat4& transform, const Ref<MaterialInstance>& overrideMaterial = nullptr);
static void SubmitMeshWithShader(Ref<Mesh> mesh, const glm::mat4& transform, Ref<Shader> shader);
static void DrawAABB(const AABB& aabb, const glm::mat4& transform, const glm::vec4& color = glm::vec4(1.0f)); static void DrawAABB(const AABB& aabb, const glm::mat4& transform, const glm::vec4& color = glm::vec4(1.0f));
static void DrawAABB(const Ref<Mesh>& mesh,const glm::mat4& transform, const glm::vec4& color = glm::vec4(1.0f)); static void DrawAABB(const Ref<Mesh>& mesh,const glm::mat4& transform, const glm::vec4& color = glm::vec4(1.0f));

View File

@ -187,7 +187,7 @@ namespace Prism
s_Data.QuadVertexBuffer->Bind(); s_Data.QuadVertexBuffer->Bind();
s_Data.QuadPipeline->Bind(); s_Data.QuadPipeline->Bind();
s_Data.QuadIndexBuffer->Bind(); s_Data.QuadIndexBuffer->Bind();
Renderer::DrawIndexed(s_Data.QuadIndexCount, PrimitiveType::Triangles, s_Data.DepthTest); Renderer::DrawIndexed(s_Data.QuadIndexCount, PrimitiveType::Triangles, false);
s_Data.Stats.DrawCalls++; s_Data.Stats.DrawCalls++;
} }

View File

@ -0,0 +1,15 @@
//
// Created by sfd on 26-1-1.
//
#include "SceneEnvironment.h"
#include "SceneRenderer.h"
namespace Prism
{
Environment Environment::Load(const std::string& filepath)
{
auto [radiance, irradiance] = SceneRenderer::CreateEnvironmentMap(filepath);
return { filepath, radiance, irradiance };
}
}

View File

@ -0,0 +1,24 @@
//
// Created by sfd on 26-1-1.
//
#ifndef SCENEENVIRONMENT_H
#define SCENEENVIRONMENT_H
#include "Texture.h"
#include "Prism/Core/Ref.h"
namespace Prism
{
struct Environment
{
std::string FilePath;
Ref<TextureCube> RadianceMap;
Ref<TextureCube> IrradianceMap;
static PRISM_API Environment Load(const std::string& filepath);
};
}
#endif //SCENEENVIRONMENT_H

View File

@ -12,6 +12,7 @@
#include "Renderer2D.h" #include "Renderer2D.h"
#include "RenderPass.h" #include "RenderPass.h"
#include "glad/glad.h" #include "glad/glad.h"
#include "Prism/Core/Timer.h"
namespace Prism namespace Prism
{ {
@ -21,6 +22,8 @@ namespace Prism
struct SceneInfo struct SceneInfo
{ {
SceneRendererCamera SceneCamera; SceneRendererCamera SceneCamera;
float SceneEnvironmentIntensity;
LightEnvironment SceneLightEnvironment;
// Resources // Resources
Ref<MaterialInstance> SkyboxMaterial; Ref<MaterialInstance> SkyboxMaterial;
@ -30,9 +33,37 @@ namespace Prism
Ref<Texture2D> BRDFLUT; Ref<Texture2D> BRDFLUT;
Ref<Shader> CompositeShader; Ref<Shader> CompositeShader;
Ref<Shader> BloomBlurShader;
Ref<Shader> BloomBlendShader;
Ref<RenderPass> GeoPass; Ref<RenderPass> GeoPass;
Ref<RenderPass> CompositePass; Ref<RenderPass> CompositePass;
Ref<RenderPass> BloomBlurPass[2];
Ref<RenderPass> BloomBlendPass;
Ref<Shader> ShadowMapShader, ShadowMapAnimShader;
Ref<RenderPass> ShadowMapRenderPass[4];
float ShadowMapSize = 20.0f;
float LightDistance = 0.1f;
glm::mat4 LightMatrices[4];
glm::mat4 LightViewMatrix;
float CascadeSplitLambda = 0.91f;
glm::vec4 CascadeSplits;
float CascadeFarPlaneOffset = 15.0f, CascadeNearPlaneOffset = -15.0f;
bool ShowCascades = false;
bool SoftShadows = true;
float LightSize = 0.5f;
float MaxShadowDistance = 200.0f;
float ShadowFade = 25.0f;
float CascadeTransitionFade = 1.0f;
bool CascadeFading = true;
bool EnableBloom = false;
float BloomThreshold = 1.5f;
glm::vec2 FocusPoint = { 0.5f, 0.5f };
RendererID ShadowMapSampler;
struct DrawCommand struct DrawCommand
{ {
@ -43,23 +74,34 @@ namespace Prism
std::vector<DrawCommand> DrawList; std::vector<DrawCommand> DrawList;
std::vector<DrawCommand> SelectedMeshDrawList; std::vector<DrawCommand> SelectedMeshDrawList;
std::vector<DrawCommand> ColliderDrawList; std::vector<DrawCommand> ColliderDrawList;
std::vector<DrawCommand> ShadowPassDrawList;
// Grid // Grid
Ref<MaterialInstance> GridMaterial; Ref<MaterialInstance> GridMaterial;
Ref<MaterialInstance> OutlineMaterial; Ref<MaterialInstance> OutlineMaterial, OutlineAnimMaterial;
Ref<MaterialInstance> ColliderMaterial; Ref<MaterialInstance> ColliderMaterial;
SceneRendererOptions Options; SceneRendererOptions Options;
}; };
struct SceneRendererStats
{
float ShadowPass = 0.0f;
float GeometryPass = 0.0f;
float CompositePass = 0.0f;
Timer ShadowPassTimer;
Timer GeometryPassTimer;
Timer CompositePassTimer;
};
static SceneRendererData s_Data; static SceneRendererData s_Data;
static SceneRendererStats s_Stats;
void SceneRenderer::Init() void SceneRenderer::Init()
{ {
FramebufferSpecification geoFramebufferSpec; FramebufferSpecification geoFramebufferSpec;
geoFramebufferSpec.Width = 1280; geoFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F, FramebufferTextureFormat::RGBA16F, FramebufferTextureFormat::Depth };
geoFramebufferSpec.Height = 720;
geoFramebufferSpec.Format = FramebufferFormat::RGBA16F;
geoFramebufferSpec.Samples = 8; geoFramebufferSpec.Samples = 8;
geoFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; geoFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
@ -68,16 +110,34 @@ namespace Prism
s_Data.GeoPass = RenderPass::Create(geoRenderPassSpec); s_Data.GeoPass = RenderPass::Create(geoRenderPassSpec);
FramebufferSpecification compFramebufferSpec; FramebufferSpecification compFramebufferSpec;
compFramebufferSpec.Width = 1280; compFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8 };
compFramebufferSpec.Height = 720; compFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
compFramebufferSpec.Format = FramebufferFormat::RGBA8;
compFramebufferSpec.ClearColor = { 0.5f, 0.1f, 0.1f, 1.0f };
RenderPassSpecification compRenderPassSpec; RenderPassSpecification compRenderPassSpec;
compRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(compFramebufferSpec); compRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(compFramebufferSpec);
s_Data.CompositePass = RenderPass::Create(compRenderPassSpec); s_Data.CompositePass = RenderPass::Create(compRenderPassSpec);
FramebufferSpecification bloomBlurFramebufferSpec;
bloomBlurFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F };
bloomBlurFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
RenderPassSpecification bloomBlurRenderPassSpec;
bloomBlurRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlurFramebufferSpec);
s_Data.BloomBlurPass[0] = RenderPass::Create(bloomBlurRenderPassSpec);
bloomBlurRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlurFramebufferSpec);
s_Data.BloomBlurPass[1] = RenderPass::Create(bloomBlurRenderPassSpec);
FramebufferSpecification bloomBlendFramebufferSpec;
bloomBlendFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8 };
bloomBlendFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
RenderPassSpecification bloomBlendRenderPassSpec;
bloomBlendRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlendFramebufferSpec);
s_Data.BloomBlendPass = RenderPass::Create(bloomBlendRenderPassSpec);
s_Data.CompositeShader = Shader::Create("assets/shaders/SceneComposite.glsl"); s_Data.CompositeShader = Shader::Create("assets/shaders/SceneComposite.glsl");
s_Data.BloomBlurShader = Shader::Create("assets/shaders/BloomBlur.glsl");
s_Data.BloomBlendShader = Shader::Create("assets/shaders/BloomBlend.glsl");
s_Data.BRDFLUT = Texture2D::Create("assets/textures/BRDF_LUT.tga"); s_Data.BRDFLUT = Texture2D::Create("assets/textures/BRDF_LUT.tga");
@ -98,6 +158,41 @@ namespace Prism
const auto colliderShader = Shader::Create("assets/shaders/Collider.glsl"); const auto colliderShader = Shader::Create("assets/shaders/Collider.glsl");
s_Data.ColliderMaterial = MaterialInstance::Create(Material::Create(colliderShader)); s_Data.ColliderMaterial = MaterialInstance::Create(Material::Create(colliderShader));
s_Data.ColliderMaterial->SetFlag(MaterialFlag::DepthTest, false); s_Data.ColliderMaterial->SetFlag(MaterialFlag::DepthTest, false);
auto outlineAnimShader = Shader::Create("assets/shaders/Outline_Anim.glsl");
s_Data.OutlineAnimMaterial = MaterialInstance::Create(Material::Create(outlineAnimShader));
s_Data.OutlineAnimMaterial->SetFlag(MaterialFlag::DepthTest, false);
// Shadow Map
s_Data.ShadowMapShader = Shader::Create("assets/shaders/ShadowMap.glsl");
s_Data.ShadowMapAnimShader = Shader::Create("assets/shaders/ShadowMap_Anim.glsl");
FramebufferSpecification shadowMapFramebufferSpec;
shadowMapFramebufferSpec.Width = 4096;
shadowMapFramebufferSpec.Height = 4096;
shadowMapFramebufferSpec.Attachments = { FramebufferTextureFormat::DEPTH32F };
shadowMapFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
shadowMapFramebufferSpec.NoResize = true;
// 4 cascades
for (int i = 0; i < 4; i++)
{
RenderPassSpecification shadowMapRenderPassSpec;
shadowMapRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(shadowMapFramebufferSpec);
s_Data.ShadowMapRenderPass[i] = RenderPass::Create(shadowMapRenderPassSpec);
}
Renderer::Submit([]()
{
glGenSamplers(1, &s_Data.ShadowMapSampler);
// Setup the shadowmap depth sampler
glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
});
} }
void SceneRenderer::SetViewportSize(uint32_t width, uint32_t height) void SceneRenderer::SetViewportSize(uint32_t width, uint32_t height)
@ -115,7 +210,9 @@ namespace Prism
s_Data.SceneData.SceneCamera = camera; s_Data.SceneData.SceneCamera = camera;
s_Data.SceneData.SkyboxMaterial = scene->m_SkyboxMaterial; s_Data.SceneData.SkyboxMaterial = scene->m_SkyboxMaterial;
s_Data.SceneData.SceneEnvironment = scene->m_Environment; s_Data.SceneData.SceneEnvironment = scene->m_Environment;
s_Data.SceneData.SceneEnvironmentIntensity = scene->m_EnvironmentIntensity;
s_Data.SceneData.ActiveLight = scene->m_Light; s_Data.SceneData.ActiveLight = scene->m_Light;
s_Data.SceneData.SceneLightEnvironment = scene->m_LightEnvironment;
} }
void SceneRenderer::EndScene() void SceneRenderer::EndScene()
@ -132,11 +229,13 @@ namespace Prism
// TODO: Culling, sorting, etc. // TODO: Culling, sorting, etc.
s_Data.DrawList.push_back({ mesh, overrideMaterial, transform }); s_Data.DrawList.push_back({ mesh, overrideMaterial, transform });
s_Data.ShadowPassDrawList.push_back({ mesh, overrideMaterial, transform });
} }
void SceneRenderer::SubmitSelectedMesh(const Ref<Mesh>& mesh, const glm::mat4& transform) void SceneRenderer::SubmitSelectedMesh(const Ref<Mesh>& mesh, const glm::mat4& transform)
{ {
s_Data.SelectedMeshDrawList.push_back({ mesh, nullptr, transform }); s_Data.SelectedMeshDrawList.push_back({ mesh, nullptr, transform });
s_Data.ShadowPassDrawList.push_back({ mesh, nullptr, transform });
} }
void SceneRenderer::SubmitColliderMesh(const BoxColliderComponent& component, const glm::mat4& parentTransform) void SceneRenderer::SubmitColliderMesh(const BoxColliderComponent& component, const glm::mat4& parentTransform)
@ -156,7 +255,8 @@ namespace Prism
void SceneRenderer::SubmitColliderMesh(const MeshColliderComponent& component, const glm::mat4& parentTransform) void SceneRenderer::SubmitColliderMesh(const MeshColliderComponent& component, const glm::mat4& parentTransform)
{ {
s_Data.ColliderDrawList.push_back({ component.ProcessedMesh, nullptr, parentTransform }); for (const auto debugMesh : component.ProcessedMeshes)
s_Data.ColliderDrawList.push_back({ debugMesh, nullptr, parentTransform });
} }
@ -237,6 +337,11 @@ namespace Prism
return nullptr; return nullptr;
} }
void SceneRenderer::SetFocusPoint(const glm::vec2& point)
{
s_Data.FocusPoint = point;
}
uint32_t SceneRenderer::GetFinalColorBufferRendererID() uint32_t SceneRenderer::GetFinalColorBufferRendererID()
{ {
return s_Data.CompositePass->GetSpecification().TargetFramebuffer->GetColorAttachmentRendererID(); return s_Data.CompositePass->GetSpecification().TargetFramebuffer->GetColorAttachmentRendererID();
@ -247,16 +352,54 @@ namespace Prism
return s_Data.Options; return s_Data.Options;
} }
void SceneRenderer::FlushDrawList() void SceneRenderer::FlushDrawList()
{ {
PM_CORE_ASSERT(!s_Data.ActiveScene); PM_CORE_ASSERT(!s_Data.ActiveScene);
GeometryPass(); memset(&s_Stats, 0, sizeof(SceneRendererStats));
CompositePass();
{
Renderer::Submit([]()
{
s_Stats.ShadowPassTimer.Reset();
});
ShadowMapPass();
Renderer::Submit([]
{
s_Stats.ShadowPass = s_Stats.ShadowPassTimer.ElapsedMillis();
});
}
{
Renderer::Submit([]()
{
s_Stats.GeometryPassTimer.Reset();
});
GeometryPass();
Renderer::Submit([]
{
s_Stats.GeometryPass = s_Stats.GeometryPassTimer.ElapsedMillis();
});
}
{
Renderer::Submit([]()
{
s_Stats.CompositePassTimer.Reset();
});
CompositePass();
Renderer::Submit([]
{
s_Stats.CompositePass = s_Stats.CompositePassTimer.ElapsedMillis();
});
// BloomBlurPass();
}
s_Data.DrawList.clear(); s_Data.DrawList.clear();
s_Data.SelectedMeshDrawList.clear(); s_Data.SelectedMeshDrawList.clear();
s_Data.ColliderDrawList.clear(); s_Data.ColliderDrawList.clear();
s_Data.ShadowPassDrawList.clear();
s_Data.SceneData = {}; s_Data.SceneData = {};
} }
@ -283,21 +426,43 @@ namespace Prism
}); });
} }
const auto viewProjection = s_Data.SceneData.SceneCamera.Camera.GetProjectionMatrix() * s_Data.SceneData.SceneCamera.ViewMatrix; const auto& sceneCamera = s_Data.SceneData.SceneCamera;
glm::vec3 cameraPosition = glm::inverse(s_Data.SceneData.SceneCamera.ViewMatrix)[3];
const auto viewProjection = sceneCamera.Camera.GetProjectionMatrix() * sceneCamera.ViewMatrix;
const glm::vec3 cameraPosition = glm::inverse(s_Data.SceneData.SceneCamera.ViewMatrix)[3]; // TODO: Negate instead
// Skybox // Skybox
auto skyboxShader = s_Data.SceneData.SkyboxMaterial->GetShader(); auto skyboxShader = s_Data.SceneData.SkyboxMaterial->GetShader();
s_Data.SceneData.SkyboxMaterial->Set("u_InverseVP", glm::inverse(viewProjection)); s_Data.SceneData.SkyboxMaterial->Set("u_InverseVP", glm::inverse(viewProjection));
s_Data.SceneData.SkyboxMaterial->Set("u_SkyIntensity", s_Data.SceneData.SceneEnvironmentIntensity);
// s_Data.SceneInfo.EnvironmentIrradianceMap->Bind(0); // s_Data.SceneInfo.EnvironmentIrradianceMap->Bind(0);
Renderer::SubmitFullscreenQuad(s_Data.SceneData.SkyboxMaterial); Renderer::SubmitFullscreenQuad(s_Data.SceneData.SkyboxMaterial);
const float aspectRatio = (float)s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetWidth() / (float)s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetHeight();
float frustumSize = 2.0f * sceneCamera.Near * glm::tan(sceneCamera.FOV * 0.5f) * aspectRatio;
// Render entities // Render entities
for (auto& dc : s_Data.DrawList) for (auto& dc : s_Data.DrawList)
{ {
auto baseMaterial = dc.mesh->GetMaterial(); auto baseMaterial = dc.mesh->GetMaterial();
baseMaterial->Set("u_ViewProjectionMatrix", viewProjection); baseMaterial->Set("u_ViewProjectionMatrix", viewProjection);
baseMaterial->Set("u_ViewMatrix", sceneCamera.ViewMatrix);
baseMaterial->Set("u_CameraPosition", cameraPosition); baseMaterial->Set("u_CameraPosition", cameraPosition);
baseMaterial->Set("u_LightMatrixCascade0", s_Data.LightMatrices[0]);
baseMaterial->Set("u_LightMatrixCascade1", s_Data.LightMatrices[1]);
baseMaterial->Set("u_LightMatrixCascade2", s_Data.LightMatrices[2]);
baseMaterial->Set("u_LightMatrixCascade3", s_Data.LightMatrices[3]);
baseMaterial->Set("u_ShowCascades", s_Data.ShowCascades);
baseMaterial->Set("u_LightView", s_Data.LightViewMatrix);
baseMaterial->Set("u_CascadeSplits", s_Data.CascadeSplits);
baseMaterial->Set("u_SoftShadows", s_Data.SoftShadows);
baseMaterial->Set("u_LightSize", s_Data.LightSize);
baseMaterial->Set("u_MaxShadowDistance", s_Data.MaxShadowDistance);
baseMaterial->Set("u_ShadowFade", s_Data.ShadowFade);
baseMaterial->Set("u_CascadeFading", s_Data.CascadeFading);
baseMaterial->Set("u_CascadeTransitionFade", s_Data.CascadeTransitionFade);
baseMaterial->Set("u_IBLContribution", s_Data.SceneData.SceneEnvironmentIntensity);
// Environment (TODO: don't do this per mesh) // Environment (TODO: don't do this per mesh)
baseMaterial->Set("u_EnvRadianceTex", s_Data.SceneData.SceneEnvironment.RadianceMap); baseMaterial->Set("u_EnvRadianceTex", s_Data.SceneData.SceneEnvironment.RadianceMap);
@ -305,12 +470,109 @@ namespace Prism
baseMaterial->Set("u_BRDFLUTTexture", s_Data.BRDFLUT); baseMaterial->Set("u_BRDFLUTTexture", s_Data.BRDFLUT);
// Set lights (TODO: move to light environment and don't do per mesh) // Set lights (TODO: move to light environment and don't do per mesh)
baseMaterial->Set("lights", s_Data.SceneData.ActiveLight); auto directionalLight = s_Data.SceneData.SceneLightEnvironment.DirectionalLights[0];
baseMaterial->Set("u_DirectionalLights", directionalLight);
auto overrideMaterial = nullptr; // dc.Material; auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMapTexture");
if (rd)
{
auto reg = rd->GetRegister();
auto tex = s_Data.ShadowMapRenderPass[0]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID();
auto tex1 = s_Data.ShadowMapRenderPass[1]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID();
auto tex2 = s_Data.ShadowMapRenderPass[2]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID();
auto tex3 = s_Data.ShadowMapRenderPass[3]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID();
Renderer::Submit([reg, tex, tex1, tex2, tex3]() mutable
{
// 4 cascades
glBindTextureUnit(reg, tex);
glBindSampler(reg++, s_Data.ShadowMapSampler);
glBindTextureUnit(reg, tex1);
glBindSampler(reg++, s_Data.ShadowMapSampler);
glBindTextureUnit(reg, tex2);
glBindSampler(reg++, s_Data.ShadowMapSampler);
glBindTextureUnit(reg, tex3);
glBindSampler(reg++, s_Data.ShadowMapSampler);
});
}
constexpr auto overrideMaterial = nullptr; // dc.Material;
Renderer::SubmitMesh(dc.mesh, dc.Transform, overrideMaterial); Renderer::SubmitMesh(dc.mesh, dc.Transform, overrideMaterial);
} }
if (outline)
{
Renderer::Submit([]()
{
glStencilFunc(GL_ALWAYS, 1, 0xff);
glStencilMask(0xff);
});
}
for (auto& dc : s_Data.SelectedMeshDrawList)
{
auto baseMaterial = dc.mesh->GetMaterial();
baseMaterial->Set("u_ViewProjectionMatrix", viewProjection);
baseMaterial->Set("u_ViewMatrix", sceneCamera.ViewMatrix);
baseMaterial->Set("u_CameraPosition", cameraPosition);
baseMaterial->Set("u_CascadeSplits", s_Data.CascadeSplits);
baseMaterial->Set("u_ShowCascades", s_Data.ShowCascades);
baseMaterial->Set("u_SoftShadows", s_Data.SoftShadows);
baseMaterial->Set("u_LightSize", s_Data.LightSize);
baseMaterial->Set("u_MaxShadowDistance", s_Data.MaxShadowDistance);
baseMaterial->Set("u_ShadowFade", s_Data.ShadowFade);
baseMaterial->Set("u_CascadeFading", s_Data.CascadeFading);
baseMaterial->Set("u_CascadeTransitionFade", s_Data.CascadeTransitionFade);
baseMaterial->Set("u_IBLContribution", s_Data.SceneData.SceneEnvironmentIntensity);
// 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);
baseMaterial->Set("u_LightMatrixCascade0", s_Data.LightMatrices[0]);
baseMaterial->Set("u_LightMatrixCascade1", s_Data.LightMatrices[1]);
baseMaterial->Set("u_LightMatrixCascade2", s_Data.LightMatrices[2]);
baseMaterial->Set("u_LightMatrixCascade3", s_Data.LightMatrices[3]);
// Set lights (TODO: move to light environment and don't do per mesh)
baseMaterial->Set("u_DirectionalLights", s_Data.SceneData.SceneLightEnvironment.DirectionalLights[0]);
auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMapTexture");
if (rd)
{
auto reg = rd->GetRegister();
auto tex = s_Data.ShadowMapRenderPass[0]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID();
auto tex1 = s_Data.ShadowMapRenderPass[1]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID();
auto tex2 = s_Data.ShadowMapRenderPass[2]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID();
auto tex3 = s_Data.ShadowMapRenderPass[3]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID();
Renderer::Submit([reg, tex, tex1, tex2, tex3]() mutable
{
// 4 cascades
glBindTextureUnit(reg, tex);
glBindSampler(reg++, s_Data.ShadowMapSampler);
glBindTextureUnit(reg, tex1);
glBindSampler(reg++, s_Data.ShadowMapSampler);
glBindTextureUnit(reg, tex2);
glBindSampler(reg++, s_Data.ShadowMapSampler);
glBindTextureUnit(reg, tex3);
glBindSampler(reg++, s_Data.ShadowMapSampler);
});
}
constexpr auto overrideMaterial = nullptr; // dc.Material;
Renderer::SubmitMesh(dc.mesh, dc.Transform, overrideMaterial);
}
if (outline) if (outline)
{ {
@ -327,9 +589,11 @@ namespace Prism
// Draw outline here // Draw outline here
s_Data.OutlineMaterial->Set("u_ViewProjection", viewProjection); s_Data.OutlineMaterial->Set("u_ViewProjection", viewProjection);
s_Data.OutlineAnimMaterial->Set("u_ViewProjection", viewProjection);
for (auto& dc : s_Data.SelectedMeshDrawList) for (auto& dc : s_Data.SelectedMeshDrawList)
{ {
Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.OutlineMaterial); // Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.OutlineMaterial);
Renderer::SubmitMesh(dc.mesh, dc.Transform, dc.mesh->IsAnimated() ? s_Data.OutlineAnimMaterial : s_Data.OutlineMaterial);
} }
Renderer::Submit([]() Renderer::Submit([]()
@ -339,7 +603,8 @@ namespace Prism
}); });
for (auto& dc : s_Data.SelectedMeshDrawList) for (auto& dc : s_Data.SelectedMeshDrawList)
{ {
Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.OutlineMaterial); // Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.OutlineMaterial);
Renderer::SubmitMesh(dc.mesh, dc.Transform, dc.mesh->IsAnimated() ? s_Data.OutlineAnimMaterial : s_Data.OutlineMaterial);
} }
Renderer::Submit([]() Renderer::Submit([]()
@ -414,12 +679,303 @@ namespace Prism
void SceneRenderer::CompositePass() void SceneRenderer::CompositePass()
{ {
auto& compositeBuffer = s_Data.CompositePass->GetSpecification().TargetFramebuffer;
Renderer::BeginRenderPass(s_Data.CompositePass); Renderer::BeginRenderPass(s_Data.CompositePass);
s_Data.CompositeShader->Bind(); s_Data.CompositeShader->Bind();
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure()); s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure());
s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples); s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples);
// s_Data.CompositeShader->SetFloat2("u_ViewportSize", glm::vec2(compositeBuffer->GetWidth(), compositeBuffer->GetHeight()));
// s_Data.CompositeShader->SetFloat2("u_FocusPoint", s_Data.FocusPoint);
s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples);
// s_Data.CompositeShader->SetFloat("u_BloomThreshold", s_Data.BloomThreshold);
s_Data.GeoPass->GetSpecification().TargetFramebuffer->BindTexture(); s_Data.GeoPass->GetSpecification().TargetFramebuffer->BindTexture();
Renderer::Submit([]()
{
glBindTextureUnit(1, s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID());
});
Renderer::SubmitFullscreenQuad(nullptr); Renderer::SubmitFullscreenQuad(nullptr);
Renderer::EndRenderPass(); Renderer::EndRenderPass();
} }
void SceneRenderer::BloomBlurPass()
{
int amount = 10;
int index = 0;
int horizontalCounter = 0, verticalCounter = 0;
for (int i = 0; i < amount; i++)
{
index = i % 2;
Renderer::BeginRenderPass(s_Data.BloomBlurPass[index]);
s_Data.BloomBlurShader->Bind();
s_Data.BloomBlurShader->SetBool("u_Horizontal", index);
if (index)
horizontalCounter++;
else
verticalCounter++;
if (i > 0)
{
auto fb = s_Data.BloomBlurPass[1 - index]->GetSpecification().TargetFramebuffer;
fb->BindTexture();
}
else
{
auto fb = s_Data.CompositePass->GetSpecification().TargetFramebuffer;
auto id = fb->GetColorAttachmentRendererID(1);
Renderer::Submit([id]()
{
glBindTextureUnit(0, id);
});
}
Renderer::SubmitFullscreenQuad(nullptr);
Renderer::EndRenderPass();
}
// Composite bloom
{
Renderer::BeginRenderPass(s_Data.BloomBlendPass);
s_Data.BloomBlendShader->Bind();
s_Data.BloomBlendShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure());
s_Data.BloomBlendShader->SetBool("u_EnableBloom", s_Data.EnableBloom);
s_Data.CompositePass->GetSpecification().TargetFramebuffer->BindTexture(0);
s_Data.BloomBlurPass[index]->GetSpecification().TargetFramebuffer->BindTexture(1);
Renderer::SubmitFullscreenQuad(nullptr);
Renderer::EndRenderPass();
}
}
struct FrustumBounds
{
float r, l, b, t, f, n;
};
struct CascadeData
{
glm::mat4 ViewProj;
glm::mat4 View;
float SplitDepth;
};
static void CalculateCascades(CascadeData* cascades, const glm::vec3& lightDirection)
{
// FrustumBounds frustumBounds[3];
auto& sceneCamera = s_Data.SceneData.SceneCamera;
auto viewProjection = sceneCamera.Camera.GetProjectionMatrix() * sceneCamera.ViewMatrix;
constexpr int SHADOW_MAP_CASCADE_COUNT = 4;
float cascadeSplits[SHADOW_MAP_CASCADE_COUNT];
// TODO: less hard-coding!
float nearClip = 0.1f;
float farClip = 1000.0f;
float clipRange = farClip - nearClip;
float minZ = nearClip;
float maxZ = nearClip + clipRange;
float range = maxZ - minZ;
float ratio = maxZ / minZ;
// Calculate split depths based on view camera frustum
// Based on method presented in https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html
for (uint32_t i = 0; i < SHADOW_MAP_CASCADE_COUNT; i++)
{
float p = (i + 1) / static_cast<float>(SHADOW_MAP_CASCADE_COUNT);
float log = minZ * std::pow(ratio, p);
float uniform = minZ + range * p;
float d = s_Data.CascadeSplitLambda * (log - uniform) + uniform;
cascadeSplits[i] = (d - nearClip) / clipRange;
}
cascadeSplits[3] = 0.3f;
// Manually set cascades here
// cascadeSplits[0] = 0.05f;
// cascadeSplits[1] = 0.15f;
// cascadeSplits[2] = 0.3f;
// cascadeSplits[3] = 1.0f;
// Calculate orthographic projection matrix for each cascade
float lastSplitDist = 0.0;
for (uint32_t i = 0; i < SHADOW_MAP_CASCADE_COUNT; i++)
{
float splitDist = cascadeSplits[i];
glm::vec3 frustumCorners[8] =
{
glm::vec3(-1.0f, 1.0f, -1.0f),
glm::vec3( 1.0f, 1.0f, -1.0f),
glm::vec3( 1.0f, -1.0f, -1.0f),
glm::vec3(-1.0f, -1.0f, -1.0f),
glm::vec3(-1.0f, 1.0f, 1.0f),
glm::vec3( 1.0f, 1.0f, 1.0f),
glm::vec3( 1.0f, -1.0f, 1.0f),
glm::vec3(-1.0f, -1.0f, 1.0f),
};
// Project frustum corners into world space
glm::mat4 invCam = glm::inverse(viewProjection);
for (uint32_t i = 0; i < 8; i++)
{
glm::vec4 invCorner = invCam * glm::vec4(frustumCorners[i], 1.0f);
frustumCorners[i] = invCorner / invCorner.w;
}
for (uint32_t i = 0; i < 4; i++)
{
glm::vec3 dist = frustumCorners[i + 4] - frustumCorners[i];
frustumCorners[i + 4] = frustumCorners[i] + (dist * splitDist);
frustumCorners[i] = frustumCorners[i] + (dist * lastSplitDist);
}
// Get frustum center
glm::vec3 frustumCenter = glm::vec3(0.0f);
for (uint32_t i = 0; i < 8; i++)
frustumCenter += frustumCorners[i];
frustumCenter /= 8.0f;
//frustumCenter *= 0.01f;
float radius = 0.0f;
for (uint32_t i = 0; i < 8; i++)
{
float distance = glm::length(frustumCorners[i] - frustumCenter);
radius = glm::max(radius, distance);
}
radius = std::ceil(radius * 16.0f) / 16.0f;
glm::vec3 maxExtents = glm::vec3(radius);
glm::vec3 minExtents = -maxExtents;
glm::vec3 lightDir = -lightDirection;
glm::mat4 lightViewMatrix = glm::lookAt(frustumCenter - lightDir * -minExtents.z, frustumCenter, glm::vec3(0.0f, 0.0f, 1.0f));
glm::mat4 lightOrthoMatrix = glm::ortho(minExtents.x, maxExtents.x, minExtents.y, maxExtents.y, 0.0f + s_Data.CascadeNearPlaneOffset, maxExtents.z - minExtents.z + s_Data.CascadeFarPlaneOffset);
// Store split distance and matrix in cascade
cascades[i].SplitDepth = (nearClip + splitDist * clipRange) * -1.0f;
cascades[i].ViewProj = lightOrthoMatrix * lightViewMatrix;
cascades[i].View = lightViewMatrix;
lastSplitDist = cascadeSplits[i];
}
}
void SceneRenderer::ShadowMapPass()
{
const auto& directionalLights = s_Data.SceneData.SceneLightEnvironment.DirectionalLights;
if (directionalLights[0].Multiplier == 0.0f || !directionalLights[0].CastShadows)
{
for (int i = 0; i < 4; i++)
{
// Clear shadow maps
Renderer::BeginRenderPass(s_Data.ShadowMapRenderPass[i]);
Renderer::EndRenderPass();
}
return;
}
CascadeData cascades[4];
CalculateCascades(cascades, directionalLights[0].Direction);
s_Data.LightViewMatrix = cascades[0].View;
Renderer::Submit([]()
{
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
});
for (int i = 0; i < 4; i++)
{
s_Data.CascadeSplits[i] = cascades[i].SplitDepth;
Renderer::BeginRenderPass(s_Data.ShadowMapRenderPass[i]);
glm::mat4 shadowMapVP = cascades[i].ViewProj;
// s_Data.ShadowMapShader->SetMat4("u_ViewProjection", shadowMapVP);
static glm::mat4 scaleBiasMatrix = glm::scale(glm::mat4(1.0f), { 0.5f, 0.5f, 0.5f }) * glm::translate(glm::mat4(1.0f), { 1, 1, 1 });
s_Data.LightMatrices[i] = scaleBiasMatrix * cascades[i].ViewProj;
// Render entities
for (auto& dc : s_Data.ShadowPassDrawList)
{
Ref<Shader> shader = dc.mesh->IsAnimated() ? s_Data.ShadowMapAnimShader : s_Data.ShadowMapShader;
shader->SetMat4("u_ViewProjection", shadowMapVP);
Renderer::SubmitMeshWithShader(dc.mesh, dc.Transform, shader);
// Renderer::SubmitMeshWithShader(dc.mesh, dc.Transform, s_Data.ShadowMapShader);
}
Renderer::EndRenderPass();
}
}
void SceneRenderer::OnImGuiRender()
{
ImGui::Begin("Scene Renderer");
if (UI::BeginTreeNode("Shadows"))
{
UI::BeginPropertyGrid();
UI::Property("Soft Shadows", s_Data.SoftShadows);
UI::Property("Light Size", s_Data.LightSize, 0.01f);
UI::Property("Max Shadow Distance", s_Data.MaxShadowDistance, 1.0f);
UI::Property("Shadow Fade", s_Data.ShadowFade, 5.0f);
UI::EndPropertyGrid();
if (UI::BeginTreeNode("Cascade Settings"))
{
UI::BeginPropertyGrid();
UI::Property("Show Cascades", s_Data.ShowCascades);
UI::Property("Cascade Fading", s_Data.CascadeFading);
UI::Property("Cascade Transition Fade", s_Data.CascadeTransitionFade, 0.05f, 0.0f, FLT_MAX);
UI::Property("Cascade Split", s_Data.CascadeSplitLambda, 0.01f);
UI::Property("CascadeNearPlaneOffset", s_Data.CascadeNearPlaneOffset, 0.1f, -FLT_MAX, 0.0f);
UI::Property("CascadeFarPlaneOffset", s_Data.CascadeFarPlaneOffset, 0.1f, 0.0f, FLT_MAX);
UI::EndPropertyGrid();
UI::EndTreeNode();
}
if (UI::BeginTreeNode("Shadow Map", false))
{
static int cascadeIndex = 0;
auto fb = s_Data.ShadowMapRenderPass[cascadeIndex]->GetSpecification().TargetFramebuffer;
auto id = fb->GetDepthAttachmentRendererID();
float size = ImGui::GetContentRegionAvail().x; // (float)fb->GetWidth() * 0.5f, (float)fb->GetHeight() * 0.5f
UI::BeginPropertyGrid();
UI::PropertySlider("Cascade Index", cascadeIndex, 0, 3);
UI::EndPropertyGrid();
ImGui::Image((ImTextureID)id, { size, size }, { 0, 1 }, { 1, 0 });
UI::EndTreeNode();
}
UI::EndTreeNode();
}
if (UI::BeginTreeNode("Bloom"))
{
UI::BeginPropertyGrid();
UI::Property("Bloom", s_Data.EnableBloom);
UI::Property("Bloom threshold", s_Data.BloomThreshold, 0.05f);
UI::EndPropertyGrid();
auto fb = s_Data.BloomBlurPass[0]->GetSpecification().TargetFramebuffer;
auto id = fb->GetColorAttachmentRendererID();
float size = ImGui::GetContentRegionAvail().x; // (float)fb->GetWidth() * 0.5f, (float)fb->GetHeight() * 0.5f
float w = size;
float h = w / ((float)fb->GetWidth() / (float)fb->GetHeight());
ImGui::Image((ImTextureID)id, { w, h }, { 0, 1 }, { 1, 0 });
UI::EndTreeNode();
}
ImGui::End();
}
} }

View File

@ -23,6 +23,9 @@ namespace Prism
{ {
Prism::Camera Camera; Prism::Camera Camera;
glm::mat4 ViewMatrix; glm::mat4 ViewMatrix;
float Near, Far;
float FOV;
}; };
class PRISM_API SceneRenderer class PRISM_API SceneRenderer
@ -48,15 +51,21 @@ namespace Prism
static Ref<RenderPass> GetFinalRenderPass(); static Ref<RenderPass> GetFinalRenderPass();
static Ref<Texture2D> GetFinalColorBuffer(); static Ref<Texture2D> GetFinalColorBuffer();
static void SetFocusPoint(const glm::vec2& point);
// TODO: Temp // TODO: Temp
static uint32_t GetFinalColorBufferRendererID(); static uint32_t GetFinalColorBufferRendererID();
static SceneRendererOptions& GetOptions(); static SceneRendererOptions& GetOptions();
static void OnImGuiRender();
private: private:
static void FlushDrawList(); static void FlushDrawList();
static void GeometryPass(); static void GeometryPass();
static void CompositePass(); static void CompositePass();
static void BloomBlurPass();
static void ShadowMapPass();
}; };
} }

View File

@ -110,9 +110,11 @@ namespace Prism
virtual void Bind() = 0; virtual void Bind() = 0;
virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) = 0; virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) = 0;
virtual void SetFloat(const std::string& name, float value) = 0;
virtual void SetFloat3(const std::string& name, const glm::vec3& value) = 0;
virtual void SetInt(const std::string& name, int value) = 0; virtual void SetInt(const std::string& name, int value) = 0;
virtual void SetBool(const std::string& name, bool value) = 0;
virtual void SetFloat(const std::string& name, float value) = 0;
virtual void SetFloat2(const std::string& name, const glm::vec2& value) = 0;
virtual void SetFloat3(const std::string& name, const glm::vec3& value) = 0;
virtual void SetMat4(const std::string& name, const glm::mat4& value) = 0; virtual void SetMat4(const std::string& name, const glm::mat4& value) = 0;
virtual void SetMat4FromRenderThread(const std::string& name, const glm::mat4& value, bool bind = true) = 0; virtual void SetMat4FromRenderThread(const std::string& name, const glm::mat4& value, bool bind = true) = 0;

View File

@ -18,6 +18,8 @@
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/quaternion.hpp> #include <glm/gtx/quaternion.hpp>
#include "Prism/Renderer/SceneEnvironment.h"
namespace Prism namespace Prism
{ {
struct IDComponent struct IDComponent
@ -222,7 +224,9 @@ namespace Prism
struct MeshColliderComponent struct MeshColliderComponent
{ {
Ref<Prism::Mesh> CollisionMesh; Ref<Prism::Mesh> CollisionMesh;
Ref<Prism::Mesh> ProcessedMesh; std::vector<Ref<Prism::Mesh>> ProcessedMeshes;
bool IsConvex = false;
bool IsTrigger = false; bool IsTrigger = false;
MeshColliderComponent() = default; MeshColliderComponent() = default;
@ -235,6 +239,29 @@ namespace Prism
operator Ref<Prism::Mesh>() { return CollisionMesh; } operator Ref<Prism::Mesh>() { return CollisionMesh; }
}; };
// Lights
// TODO: Move to renderer
enum class LightType
{
None = 0, Directional = 1, Point = 2, Spot = 3
};
struct DirectionalLightComponent
{
glm::vec3 Radiance = { 1.0f, 1.0f, 1.0f };
float Intensity = 1.0f;
bool CastShadows = true;
bool SoftShadows = true;
float LightSize = 0.5f; // For PCSS
};
struct SkyLightComponent
{
Environment SceneEnvironment;
float Intensity = 1.0f;
float Angle = 0.0f;
};
} }

View File

@ -18,6 +18,8 @@
#define PX_PHYSX_STATIC_LIB #define PX_PHYSX_STATIC_LIB
#include <PxPhysicsAPI.h> #include <PxPhysicsAPI.h>
#include "Prism/Core/Input.h"
namespace Prism namespace Prism
{ {
@ -106,11 +108,6 @@ namespace Prism
ScriptEngine::OnScriptComponentDestroyed(sceneID, entityID); ScriptEngine::OnScriptComponentDestroyed(sceneID, entityID);
} }
Environment Environment::Load(const std::string& filepath)
{
auto [radiance, irradiance] = SceneRenderer::CreateEnvironmentMap(filepath);
return { filepath, radiance, irradiance };
}
Scene::Scene(const std::string& debugName, const bool isEditorScene) Scene::Scene(const std::string& debugName, const bool isEditorScene)
: m_SceneID(++s_SceneIDCounter), m_DebugName(debugName) : m_SceneID(++s_SceneIDCounter), m_DebugName(debugName)
@ -160,16 +157,6 @@ namespace Prism
void Scene::OnUpdate(TimeStep ts) void Scene::OnUpdate(TimeStep ts)
{ {
// Update all entities
{
const auto view = m_Registry.view<ScriptComponent>();
for (const auto entity : view)
{
Entity e = { entity, this };
if (ScriptEngine::ModuleExists(e.GetComponent<ScriptComponent>().ModuleName))
ScriptEngine::OnUpdateEntity(e, ts);
}
}
// Box2D physics // Box2D physics
@ -202,6 +189,17 @@ namespace Prism
} }
Physics3D::Simulate(ts); Physics3D::Simulate(ts);
// Update all entities
{
const auto view = m_Registry.view<ScriptComponent>();
for (const auto entity : view)
{
Entity e = { entity, this };
if (ScriptEngine::ModuleExists(e.GetComponent<ScriptComponent>().ModuleName))
ScriptEngine::OnUpdateEntity(e, ts);
}
}
} }
void Scene::OnRenderRuntime(TimeStep ts) void Scene::OnRenderRuntime(TimeStep ts)
@ -218,6 +216,39 @@ namespace Prism
SceneCamera& camera = cameraEntity.GetComponent<CameraComponent>(); SceneCamera& camera = cameraEntity.GetComponent<CameraComponent>();
camera.SetViewportSize(m_ViewportWidth, m_ViewportHeight); camera.SetViewportSize(m_ViewportWidth, m_ViewportHeight);
// Process lights
{
m_LightEnvironment = LightEnvironment();
auto lights = m_Registry.group<DirectionalLightComponent>(entt::get<TransformComponent>);
uint32_t directionalLightIndex = 0;
for (auto entity : lights)
{
auto [transformComponent, lightComponent] = lights.get<TransformComponent, DirectionalLightComponent>(entity);
glm::vec3 direction = -glm::normalize(glm::mat3(transformComponent.GetTransform()) * glm::vec3(1.0f));
m_LightEnvironment.DirectionalLights[directionalLightIndex++] =
{
direction,
lightComponent.Radiance,
lightComponent.Intensity,
lightComponent.CastShadows
};
}
}
// TODO: only one sky light at the moment!
{
m_Environment = Environment();
auto lights = m_Registry.group<SkyLightComponent>(entt::get<TransformComponent>);
for (auto entity : lights)
{
auto [transformComponent, skyLightComponent] = lights.get<TransformComponent, SkyLightComponent>(entity);
m_Environment = skyLightComponent.SceneEnvironment;
m_EnvironmentIntensity = skyLightComponent.Intensity;
SetSkybox(m_Environment.RadianceMap);
}
}
m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod); m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod);
auto group = m_Registry.group<MeshComponent>(entt::get<TransformComponent>); auto group = m_Registry.group<MeshComponent>(entt::get<TransformComponent>);
@ -236,6 +267,7 @@ namespace Prism
SceneRenderer::EndScene(); SceneRenderer::EndScene();
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
#if 0 #if 0
// Render all sprites // Render all sprites
Renderer2D::BeginScene(*camera); Renderer2D::BeginScene(*camera);
@ -260,10 +292,44 @@ namespace Prism
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// RENDER 3D SCENE // // RENDER 3D SCENE //
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// Process lights
{
m_LightEnvironment = LightEnvironment();
auto lights = m_Registry.group<DirectionalLightComponent>(entt::get<TransformComponent>);
uint32_t directionalLightIndex = 0;
for (auto entity : lights)
{
auto [transformComponent, lightComponent] = lights.get<TransformComponent, DirectionalLightComponent>(entity);
glm::vec3 direction = -glm::normalize(glm::mat3(transformComponent.GetTransform()) * glm::vec3(1.0f));
m_LightEnvironment.DirectionalLights[directionalLightIndex++] =
{
direction,
lightComponent.Radiance,
lightComponent.Intensity,
lightComponent.CastShadows
};
}
}
// TODO: only one sky light at the moment!
{
m_Environment = Environment();
auto lights = m_Registry.group<SkyLightComponent>(entt::get<TransformComponent>);
for (auto entity : lights)
{
auto [transformComponent, skyLightComponent] = lights.get<TransformComponent, SkyLightComponent>(entity);
m_Environment = skyLightComponent.SceneEnvironment;
m_EnvironmentIntensity = skyLightComponent.Intensity;
SetSkybox(m_Environment.RadianceMap);
}
}
m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod); m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod);
auto group = m_Registry.group<MeshComponent>(entt::get<TransformComponent>); auto group = m_Registry.group<MeshComponent>(entt::get<TransformComponent>);
SceneRenderer::BeginScene(this, { static_cast<Camera>(editorCamera), editorCamera.GetViewMatrix() }); // SceneRenderer::BeginScene(this, { static_cast<Camera>(editorCamera), editorCamera.GetViewMatrix() });
SceneRenderer::BeginScene(this, { static_cast<Camera>(editorCamera), editorCamera.GetViewMatrix(), 0.1f, 1000.0f, 45.0f }); // TODO: real values
for (auto entity : group) for (auto entity : group)
{ {
const auto& [transformComponent, meshComponent] = group.get<TransformComponent, MeshComponent>(entity); const auto& [transformComponent, meshComponent] = group.get<TransformComponent, MeshComponent>(entity);
@ -474,6 +540,8 @@ namespace Prism
void Scene::OnRuntimeStop() void Scene::OnRuntimeStop()
{ {
Input::SetCursorMode(CursorMode::Normal);
Physics3D::DestroyScene(); Physics3D::DestroyScene();
delete[] m_Physics3DBodyEntityBuffer; delete[] m_Physics3DBodyEntityBuffer;
@ -487,12 +555,6 @@ namespace Prism
m_ViewportHeight = height; m_ViewportHeight = height;
} }
void Scene::SetEnvironment(const Environment& environment)
{
m_Environment = environment;
SetSkybox(environment.RadianceMap);
}
void Scene::SetSkybox(const Ref<TextureCube>& skybox) void Scene::SetSkybox(const Ref<TextureCube>& skybox)
{ {
m_SkyboxTexture = skybox; m_SkyboxTexture = skybox;
@ -580,6 +642,8 @@ namespace Prism
CopyComponentIfExists<TransformComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists<TransformComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<MeshComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists<MeshComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<DirectionalLightComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<SkyLightComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<ScriptComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists<ScriptComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<CameraComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists<CameraComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<SpriteRendererComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists<SpriteRendererComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
@ -629,6 +693,8 @@ namespace Prism
CopyComponent<TagComponent>(target->m_Registry, m_Registry, enttMap); CopyComponent<TagComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<TransformComponent>(target->m_Registry, m_Registry, enttMap); CopyComponent<TransformComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<MeshComponent>(target->m_Registry, m_Registry, enttMap); CopyComponent<MeshComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<DirectionalLightComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<SkyLightComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<ScriptComponent>(target->m_Registry, m_Registry, enttMap); CopyComponent<ScriptComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<CameraComponent>(target->m_Registry, m_Registry, enttMap); CopyComponent<CameraComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<SpriteRendererComponent>(target->m_Registry, m_Registry, enttMap); CopyComponent<SpriteRendererComponent>(target->m_Registry, m_Registry, enttMap);

View File

@ -13,19 +13,11 @@
#include "Prism/Core/UUID.h" #include "Prism/Core/UUID.h"
#include "Prism/Editor/EditorCamera.h" #include "Prism/Editor/EditorCamera.h"
#include "Prism/Renderer/SceneEnvironment.h"
namespace Prism namespace Prism
{ {
struct PRISM_API Environment
{
std::string FilePath;
Ref<TextureCube> RadianceMap;
Ref<TextureCube> IrradianceMap;
static Environment Load(const std::string& filepath);
};
struct Light struct Light
{ {
glm::vec3 Direction{-0.314f, -0.941f, -0.209f}; glm::vec3 Direction{-0.314f, -0.941f, -0.209f};
@ -34,6 +26,23 @@ namespace Prism
float Multiplier = 1.0f; float Multiplier = 1.0f;
}; };
struct DirectionalLight
{
glm::vec3 Direction = { 0.0f, 0.0f, 0.0f };
glm::vec3 Radiance = { 0.0f, 0.0f, 0.0f };
float Multiplier = 0.0f;
// C++ only
bool CastShadows = true;
};
struct LightEnvironment
{
DirectionalLight DirectionalLights[4];
};
class Entity; class Entity;
using EntityMap = std::unordered_map<UUID, Entity>; using EntityMap = std::unordered_map<UUID, Entity>;
@ -56,7 +65,6 @@ namespace Prism
void SetViewportSize(uint32_t width, uint32_t height); void SetViewportSize(uint32_t width, uint32_t height);
void SetEnvironment(const Environment& environment);
const Environment& GetEnvironment() const { return m_Environment; } const Environment& GetEnvironment() const { return m_Environment; }
void SetSkybox(const Ref<TextureCube>& skybox); void SetSkybox(const Ref<TextureCube>& skybox);
@ -115,9 +123,12 @@ namespace Prism
Light m_Light; Light m_Light;
float m_LightMultiplier = 0.3f; float m_LightMultiplier = 0.3f;
LightEnvironment m_LightEnvironment;
bool m_IsPlaying = false; bool m_IsPlaying = false;
Environment m_Environment; Environment m_Environment;
float m_EnvironmentIntensity = 1.0f;
Ref<TextureCube> m_SkyboxTexture; Ref<TextureCube> m_SkyboxTexture;
Ref<MaterialInstance> m_SkyboxMaterial; Ref<MaterialInstance> m_SkyboxMaterial;

View File

@ -244,6 +244,33 @@ namespace Prism
out << YAML::EndMap; // MeshComponent out << YAML::EndMap; // MeshComponent
} }
if (entity.HasComponent<DirectionalLightComponent>())
{
out << YAML::Key << "DirectionalLightComponent";
out << YAML::BeginMap; // DirectionalLightComponent
auto& directionalLightComponent = entity.GetComponent<DirectionalLightComponent>();
out << YAML::Key << "Radiance" << YAML::Value << directionalLightComponent.Radiance;
out << YAML::Key << "CastShadows" << YAML::Value << directionalLightComponent.CastShadows;
out << YAML::Key << "SoftShadows" << YAML::Value << directionalLightComponent.SoftShadows;
out << YAML::Key << "LightSize" << YAML::Value << directionalLightComponent.LightSize;
out << YAML::EndMap; // DirectionalLightComponent
}
if (entity.HasComponent<SkyLightComponent>())
{
out << YAML::Key << "SkyLightComponent";
out << YAML::BeginMap; // SkyLightComponent
auto& skyLightComponent = entity.GetComponent<SkyLightComponent>();
out << YAML::Key << "EnvironmentAssetPath" << YAML::Value << skyLightComponent.SceneEnvironment.FilePath;
out << YAML::Key << "Intensity" << YAML::Value << skyLightComponent.Intensity;
out << YAML::Key << "Angle" << YAML::Value << skyLightComponent.Angle;
out << YAML::EndMap; // SkyLightComponent
}
if (entity.HasComponent<CameraComponent>()) if (entity.HasComponent<CameraComponent>())
{ {
out << YAML::Key << "CameraComponent"; out << YAML::Key << "CameraComponent";
@ -394,6 +421,7 @@ namespace Prism
auto meshColliderComponent = entity.GetComponent<MeshColliderComponent>(); auto meshColliderComponent = entity.GetComponent<MeshColliderComponent>();
out << YAML::Key << "AssetPath" << YAML::Value << meshColliderComponent.CollisionMesh->GetFilePath(); out << YAML::Key << "AssetPath" << YAML::Value << meshColliderComponent.CollisionMesh->GetFilePath();
out << YAML::Key << "IsConvex" << YAML::Value << meshColliderComponent.IsConvex;
out << YAML::Key << "IsTrigger" << YAML::Value << meshColliderComponent.IsTrigger; out << YAML::Key << "IsTrigger" << YAML::Value << meshColliderComponent.IsTrigger;
out << YAML::EndMap; // MeshColliderComponent out << YAML::EndMap; // MeshColliderComponent
@ -489,7 +517,7 @@ namespace Prism
if (environment) if (environment)
{ {
std::string envPath = environment["AssetPath"].as<std::string>(); std::string envPath = environment["AssetPath"].as<std::string>();
m_Scene->SetEnvironment(Environment::Load(envPath)); // m_Scene->SetEnvironment(Environment::Load(envPath));
auto lightNode = environment["Light"]; auto lightNode = environment["Light"];
if (lightNode) if (lightNode)
@ -606,7 +634,7 @@ namespace Prism
if (auto meshComponent = entity["MeshComponent"]) if (auto meshComponent = entity["MeshComponent"])
{ {
std::string meshPath = meshComponent["AssetPath"].as<std::string>(); const std::string meshPath = meshComponent["AssetPath"].as<std::string>();
// TEMP (because script creates mesh component...) // TEMP (because script creates mesh component...)
if (!deserializedEntity.HasComponent<MeshComponent>()) if (!deserializedEntity.HasComponent<MeshComponent>())
deserializedEntity.AddComponent<MeshComponent>(Ref<Mesh>::Create(meshPath)); deserializedEntity.AddComponent<MeshComponent>(Ref<Mesh>::Create(meshPath));
@ -614,6 +642,25 @@ namespace Prism
PM_CORE_INFO(" Mesh Asset Path: {0}", meshPath); PM_CORE_INFO(" Mesh Asset Path: {0}", meshPath);
} }
if (auto directionalLightComponent = entity["DirectionalLightComponent"])
{
auto& component = deserializedEntity.AddComponent<DirectionalLightComponent>();
component.Radiance = directionalLightComponent["Radiance"].as<glm::vec3>();
component.CastShadows = directionalLightComponent["CastShadows"].as<bool>();
component.SoftShadows = directionalLightComponent["SoftShadows"].as<bool>();
component.LightSize = directionalLightComponent["LightSize"].as<float>();
}
if (auto skyLightComponent = entity["SkyLightComponent"])
{
auto& component = deserializedEntity.AddComponent<SkyLightComponent>();
const std::string env = skyLightComponent["EnvironmentAssetPath"].as<std::string>();
if (!env.empty())
component.SceneEnvironment = Environment::Load(env);
component.Intensity = skyLightComponent["Intensity"].as<float>();
component.Angle = skyLightComponent["Angle"].as<float>();
}
if (auto cameraComponent = entity["CameraComponent"]) if (auto cameraComponent = entity["CameraComponent"])
{ {
auto& component = deserializedEntity.AddComponent<CameraComponent>(); auto& component = deserializedEntity.AddComponent<CameraComponent>();
@ -712,8 +759,12 @@ namespace Prism
{ {
auto meshPath = meshColliderComponent["AssetPath"].as<std::string>(); auto meshPath = meshColliderComponent["AssetPath"].as<std::string>();
auto& component = deserializedEntity.AddComponent<MeshColliderComponent>(Ref<Mesh>::Create(meshPath)); auto& component = deserializedEntity.AddComponent<MeshColliderComponent>(Ref<Mesh>::Create(meshPath));
component.IsConvex = meshColliderComponent["IsConvex"] ? meshColliderComponent["IsConvex"].as<bool>() : false;
component.IsTrigger = meshColliderComponent["IsTrigger"] ? meshColliderComponent["IsTrigger"].as<bool>() : false; component.IsTrigger = meshColliderComponent["IsTrigger"] ? meshColliderComponent["IsTrigger"].as<bool>() : false;
PhysicsWrappers::CreateConvexMesh(component); if (component.IsConvex)
PhysicsWrappers::CreateConvexMesh(component);
else
PhysicsWrappers::CreateTriangleMesh(component);
PM_CORE_INFO(" Mesh Collider Asset Path: {0}", meshPath); PM_CORE_INFO(" Mesh Collider Asset Path: {0}", meshPath);
} }