Integrating github/staging through commit b0dd7ed

This commit is contained in:
alexpete
2021-04-09 12:03:26 -07:00
parent 1044dc3da1
commit c5b955d281
180 changed files with 6416 additions and 1850 deletions
+4
View File
@@ -71,10 +71,14 @@ COMPILE_TIME_ASSERT(sizeof(uint32) == 4);
COMPILE_TIME_ASSERT(sizeof(sint32) == 4);
typedef slonglong int64;
#ifndef O3DE_INT64_DEFINED
#define O3DE_INT64_DEFINED
typedef slonglong sint64;
typedef ulonglong uint64;
COMPILE_TIME_ASSERT(sizeof(uint64) == 8);
COMPILE_TIME_ASSERT(sizeof(sint64) == 8);
#endif
typedef float f32;
+1 -1
View File
@@ -19,6 +19,6 @@ ly_add_target(
.
BUILD_DEPENDENCIES
PRIVATE
3rdParty::FreeType2
3rdParty::freetype
Legacy::CryCommon
)
+1 -1
View File
@@ -22,7 +22,7 @@
#endif
#if !(defined(ANDROID) || defined(IOS) || defined(LINUX)) && AZ_LEGACY_CRYSYSTEM_TRAIT_IMAGEHANDLER_TIFFIO // Rally US1050 - Compile libtiff for Android and IOS
#include <libtiff/tiffio.h>
#include <tiffio.h>
static_assert(sizeof(thandle_t) >= sizeof(AZ::IO::HandleType), "Platform defines thandle_t to be smaller than required");
#endif
@@ -43,6 +43,9 @@ namespace AZ
{
const static AZ::Crc32 RuntimeEBusAttribute = AZ_CRC("RuntimeEBus", 0x466b899b); ///< Signals that this reflected ebus should only be available at runtime, helps tools filter out data driven ebuses
constexpr const char* k_PropertyNameGetterSuffix = "::Getter";
constexpr const char* k_PropertyNameSetterSuffix = "::Setter";
/// Typedef for class unwrapping callback (i.e. used for things like smart_ptr<T> to unwrap for T)
using BehaviorClassUnwrapperFunction = void(*)(void* /*classPtr*/, void*& /*unwrappedClass*/, AZ::Uuid& /*unwrappedClassTypeId*/, void* /*userData*/);
@@ -2525,7 +2528,7 @@ namespace AZ
getterPropertyName += "::";
}
getterPropertyName += m_name;
getterPropertyName += "::Getter";
getterPropertyName += k_PropertyNameGetterSuffix;
m_getter = aznew GetterType(getter, context, getterPropertyName);
if (AZStd::is_class<typename GetterType::ClassType>::value)
@@ -2603,7 +2606,7 @@ namespace AZ
setterPropertyName += "::";
}
setterPropertyName += m_name;
setterPropertyName += "::Setter";
setterPropertyName += k_PropertyNameSetterSuffix;
m_setter = aznew SetterType(setter, context, setterPropertyName);
if (AZStd::is_class<typename SetterType::ClassType>::value)
{
@@ -243,6 +243,28 @@ namespace AZ
return variance;
}
void RemovePropertyGetterNameArtifacts(AZStd::string& name)
{
if (name.ends_with(k_PropertyNameGetterSuffix))
{
AZ::StringFunc::Replace(name, k_PropertyNameGetterSuffix, "");
}
}
void RemovePropertySetterNameArtifacts(AZStd::string& name)
{
if (name.ends_with(k_PropertyNameSetterSuffix))
{
AZ::StringFunc::Replace(name, k_PropertyNameSetterSuffix, "");
}
}
void RemovePropertyNameArtifacts(AZStd::string& name)
{
RemovePropertyGetterNameArtifacts(name);
RemovePropertySetterNameArtifacts(name);
}
AZStd::string ReplaceCppArtifacts(AZStd::string_view sourceName)
{
using namespace AZ::StringFunc;
@@ -68,6 +68,12 @@ namespace AZ
AZStd::vector<AZStd::pair<const BehaviorMethod*, const BehaviorClass*>> OverloadsToVector(const BehaviorMethod&, const BehaviorClass*);
void RemovePropertyGetterNameArtifacts(AZStd::string& name);
void RemovePropertySetterNameArtifacts(AZStd::string& name);
void RemovePropertyNameArtifacts(AZStd::string& name);
AZStd::string ReplaceCppArtifacts(AZStd::string_view sourceName);
void StripQualifiers(AZStd::string& name);
@@ -0,0 +1,531 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "CameraInput.h"
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Math/Plane.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Windowing/WindowBus.h>
namespace AzFramework
{
void CameraSystem::HandleEvents(const InputEvent& event)
{
if (const auto& cursor_motion = AZStd::get_if<CursorMotionEvent>(&event))
{
m_currentCursorPosition = cursor_motion->m_position;
}
else if (const auto& scroll = AZStd::get_if<ScrollEvent>(&event))
{
m_scrollDelta = scroll->m_delta;
}
m_cameras.HandleEvents(event);
}
Camera CameraSystem::StepCamera(const Camera& targetCamera, float deltaTime)
{
const auto cursorDelta = m_currentCursorPosition.has_value() && m_lastCursorPosition.has_value()
? m_currentCursorPosition.value() - m_lastCursorPosition.value()
: ScreenVector(0, 0);
if (m_currentCursorPosition.has_value())
{
m_lastCursorPosition = m_currentCursorPosition;
}
const auto nextCamera = m_cameras.StepCamera(targetCamera, cursorDelta, m_scrollDelta, deltaTime);
m_scrollDelta = 0.0f;
return nextCamera;
}
void Cameras::AddCamera(AZStd::shared_ptr<CameraInput> camera_input)
{
m_idleCameraInputs.push_back(AZStd::move(camera_input));
}
void Cameras::HandleEvents(const InputEvent& event)
{
for (auto& camera_input : m_activeCameraInputs)
{
camera_input->HandleEvents(event);
}
for (auto& camera_input : m_idleCameraInputs)
{
camera_input->HandleEvents(event);
}
}
Camera Cameras::StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, const float deltaTime)
{
for (int i = 0; i < m_idleCameraInputs.size();)
{
auto& camera_input = m_idleCameraInputs[i];
const bool can_begin = camera_input->Beginning() &&
std::all_of(m_activeCameraInputs.cbegin(), m_activeCameraInputs.cend(),
[](const auto& input) { return !input->Exclusive(); }) &&
(!camera_input->Exclusive() || (camera_input->Exclusive() && m_activeCameraInputs.empty()));
if (can_begin)
{
m_activeCameraInputs.push_back(camera_input);
using AZStd::swap;
swap(m_idleCameraInputs[i], m_idleCameraInputs[m_idleCameraInputs.size() - 1]);
m_idleCameraInputs.pop_back();
}
else
{
i++;
}
}
// accumulate
Camera nextCamera = targetCamera;
for (auto& camera_input : m_activeCameraInputs)
{
nextCamera = camera_input->StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime);
}
for (int i = 0; i < m_activeCameraInputs.size();)
{
auto& camera_input = m_activeCameraInputs[i];
if (camera_input->Ending())
{
camera_input->ClearActivation();
m_idleCameraInputs.push_back(camera_input);
using AZStd::swap;
swap(m_activeCameraInputs[i], m_activeCameraInputs[m_activeCameraInputs.size() - 1]);
m_activeCameraInputs.pop_back();
}
else
{
camera_input->ContinueActivation();
i++;
}
}
return nextCamera;
}
void Cameras::Reset()
{
for (int i = 0; i < m_activeCameraInputs.size();)
{
m_activeCameraInputs[i]->Reset();
m_idleCameraInputs.push_back(m_activeCameraInputs[i]);
m_activeCameraInputs[i] = m_activeCameraInputs[m_activeCameraInputs.size() - 1];
m_activeCameraInputs.pop_back();
}
}
void RotateCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == m_channelId)
{
if (input->m_state == InputChannel::State::Began)
{
BeginActivation();
}
else if (input->m_state == InputChannel::State::Ended)
{
EndActivation();
}
}
}
}
Camera RotateCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
nextCamera.m_pitch += float(cursorDelta.m_y) * m_props.m_rotateSpeed;
nextCamera.m_yaw += float(cursorDelta.m_x) * m_props.m_rotateSpeed;
auto clamp_rotation = [](const float angle) { return std::fmod(angle + AZ::Constants::TwoOverPi, AZ::Constants::TwoOverPi); };
nextCamera.m_yaw = clamp_rotation(nextCamera.m_yaw);
// clamp pitch to be +-90 degrees
nextCamera.m_pitch = AZ::GetClamp(nextCamera.m_pitch, -AZ::Constants::Pi * 0.5f, AZ::Constants::Pi * 0.5f);
return nextCamera;
}
void PanCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == InputDeviceMouse::Button::Middle)
{
if (input->m_state == InputChannel::State::Began)
{
BeginActivation();
}
else if (input->m_state == InputChannel::State::Ended)
{
EndActivation();
}
}
}
}
Camera PanCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
const auto pan_axes = m_panAxesFn(nextCamera);
const auto delta_pan_x = float(cursorDelta.m_x) * pan_axes.m_horizontalAxis * m_props.m_panSpeed;
const auto delta_pan_y = float(cursorDelta.m_y) * pan_axes.m_verticalAxis * m_props.m_panSpeed;
const auto inv = [](const bool invert) {
constexpr float Dir[] = {1.0f, -1.0f};
return Dir[static_cast<int>(invert)];
};
nextCamera.m_lookAt += delta_pan_x * inv(m_props.m_panInvertX);
nextCamera.m_lookAt += delta_pan_y * -inv(m_props.m_panInvertY);
return nextCamera;
}
TranslateCameraInput::TranslationType TranslateCameraInput::translationFromKey(InputChannelId channelId)
{
// note: remove hard-coded InputDevice keys
if (channelId == InputDeviceKeyboard::Key::AlphanumericW)
{
return TranslationType::Forward;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericS)
{
return TranslationType::Backward;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericA)
{
return TranslationType::Left;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericD)
{
return TranslationType::Right;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericQ)
{
return TranslationType::Down;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericE)
{
return TranslationType::Up;
}
return TranslationType::Nil;
}
void TranslateCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_state == InputChannel::State::Began)
{
if (input->m_state == InputChannel::State::Updated)
{
return;
}
m_translation |= translationFromKey(input->m_channelId);
if (m_translation != TranslationType::Nil)
{
BeginActivation();
}
if (input->m_channelId == InputDeviceKeyboard::Key::ModifierShiftL)
{
m_boost = true;
}
}
else if (input->m_state == InputChannel::State::Ended)
{
m_translation ^= translationFromKey(input->m_channelId);
if (m_translation == TranslationType::Nil)
{
EndActivation();
}
if (input->m_channelId == InputDeviceKeyboard::Key::ModifierShiftL)
{
m_boost = false;
}
}
}
}
Camera TranslateCameraInput::StepCamera(
const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta,
const float deltaTime)
{
Camera nextCamera = targetCamera;
const auto translation_basis = m_translationAxesFn(nextCamera);
const auto axisX = translation_basis.GetBasisX();
const auto axisY = translation_basis.GetBasisY();
const auto axisZ = translation_basis.GetBasisZ();
const float speed = [boost = m_boost, props = m_props]() {
return props.m_translateSpeed * (boost ? props.m_boostMultiplier : 1.0f);
}();
if ((m_translation & TranslationType::Forward) == TranslationType::Forward)
{
nextCamera.m_lookAt += axisY * speed * deltaTime;
}
if ((m_translation & TranslationType::Backward) == TranslationType::Backward)
{
nextCamera.m_lookAt -= axisY * speed * deltaTime;
}
if ((m_translation & TranslationType::Left) == TranslationType::Left)
{
nextCamera.m_lookAt -= axisX * speed * deltaTime;
}
if ((m_translation & TranslationType::Right) == TranslationType::Right)
{
nextCamera.m_lookAt += axisX * speed * deltaTime;
}
if ((m_translation & TranslationType::Up) == TranslationType::Up)
{
nextCamera.m_lookAt += axisZ * speed * deltaTime;
}
if ((m_translation & TranslationType::Down) == TranslationType::Down)
{
nextCamera.m_lookAt -= axisZ * speed * deltaTime;
}
if (Ending())
{
m_translation = TranslationType::Nil;
}
return nextCamera;
}
void TranslateCameraInput::ResetImpl()
{
m_translation = TranslationType::Nil;
m_boost = false;
}
void OrbitCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto* input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == InputDeviceKeyboard::Key::ModifierAltL)
{
if (input->m_state == InputChannel::State::Updated)
{
goto end;
}
if (input->m_state == InputChannel::State::Began)
{
BeginActivation();
}
else if (input->m_state == InputChannel::State::Ended)
{
EndActivation();
}
}
}
end:
if (Active())
{
m_orbitCameras.HandleEvents(event);
}
}
Camera OrbitCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, const float scrollDelta, float deltaTime)
{
Camera nextCamera = targetCamera;
if (Beginning())
{
float hit_distance = 0.0f;
if (AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3::CreateAxisZ(), AZ::Vector3::CreateZero())
.CastRay(targetCamera.Translation(), targetCamera.Rotation().GetBasisY() * m_props.m_maxOrbitDistance, hit_distance))
{
nextCamera.m_lookDist = -hit_distance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * hit_distance;
}
else
{
nextCamera.m_lookDist = -m_props.m_defaultOrbitDistance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * m_props.m_defaultOrbitDistance;
}
}
if (Active())
{
// todo: need to return nested cameras to idle state when ending
nextCamera = m_orbitCameras.StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime);
}
if (Ending())
{
m_orbitCameras.Reset();
nextCamera.m_lookAt = nextCamera.Translation();
nextCamera.m_lookDist = 0.0f;
}
return nextCamera;
}
void OrbitDollyScrollCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
{
BeginActivation();
}
}
Camera OrbitDollyScrollCameraInput::StepCamera(
const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, const float scrollDelta,
[[maybe_unused]] float deltaTime)
{
Camera nextCamera = targetCamera;
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + scrollDelta * m_props.m_dollySpeed, 0.0f);
EndActivation();
return nextCamera;
}
void OrbitDollyCursorMoveCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == InputDeviceMouse::Button::Right)
{
if (input->m_state == InputChannel::State::Began)
{
BeginActivation();
}
else if (input->m_state == InputChannel::State::Ended)
{
EndActivation();
}
}
}
}
Camera OrbitDollyCursorMoveCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + float(cursorDelta.m_y) * m_props.m_dollySpeed, 0.0f);
return nextCamera;
}
void ScrollTranslationCameraInput::HandleEvents(const InputEvent& event)
{
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
{
BeginActivation();
}
}
Camera ScrollTranslationCameraInput::StepCamera(
const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
const auto translation_basis = LookTranslation(nextCamera);
const auto axisY = translation_basis.GetBasisY();
nextCamera.m_lookAt += axisY * scrollDelta * m_props.m_translateSpeed;
EndActivation();
return nextCamera;
}
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const SmoothProps& props, const float deltaTime)
{
const auto clamp_rotation = [](const float angle) { return std::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); };
// keep yaw in 0 - 360 range
float target_yaw = clamp_rotation(targetCamera.m_yaw);
const float current_yaw = clamp_rotation(currentCamera.m_yaw);
auto sign = [](const float value) { return static_cast<float>((0.0f < value) - (value < 0.0f)); };
// ensure smooth transition when moving across 0 - 360 boundary
const float yaw_delta = target_yaw - current_yaw;
if (std::abs(yaw_delta) >= AZ::Constants::Pi)
{
target_yaw -= AZ::Constants::TwoPi * sign(yaw_delta);
}
Camera camera;
// note: the math for the lerp smoothing implementation for camera rotation and translation was inspired by this excellent
// article by Scott Lembcke: https://www.gamasutra.com/blogs/ScottLembcke/20180404/316046/Improved_Lerp_Smoothing.php
const float lookRate = std::exp2(props.m_lookSmoothness);
const float lookT = std::exp2(-lookRate * deltaTime);
camera.m_pitch = AZ::Lerp(targetCamera.m_pitch, currentCamera.m_pitch, lookT);
camera.m_yaw = AZ::Lerp(target_yaw, current_yaw, lookT);
const float moveRate = std::exp2(props.m_moveSmoothness);
const float moveT = std::exp2(-moveRate * deltaTime);
camera.m_lookDist = AZ::Lerp(targetCamera.m_lookDist, currentCamera.m_lookDist, moveT);
camera.m_lookAt = targetCamera.m_lookAt.Lerp(currentCamera.m_lookAt, moveT);
return camera;
}
InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize)
{
const auto& inputChannelId = inputChannel.GetInputChannelId();
const auto& inputDeviceId = inputChannel.GetInputDevice().GetInputDeviceId();
if (inputChannelId == InputDeviceMouse::SystemCursorPosition)
{
AZ::Vector2 systemCursorPositionNormalized = AZ::Vector2::CreateZero();
InputSystemCursorRequestBus::EventResult(
systemCursorPositionNormalized, inputDeviceId, &InputSystemCursorRequestBus::Events::GetSystemCursorPositionNormalized);
return CursorMotionEvent{ScreenPoint(
systemCursorPositionNormalized.GetX() * windowSize.m_width, systemCursorPositionNormalized.GetY() * windowSize.m_height)};
}
else if (inputChannelId == InputDeviceMouse::Movement::Z)
{
return ScrollEvent{inputChannel.GetValue()};
}
else if (InputDeviceMouse::IsMouseDevice(inputDeviceId) || InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId))
{
return DiscreteInputEvent{inputChannelId, inputChannel.GetState()};
}
return AZStd::monostate{};
}
} // namespace AzFramework
@@ -0,0 +1,420 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/std/containers/variant.h>
#include <AzCore/std/optional.h>
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
namespace AzFramework
{
struct WindowSize;
struct Camera
{
AZ::Vector3 m_lookAt = AZ::Vector3::CreateZero(); //!< Position of camera when m_lookDist is zero,
//!< or position of m_lookAt when m_lookDist is greater
//!< than zero.
float m_yaw{0.0};
float m_pitch{0.0};
float m_lookDist{0.0}; //!< Zero gives first person free look, otherwise orbit about m_lookAt
//! View camera transform (v in MVP).
AZ::Transform View() const;
//! World camera transform.
AZ::Transform Transform() const;
//! World rotation.
AZ::Matrix3x3 Rotation() const;
//! World translation.
AZ::Vector3 Translation() const;
};
inline AZ::Transform Camera::View() const
{
return Transform().GetInverse();
}
inline AZ::Transform Camera::Transform() const
{
return AZ::Transform::CreateTranslation(m_lookAt) * AZ::Transform::CreateRotationX(m_pitch) *
AZ::Transform::CreateRotationZ(m_yaw) * AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisZ(m_lookDist));
}
inline AZ::Matrix3x3 Camera::Rotation() const
{
return AZ::Matrix3x3::CreateFromQuaternion(Transform().GetRotation());
}
inline AZ::Vector3 Camera::Translation() const
{
return Transform().GetTranslation();
}
struct CursorMotionEvent
{
ScreenPoint m_position;
};
struct ScrollEvent
{
float m_delta;
};
struct DiscreteInputEvent
{
InputChannelId m_channelId; //!< Channel type. (e.g. Keyboard key, mouse button or other device input).
InputChannel::State m_state; //!< Channel state. (e.g. Begin/update/end event).
};
using InputEvent = AZStd::variant<AZStd::monostate, CursorMotionEvent, ScrollEvent, DiscreteInputEvent>;
class CameraInput
{
public:
enum class Activation
{
Idle,
Begin,
Active,
End
};
virtual ~CameraInput() = default;
bool Beginning() const
{
return m_activation == Activation::Begin;
}
bool Ending() const
{
return m_activation == Activation::End;
}
bool Idle() const
{
return m_activation == Activation::Idle;
}
bool Active() const
{
return m_activation == Activation::Active;
}
void BeginActivation()
{
m_activation = Activation::Begin;
}
void EndActivation()
{
m_activation = Activation::End;
}
void ContinueActivation()
{
m_activation = Activation::Active;
}
void ClearActivation()
{
m_activation = Activation::Idle;
}
void Reset()
{
ClearActivation();
ResetImpl();
}
virtual void HandleEvents(const InputEvent& event) = 0;
virtual Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) = 0;
virtual bool Exclusive() const
{
return false;
}
protected:
virtual void ResetImpl()
{
}
private:
Activation m_activation = Activation::Idle;
};
struct SmoothProps
{
float m_lookSmoothness = 5.0f;
float m_moveSmoothness = 5.0f;
};
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const SmoothProps& props, float deltaTime);
class Cameras
{
public:
void AddCamera(AZStd::shared_ptr<CameraInput> cameraInput);
void HandleEvents(const InputEvent& event);
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime);
void Reset();
private:
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_activeCameraInputs;
AZStd::vector<AZStd::shared_ptr<CameraInput>> m_idleCameraInputs;
};
class CameraSystem
{
public:
void HandleEvents(const InputEvent& event);
Camera StepCamera(const Camera& targetCamera, float deltaTime);
Cameras m_cameras;
private:
float m_scrollDelta = 0.0f;
AZStd::optional<ScreenPoint> m_lastCursorPosition;
AZStd::optional<ScreenPoint> m_currentCursorPosition;
};
class RotateCameraInput : public CameraInput
{
public:
explicit RotateCameraInput(const InputChannelId channelId)
: m_channelId(channelId)
{
}
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
InputChannelId m_channelId;
struct Props
{
float m_rotateSpeed = 0.005f;
} m_props;
};
struct PanAxes
{
AZ::Vector3 m_horizontalAxis;
AZ::Vector3 m_verticalAxis;
};
using PanAxesFn = AZStd::function<PanAxes(const Camera& camera)>;
inline PanAxes LookPan(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
return {orientation.GetBasisX(), orientation.GetBasisZ()};
}
inline PanAxes OrbitPan(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
const auto basisX = orientation.GetBasisX();
const auto basisY = [&orientation] {
const auto forward = orientation.GetBasisY();
return AZ::Vector3(forward.GetX(), forward.GetY(), 0.0f).GetNormalized();
}();
return {basisX, basisY};
}
class PanCameraInput : public CameraInput
{
public:
explicit PanCameraInput(PanAxesFn panAxesFn)
: m_panAxesFn(AZStd::move(panAxesFn))
{
}
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_panSpeed = 0.01f;
bool m_panInvertX = true;
bool m_panInvertY = true;
} m_props;
private:
PanAxesFn m_panAxesFn;
};
using TranslationAxesFn = AZStd::function<AZ::Matrix3x3(const Camera& camera)>;
inline AZ::Matrix3x3 LookTranslation(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
const auto basisX = orientation.GetBasisX();
const auto basisY = orientation.GetBasisY();
const auto basisZ = AZ::Vector3::CreateAxisZ();
return AZ::Matrix3x3::CreateFromColumns(basisX, basisY, basisZ);
}
inline AZ::Matrix3x3 OrbitTranslation(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
const auto basisX = orientation.GetBasisX();
const auto basisY = [&orientation] {
const auto forward = orientation.GetBasisY();
return AZ::Vector3(forward.GetX(), forward.GetY(), 0.0f).GetNormalized();
}();
const auto basisZ = AZ::Vector3::CreateAxisZ();
return AZ::Matrix3x3::CreateFromColumns(basisX, basisY, basisZ);
}
class TranslateCameraInput : public CameraInput
{
public:
explicit TranslateCameraInput(TranslationAxesFn translationAxesFn)
: m_translationAxesFn(AZStd::move(translationAxesFn))
{
}
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
void ResetImpl() override;
struct Props
{
float m_translateSpeed = 10.0f;
float m_boostMultiplier = 3.0f;
} m_props;
private:
enum class TranslationType
{
// clang-format off
Nil = 0,
Forward = 1 << 0,
Backward = 1 << 1,
Left = 1 << 2,
Right = 1 << 3,
Up = 1 << 4,
Down = 1 << 5,
// clang-format on
};
friend TranslationType operator|(const TranslationType lhs, const TranslationType rhs)
{
return static_cast<TranslationType>(
static_cast<std::underlying_type_t<TranslationType>>(lhs) | static_cast<std::underlying_type_t<TranslationType>>(rhs));
}
friend TranslationType& operator|=(TranslationType& lhs, const TranslationType rhs)
{
lhs = lhs | rhs;
return lhs;
}
friend TranslationType operator^(const TranslationType lhs, const TranslationType rhs)
{
return static_cast<TranslationType>(
static_cast<std::underlying_type_t<TranslationType>>(lhs) ^ static_cast<std::underlying_type_t<TranslationType>>(rhs));
}
friend TranslationType& operator^=(TranslationType& lhs, const TranslationType rhs)
{
lhs = lhs ^ rhs;
return lhs;
}
friend TranslationType operator&(const TranslationType lhs, const TranslationType rhs)
{
return static_cast<TranslationType>(
static_cast<std::underlying_type_t<TranslationType>>(lhs) & static_cast<std::underlying_type_t<TranslationType>>(rhs));
}
friend TranslationType& operator&=(TranslationType& lhs, const TranslationType rhs)
{
lhs = lhs & rhs;
return lhs;
}
static TranslationType translationFromKey(InputChannelId channelId);
TranslationType m_translation = TranslationType::Nil;
TranslationAxesFn m_translationAxesFn;
bool m_boost = false;
};
class OrbitDollyScrollCameraInput : public CameraInput
{
public:
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_dollySpeed = 0.2f;
} m_props;
};
class OrbitDollyCursorMoveCameraInput : public CameraInput
{
public:
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_dollySpeed = 0.1f;
} m_props;
};
class ScrollTranslationCameraInput : public CameraInput
{
public:
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_translateSpeed = 0.2f;
} m_props;
};
class OrbitCameraInput : public CameraInput
{
public:
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
bool Exclusive() const override
{
return true;
}
Cameras m_orbitCameras;
struct Props
{
float m_defaultOrbitDistance = 15.0f;
float m_maxOrbitDistance = 100.0f;
} m_props;
};
InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize);
} // namespace AzFramework
@@ -13,6 +13,7 @@
#pragma once
#include <AzFramework/Viewport/ViewportId.h>
#include <AzFramework/Windowing/WindowBus.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
@@ -60,17 +61,18 @@ namespace AzFramework
{
//! The viewport ID this event was dispatched to.
ViewportId m_viewportId;
//! The native window handle for the application.
NativeWindowHandle m_windowHandle;
//! The input channel data for this event.
const AzFramework::InputChannel& m_inputChannel;
//! The priority this event was dispatched at.
ViewportControllerPriority m_priority;
ViewportControllerInputEvent(
ViewportId viewportId,
const AzFramework::InputChannel& inputChannel,
ViewportControllerPriority priority = ViewportControllerPriority::DispatchToAllPriorities
)
ViewportId viewportId, NativeWindowHandle windowHandle, const AzFramework::InputChannel& inputChannel,
ViewportControllerPriority priority = ViewportControllerPriority::DispatchToAllPriorities)
: m_viewportId(viewportId)
, m_windowHandle(windowHandle)
, m_inputChannel(inputChannel)
, m_priority(priority)
{
@@ -102,6 +102,8 @@ set(FILES
Viewport/ScreenGeometry.cpp
Viewport/CameraState.h
Viewport/CameraState.cpp
Viewport/CameraInput.h
Viewport/CameraInput.cpp
Viewport/DisplayContextRequestBus.h
Entity/BehaviorEntity.cpp
Entity/BehaviorEntity.h
@@ -43,14 +43,12 @@ namespace AzToolsFramework
const PrefabDom& modifiedState, const LinkId linkId) = 0;
//! Updates the affected template for a given entityId using the providedPatch
virtual bool PatchEntityInTemplate(PrefabDomValue& providedPatch, const AZ::EntityId& entityId) = 0;
virtual bool PatchEntityInTemplate(PrefabDomValue& providedPatch, const EntityAlias& entityAlias, const TemplateId& templateId) = 0;
virtual bool PatchEntityInTemplate(PrefabDom& providedPatch, AZ::EntityId entityId) = 0;
virtual void AppendEntityAliasToPatchPaths(PrefabDom& providedPatch, const AZ::EntityId& entityId) = 0;
//! Updates the template links (updating instances) for the given templateId using the providedPatch
virtual void PatchTemplate(PrefabDomValue& providedPatch, const TemplateId& templateId) = 0;
virtual bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId) = 0;
virtual void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) = 0;
@@ -107,7 +107,7 @@ namespace AzToolsFramework
return result.GetProcessing() != AZ::JsonSerializationResult::Processing::Halted;
}
bool InstanceToTemplatePropagator::PatchEntityInTemplate(PrefabDomValue& providedPatch, const AZ::EntityId& entityId)
bool InstanceToTemplatePropagator::PatchEntityInTemplate(PrefabDom& providedPatch, AZ::EntityId entityId)
{
InstanceOptionalReference instanceOptionalReference = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
@@ -119,53 +119,10 @@ namespace AzToolsFramework
return false;
}
//get template space associated with instance
Instance& instance = instanceOptionalReference->get();
TemplateId templateId = instance.GetTemplateId();
//alias entity goes by in template -> get via owning instance map
AZStd::optional<EntityAlias> entityAlias = instance.GetEntityAlias(entityId);
if (!entityAlias)
{
AZ_Error("Prefab", false, "Failed to find an entity alias for the provided entity");
return false;
}
return PatchEntityInTemplate(providedPatch, entityAlias.value(), templateId);
}
bool InstanceToTemplatePropagator::PatchEntityInTemplate(PrefabDomValue& providedPatch, const EntityAlias& entityAlias, const TemplateId& templateId)
{
PrefabDom& templateDomReference = m_prefabSystemComponentInterface->FindTemplateDom(templateId);
//query into the template dom for the alias
PrefabDomValueReference entityList = PrefabDomUtils::FindPrefabDomValue(templateDomReference, PrefabDomUtils::EntitiesName);
if (!entityList)
{
AZ_Error("Prefab", false, "Cannot patch entity in Template with id [%llu] because entity couldn't be found in the template", templateId);
return false;
}
PrefabDomValueReference entity = PrefabDomUtils::FindPrefabDomValue(entityList->get(), entityAlias.c_str());
if (!entity)
{
AZ_Error("Prefab", false, "Failed to aquire entity value reference");
return false;
}
//apply patch to section
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::ApplyPatch(entity->get(),
templateDomReference.GetAllocator(), providedPatch, AZ::JsonMergeApproach::JsonPatch);
AZ_Error("Prefab", result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success, "Patch was not successfully applied")
//trigger propagation
m_prefabSystemComponentInterface->PropagateTemplateChanges(templateId);
return true;
//get template id associated with instance
TemplateId templateId = instanceOptionalReference->get().GetTemplateId();
AppendEntityAliasToPatchPaths(providedPatch, entityId);
return PatchTemplate(providedPatch, templateId);
}
void InstanceToTemplatePropagator::AppendEntityAliasToPatchPaths(PrefabDom& providedPatch, const AZ::EntityId& entityId)
@@ -215,7 +172,7 @@ namespace AzToolsFramework
}
}
void InstanceToTemplatePropagator::PatchTemplate(PrefabDomValue& providedPatch, const TemplateId& templateId)
bool InstanceToTemplatePropagator::PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId)
{
PrefabDom& templateDomReference = m_prefabSystemComponentInterface->FindTemplateDom(templateId);
@@ -223,14 +180,17 @@ namespace AzToolsFramework
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::ApplyPatch(templateDomReference,
templateDomReference.GetAllocator(), providedPatch, AZ::JsonMergeApproach::JsonPatch);
AZ_Error("Prefab", result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success,
"Patch was not successfully applied");
//trigger propagation
if (result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success)
{
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(templateId, true);
m_prefabSystemComponentInterface->PropagateTemplateChanges(templateId);
return true;
}
else
{
AZ_Error("Prefab", false, "Patch was not successfully applied");
return false;
}
}
@@ -287,6 +247,8 @@ namespace AzToolsFramework
AddPatchesToLink(patches, linkToApplyPatches);
linkToApplyPatches.UpdateTarget();
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(linkToApplyPatches.GetTargetTemplateId(), true);
m_prefabSystemComponentInterface->PropagateTemplateChanges(linkToApplyPatches.GetTargetTemplateId());
}
@@ -31,21 +31,19 @@ namespace AzToolsFramework
bool GeneratePatch(PrefabDom& generatedPatch, const PrefabDom& initialState, const PrefabDom& modifiedState) override;
bool GeneratePatchForLink(PrefabDom& generatedPatch, const PrefabDom& initialState,
const PrefabDom& modifiedState, LinkId linkId) override;
bool PatchEntityInTemplate(PrefabDomValue& providedPatch, const AZ::EntityId& entityId) override;
bool PatchEntityInTemplate(PrefabDomValue& providedPatch, const EntityAlias& entityAlias, const TemplateId& templateId) override;
bool PatchEntityInTemplate(PrefabDom& providedPatch, AZ::EntityId entityId) override;
void AppendEntityAliasToPatchPaths(PrefabDom& providedPatch, const AZ::EntityId& entityId) override;
InstanceOptionalReference GetTopMostInstanceInHierarchy(AZ::EntityId entityId);
void PatchTemplate(PrefabDomValue& providedPatch, const AzToolsFramework::Prefab::TemplateId& templateId) override;
bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId) override;
void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) override;
void AddPatchesToLink(PrefabDom& patches, Link& link);
private:
InstanceEntityMapperInterface* m_instanceEntityMapperInterface;
PrefabSystemComponentInterface* m_prefabSystemComponentInterface;
@@ -127,12 +127,12 @@ namespace AzToolsFramework
InstanceEntityScrubber instanceEntityScrubber(newlyAddedEntities);
settings.m_metadata.Add(&instanceEntityScrubber);
AZ::JsonSerializationResult::ResultCode result =
AZ::JsonSerialization::Load(instance, prefabDom, settings);
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(instance, prefabDom, settings);
if (result.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
{
AZ_Error("Prefab", false,
AZ_Error(
"Prefab", false,
"Failed to de-serialize Prefab Instance from Prefab DOM. "
"Unable to proceed.");
@@ -348,8 +348,7 @@ namespace AzToolsFramework
"Prefab", false,
"PrefabLoader::SaveTemplate - Unable to save Prefab Template with id: %llu. "
"Template with that id is invalid",
templateId
);
templateId);
return AZStd::nullopt;
}
@@ -79,13 +79,16 @@ namespace AzToolsFramework
//generate undo/redo patches
m_instanceToTemplateInterface->GeneratePatch(m_redoPatch, initialState, endState);
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(m_redoPatch, entityId);
m_instanceToTemplateInterface->GeneratePatch(m_undoPatch, endState, initialState);
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(m_undoPatch, entityId);
}
void PrefabUndoEntityUpdate::Undo()
{
[[maybe_unused]] bool isPatchApplicationSuccessful =
m_instanceToTemplateInterface->PatchEntityInTemplate(m_undoPatch, m_entityAlias, m_templateId);
m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId);
AZ_Error(
"Prefab", isPatchApplicationSuccessful,
"Applying the undo patch on the entity with alias '%s' in template with id '%llu' was unsuccessful", m_entityAlias.c_str(),
@@ -95,7 +98,8 @@ namespace AzToolsFramework
void PrefabUndoEntityUpdate::Redo()
{
[[maybe_unused]] bool isPatchApplicationSuccessful =
m_instanceToTemplateInterface->PatchEntityInTemplate(m_redoPatch, m_entityAlias, m_templateId);
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId);
AZ_Error(
"Prefab", isPatchApplicationSuccessful,
"Applying the redo patch on the entity with alias '%s' in template with id '%llu' was unsuccessful", m_entityAlias.c_str(),
@@ -21,7 +21,12 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Viewport/ViewportTypes.h>
class QPoint;
class QPoint; // LYN-2315 in-progress, remove this
namespace AzFramework
{
struct ScreenPoint;
}
namespace AzToolsFramework
{
@@ -235,12 +240,12 @@ namespace AzToolsFramework
/// Restores the cursor and ends locking it in place, allowing it to be moved freely.
virtual void EndCursorCapture() = 0;
/// Gets the most recent recorded cursor position in the viewport in screen space coordinates.
virtual QPoint ViewportCursorScreenPosition() = 0;
virtual AzFramework::ScreenPoint ViewportCursorScreenPosition() = 0;
/// Gets the cursor position recorded prior to the most recent cursor position.
/// Note: The cursor may be captured by the viewport, in which case this may not correspond to the last result
/// from ViewportCursorScreenPosition. This method will always return the correct position to generate a mouse
/// position delta.
virtual AZStd::optional<QPoint> PreviousViewportCursorScreenPosition() = 0;
virtual AZStd::optional<AzFramework::ScreenPoint> PreviousViewportCursorScreenPosition() = 0;
protected:
~ViewportMouseCursorRequests() = default;
@@ -202,12 +202,18 @@ namespace AzToolsFramework
return mouseInteractionEvent.m_wheelDelta;
}
/// Return Qt QPoint from an Viewport ScreenPoint.
/// Return QPoint from AzFramework::ScreenPoint.
inline QPoint QPointFromScreenPoint(const AzFramework::ScreenPoint& screenPoint)
{
return {screenPoint.m_x, screenPoint.m_y};
}
/// Return AzFramework::ScreenPoint from QPoint.
inline AzFramework::ScreenPoint ScreenPointFromQPoint(const QPoint& qpoint)
{
return AzFramework::ScreenPoint{qpoint.x(), qpoint.y()};
}
/// Map from Qt -> Lumberyard buttons.
inline AZ::u32 TranslateMouseButtons(const Qt::MouseButtons buttons)
{
@@ -75,7 +75,7 @@ namespace UnitTest
EntityAlias entityAlias = entityAliasRef.value();
//update template
ASSERT_TRUE(m_instanceToTemplateInterface->PatchEntityInTemplate(patch, entityAlias, templateId));
ASSERT_TRUE(m_instanceToTemplateInterface->PatchEntityInTemplate(patch, entityId));
//undo change
instanceEntityUndo.Undo();
@@ -12,5 +12,5 @@
set(LY_BUILD_DEPENDENCIES
PUBLIC
3rdParty::FreeType2
3rdParty::freetype
)
+16 -1
View File
@@ -76,6 +76,7 @@
#include "EditorPreferencesPageGeneral.h"
#include "ViewportManipulatorController.h"
#include "LegacyViewportCameraController.h"
#include "ModernViewportCameraController.h"
#include "ViewPane.h"
#include "CustomResolutionDlg.h"
@@ -92,6 +93,7 @@
// Atom
#include <Atom/RPI.Public/View.h>
#include <Atom/RPI.Public/ViewportContextManager.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Math/MatrixUtils.h>
#include <QtGui/private/qhighdpiscaling_p.h>
@@ -106,6 +108,10 @@ void StartFixedCursorMode(QObject *viewport);
#define RENDER_MESH_TEST_DISTANCE (0.2f)
#define CURSOR_FONT_HEIGHT 8.0f
AZ_CVAR(
bool, ed_useNewCameraSystem, false, nullptr, AZ::ConsoleFunctorFlags::Null,
"Use the new Editor camera system (the Atom-native Editor viewport (experimental) must also be enabled)");
namespace AZ::ViewportHelpers
{
static const char TextCantCreateCameraNoLevel[] = "Cannot create camera when no level is loaded.";
@@ -1236,7 +1242,16 @@ void EditorViewportWidget::SetViewportId(int id)
viewportContext->ConnectProjectionMatrixChangedHandler(m_cameraProjectionMatrixChangeHandler);
m_renderViewport->GetControllerList()->Add(AZStd::make_shared<SandboxEditor::ViewportManipulatorController>());
m_renderViewport->GetControllerList()->Add(AZStd::make_shared<SandboxEditor::LegacyViewportCameraController>());
if (ed_useNewCameraSystem)
{
m_renderViewport->GetControllerList()->Add(AZStd::make_shared<SandboxEditor::ModernViewportCameraController>());
}
else
{
m_renderViewport->GetControllerList()->Add(AZStd::make_shared<SandboxEditor::LegacyViewportCameraController>());
}
UpdateScene();
}
@@ -11,8 +11,10 @@
*/
#include "LegacyViewportCameraController.h"
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
#include <Atom/RPI.Public/ViewportContext.h>
@@ -73,7 +75,8 @@ AZ::RPI::ViewportContextPtr LegacyViewportCameraControllerInstance::GetViewportC
return viewportContextManager->GetViewportContextById(GetViewportId());
}
bool LegacyViewportCameraControllerInstance::HandleMouseMove(const QPoint& currentMousePos, const QPoint& previousMousePos)
bool LegacyViewportCameraControllerInstance::HandleMouseMove(
const AzFramework::ScreenPoint& currentMousePos, const AzFramework::ScreenPoint& previousMousePos)
{
if (previousMousePos == currentMousePos)
{
@@ -100,7 +103,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(const QPoint& curre
Vec3 ydir = m.GetColumn1().GetNormalized();
Vec3 pos = m.GetTranslation();
const float posDelta = 0.2f * (previousMousePos.y() - currentMousePos.y()) * speedScale;
const float posDelta = 0.2f * (previousMousePos.m_y - currentMousePos.m_y) * speedScale;
pos = pos - ydir * posDelta;
m_orbitDistance = m_orbitDistance + posDelta;
m_orbitDistance = fabs(m_orbitDistance);
@@ -111,7 +114,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(const QPoint& curre
}
else if (m_inRotateMode)
{
Ang3 angles(-currentMousePos.y() + previousMousePos.y(), 0, -currentMousePos.x() + previousMousePos.x());
Ang3 angles(-currentMousePos.m_y + previousMousePos.m_y, 0, -currentMousePos.m_x + previousMousePos.m_x);
angles = angles * 0.002f * gSettings.cameraRotateSpeed;
if (gSettings.invertYRotation)
{
@@ -143,7 +146,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(const QPoint& curre
}
Vec3 pos = m.GetTranslation();
pos += 0.1f * xdir * (currentMousePos.x() - previousMousePos.x()) * speedScale + 0.1f * zdir * (previousMousePos.y() - currentMousePos.y()) * speedScale;
pos += 0.1f * xdir * (currentMousePos.m_x - previousMousePos.m_x) * speedScale + 0.1f * zdir * (previousMousePos.m_y - currentMousePos.m_y) * speedScale;
m.SetTranslation(pos);
AZ::Transform transform = viewportContext->GetCameraTransform();
@@ -153,7 +156,7 @@ bool LegacyViewportCameraControllerInstance::HandleMouseMove(const QPoint& curre
}
else if (m_inOrbitMode)
{
Ang3 angles(-currentMousePos.y() + previousMousePos.y(), 0, -currentMousePos.x() + previousMousePos.x());
Ang3 angles(-currentMousePos.m_y + previousMousePos.m_y, 0, -currentMousePos.m_x + previousMousePos.m_x);
angles = angles * 0.002f * gSettings.cameraRotateSpeed;
if (gSettings.invertPan)
@@ -289,14 +292,13 @@ bool LegacyViewportCameraControllerInstance::HandleInputChannelEvent(const AzFra
if (id == AzFramework::InputDeviceMouse::SystemCursorPosition)
{
QPoint screenPosition = QPoint();
bool result = false;
AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Event(
GetViewportId(),
[this, &result](AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequests* mouseRequests)
{
auto previousMousePosition = mouseRequests->PreviousViewportCursorScreenPosition();
if (previousMousePosition.has_value())
if (auto previousMousePosition = mouseRequests->PreviousViewportCursorScreenPosition();
previousMousePosition.has_value())
{
result = HandleMouseMove(mouseRequests->ViewportCursorScreenPosition(), previousMousePosition.value());
}
@@ -21,6 +21,11 @@
#include <QtCore/qnamespace.h>
#include <QPoint>
namespace AzFramework
{
struct ScreenPoint;
}
namespace SandboxEditor
{
class LegacyViewportCameraControllerInstance final
@@ -45,7 +50,7 @@ namespace SandboxEditor
AZ::RPI::ViewportContextPtr GetViewportContext();
bool HandleMouseMove(const QPoint& currentMousePos, const QPoint& previousMousePos);
bool HandleMouseMove(const AzFramework::ScreenPoint& currentMousePos, const AzFramework::ScreenPoint& previousMousePos);
bool HandleMouseWheel(float zDelta);
bool IsKeyDown(Qt::Key key) const;
@@ -0,0 +1,97 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "ModernViewportCameraController.h"
#include <Atom/RPI.Public/ViewportContext.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
#include <AzCore/Interface/Interface.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzFramework/Windowing/WindowBus.h>
namespace SandboxEditor
{
static AZ::RPI::ViewportContextPtr RetrieveViewportContext(const AzFramework::ViewportId viewportId)
{
auto viewportContextManager = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
if (!viewportContextManager)
{
return nullptr;
}
auto viewportContext = viewportContextManager->GetViewportContextById(viewportId);
if (!viewportContext)
{
return nullptr;
}
return viewportContext;
}
ModernViewportCameraControllerInstance::ModernViewportCameraControllerInstance(const AzFramework::ViewportId viewportId)
: MultiViewportControllerInstanceInterface(viewportId)
{
// LYN-2315 TODO - move setup out of constructor, pass cameras in
auto firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Right);
auto firstPersonPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(AzFramework::LookPan);
auto firstPersonTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::LookTranslation);
auto firstPersonWheelCamera = AZStd::make_shared<AzFramework::ScrollTranslationCameraInput>();
auto orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>();
auto orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Left);
auto orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::OrbitTranslation);
auto orbitDollyWheelCamera = AZStd::make_shared<AzFramework::OrbitDollyScrollCameraInput>();
auto orbitDollyMoveCamera = AZStd::make_shared<AzFramework::OrbitDollyCursorMoveCameraInput>();
auto orbitPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(AzFramework::OrbitPan);
orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitDollyWheelCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitDollyMoveCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitPanCamera);
m_cameraSystem.m_cameras.AddCamera(firstPersonRotateCamera);
m_cameraSystem.m_cameras.AddCamera(firstPersonPanCamera);
m_cameraSystem.m_cameras.AddCamera(firstPersonTranslateCamera);
m_cameraSystem.m_cameras.AddCamera(firstPersonWheelCamera);
m_cameraSystem.m_cameras.AddCamera(orbitCamera);
if (const auto viewportContext = RetrieveViewportContext(viewportId))
{
// set position but not orientation
m_targetCamera.m_lookAt = viewportContext->GetCameraTransform().GetTranslation();
// LYN-2315 TODO https://www.geometrictools.com/Documentation/EulerAngles.pdf
m_camera = m_targetCamera;
}
}
bool ModernViewportCameraControllerInstance::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event)
{
AzFramework::WindowSize windowSize;
AzFramework::WindowRequestBus::EventResult(
windowSize, event.m_windowHandle, &AzFramework::WindowRequestBus::Events::GetClientAreaSize);
m_cameraSystem.HandleEvents(AzFramework::BuildInputEvent(event.m_inputChannel, windowSize));
return true; // consume event
}
void ModernViewportCameraControllerInstance::UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event)
{
if (auto viewportContext = RetrieveViewportContext(GetViewportId()))
{
m_targetCamera = m_cameraSystem.StepCamera(m_targetCamera, event.m_deltaTime.count());
m_camera = AzFramework::SmoothCamera(m_camera, m_targetCamera, m_smoothProps, event.m_deltaTime.count());
viewportContext->SetCameraTransform(m_camera.Transform());
}
}
} // namespace SandboxEditor
@@ -0,0 +1,37 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Viewport/CameraInput.h>
#include <AzFramework/Viewport/MultiViewportController.h>
namespace SandboxEditor
{
class ModernViewportCameraControllerInstance final : public AzFramework::MultiViewportControllerInstanceInterface
{
public:
explicit ModernViewportCameraControllerInstance(AzFramework::ViewportId viewportId);
// MultiViewportControllerInstanceInterface overrides ...
bool HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) override;
void UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) override;
private:
AzFramework::Camera m_camera;
AzFramework::Camera m_targetCamera;
AzFramework::SmoothProps m_smoothProps;
AzFramework::CameraSystem m_cameraSystem;
};
using ModernViewportCameraController = AzFramework::MultiViewportController<ModernViewportCameraControllerInstance>;
} // namespace SandboxEditor
+1 -1
View File
@@ -16,7 +16,7 @@
#include "ImageTIF.h"
/// libTiff
#include <libtiff/tiffio.h> // TIFF library
#include <tiffio.h> // TIFF library
// Function prototypes
static tsize_t libtiffDummyReadProc (thandle_t fd, tdata_t buf, tsize_t size);
@@ -16,6 +16,7 @@
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzCore/Script/ScriptTimePoint.h>
#include <QApplication>
@@ -99,18 +100,16 @@ bool ViewportManipulatorControllerInstance::HandleInputChannelEvent(const AzFram
// Cache the ray trace results when doing manipulator interaction checks, no need to recalculate after
if (event.m_priority == ManipulatorPriority)
{
QPoint screenPosition = QPoint();
AzFramework::ScreenPoint screenPosition = AzFramework::ScreenPoint(0, 0);
ViewportMouseCursorRequestBus::EventResult(
screenPosition, GetViewportId(),
&ViewportMouseCursorRequestBus::Events::ViewportCursorScreenPosition
);
m_state.m_mousePick.m_screenCoordinates = AzFramework::ScreenPoint{screenPosition.x(), screenPosition.y()};
screenPosition, GetViewportId(), &ViewportMouseCursorRequestBus::Events::ViewportCursorScreenPosition);
m_state.m_mousePick.m_screenCoordinates = screenPosition;
AZStd::optional<ProjectedViewportRay> ray;
ViewportInteractionRequestBus::EventResult(
ray, GetViewportId(),
&ViewportInteractionRequestBus::Events::ViewportScreenToWorldRay,
screenPosition
);
ray, GetViewportId(), &ViewportInteractionRequestBus::Events::ViewportScreenToWorldRay,
QPoint(screenPosition.m_x, screenPosition.m_y));
if (ray.has_value())
{
m_state.m_mousePick.m_rayOrigin = ray.value().origin;
@@ -996,6 +996,8 @@ set(FILES
ViewportManipulatorController.h
LegacyViewportCameraController.cpp
LegacyViewportCameraController.h
ModernViewportCameraController.cpp
ModernViewportCameraController.h
RenderViewport.cpp
RenderViewport.h
TopRendererWnd.cpp
@@ -41,6 +41,65 @@
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
// Class to implement the WindowRequestBus::Handler instead of the QViewport class.
// This is to bypass a link warning that occurs in unity builds if EditorCommon dll
// is linked along with a gem that also implements WindowRequestBus::Handler. The
// issue is that QViewport has a dllexport so it causes duplicate code to be linked
// in and a warning that there will be two of the same symbol in memory
class QViewportRequests
: public AzFramework::WindowRequestBus::Handler
{
public:
QViewportRequests(QViewport& viewport)
: m_viewport(viewport)
{
}
~QViewportRequests() override
{
AzFramework::WindowRequestBus::Handler::BusDisconnect();
}
// WindowRequestBus::Handler...
void SetWindowTitle(const AZStd::string& title) override
{
m_viewport.SetWindowTitle(title);
}
AzFramework::WindowSize GetClientAreaSize() const override
{
return m_viewport.GetClientAreaSize();
}
void ResizeClientArea(AzFramework::WindowSize clientAreaSize) override
{
m_viewport.ResizeClientArea(clientAreaSize);
}
bool GetFullScreenState() const override
{
return m_viewport.GetFullScreenState();
}
void SetFullScreenState(bool fullScreenState) override
{
m_viewport.SetFullScreenState(fullScreenState);
}
bool CanToggleFullScreenState() const override
{
return m_viewport.CanToggleFullScreenState();
}
void ToggleFullScreenState() override
{
m_viewport.ToggleFullScreenState();
}
private:
QViewport& m_viewport;
};
struct QViewport::SPreviousContext
{
CCamera renderCamera;
@@ -188,6 +247,8 @@ QViewport::QViewport(QWidget* parent, StartupMode startupMode)
, m_private(new SPrivate())
, m_cameraControlMode(CameraControlMode::NONE)
{
m_viewportRequests = AZStd::make_unique<QViewportRequests>(*this);
if (startupMode & StartupMode_Immediate)
Startup();
}
@@ -213,6 +274,8 @@ void QViewport::Startup()
QViewport::~QViewport()
{
DestroyRenderContext();
m_viewportRequests.reset();
}
void QViewport::UpdateBackgroundColor()
@@ -316,7 +379,7 @@ bool QViewport::CreateRenderContext()
if (AZ::Interface<AzFramework::AtomActiveInterface>::Get())
{
AzFramework::WindowRequestBus::Handler::BusConnect(windowHandle);
m_viewportRequests.get()->BusConnect(windowHandle);
AzFramework::WindowSystemNotificationBus::Broadcast(&AzFramework::WindowSystemNotificationBus::Handler::OnWindowCreated, windowHandle);
m_lastHwnd = windowHandle;
@@ -346,7 +409,7 @@ void QViewport::DestroyRenderContext()
m_renderContextCreated = false;
AzFramework::WindowNotificationBus::Event(windowHandle, &AzFramework::WindowNotificationBus::Handler::OnWindowClosed);
AzFramework::WindowRequestBus::Handler::BusDisconnect();
m_viewportRequests.get()->BusDisconnect();
m_lastHwnd = 0;
}
}
+10 -9
View File
@@ -41,6 +41,7 @@ struct SMouseEvent;
struct SViewportSettings;
struct SViewportState;
class QElapsedTimer;
class QViewportRequests;
class EDITOR_COMMON_API QViewport;
struct SRenderContext
@@ -66,7 +67,6 @@ AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
class EDITOR_COMMON_API QViewport
: public QWidget
, public AzFramework::WindowRequestBus::Handler
{
Q_OBJECT
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
@@ -107,14 +107,14 @@ public:
void SetSize(const QSize& size);
void GetImageOffscreen(CImageEx& image, const QSize& customSize);
// WindowRequestBus::Handler...
void SetWindowTitle(const AZStd::string& title) override;
AzFramework::WindowSize GetClientAreaSize() const override;
void ResizeClientArea(AzFramework::WindowSize clientAreaSize) override;
bool GetFullScreenState() const override;
void SetFullScreenState(bool fullScreenState) override;
bool CanToggleFullScreenState() const override;
void ToggleFullScreenState() override;
// WindowRequestBus::Handler... (handler moved to cpp to resolve link issues in unity builds)
void SetWindowTitle(const AZStd::string& title);
AzFramework::WindowSize GetClientAreaSize() const;
void ResizeClientArea(AzFramework::WindowSize clientAreaSize);
bool GetFullScreenState() const;
void SetFullScreenState(bool fullScreenState);
bool CanToggleFullScreenState() const;
void ToggleFullScreenState();
public slots:
void Update();
@@ -197,6 +197,7 @@ private:
std::unique_ptr<SViewportSettings> m_settings;
std::unique_ptr<SViewportState> m_state;
std::vector<QViewportConsumer*> m_consumers;
AZStd::unique_ptr<QViewportRequests> m_viewportRequests;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
HWND m_lastHwnd = 0;
bool m_resizeWindowEvent = false;
@@ -170,7 +170,9 @@ bool CCrySimpleJob::ExecuteCommand(const std::string& rCmd, std::string& outErro
threadIdStream << threadId;
// Multiple threads could execute a command, therefore the temporary file has to be unique per thread.
std::string stdErrorTempFilename = SEnviropment::Instance().m_TempPath + "stderr_" + threadIdStream.str() + ".log";
AZ::IO::Path errorTempFilePath = SEnviropment::Instance().m_TempPath / AZStd::string::format("stderr_%s.log", threadIdStream.str().c_str());
std::string stdErrorTempFilename{ errorTempFilePath.c_str(), errorTempFilePath.Native().size() };
CCrySimpleFileGuard FGTmpOutput(stdErrorTempFilename); // Delete file at the end of this function
std::string systemCmd = rCmd;
@@ -105,7 +105,7 @@ namespace ImageProcessingAtom
{
}
explicit ColorRGBA16(uint64 a_u)
explicit ColorRGBA16(AZ::u64 a_u)
: u(a_u)
{
}
@@ -152,7 +152,7 @@ namespace ImageProcessingAtom
uint16 b;
uint16 a;
};
uint64 u;
AZ::u64 u;
};
};
@@ -224,7 +224,7 @@ namespace ImageProcessingAtom
srcImage->GetImagePointer(mip, srcMem, srcPitch);
const pvrtexture::CPVRTextureHeader srcHeader(
srcPixelType.PixelTypeID, // uint64 u64PixelFormat,
srcPixelType.PixelTypeID, // AZ::u64 u64PixelFormat,
width, // uint32 u32Height=1,
height, // uint32 u32Width=1,
1, // uint32 u32Depth=1,
@@ -315,7 +315,7 @@ namespace ImageProcessingAtom
// Preparing source compressed data
const pvrtexture::CPVRTextureHeader compressedHeader(
FindPvrPixelFormat(fmtSrc), // uint64 u64PixelFormat,
FindPvrPixelFormat(fmtSrc), // AZ::u64 u64PixelFormat,
width, // uint32 u32Height=1,
height, // uint32 u32Width=1,
1, // uint32 u32Depth=1,
@@ -25,9 +25,5 @@ typedef AZ::s32 int32;
typedef AZ::s32 sint32;
typedef AZ::u32 uint32;
typedef AZ::s64 int64;
typedef AZ::s64 sint64;
typedef AZ::u64 uint64;
typedef float f32;
typedef double f64;
@@ -20,7 +20,7 @@
#include <QString>
#include <libtiff/tiffio.h> // TIFF library
#include <tiffio.h> // TIFF library
namespace ImageProcessingAtom
{
@@ -106,4 +106,4 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
{
return SampleDepthOrHeightMap(MaterialSrg::m_depthInverted, MaterialSrg::m_depthMap, MaterialSrg::m_sampler, uv, uv_ddx, uv_ddy);
}
}
@@ -69,6 +69,7 @@ struct VSOutput
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include <Atom/Features/PBR/LightingModel.azsli>
#include <Atom/Features/Vertex/VertexHelper.azsli>
VSOutput EnhancedPbr_ForwardPassVS(VSInput IN)
{
@@ -88,7 +89,7 @@ VSOutput EnhancedPbr_ForwardPassVS(VSInput IN)
OUT.m_detailUv[0] = mul(MaterialSrg::m_detailUvMatrix, float3(IN.m_uv0, 1.0)).xy;
OUT.m_detailUv[1] = mul(MaterialSrg::m_detailUvMatrix, float3(IN.m_uv1, 1.0)).xy;
PbrVsHelper(IN, OUT, worldPosition, o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset);
VertexHelper(IN, OUT, worldPosition, o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset);
return OUT;
}
@@ -248,7 +249,8 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
// Convert the angle from [0..1] = [0 .. 180 degrees] to radians [0 .. PI]
const float2 anisotropy = float2(MaterialSrg::m_anisotropicAngle * PI, MaterialSrg::m_anisotropicFactor);
PbrLightingOutput lightingOutput = PbrLighting(IN, baseColor, metallic, roughness, specularF0Factor,
PbrLightingOutput lightingOutput = PbrLighting(IN,
baseColor, metallic, roughness, specularF0Factor,
normal, IN.m_tangent, IN.m_bitangent, anisotropy,
emissive, occlusion, transmissionTintThickness, MaterialSrg::m_transmissionParams, clearCoatFactor, clearCoatRoughness, clearCoatNormal, alpha, o_opacity_mode);
@@ -57,7 +57,7 @@ void GetClearCoatInputs(Texture2D influenceMap, float2 influenceUV, float clearC
if (useNormalMap)
{
float4 sampledValue = normalMap.Sample(mapSampler, normalUV);
clearCoatNormal = GetWorldSpaceNormal(sampledValue, normal, tangent, bitangent, uvMatrix, normalStrength);
clearCoatNormal = GetWorldSpaceNormal(sampledValue.xy, normal, tangent, bitangent, uvMatrix, normalStrength);
}
else
{
@@ -91,6 +91,7 @@ struct VSOutput
#include <Atom/Features/PBR/AlphaUtils.azsli> // TODO: Remove this after OpacityMode is removed from LightingModel
#include <Atom/Features/PBR/LightingModel.azsli>
#include <Atom/Features/Vertex/VertexHelper.azsli>
VSOutput SkinVS(VSInput IN)
{
@@ -125,7 +126,7 @@ VSOutput SkinVS(VSInput IN)
OUT.m_blendMask = float4(0,1,0,0);
}
PbrVsHelper(IN, OUT, worldPosition, false);
VertexHelper(IN, OUT, worldPosition, false);
return OUT;
}
@@ -85,6 +85,7 @@ struct VSOutput
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include <Atom/Features/PBR/LightingModel.azsli>
#include <Atom/Features/Vertex/VertexHelper.azsli>
VSOutput ForwardPassVS(VSInput IN)
{
@@ -109,7 +110,7 @@ VSOutput ForwardPassVS(VSInput IN)
// We can skip per-vertex shadow coords when parallax is enabled because we need to calculate per-pixel shadow coords anyway.
// We cannot skip shadow coords when o_debugDrawMode is on because some debug draw modes return before parallax.
bool skipShadowCoords = o_debugDrawMode == DebugDrawMode::None && o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset;
PbrVsHelper(IN, OUT, worldPosition, skipShadowCoords);
VertexHelper(IN, OUT, worldPosition, skipShadowCoords);
return OUT;
}
@@ -346,7 +347,8 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
const float2 anisotropy = 0.0; // Does not affect calculations unless 'o_enableAnisotropy' is enabled
PbrLightingOutput lightingOutput = PbrLighting(IN, baseColor, metallic, roughness, specularF0Factor,
PbrLightingOutput lightingOutput = PbrLighting(IN,
baseColor, metallic, roughness, specularF0Factor,
normalWS, tangents[0], bitangents[0], anisotropy,
emissive, occlusion, transmissionTintThickness, MaterialSrg::m_transmissionParams, clearCoatFactor, clearCoatRoughness, clearCoatNormal, alpha, o_opacity_mode);
@@ -68,18 +68,19 @@ struct VSOutput
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include <Atom/Features/PBR/LightingModel.azsli>
#include <Atom/Features/Vertex/VertexHelper.azsli>
VSOutput StandardPbr_ForwardPassVS(VSInput IN)
{
VSOutput OUT;
float3 worldPosition = mul(ObjectSrg::GetWorldMatrix(), float4(IN.m_position, 1.0)).xyz;
// By design, only UV0 is allowed to apply transforms.
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
OUT.m_uv[1] = IN.m_uv1;
PbrVsHelper(IN, OUT, worldPosition, o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset);
VertexHelper(IN, OUT, worldPosition, o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset);
return OUT;
}
@@ -126,6 +127,10 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
}
}
Surface surface;
surface.position = IN.m_worldPosition.xyz;
// ------- Alpha & Clip -------
float2 baseColorUv = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
@@ -137,7 +142,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
float2 normalUv = IN.m_uv[MaterialSrg::m_normalMapUvIndex];
float3x3 uvMatrix = MaterialSrg::m_normalMapUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); // By design, only UV0 is allowed to apply transforms.
float3 normalWS = GetNormalInputWS(MaterialSrg::m_normalMap, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, isFrontFace, IN.m_normal,
surface.normal = GetNormalInputWS(MaterialSrg::m_normalMap, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, isFrontFace, IN.m_normal,
tangents[MaterialSrg::m_normalMapUvIndex], bitangents[MaterialSrg::m_normalMapUvIndex], uvMatrix, o_normal_useTexture, MaterialSrg::m_normalFactor);
// ------- Base Color -------
@@ -154,26 +159,19 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
metallic = GetMetallicInput(MaterialSrg::m_metallicMap, MaterialSrg::m_sampler, metallicUv, MaterialSrg::m_metallicFactor, o_metallic_useTexture);
}
// ------- Roughness -------
float2 roughnessUv = IN.m_uv[MaterialSrg::m_roughnessMapUvIndex];
float roughness = GetRoughnessInput(MaterialSrg::m_roughnessMap, MaterialSrg::m_sampler, roughnessUv, MaterialSrg::m_roughnessFactor,
MaterialSrg::m_roughnessLowerBound, MaterialSrg::m_roughnessUpperBound, o_roughness_useTexture);
// ------- Specular -------
float2 specularUv = IN.m_uv[MaterialSrg::m_specularF0MapUvIndex];
float specularF0Factor = GetSpecularInput(MaterialSrg::m_specularF0Map, MaterialSrg::m_sampler, specularUv, MaterialSrg::m_specularF0Factor, o_specularF0_useTexture);
float specularF0 = GetSpecularInput(MaterialSrg::m_specularF0Map, MaterialSrg::m_sampler, specularUv, MaterialSrg::m_specularF0Factor, o_specularF0_useTexture);
// ------- Emissive -------
surface.SetAlbedoAndSpecularF0(baseColor, specularF0, metallic);
float2 emissiveUv = IN.m_uv[MaterialSrg::m_emissiveMapUvIndex];
float3 emissive = GetEmissiveInput(MaterialSrg::m_emissiveMap, MaterialSrg::m_sampler, emissiveUv, MaterialSrg::m_emissiveIntensity, MaterialSrg::m_emissiveColor.rgb, o_emissiveEnabled, o_emissive_useTexture);
// ------- Roughness -------
// ------- Occlusion -------
float2 occlusionUv = IN.m_uv[MaterialSrg::m_ambientOcclusionMapUvIndex];
float occlusion = GetOcclusionInput(MaterialSrg::m_ambientOcclusionMap, MaterialSrg::m_sampler, occlusionUv, MaterialSrg::m_ambientOcclusionFactor, o_ambientOcclusion_useTexture);
float2 roughnessUv = IN.m_uv[MaterialSrg::m_roughnessMapUvIndex];
surface.roughnessLinear = GetRoughnessInput(MaterialSrg::m_roughnessMap, MaterialSrg::m_sampler, roughnessUv, MaterialSrg::m_roughnessFactor,
MaterialSrg::m_roughnessLowerBound, MaterialSrg::m_roughnessUpperBound, o_roughness_useTexture);
surface.CalculateRoughnessA();
// ------- Subsurface -------
@@ -184,31 +182,99 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
float2 transmissionUv = IN.m_uv[MaterialSrg::m_transmissionThicknessMapUvIndex];
float4 transmissionTintThickness = GeTransmissionInput(MaterialSrg::m_transmissionThicknessMap, MaterialSrg::m_sampler, transmissionUv, MaterialSrg::m_transmissionTintThickness);
surface.transmission.tint = transmissionTintThickness.rgb;
surface.transmission.thickness = transmissionTintThickness.w;
surface.transmission.transmissionParams = MaterialSrg::m_transmissionParams;
// ------- Anisotropy -------
if (o_enableAnisotropy)
{
const float anisotropyAngle = 0.0f;
const float anisotropyFactor = 0.0f;
surface.anisotropy.Init(surface.normal, tangents[0], bitangents[0], anisotropyAngle, anisotropyFactor, surface.roughnessA);
}
// ------- Lighting Data -------
LightingData lightingData;
// Light iterator
lightingData.tileIterator.Init(IN.m_position, PassSrg::m_lightListRemapped, PassSrg::m_tileLightData);
lightingData.Init(surface.position, surface.normal, surface.roughnessLinear);
// Directional light shadow coordinates
lightingData.shadowCoords = IN.m_shadowCoords;
// ------- Emissive -------
float2 emissiveUv = IN.m_uv[MaterialSrg::m_emissiveMapUvIndex];
lightingData.emissiveLighting = GetEmissiveInput(MaterialSrg::m_emissiveMap, MaterialSrg::m_sampler, emissiveUv, MaterialSrg::m_emissiveIntensity, MaterialSrg::m_emissiveColor.rgb, o_emissiveEnabled, o_emissive_useTexture);
// ------- Occlusion -------
float2 occlusionUv = IN.m_uv[MaterialSrg::m_ambientOcclusionMapUvIndex];
lightingData.occlusion = GetOcclusionInput(MaterialSrg::m_ambientOcclusionMap, MaterialSrg::m_sampler, occlusionUv, MaterialSrg::m_ambientOcclusionFactor, o_ambientOcclusion_useTexture);
// ------- Clearcoat -------
float clearCoatFactor = 0.0;
float clearCoatRoughness = 0.0;
float3 clearCoatNormal = float3(0.0, 0.0, 0.0);
// [GFX TODO][ATOM-14603]: Clean up the double uses of these clear coat flags
if(o_clearCoat_enabled && o_clearCoat_feature_enabled)
if(o_clearCoat_feature_enabled)
{
float3x3 uvMatrix = MaterialSrg::m_clearCoatNormalMapUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
GetClearCoatInputs(MaterialSrg::m_clearCoatInfluenceMap, IN.m_uv[MaterialSrg::m_clearCoatInfluenceMapUvIndex], MaterialSrg::m_clearCoatFactor, o_clearCoat_factor_useTexture,
MaterialSrg::m_clearCoatRoughnessMap, IN.m_uv[MaterialSrg::m_clearCoatRoughnessMapUvIndex], MaterialSrg::m_clearCoatRoughness, o_clearCoat_roughness_useTexture,
MaterialSrg::m_clearCoatNormalMap, IN.m_uv[MaterialSrg::m_clearCoatNormalMapUvIndex], IN.m_normal, o_clearCoat_normal_useTexture, MaterialSrg::m_clearCoatNormalStrength,
uvMatrix, tangents[MaterialSrg::m_clearCoatNormalMapUvIndex], bitangents[MaterialSrg::m_clearCoatNormalMapUvIndex],
MaterialSrg::m_sampler, isFrontFace,
clearCoatFactor, clearCoatRoughness, clearCoatNormal);
if(o_clearCoat_enabled)
{
float3x3 uvMatrix = MaterialSrg::m_clearCoatNormalMapUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
GetClearCoatInputs(MaterialSrg::m_clearCoatInfluenceMap, IN.m_uv[MaterialSrg::m_clearCoatInfluenceMapUvIndex], MaterialSrg::m_clearCoatFactor, o_clearCoat_factor_useTexture,
MaterialSrg::m_clearCoatRoughnessMap, IN.m_uv[MaterialSrg::m_clearCoatRoughnessMapUvIndex], MaterialSrg::m_clearCoatRoughness, o_clearCoat_roughness_useTexture,
MaterialSrg::m_clearCoatNormalMap, IN.m_uv[MaterialSrg::m_clearCoatNormalMapUvIndex], IN.m_normal, o_clearCoat_normal_useTexture, MaterialSrg::m_clearCoatNormalStrength,
uvMatrix, tangents[MaterialSrg::m_clearCoatNormalMapUvIndex], bitangents[MaterialSrg::m_clearCoatNormalMapUvIndex],
MaterialSrg::m_sampler, isFrontFace,
surface.clearCoat.factor, surface.clearCoat.roughness, surface.clearCoat.normal);
}
// manipulate base layer f0 if clear coat is enabled
// modify base layer's normal incidence reflectance
// for the derivation of the following equation please refer to:
// https://google.github.io/filament/Filament.md.html#materialsystem/clearcoatmodel/baselayermodification
float3 f0 = (1.0 - 5.0 * sqrt(surface.specularF0)) / (5.0 - sqrt(surface.specularF0));
surface.specularF0 = lerp(surface.specularF0, f0 * f0, surface.clearCoat.factor);
}
// Diffuse and Specular response (used in IBL calculations)
lightingData.specularResponse = FresnelSchlickWithRoughness(lightingData.NdotV, surface.specularF0, surface.roughnessLinear);
lightingData.diffuseResponse = 1.0 - lightingData.specularResponse;
if(o_clearCoat_feature_enabled)
{
// Clear coat layer has fixed IOR = 1.5 and transparent => F0 = (1.5 - 1)^2 / (1.5 + 1)^2 = 0.04
lightingData.diffuseResponse *= 1.0 - (FresnelSchlickWithRoughness(lightingData.NdotV, float3(0.04, 0.04, 0.04), surface.clearCoat.roughness) * surface.clearCoat.factor);
}
// Multiscatter compensation factor
lightingData.CalculateMultiscatterCompensation(surface.specularF0, o_specularF0_enableMultiScatterCompensation);
// ------- Lighting Calculation -------
const float2 anisotropy = 0.0; // Does not affect calculations unless 'o_enableAnisotropy' is enabled
// Apply Decals
ApplyDecals(lightingData.tileIterator, surface);
PbrLightingOutput lightingOutput = PbrLighting(IN, baseColor, metallic, roughness, specularF0Factor,
normalWS, tangents[0], bitangents[0], anisotropy,
emissive, occlusion, transmissionTintThickness, MaterialSrg::m_transmissionParams, clearCoatFactor, clearCoatRoughness, clearCoatNormal, alpha, o_opacity_mode);
// Apply Direct Lighting
ApplyDirectLighting(surface, lightingData);
// Apply Image Based Lighting (IBL)
ApplyIBL(surface, lightingData);
// Finalize Lighting
lightingData.FinalizeLighting(surface.transmission.tint);
if (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent)
{
alpha = FresnelSchlickWithRoughness(lightingData.NdotV, alpha, surface.roughnessLinear).x; // Increase opacity at grazing angles.
}
PbrLightingOutput lightingOutput = GetPbrLightingOutput(surface, lightingData, alpha);
// ------- Opacity -------
@@ -30,6 +30,7 @@
}
},
"CompilerHints" : {
"DisableOptimizations" : false
},
@@ -1,5 +1,6 @@
#pragma once
#include <Atom/Features/PBR/LightingOptions.azsli>
#include <Atom/Features/PBR/Surface.azsli>
// Analytical integation (approximation) of diffusion profile over radius, could be replaced by other pre integrated kernels
@@ -10,29 +11,36 @@ float3 TransmissionKernel(float t, float3 s)
return 0.25 * (1.0 / exp(exponent) + 3.0 / exp(exponent / 3.0));
}
float3 GetBackLighting(Surface surface, float3 lightIntensity, float3 dirToCamera, float3 dirToLight, float shadowRatio)
float3 GetBackLighting(Surface surface, LightingData lightingData, float3 lightIntensity, float3 dirToLight, float shadowRatio)
{
float3 result = float3(0.0, 0.0, 0.0);
float thickness = 0.0;
float4 transmissionParams = surface.transmission.transmissionParams;
switch(o_transmission_mode)
{
case TransmissionMode::None:
break;
// Thick object mode, using back lighting approximation proposed by Brisebois B. C. and Bouchard M. 2011
// https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/
case TransmissionMode::ThickObject:
thickness = max(shadowRatio, surface.thickness);
// (transmittance) * (Lambert's attenuation) * light intensity
result = (pow(saturate(dot(dirToCamera, -normalize(dirToLight + surface.normal * surface.transmissionParams.z))), surface.transmissionParams.y) * surface.transmissionParams.w) *
(exp(-thickness * surface.transmissionParams.x) * saturate(1.0 - thickness)) *
lightIntensity;
// Thick object mode, using back lighting approximation proposed by Brisebois B. C. and Bouchard M. 2011
// https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/
{
thickness = max(shadowRatio, surface.transmission.thickness);
float transmittance = pow( saturate( dot( lightingData.dirToCamera, -normalize( dirToLight + surface.normal * transmissionParams.z ) ) ), transmissionParams.y ) * transmissionParams.w;
float lamberAttenuation = exp(-thickness * transmissionParams.x) * saturate(1.0 - thickness);
result = transmittance * lamberAttenuation * lightIntensity;
}
break;
// Thin object mode, using thin-film assumption proposed by Jimenez J. et al, 2010, "Real-Time Realistic Skin Translucency"
// http://www.iryoku.com/translucency/downloads/Real-Time-Realistic-Skin-Translucency.pdf
case TransmissionMode::ThinObject:
result = shadowRatio ? float3(0.0, 0.0, 0.0) : TransmissionKernel(surface.thickness * surface.transmissionParams.w, rcp(surface.transmissionParams.xyz)) *
// Thin object mode, using thin-film assumption proposed by Jimenez J. et al, 2010, "Real-Time Realistic Skin Translucency"
// http://www.iryoku.com/translucency/downloads/Real-Time-Realistic-Skin-Translucency.pdf
result = shadowRatio ? float3(0.0, 0.0, 0.0) : TransmissionKernel(surface.transmission.thickness * transmissionParams.w, rcp(transmissionParams.xyz)) *
saturate(dot(-surface.normal, dirToLight)) * lightIntensity * shadowRatio;
break;
}
return result;
@@ -0,0 +1,14 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
@@ -0,0 +1,89 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <viewsrg.srgi>
#include <Atom/Features/LightCulling/LightCullingTileIterator.azsli>
#include <Atom/Features/PBR/LightingUtils.azsli>
class LightingData
{
LightCullingTileIterator tileIterator;
// Lighting outputs
float3 diffuseLighting;
float3 specularLighting;
float3 translucentBackLighting;
// Factors for the amount of diffuse and specular lighting applied
float3 diffuseResponse;
float3 specularResponse;
// Direction light shadow coordinates
float3 shadowCoords[ViewSrg::MaxCascadeCount];
// Normalized direction from surface to camera
float3 dirToCamera;
// Scaling term to approximate multiscattering contribution in specular BRDF
float3 multiScatterCompensation;
// Lighting emitted from the surface
float3 emissiveLighting;
// BRDF texture values
float2 brdf;
// Normal . View
float NdotV;
// 0 = dark, 1 = light
float occlusion;
void Init(float3 positionWS, float3 normal, float roughnessLinear);
void CalculateMultiscatterCompensation(float3 specularF0, bool enabled);
void FinalizeLighting(float3 transmissionTint);
};
void LightingData::Init(float3 positionWS, float3 normal, float roughnessLinear)
{
diffuseLighting = 0;
specularLighting = 0;
translucentBackLighting = 0;
multiScatterCompensation = 1.0f;
emissiveLighting = float3(0.0f, 0.0f, 0.0f);
occlusion = 1.0f;
dirToCamera = normalize(ViewSrg::m_worldPosition.xyz - positionWS);
// sample BRDF map (indexed by smoothness values rather than roughness)
NdotV = saturate(dot(normal, dirToCamera));
float2 brdfUV = float2(NdotV, (1.0f - roughnessLinear));
brdf = PassSrg::m_brdfMap.Sample(PassSrg::LinearSampler, brdfUV).rg;
}
void LightingData::CalculateMultiscatterCompensation(float3 specularF0, bool enabled)
{
multiScatterCompensation = GetMultiScatterCompensation(specularF0, brdf, enabled);
}
void LightingData::FinalizeLighting(float3 transmissionTint)
{
specularLighting += emissiveLighting;
// Transmitted light
if(o_transmission_mode != TransmissionMode::None)
{
diffuseLighting += translucentBackLighting * transmissionTint;
}
}
@@ -0,0 +1,63 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
// Include options first
#include <Atom/Features/PBR/LightingOptions.azsli>
// Then include custom surface and lighting data types
#include <Atom/Features/PBR/Lighting/LightingData.azsli>
#include <Atom/Features/PBR/Surfaces/StandardSurface.azsli>
// Then include everything else
#include <Atom/Features/PBR/Lights/Lights.azsli>
#include <Atom/Features/PBR/Lights/Ibl.azsli>
struct PbrLightingOutput
{
float4 m_diffuseColor;
float4 m_specularColor;
float4 m_albedo;
float4 m_specularF0;
float4 m_normal;
float4 m_clearCoatNormal;
float3 m_scatterDistance;
};
PbrLightingOutput GetPbrLightingOutput(Surface surface, LightingData lightingData, float alpha)
{
PbrLightingOutput lightingOutput;
lightingOutput.m_diffuseColor = float4(lightingData.diffuseLighting, alpha);
lightingOutput.m_specularColor = float4(lightingData.specularLighting, 1.0);
// albedo, specularF0, roughness, and normals for later passes (specular IBL, Diffuse GI, SSR, AO, etc)
lightingOutput.m_specularF0 = float4(surface.specularF0, surface.roughnessLinear);
lightingOutput.m_albedo.rgb = surface.albedo * lightingData.diffuseResponse;
lightingOutput.m_albedo.a = lightingData.occlusion;
lightingOutput.m_normal.rgb = EncodeNormalSignedOctahedron(surface.normal);
lightingOutput.m_normal.a = o_specularF0_enableMultiScatterCompensation ? 1.0f : 0.0f;
// layout: (packedNormal.x, packedNormal.y, strength factor, clear coat roughness (not base material's roughness))
lightingOutput.m_clearCoatNormal = float4(EncodeNormalSphereMap(surface.clearCoat.normal), o_clearCoat_feature_enabled ? surface.clearCoat.factor : 0.0, surface.clearCoat.roughness);
return lightingOutput;
}
@@ -12,17 +12,7 @@
#pragma once
option bool o_specularF0_enableMultiScatterCompensation;
option bool o_enableShadows = true;
option bool o_enableDirectionalLights = true;
option bool o_enablePunctualLights = true;
option bool o_enableAreaLights = true;
option bool o_enableIBL = true;
option bool o_enableSubsurfaceScattering;
option bool o_clearCoat_feature_enabled;
option enum class TransmissionMode {None, ThickObject, ThinObject} o_transmission_mode;
option bool o_meshUseForwardPassIBLSpecular = false;
option bool o_materialUseForwardPassIBLSpecular = false;
#include <Atom/Features/PBR/LightingOptions.azsli>
#include <viewsrg.srgi>
#include <scenesrg.srgi>
@@ -35,26 +25,12 @@ option bool o_materialUseForwardPassIBLSpecular = false;
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <Atom/Features/PBR/ForwardPassSrg.azsli>
#include <Atom/Features/PBR/Surface.azsli>
#include <Atom/Features/PBR/Lighting/StandardLighting.azsli>
#include <Atom/Features/PBR/Decals.azsli>
#include <Atom/Features/PBR/Lights/DirectionalLight.azsli>
#include <Atom/Features/PBR/Lights/PointLight.azsli>
#include <Atom/Features/PBR/Lights/SpotLight.azsli>
#include <Atom/Features/PBR/Lights/DiskLight.azsli>
#include <Atom/Features/PBR/Lights/CapsuleLight.azsli>
#include <Atom/Features/PBR/Lights/PolygonLight.azsli>
#include <Atom/Features/PBR/Lights/QuadLight.azsli>
#include <Atom/Features/PBR/Lights/Ibl.azsli>
/**
* The StandardPBR Material Template provides a foundation for creating PBR base materials.
* A default StandardPBR base material is provided as well, built on top of this template, and
* should suit many needs. Additional base materials can created as needed, using StandardPBR.material
* as a reference.
*/
// VSInput, VSOutput, ObjectSrg must be defined before including this file.
// DEPRECATED: Please use the VertexHelper(...) function in VertexHelper.azsli instead.
//! @param skipShadowCoords can be useful for example when PixelDepthOffset is enable, because the pixel shader will have to run before the final world position is known
void PbrVsHelper(in VSInput IN, inout VSOutput OUT, float3 worldPosition, bool skipShadowCoords = false)
{
@@ -77,18 +53,10 @@ void PbrVsHelper(in VSInput IN, inout VSOutput OUT, float3 worldPosition, bool s
}
}
struct PbrLightingOutput
{
float4 m_diffuseColor;
float4 m_specularColor;
float4 m_albedo;
float4 m_specularF0;
float4 m_normal;
float4 m_clearCoatNormal;
float3 m_scatterDistance;
};
PbrLightingOutput PbrLighting( in VSOutput IN,
// DEPRECATED: Please use the functions in StandardLighting.azsli instead.
// For an example on how to use those functions, see StandardPBR_forwardPass.azsl
PbrLightingOutput PbrLighting( VSOutput IN,
float3 baseColor,
float metallic,
float roughness,
@@ -107,24 +75,43 @@ PbrLightingOutput PbrLighting( in VSOutput IN,
float alpha,
OpacityMode opacityMode)
{
static const float3 MaxDielectricSpecularF0 = 0.08f;
float3 dirToCamera = normalize(ViewSrg::m_worldPosition.xyz - IN.m_worldPosition);
float3 worldPosition = IN.m_worldPosition;
float4 position = IN.m_position;
float3 shadowCoords[ViewSrg::MaxCascadeCount] = IN.m_shadowCoords;
// sample BRDF map (indexed by smoothness values rather than roughness)
float NdotV = dot(normal, dirToCamera);
float2 brdfUV = float2(saturate(NdotV), (1.0f - roughness));
float2 brdf = PassSrg::m_brdfMap.Sample(PassSrg::LinearSampler, brdfUV).rg;
// ______________________________________________________________________________________________
// Surface
// compute albedo and specularF0 based on metalness
float3 albedo = (o_enableSubsurfaceScattering) ? baseColor : lerp(baseColor, float3(0.0f, 0.0f, 0.0f), metallic);
Surface surface;
surface.position = worldPosition;
surface.normal = normal;
surface.roughnessLinear = roughness;
surface.transmission.tint = transmissionTintThickness.rgb;
surface.transmission.thickness = transmissionTintThickness.w;
surface.transmission.transmissionParams = transmissionParams;
surface.clearCoat.factor = clearCoatFactor;
surface.clearCoat.roughness = clearCoatRoughness;
surface.clearCoat.normal = clearCoatNormal;
surface.CalculateRoughnessA();
surface.SetAlbedoAndSpecularF0(baseColor, specularF0Factor, metallic);
surface.anisotropy.Init(normal, vtxTangent, vtxBitangent, anisotropy.x, anisotropy.y, surface.roughnessA);
// ______________________________________________________________________________________________
// LightingData
LightingData lightingData;
// Light iterator
lightingData.tileIterator.Init(position, PassSrg::m_lightListRemapped, PassSrg::m_tileLightData);
lightingData.Init(surface.position, surface.normal, surface.roughnessLinear);
// since the left hand side value of this interpolation is achromatic color, this value doesn't convert a color space.
float3 dielectricSpecularF0 = MaxDielectricSpecularF0 * specularF0Factor;
lightingData.emissiveLighting = emissive;
lightingData.occlusion = occlusion;
// because most of metal material theoratically conflicts with subsurface scattering
// (electrons hit to a conductor will be either 'absorbed' or reflected hence no chance to transmit),
// metallic is disabled if subsurface scattering turned on
float3 specularF0 = (o_enableSubsurfaceScattering) ? dielectricSpecularF0 : lerp(dielectricSpecularF0, baseColor, metallic);
// Directional light shadow coordinates
lightingData.shadowCoords = shadowCoords;
// manipulate base layer f0 if clear coat is enabled
if(o_clearCoat_feature_enabled)
@@ -132,140 +119,44 @@ PbrLightingOutput PbrLighting( in VSOutput IN,
// modify base layer's normal incidence reflectance
// for the derivation of the following equation please refer to:
// https://google.github.io/filament/Filament.md.html#materialsystem/clearcoatmodel/baselayermodification
float3 f0 = (1.0 - 5.0 * sqrt(specularF0)) / (5.0 - sqrt(specularF0));
specularF0 = lerp(specularF0, f0 * f0, clearCoatFactor);
float3 f0 = (1.0 - 5.0 * sqrt(surface.specularF0)) / (5.0 - sqrt(surface.specularF0));
surface.specularF0 = lerp(surface.specularF0, f0 * f0, clearCoatFactor);
}
// compute specular and diffuse response
float3 specularResponse = FresnelSchlickWithRoughness(NdotV, specularF0, roughness);
float3 diffuseResponse = 1.0 - specularResponse;
if (o_clearCoat_feature_enabled)
// Diffuse and Specular response (used in IBL calculations)
lightingData.specularResponse = FresnelSchlickWithRoughness(lightingData.NdotV, surface.specularF0, surface.roughnessLinear);
lightingData.diffuseResponse = 1.0 - lightingData.specularResponse;
if(o_clearCoat_feature_enabled)
{
// Clear coat layer has fixed IOR = 1.5 and transparent => F0 = (1.5 - 1)^2 / (1.5 + 1)^2 = 0.04
diffuseResponse *= 1.0 - (FresnelSchlickWithRoughness(NdotV, float3(0.04, 0.04, 0.04), clearCoatRoughness) * clearCoatFactor);
lightingData.diffuseResponse *= 1.0 - (FresnelSchlickWithRoughness(lightingData.NdotV, float3(0.04, 0.04, 0.04), surface.clearCoat.roughness) * surface.clearCoat.factor);
}
// Set up the surface parameters.
Surface surface = (Surface)0.0;
surface.position = IN.m_worldPosition.xyz;
surface.normal = normal;
surface.albedo = albedo;
surface.specularF0 = specularF0;
surface.multiScatterCompensation = GetMultiScatterCompensation(NdotV, surface.specularF0, brdf, o_specularF0_enableMultiScatterCompensation);
// The roughness value in microfacet calculations (called "alpha" in the literature) does not give perceptually
// linear results. Disney found that squaring the roughness value before using it in microfacet equations causes
// the user-provided roughness parameter to be more perceptually linear. We keep both values available as some
// equations need roughnessLinear (i.e. IBL sampling) while others need roughnessA (i.e. GGX equations).
// See Burley's Disney PBR: https://pdfs.semanticscholar.org/eeee/3b125c09044d3e2f58ed0e4b1b66a677886d.pdf
surface.roughnessLinear = roughness;
surface.thickness = transmissionTintThickness.w;
// Thick object mode: (attenuation coefficient, power, distortion, scale)
// Thin object mode: (float3 scatter distance, scale)
surface.transmissionParams = transmissionParams;
// Multiscatter compensation factor
lightingData.CalculateMultiscatterCompensation(surface.specularF0, o_specularF0_enableMultiScatterCompensation);
// Parameters: (clear coat factor, perceptual roughness)
surface.clearCoatFactor = clearCoatFactor;
surface.clearCoatRoughness = clearCoatRoughness;
surface.clearCoatNormal = clearCoatNormal;
// ______________________________________________________________________________________________
// Lighting
// Make sure roughnessA is above 0 to avoid precision and divide by zero issues. 0.0005f is sufficient for directional lights since they tend to be quite bright.
surface.roughnessA = max(roughness * roughness, 0.0005f);
// Apply Decals
ApplyDecals(lightingData.tileIterator, surface);
if (o_enableAnisotropy)
// Apply Direct Lighting
ApplyDirectLighting(surface, lightingData);
// Apply Image Based Lighting (IBL)
ApplyIBL(surface, lightingData);
// Finalize Lighting
lightingData.FinalizeLighting(surface.transmission.tint);
if (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent)
{
CalculateSurfaceDirectionalAnisotropicData(surface, anisotropy, vtxTangent, vtxBitangent);
alpha = FresnelSchlickWithRoughness(lightingData.NdotV, alpha, surface.roughnessLinear).x; // Increase opacity at grazing angles.
}
float3 diffuseLighting = 0.0f; // accumulation of diffuse lighting
float3 specularLighting = 0.0f; // accumulation of specular lighting
float3 translucentBackLighting = 0.0f; // accumulation of transmitted light on the back face of object for back lighting
LightCullingTileIterator tileIterator;
tileIterator.Init(IN.m_position, PassSrg::m_lightListRemapped, PassSrg::m_tileLightData);
ApplyDecals(tileIterator, surface);
if (o_enablePunctualLights)
{
ApplyPointLights(tileIterator, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
ApplySpotLights(tileIterator, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
}
if (o_enableAreaLights)
{
ApplyDiskLights(tileIterator, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
ApplyCapsuleLights(tileIterator, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
ApplyQuadLights(tileIterator, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
ApplyPolygonLights(dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
}
if (opacityMode == OpacityMode::Blended || opacityMode == OpacityMode::TintedTransparent)
{
// transparencies currently require IBL in the forward pass
float3 iblDiffuse = 0.0f;
float3 iblSpecular = 0.0f;
if (o_enableIBL)
{
ApplyIblDiffuse(surface, diffuseResponse, iblDiffuse);
ApplyIblSpecular(surface, specularResponse, dirToCamera, brdf, iblSpecular);
}
// Apply ambient occlusion to indirect diffuse
iblDiffuse *= occlusion;
// Adjust IBL lighting by exposure.
float iblExposureFactor = pow(2.0, SceneSrg::m_iblExposure);
iblDiffuse *= iblExposureFactor;
iblSpecular *= iblExposureFactor;
diffuseLighting += iblDiffuse;
specularLighting += iblSpecular;
alpha = FresnelSchlickWithRoughness(NdotV, alpha, roughness).x; // Increase opacity at grazing angles.
}
else if (o_meshUseForwardPassIBLSpecular || o_materialUseForwardPassIBLSpecular)
{
if (o_enableIBL)
{
float3 iblSpecular = 0.0f;
ApplyIblSpecular(surface, specularResponse, dirToCamera, brdf, iblSpecular);
float iblExposureFactor = pow(2.0f, SceneSrg::m_iblExposure);
specularLighting += (iblSpecular * iblExposureFactor);
}
}
// Emissive contribution
// Emissive light is apply to specular now, as diffuse will be used for subsurface scattering later down the pipeline
// We may change this if specular is also used for other processing
specularLighting += emissive;
if (o_enableDirectionalLights)
{
ApplyDirectionalLights(dirToCamera, surface, IN.m_shadowCoords, diffuseLighting, specularLighting, translucentBackLighting);
}
// Transmitted light
if(o_transmission_mode != TransmissionMode::None)
{
diffuseLighting += translucentBackLighting * transmissionTintThickness.xyz;
}
PbrLightingOutput lightingOutput;
lightingOutput.m_diffuseColor = float4(diffuseLighting, alpha);
lightingOutput.m_specularColor = float4(specularLighting, 1.0);
// albedo, specularF0, roughness, and normals for later passes (specular IBL, Diffuse GI, SSR, AO, etc)
lightingOutput.m_specularF0 = float4(specularF0, roughness);
lightingOutput.m_albedo.rgb = surface.albedo * diffuseResponse;
lightingOutput.m_albedo.a = occlusion;
lightingOutput.m_normal.rgb = EncodeNormalSignedOctahedron(normal);
lightingOutput.m_normal.a = o_specularF0_enableMultiScatterCompensation ? 1.0f : 0.0f;
// layout: (packedNormal.x, packedNormal.y, strength factor, clear coat roughness (not base material's roughness))
lightingOutput.m_clearCoatNormal = float4(EncodeNormalSphereMap(clearCoatNormal), o_clearCoat_feature_enabled ? clearCoatFactor : 0.0, clearCoatRoughness);
PbrLightingOutput lightingOutput = GetPbrLightingOutput(surface, lightingData, alpha);
return lightingOutput;
}
@@ -278,6 +169,7 @@ PbrLightingOutput PbrLighting( in VSOutput IN,
//! @param debugColor the color to be drawn
//! @param normalWS world space normal vector
//! @return a PbrLightingOutput as returned by the main PbrLighting() function
PbrLightingOutput MakeDebugOutput(VSOutput IN, float3 debugColor, float3 normalWS)
{
// We happen to set this up initially using baseColor, but we could consider adding an option to use
@@ -0,0 +1,26 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
option bool o_specularF0_enableMultiScatterCompensation;
option bool o_enableShadows = true;
option bool o_enableDirectionalLights = true;
option bool o_enablePunctualLights = true;
option bool o_enableAreaLights = true;
option bool o_enableIBL = true;
option bool o_enableSubsurfaceScattering;
option bool o_clearCoat_feature_enabled;
option enum class TransmissionMode {None, ThickObject, ThinObject} o_transmission_mode;
option bool o_meshUseForwardPassIBLSpecular = false;
option bool o_materialUseForwardPassIBLSpecular = false;
@@ -19,7 +19,7 @@ float3 GetCubemapCoords(float3 original)
}
//! Compute multiscatter compensation multiplier
float3 GetMultiScatterCompensation(float NdotV, float3 specularF0, float2 brdf, bool enabled)
float3 GetMultiScatterCompensation(float3 specularF0, float2 brdf, bool enabled)
{
if (!enabled)
{
@@ -66,4 +66,4 @@ float3 ApplyParallaxCorrection(float3 aabbMin, float3 aabbMax, float3 aabbPos, f
float distance = min(min(furthestIntersect.x, furthestIntersect.y), furthestIntersect.z);
float3 intersectPos = reflectDir * distance + positionWS;
return (intersectPos - aabbPos);
}
}
@@ -57,7 +57,7 @@ void SampleCapsule(float2 randomPoint, ViewSrg::CapsuleLight light, float capToC
}
}
void ApplyCapsuleLight(ViewSrg::CapsuleLight light, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ApplyCapsuleLight(ViewSrg::CapsuleLight light, Surface surface, inout LightingData lightingData)
{
float lightLength = light.m_length;
float3 startPoint = light.m_startPoint;
@@ -125,10 +125,10 @@ void ApplyCapsuleLight(ViewSrg::CapsuleLight light, float3 dirToCamera, Surface
intensity *= INV_PI; // normalize for lambert reflectance.
float3 lightIntensity = (intensity * radiusAttenuation * ratioVisible) * light.m_rgbIntensityCandelas;
diffuseLighting += max(0.0, surface.albedo * lightIntensity);
lightingData.diffuseLighting += max(0.0, surface.albedo * lightIntensity);
// Calculate the reflection of the normal from the view direction
float3 reflectionDir = reflect(-dirToCamera, surface.normal);
float3 reflectionDir = reflect(-lightingData.dirToCamera, surface.normal);
// Find closest point on light to reflection vector.
// See https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
@@ -140,7 +140,7 @@ void ApplyCapsuleLight(ViewSrg::CapsuleLight light, float3 dirToCamera, Surface
float3 posToLight = closestIntersectionPoint - surface.position;
// Tranmission contribution
translucentBackLighting += GetBackLighting(surface, lightIntensity, dirToCamera, normalize(posToLight), 0.0);
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(posToLight), 0.0);
// Calculate the offset from the nearest point on the reflection vector to the nearest point on the capsule light
float3 posToClosestPointAlongReflection = dot(posToLight, reflectionDir) * reflectionDir;
@@ -169,11 +169,11 @@ void ApplyCapsuleLight(ViewSrg::CapsuleLight light, float3 dirToCamera, Surface
// Specular contribution
lightIntensity = sphereToCapsuleAreaRatio * radiusAttenuation / d2;
lightIntensity *= light.m_rgbIntensityCandelas;
specularLighting += sphereIntensityNormalization * GetSpecularLighting(surface, lightIntensity, dirToCamera, normalize(posToLight));
lightingData.specularLighting += sphereIntensityNormalization * GetSpecularLighting(surface, lightingData, lightIntensity, normalize(posToLight));
}
}
void ValidateCapsuleLight(ViewSrg::CapsuleLight light, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ValidateCapsuleLight(ViewSrg::CapsuleLight light, Surface surface, inout LightingData lightingData)
{
const uint sampleCount = 1024;
@@ -205,7 +205,7 @@ void ValidateCapsuleLight(ViewSrg::CapsuleLight light, float3 dirToCamera, Surfa
float3 direction;
float2 randomPoint = GetHammersleyPoint(i, sampleCount);
SampleCapsule(randomPoint, light, capToCylinderAreaRatio, localToWorld, position, direction);
AddSampleContribution(surface, position, direction, dirToCamera, 0.0, diffuseAcc, specularAcc, translucentAcc);
AddSampleContribution(surface, lightingData, position, direction, 0.0, diffuseAcc, specularAcc, translucentAcc);
}
// Lighting value is in Candela, convert to Lumen for total light output of the light
@@ -214,28 +214,28 @@ void ValidateCapsuleLight(ViewSrg::CapsuleLight light, float3 dirToCamera, Surfa
// equal directions across the hemisphere, so we need to account for that
float3 intensity = intensityLumens * INV_PI;
diffuseLighting += (diffuseAcc / float(sampleCount)) * intensity;
translucentBackLighting += (translucentAcc / float(sampleCount)) * intensity;
specularLighting += (specularAcc / float(sampleCount)) * intensity;
lightingData.diffuseLighting += (diffuseAcc / float(sampleCount)) * intensity;
lightingData.translucentBackLighting += (translucentAcc / float(sampleCount)) * intensity;
lightingData.specularLighting += (specularAcc / float(sampleCount)) * intensity;
}
void ApplyCapsuleLights(inout LightCullingTileIterator tileIterator, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ApplyCapsuleLights(Surface surface, inout LightingData lightingData)
{
tileIterator.LoadAdvance();
lightingData.tileIterator.LoadAdvance();
while(!tileIterator.IsDone())
{
uint currLightIndex = tileIterator.GetValue();
tileIterator.LoadAdvance();
while(!lightingData.tileIterator.IsDone())
{
uint currLightIndex = lightingData.tileIterator.GetValue();
lightingData.tileIterator.LoadAdvance();
ViewSrg::CapsuleLight light = ViewSrg::m_capsuleLights[currLightIndex];
if (o_area_light_validation)
{
ValidateCapsuleLight(light, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
ValidateCapsuleLight(light, surface, lightingData);
}
else
{
ApplyCapsuleLight(light, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
ApplyCapsuleLight(light, surface, lightingData);
}
}
}
@@ -15,13 +15,7 @@
#include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
#include <Atom/Features/Shadow/DirectionalLightShadow.azsli>
void ApplyDirectionalLights(
float3 dirToCamera,
Surface surface,
float3 shadowCoords[ViewSrg::MaxCascadeCount],
inout float3 diffuseLighting,
inout float3 specularLighting,
inout float3 translucentBackLighting)
void ApplyDirectionalLights(Surface surface, inout LightingData lightingData)
{
DirectionalLightShadow::DebugInfo debugInfo = {0, false};
@@ -33,13 +27,13 @@ void ApplyDirectionalLights(
{
litRatio = DirectionalLightShadow::GetVisibility(
shadowIndex,
shadowCoords,
lightingData.shadowCoords,
surface.normal,
debugInfo);
if (o_transmission_mode == TransmissionMode::ThickObject)
{
backShadowRatio = DirectionalLightShadow::GetThickness(shadowIndex, shadowCoords);
backShadowRatio = DirectionalLightShadow::GetThickness(shadowIndex, lightingData.shadowCoords);
}
}
@@ -50,7 +44,7 @@ void ApplyDirectionalLights(
float3 dirToLight = normalize(-light.m_direction);
// Adjust the direction of the light based on its angular diameter.
float3 reflectionDir = reflect(-dirToCamera, surface.normal);
float3 reflectionDir = reflect(-lightingData.dirToCamera, surface.normal);
float3 lightDirToReflectionDir = reflectionDir - dirToLight;
float lightDirToReflectionDirLen = length(lightDirToReflectionDir);
lightDirToReflectionDir = lightDirToReflectionDir / lightDirToReflectionDirLen; // normalize the length
@@ -72,16 +66,16 @@ void ApplyDirectionalLights(
}
}
diffuseLighting += GetDiffuseLighting(surface, light.m_rgbIntensityLux, dirToCamera, dirToLight) * currentLitRatio;
specularLighting += GetSpecularLighting(surface, light.m_rgbIntensityLux, dirToCamera, dirToLight) * currentLitRatio;
translucentBackLighting += GetBackLighting(surface, light.m_rgbIntensityLux, dirToCamera, dirToLight, currentBackShadowRatio);
lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, light.m_rgbIntensityLux, dirToLight) * currentLitRatio;
lightingData.specularLighting += GetSpecularLighting(surface, lightingData, light.m_rgbIntensityLux, dirToLight) * currentLitRatio;
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, light.m_rgbIntensityLux, dirToLight, currentBackShadowRatio);
}
// Add debug coloring for directional light shadow
if (o_enableShadows && shadowIndex < SceneSrg::m_directionalLightCount)
{
specularLighting = DirectionalLightShadow::AddDebugColoring(
specularLighting,
lightingData.specularLighting = DirectionalLightShadow::AddDebugColoring(
lightingData.specularLighting,
ViewSrg::m_shadowIndexDirectionalLight,
debugInfo);
}
@@ -14,7 +14,7 @@
#include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
void ApplyDiskLight(ViewSrg::DiskLight light, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ApplyDiskLight(ViewSrg::DiskLight light, Surface surface, inout LightingData lightingData)
{
float3 posToLight = light.m_position - surface.position;
float distanceToLight2 = dot(posToLight, posToLight); // light distance squared
@@ -64,15 +64,15 @@ void ApplyDiskLight(ViewSrg::DiskLight light, float3 dirToCamera, Surface surfac
lightIntensity /= ((light.m_diskRadius / distanceToPlane) + 1.0);
// Diffuse contribution
diffuseLighting += GetDiffuseLighting(surface, lightIntensity, dirToCamera, posToLightDir);
lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, posToLightDir);
// Tranmission contribution
translucentBackLighting += GetBackLighting(surface, lightIntensity, dirToCamera, posToLightDir, 0.0);
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, posToLightDir, 0.0);
// Adjust the light direction for specular based on disk size
// Calculate the reflection off the normal from the view lightDirection
float3 reflectionDir = reflect(-dirToCamera, surface.normal);
float3 reflectionDir = reflect(-lightingData.dirToCamera, surface.normal);
float reflectionDotLight = dot(reflectionDir, -lightDirection);
// Let 'Intersection' denote the point where the reflection ray intersects the diskLight plane
@@ -104,7 +104,7 @@ void ApplyDiskLight(ViewSrg::DiskLight light, float3 dirToCamera, Surface surfac
float diskIntensityNormalization = GetIntensityAdjustedByRadiusAndRoughness(surface.roughnessA, light.m_diskRadius, distanceToLight2);
// Specular contribution
specularLighting += diskIntensityNormalization * GetSpecularLighting(surface, lightIntensity, dirToCamera, normalize(posToLight));
lightingData.specularLighting += diskIntensityNormalization * GetSpecularLighting(surface, lightingData, lightIntensity, normalize(posToLight));
}
}
@@ -135,7 +135,7 @@ float3 SampleDisk(float2 randomPoint, ViewSrg::DiskLight light)
return outPoint;
}
void ValidateDiskLight(ViewSrg::DiskLight light, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ValidateDiskLight(ViewSrg::DiskLight light, Surface surface, inout LightingData lightingData)
{
const uint sampleCount = 512;
@@ -147,31 +147,31 @@ void ValidateDiskLight(ViewSrg::DiskLight light, float3 dirToCamera, Surface sur
{
float2 randomPoint = GetHammersleyPoint(i, sampleCount);
float3 samplePoint = SampleDisk(randomPoint, light);
AddSampleContribution(surface, samplePoint, light.m_direction, dirToCamera, light.m_bothDirectionsFactor, diffuseAcc, specularAcc, translucentAcc);
AddSampleContribution(surface, lightingData, samplePoint, light.m_direction, light.m_bothDirectionsFactor, diffuseAcc, specularAcc, translucentAcc);
}
diffuseLighting += (diffuseAcc / float(sampleCount)) * light.m_rgbIntensityCandelas;
specularLighting += (specularAcc / float(sampleCount)) * light.m_rgbIntensityCandelas;
lightingData.diffuseLighting += (diffuseAcc / float(sampleCount)) * light.m_rgbIntensityCandelas;
lightingData.specularLighting += (specularAcc / float(sampleCount)) * light.m_rgbIntensityCandelas;
}
void ApplyDiskLights(inout LightCullingTileIterator tileIterator, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ApplyDiskLights(Surface surface, inout LightingData lightingData)
{
tileIterator.LoadAdvance();
lightingData.tileIterator.LoadAdvance();
while( !tileIterator.IsDone() )
{
uint currLightIndex = tileIterator.GetValue();
tileIterator.LoadAdvance();
while( !lightingData.tileIterator.IsDone() )
{
uint currLightIndex = lightingData.tileIterator.GetValue();
lightingData.tileIterator.LoadAdvance();
ViewSrg::DiskLight light = ViewSrg::m_diskLights[currLightIndex];
if (o_area_light_validation)
{
ValidateDiskLight(light, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
ValidateDiskLight(light, surface, lightingData);
}
else
{
ApplyDiskLight(light, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
ApplyDiskLight(light, surface, lightingData);
}
}
}
@@ -12,25 +12,27 @@
#pragma once
#include <Atom/Features/PBR/LightingOptions.azsli>
#include <Atom/RPI/Math.azsli>
#include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
#include <Atom/Features/PBR/LightingUtils.azsli>
void ApplyIblDiffuse(Surface surface, float3 diffuseResponse, out float3 outDiffuse)
void ApplyIblDiffuse(float3 normal, float3 albedo, float3 diffuseResponse, out float3 outDiffuse)
{
float3 irradianceDir = MultiplyVectorQuaternion(surface.normal, SceneSrg::m_iblOrientation);
float3 irradianceDir = MultiplyVectorQuaternion(normal, SceneSrg::m_iblOrientation);
float3 diffuseSample = SceneSrg::m_diffuseEnvMap.Sample(SceneSrg::m_samplerEnv, GetCubemapCoords(irradianceDir)).rgb;
outDiffuse = diffuseResponse * surface.albedo * diffuseSample;
outDiffuse = diffuseResponse * albedo * diffuseSample;
}
void ApplyIblSpecular(Surface surface, float3 specularResponse, float3 dirToCamera, float2 brdf, out float3 outSpecular)
void ApplyIblSpecular(float3 position, float3 normal, float3 specularF0, float roughnessLinear, float3 specularResponse, float3 dirToCamera, float2 brdf, out float3 outSpecular)
{
float3 reflectDir = reflect(-dirToCamera, surface.normal);
float3 reflectDir = reflect(-dirToCamera, normal);
// global
outSpecular = SceneSrg::m_specularEnvMap.SampleLevel(SceneSrg::m_samplerEnv, GetCubemapCoords(reflectDir), GetRoughnessMip(surface.roughnessLinear)).rgb;
outSpecular *= (surface.specularF0 * brdf.x + brdf.y);
outSpecular = SceneSrg::m_specularEnvMap.SampleLevel(SceneSrg::m_samplerEnv, GetCubemapCoords(reflectDir), GetRoughnessMip(roughnessLinear)).rgb;
outSpecular *= (specularF0 * brdf.x + brdf.y);
// reflection probe
if (ObjectSrg::m_reflectionProbeData.m_useReflectionProbe)
@@ -38,24 +40,56 @@ void ApplyIblSpecular(Surface surface, float3 specularResponse, float3 dirToCame
if (ObjectSrg::m_reflectionProbeData.m_useParallaxCorrection)
{
reflectDir = ApplyParallaxCorrection(
ObjectSrg::m_reflectionProbeData.m_outerAabbMin,
ObjectSrg::m_reflectionProbeData.m_outerAabbMax,
ObjectSrg::m_reflectionProbeData.m_aabbPos,
surface.position,
ObjectSrg::m_reflectionProbeData.m_outerAabbMin,
ObjectSrg::m_reflectionProbeData.m_outerAabbMax,
ObjectSrg::m_reflectionProbeData.m_aabbPos,
position,
reflectDir);
}
float3 probeSpecular = ObjectSrg::m_reflectionProbeCubeMap.SampleLevel(SceneSrg::m_samplerEnv, GetCubemapCoords(reflectDir), GetRoughnessMip(surface.roughnessLinear)).rgb;
probeSpecular *= (surface.specularF0 * brdf.x + brdf.y);
float3 probeSpecular = ObjectSrg::m_reflectionProbeCubeMap.SampleLevel(SceneSrg::m_samplerEnv, GetCubemapCoords(reflectDir), GetRoughnessMip(roughnessLinear)).rgb;
probeSpecular *= (specularF0 * brdf.x + brdf.y);
// compute blend amount based on world position in the reflection probe volume
float blendAmount = ComputeLerpBetweenInnerOuterAABBs(
ObjectSrg::m_reflectionProbeData.m_innerAabbMin,
ObjectSrg::m_reflectionProbeData.m_innerAabbMax,
ObjectSrg::m_reflectionProbeData.m_outerAabbMax,
ObjectSrg::m_reflectionProbeData.m_aabbPos,
surface.position);
ObjectSrg::m_reflectionProbeData.m_innerAabbMin,
ObjectSrg::m_reflectionProbeData.m_innerAabbMax,
ObjectSrg::m_reflectionProbeData.m_outerAabbMax,
ObjectSrg::m_reflectionProbeData.m_aabbPos,
position);
outSpecular = lerp(outSpecular, probeSpecular, blendAmount);
}
}
}
void ApplyIBL(Surface surface, inout LightingData lightingData)
{
if (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent)
{
// transparencies currently require IBL in the forward pass
if (o_enableIBL)
{
float3 iblDiffuse = 0.0f;
float3 iblSpecular = 0.0f;
ApplyIblDiffuse(surface.normal, surface.albedo, lightingData.diffuseResponse, iblDiffuse);
ApplyIblSpecular(surface.position, surface.normal, surface.specularF0, surface.roughnessLinear, lightingData.specularResponse, lightingData.dirToCamera, lightingData.brdf, iblSpecular);
// Adjust IBL lighting by exposure.
float iblExposureFactor = pow(2.0, SceneSrg::m_iblExposure);
lightingData.diffuseLighting += (iblDiffuse * iblExposureFactor * lightingData.occlusion);
lightingData.specularLighting += (iblSpecular * iblExposureFactor);
}
}
else if (o_meshUseForwardPassIBLSpecular || o_materialUseForwardPassIBLSpecular)
{
if (o_enableIBL)
{
float3 iblSpecular = 0.0f;
ApplyIblSpecular(surface.position, surface.normal, surface.specularF0, surface.roughnessLinear, lightingData.specularResponse, lightingData.dirToCamera, lightingData.brdf, iblSpecular);
float iblExposureFactor = pow(2.0f, SceneSrg::m_iblExposure);
lightingData.specularLighting += (iblSpecular * iblExposureFactor);
}
}
}
@@ -22,13 +22,13 @@
option bool o_area_light_validation = false;
float3 GetDiffuseLighting(Surface surface, float3 lightIntensity, float3 dirToCamera, float3 dirToLight)
float3 GetDiffuseLighting(Surface surface, LightingData lightingData, float3 lightIntensity, float3 dirToLight)
{
float3 diffuse;
if(o_enableSubsurfaceScattering)
{
// Use diffuse brdf contains double Fresnel (enter/exit surface) terms if subsurface scattering is enabled
diffuse = NormalizedDisneyDiffuse(surface.albedo, surface.normal, dirToCamera, dirToLight, surface.roughnessLinear);
diffuse = NormalizedDisneyDiffuse(surface.albedo, surface.normal, lightingData.dirToCamera, dirToLight, surface.roughnessLinear);
}
else
{
@@ -38,29 +38,41 @@ float3 GetDiffuseLighting(Surface surface, float3 lightIntensity, float3 dirToCa
if(o_clearCoat_feature_enabled)
{
// Attenuate diffuse term by clear coat's fresnel term to account for energy loss
float HdotV = saturate(dot(normalize(dirToLight + dirToCamera), dirToCamera));
diffuse *= 1.0 - (FresnelSchlick(HdotV, 0.04) * surface.clearCoatFactor);
float HdotV = saturate(dot(normalize(dirToLight + lightingData.dirToCamera), lightingData.dirToCamera));
diffuse *= 1.0 - (FresnelSchlick(HdotV, 0.04) * surface.clearCoat.factor);
}
diffuse *= lightIntensity;
return diffuse;
}
float3 GetSpecularLighting(Surface surface, const float3 lightIntensity, const float3 dirToCamera, const float3 dirToLight)
float3 GetSpecularLighting(Surface surface, LightingData lightingData, const float3 lightIntensity, const float3 dirToLight)
{
float3 specular = SpecularGGX(surface, dirToCamera, dirToLight);
float3 specular;
if (o_enableAnisotropy)
{
//AnisotropicGGX( float3 dirToCamera, float3 dirToLight, float3 normal, float3 tangent, float3 bitangent, float2 anisotropyFactors,
// float3 specularF0, float NdotV, float multiScatterCompensation )
specular = AnisotropicGGX( lightingData.dirToCamera, dirToLight, surface.normal, surface.anisotropy.tangent, surface.anisotropy.bitangent, surface.anisotropy.anisotropyFactors,
surface.specularF0, lightingData.NdotV, lightingData.multiScatterCompensation );
}
else
{
specular = SpecularGGX(lightingData.dirToCamera, dirToLight, surface.normal, surface.specularF0, lightingData.NdotV, surface.roughnessA2, lightingData.multiScatterCompensation);
}
if(o_clearCoat_feature_enabled)
{
float3 halfVector = normalize(dirToLight + dirToCamera);
float NdotH = saturate(dot(surface.clearCoatNormal, halfVector));
float NdotL = saturate(dot(surface.clearCoatNormal, dirToLight));
float3 halfVector = normalize(dirToLight + lightingData.dirToCamera);
float NdotH = saturate(dot(surface.clearCoat.normal, halfVector));
float NdotL = saturate(dot(surface.clearCoat.normal, dirToLight));
float HdotL = saturate(dot(halfVector, dirToLight));
// HdotV = HdotL due to the definition of half vector
float3 clearCoatF = FresnelSchlick(HdotL, 0.04) * surface.clearCoatFactor;
float clearCoatRoughness = max(surface.clearCoatRoughness * surface.clearCoatRoughness, 0.0005f);
float3 clearCoatSpecular = ClearCoatGGX(NdotH, HdotL, NdotL, surface.clearCoatNormal, clearCoatRoughness, clearCoatF );
float3 clearCoatF = FresnelSchlick(HdotL, 0.04) * surface.clearCoat.factor;
float clearCoatRoughness = max(surface.clearCoat.roughness * surface.clearCoat.roughness, 0.0005f);
float3 clearCoatSpecular = ClearCoatGGX(NdotH, HdotL, NdotL, surface.clearCoat.normal, clearCoatRoughness, clearCoatF );
specular = specular * (1.0 - clearCoatF) * (1.0 - clearCoatF) + clearCoatSpecular;
}
@@ -81,9 +93,9 @@ float GetIntensityAdjustedByRadiusAndRoughness(float roughnessA, float radius, f
//! Adds diffuse and specular contribution for a single sample of a lambertian emitter
void AddSampleContribution(
in Surface surface,
in LightingData lightingData,
in float3 lightSamplePoint,
in float3 lightSampleDirection,
in float3 dirToCamera,
in float bothDirectionsFactor,
inout float3 diffuseAcc,
inout float3 specularAcc,
@@ -102,7 +114,7 @@ void AddSampleContribution(
float3 intensityRgb = float3(intensity, intensity, intensity);
diffuseAcc += GetDiffuseLighting(surface, intensityRgb, dirToCamera, posToLightSampleDir);
translucentAcc += GetBackLighting(surface, intensityRgb, dirToCamera, posToLightSampleDir, 0.0);
specularAcc += GetSpecularLighting(surface, intensityRgb, dirToCamera, posToLightSampleDir);
diffuseAcc += GetDiffuseLighting(surface, lightingData, intensityRgb, posToLightSampleDir);
translucentAcc += GetBackLighting(surface, lightingData, intensityRgb, posToLightSampleDir, 0.0);
specularAcc += GetSpecularLighting(surface, lightingData, intensityRgb, posToLightSampleDir);
}
@@ -0,0 +1,41 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Atom/Features/PBR/Lights/CapsuleLight.azsli>
#include <Atom/Features/PBR/Lights/DirectionalLight.azsli>
#include <Atom/Features/PBR/Lights/DiskLight.azsli>
#include <Atom/Features/PBR/Lights/PointLight.azsli>
#include <Atom/Features/PBR/Lights/PolygonLight.azsli>
#include <Atom/Features/PBR/Lights/QuadLight.azsli>
#include <Atom/Features/PBR/Lights/SpotLight.azsli>
void ApplyDirectLighting(Surface surface, inout LightingData lightingData)
{
if (o_enableDirectionalLights)
{
ApplyDirectionalLights(surface, lightingData);
}
if (o_enablePunctualLights)
{
ApplyPointLights(surface, lightingData);
ApplySpotLights(surface, lightingData);
}
if (o_enableAreaLights)
{
ApplyDiskLights(surface, lightingData);
ApplyCapsuleLights(surface, lightingData);
ApplyQuadLights(surface, lightingData);
ApplyPolygonLights(surface, lightingData);
}
}
@@ -359,7 +359,7 @@ float LtcPolygonEvaluate(in float3 pos, in float3 normal, in float3 dirToView, i
// Find the previous clip point so it can be used when the polygon goes above the horizon by
// searching backwards, updating the endIdx along the way to avoid reprocessing those points later
for (endIdx; endIdx > startIdx + 1; --endIdx)
for ( ; endIdx > startIdx + 1; --endIdx)
{
float3 prevPoint = mul(ltcMat, positions[endIdx - 1].xyz - pos);
if (prevPoint.z > 0)
@@ -392,4 +392,4 @@ float LtcPolygonEvaluate(in float3 pos, in float3 normal, in float3 dirToView, i
// Note: negated due to winding order
return -sum;
}
}
@@ -14,7 +14,7 @@
#include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
void ApplyPointLight(ViewSrg::PointLight light, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingData lightingData)
{
float3 posToLight = light.m_position - surface.position;
float d2 = dot(posToLight, posToLight); // light distance squared
@@ -32,15 +32,15 @@ void ApplyPointLight(ViewSrg::PointLight light, float3 dirToCamera, Surface surf
float3 lightIntensity = (light.m_rgbIntensityCandelas / d2) * radiusAttenuation;
// Diffuse contribution
diffuseLighting += GetDiffuseLighting(surface, lightIntensity, dirToCamera, normalize(posToLight));
lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, normalize(posToLight));
// Tranmission contribution
translucentBackLighting += GetBackLighting(surface, lightIntensity, dirToCamera, normalize(posToLight), 0.0);
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(posToLight), 0.0);
// Adjust the light direcion for specular based on bulb size
// Calculate the reflection off the normal from the view direction
float3 reflectionDir = reflect(-dirToCamera, surface.normal);
float3 reflectionDir = reflect(-lightingData.dirToCamera, surface.normal);
// Calculate a vector from the reflection vector to the light
float3 reflectionPosToLight = posToLight - dot(posToLight, reflectionDir) * reflectionDir;
@@ -52,7 +52,7 @@ void ApplyPointLight(ViewSrg::PointLight light, float3 dirToCamera, Surface surf
float sphereIntensityNormalization = GetIntensityAdjustedByRadiusAndRoughness(surface.roughnessA, light.m_bulbRadius, d2);
// Specular contribution
specularLighting += sphereIntensityNormalization * GetSpecularLighting(surface, lightIntensity, dirToCamera, normalize(posToLight));
lightingData.specularLighting += sphereIntensityNormalization * GetSpecularLighting(surface, lightingData, lightIntensity, normalize(posToLight));
}
}
@@ -65,7 +65,7 @@ float3 SampleSphere(float2 randomPoint)
return float3(sinTheta * cos(angle), sinTheta * sin(angle), cosTheta);
}
void ValidatePointLight(ViewSrg::PointLight light, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ValidatePointLight(ViewSrg::PointLight light, Surface surface, inout LightingData lightingData)
{
const uint sampleCount = 512;
@@ -78,7 +78,7 @@ void ValidatePointLight(ViewSrg::PointLight light, float3 dirToCamera, Surface s
float2 randomPoint = GetHammersleyPoint(i, sampleCount);
float3 sampleDirection = SampleSphere(randomPoint);
float3 samplePoint = light.m_position + sampleDirection * light.m_bulbRadius;
AddSampleContribution(surface, samplePoint, sampleDirection, dirToCamera, 0.0, diffuseAcc, specularAcc, translucentAcc);
AddSampleContribution(surface, lightingData, samplePoint, sampleDirection, 0.0, diffuseAcc, specularAcc, translucentAcc);
}
// Lighting value is in Candela, convert to Lumen for total light output of the light
@@ -87,29 +87,29 @@ void ValidatePointLight(ViewSrg::PointLight light, float3 dirToCamera, Surface s
// equal directions across the hemisphere, so we need to account for that
float3 intensity = intensityLumens * INV_PI;
diffuseLighting += (diffuseAcc / float(sampleCount)) * intensity;
translucentBackLighting += (translucentAcc / float(sampleCount)) * intensity;
specularLighting += (specularAcc / float(sampleCount)) * intensity;
lightingData.diffuseLighting += (diffuseAcc / float(sampleCount)) * intensity;
lightingData.translucentBackLighting += (translucentAcc / float(sampleCount)) * intensity;
lightingData.specularLighting += (specularAcc / float(sampleCount)) * intensity;
}
void ApplyPointLights(inout LightCullingTileIterator tileIterator, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ApplyPointLights(Surface surface, inout LightingData lightingData)
{
tileIterator.LoadAdvance();
while( !tileIterator.IsDone() )
lightingData.tileIterator.LoadAdvance();
while( !lightingData.tileIterator.IsDone() )
{
uint currLightIndex = tileIterator.GetValue();
tileIterator.LoadAdvance();
uint currLightIndex = lightingData.tileIterator.GetValue();
lightingData.tileIterator.LoadAdvance();
ViewSrg::PointLight light = ViewSrg::m_pointLights[currLightIndex];
if (o_area_light_validation)
{
ValidatePointLight(light, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
ValidatePointLight(light, surface, lightingData);
}
else
{
ApplyPointLight(light, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
ApplyPointLight(light, surface, lightingData);
}
}
}
@@ -17,7 +17,7 @@
#include <Atom/RPI/Math.azsli>
// Polygon lights using Linearly Transformed Cosines
void ApplyPoylgonLight(ViewSrg::PolygonLight light, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ApplyPoylgonLight(ViewSrg::PolygonLight light, Surface surface, inout LightingData lightingData)
{
float3 posToLight = light.m_position - surface.position;
float distanceToLight2 = dot(posToLight, posToLight); // light distance squared
@@ -57,13 +57,13 @@ void ApplyPoylgonLight(ViewSrg::PolygonLight light, float3 dirToCamera, Surface
// Diffuse
static const float3x3 identityMatrix = float3x3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);
float diffuse = LtcPolygonEvaluate(surface.position, surface.normal, dirToCamera, identityMatrix, ViewSrg::m_polygonLightPoints, startIndex, endIndex);
float diffuse = LtcPolygonEvaluate(surface.position, surface.normal, lightingData.dirToCamera, identityMatrix, ViewSrg::m_polygonLightPoints, startIndex, endIndex);
diffuse = doubleSided ? abs(diffuse) : max(0.0, diffuse);
// Specular
float2 ltcCoords = LtcCoords(dot(surface.normal, dirToCamera), surface.roughnessLinear);
float2 ltcCoords = LtcCoords(dot(surface.normal, lightingData.dirToCamera), surface.roughnessLinear);
float3x3 ltcMat = LtcMatrix(SceneSrg::m_ltcMatrix, ltcCoords);
float3 specular = LtcPolygonEvaluate(surface.position, surface.normal, dirToCamera, ltcMat, ViewSrg::m_polygonLightPoints, startIndex, endIndex);
float3 specular = LtcPolygonEvaluate(surface.position, surface.normal, lightingData.dirToCamera, ltcMat, ViewSrg::m_polygonLightPoints, startIndex, endIndex);
specular = doubleSided ? abs(specular) : max(0.0, specular);
// Apply BRDF scale terms (BRDF magnitude and Schlick Fresnel)
@@ -73,16 +73,16 @@ void ApplyPoylgonLight(ViewSrg::PolygonLight light, float3 dirToCamera, Surface
// Scale by inverse surface area of hemisphere (1/2pi), attenuation, and light intensity
float3 intensity = 0.5 * INV_PI * radiusAttenuation * abs(light.m_rgbIntensityNits);
diffuseLighting += surface.albedo * diffuse * intensity;
specularLighting += surface.specularF0 * specular * intensity;
lightingData.diffuseLighting += surface.albedo * diffuse * intensity;
lightingData.specularLighting += surface.specularF0 * specular * intensity;
}
void ApplyPolygonLights(float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ApplyPolygonLights(Surface surface, inout LightingData lightingData)
{
for (uint currLightIndex = 0; currLightIndex < ViewSrg::m_polygonLightCount; ++currLightIndex)
{
ViewSrg::PolygonLight light = ViewSrg::m_polygonLights[currLightIndex];
ApplyPoylgonLight(light, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
ApplyPoylgonLight(light, surface, lightingData);
}
}
}
@@ -79,7 +79,7 @@ float3 GetSpecularDominantDirection(float3 normal, float3 reflection, float roug
}
// Quad light approximation. Diffuse portion based on https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf Pages 49-50.
void ApplyQuadLight(ViewSrg::QuadLight light, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ApplyQuadLight(ViewSrg::QuadLight light, Surface surface, inout LightingData lightingData)
{
float3 lightDirection = cross(light.m_leftDir, light.m_upDir); // left and up are already normalized.
@@ -118,12 +118,12 @@ void ApplyQuadLight(ViewSrg::QuadLight light, float3 dirToCamera, Surface surfac
// Diffuse
float3x3 identityMatrix = float3x3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);
float diffuse = LtcQuadEvaluate(surface.normal, dirToCamera, identityMatrix, p, doubleSided);
float diffuse = LtcQuadEvaluate(surface.normal, lightingData.dirToCamera, identityMatrix, p, doubleSided);
// Specular
float2 ltcCoords = LtcCoords(dot(surface.normal, dirToCamera), surface.roughnessLinear);
float2 ltcCoords = LtcCoords(dot(surface.normal, lightingData.dirToCamera), surface.roughnessLinear);
float3x3 ltcMat = LtcMatrix(SceneSrg::m_ltcMatrix, ltcCoords);
float3 specular = LtcQuadEvaluate(surface.normal, dirToCamera, ltcMat, p, doubleSided);
float3 specular = LtcQuadEvaluate(surface.normal, lightingData.dirToCamera, ltcMat, p, doubleSided);
// Apply BRDF scale terms (BRDF magnitude and Schlick Fresnel)
float2 schlick = SceneSrg::m_ltcAmplification.Sample(PassSrg::LinearSampler, ltcCoords).xy;
@@ -132,8 +132,8 @@ void ApplyQuadLight(ViewSrg::QuadLight light, float3 dirToCamera, Surface surfac
// Scale by inverse surface area of hemisphere (1/2pi), attenuation, and light intensity
float3 intensity = 0.5 * INV_PI * radiusAttenuation * light.m_rgbIntensityNits;
diffuseLighting += surface.albedo * diffuse * intensity;
specularLighting += surface.specularF0 * specular * intensity;
lightingData.diffuseLighting += surface.albedo * diffuse * intensity;
lightingData.specularLighting += surface.specularF0 * specular * intensity;
}
else
{
@@ -152,29 +152,29 @@ void ApplyQuadLight(ViewSrg::QuadLight light, float3 dirToCamera, Surface surfac
// Each position contributes 1/5 of the light (4 corners + center)
float3 intensity = solidAngle * 0.2 * radiusAttenuation * light.m_rgbIntensityNits;
diffuseLighting +=
lightingData.diffuseLighting +=
(
GetDiffuseLighting(surface, intensity, dirToCamera, p0) +
GetDiffuseLighting(surface, intensity, dirToCamera, p1) +
GetDiffuseLighting(surface, intensity, dirToCamera, p2) +
GetDiffuseLighting(surface, intensity, dirToCamera, p3) +
GetDiffuseLighting(surface, intensity, dirToCamera, dirToLightCenter)
GetDiffuseLighting(surface, lightingData, intensity, p0) +
GetDiffuseLighting(surface, lightingData, intensity, p1) +
GetDiffuseLighting(surface, lightingData, intensity, p2) +
GetDiffuseLighting(surface, lightingData, intensity, p3) +
GetDiffuseLighting(surface, lightingData, intensity, dirToLightCenter)
);
translucentBackLighting +=
lightingData.translucentBackLighting +=
(
GetBackLighting(surface, intensity, dirToCamera, p0, 0.0) +
GetBackLighting(surface, intensity, dirToCamera, p1, 0.0) +
GetBackLighting(surface, intensity, dirToCamera, p2, 0.0) +
GetBackLighting(surface, intensity, dirToCamera, p3, 0.0) +
GetBackLighting(surface, intensity, dirToCamera, dirToLightCenter, 0.0)
GetBackLighting(surface, lightingData, intensity, p0, 0.0) +
GetBackLighting(surface, lightingData, intensity, p1, 0.0) +
GetBackLighting(surface, lightingData, intensity, p2, 0.0) +
GetBackLighting(surface, lightingData, intensity, p3, 0.0) +
GetBackLighting(surface, lightingData, intensity, dirToLightCenter, 0.0)
);
// Calculate specular by choosing a single representative point on the light's surface based on the reflection ray
// Then adjusting it's brightness based on different factors.
// Calculate the reflection ray from the view direction and surface normal
float3 reflectionDir = reflect(-dirToCamera, surface.normal);
float3 reflectionDir = reflect(-lightingData.dirToCamera, surface.normal);
// First find the reflection-plane intersection, then find the closest point on the rectangle to that intersection.
float2 halfSize = float2(light.m_halfWidth, light.m_halfHeight);
@@ -207,7 +207,7 @@ void ApplyQuadLight(ViewSrg::QuadLight light, float3 dirToCamera, Surface surfac
float distanceAdjustment = 1.0 + sqrt(lightPositionDist2) * (1.0 - rough * rough * solidAngleCoverage);
float3 specularintensity = light.m_rgbIntensityNits * roughnessAdjustment * distanceAdjustment;
specularLighting += GetSpecularLighting(surface, specularintensity, dirToCamera, dirToLight) * radiusAttenuation;
lightingData.specularLighting += GetSpecularLighting(surface, lightingData, specularintensity, dirToLight) * radiusAttenuation;
}
}
}
@@ -222,7 +222,7 @@ float3 SampleRectangle(float2 randomPoint, ViewSrg::QuadLight light)
return outPoint;
}
void ValidateQuadLight(ViewSrg::QuadLight light, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ValidateQuadLight(ViewSrg::QuadLight light, Surface surface, inout LightingData lightingData)
{
float3 lightDirection = cross(light.m_leftDir, light.m_upDir); // left and up are already normalized.
@@ -239,35 +239,35 @@ void ValidateQuadLight(ViewSrg::QuadLight light, float3 dirToCamera, Surface sur
{
float2 randomPoint = GetHammersleyPoint(i, sampleCount);
float3 samplePoint = SampleRectangle(randomPoint, light);
AddSampleContribution(surface, samplePoint, lightDirection, dirToCamera, bothDirectionsFactor, diffuseAcc, specularAcc, translucentAcc);
AddSampleContribution(surface, lightingData, samplePoint, lightDirection, bothDirectionsFactor, diffuseAcc, specularAcc, translucentAcc);
}
float area = light.m_halfWidth * light.m_halfHeight * 4.0;
float3 intensityCandelas = light.m_rgbIntensityNits * area;
diffuseLighting += (diffuseAcc / float(sampleCount)) * intensityCandelas;
translucentBackLighting += (translucentAcc / float(sampleCount)) * intensityCandelas;
specularLighting += (specularAcc / float(sampleCount)) * intensityCandelas;
lightingData.diffuseLighting += (diffuseAcc / float(sampleCount)) * intensityCandelas;
lightingData.translucentBackLighting += (translucentAcc / float(sampleCount)) * intensityCandelas;
lightingData.specularLighting += (specularAcc / float(sampleCount)) * intensityCandelas;
}
void ApplyQuadLights(inout LightCullingTileIterator tileIterator, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ApplyQuadLights(Surface surface, inout LightingData lightingData)
{
tileIterator.LoadAdvance();
lightingData.tileIterator.LoadAdvance();
while( !tileIterator.IsDone() )
while( !lightingData.tileIterator.IsDone() )
{
uint currLightIndex = tileIterator.GetValue();
tileIterator.LoadAdvance();
uint currLightIndex = lightingData.tileIterator.GetValue();
lightingData.tileIterator.LoadAdvance();
ViewSrg::QuadLight light = ViewSrg::m_quadLights[currLightIndex];
if (o_area_light_validation)
{
ValidateQuadLight(light, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
ValidateQuadLight(light, surface, lightingData);
}
else
{
ApplyQuadLight(light, dirToCamera, surface, diffuseLighting, specularLighting, translucentBackLighting);
ApplyQuadLight(light, surface, lightingData);
}
}
}
@@ -15,7 +15,7 @@
#include <Atom/Features/PBR/Lights/LightTypesCommon.azsli>
#include <Atom/Features/Shadow/SpotLightShadow.azsli>
void ApplySpotLight(ViewSrg::SpotLight spotLight, float3 dirToCamera, Surface surface, uint lightIndex, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ApplySpotLight(ViewSrg::SpotLight spotLight, Surface surface, inout LightingData lightingData, uint lightIndex)
{
float3 posToLight = spotLight.m_position - surface.position;
float distanceToLight2 = dot(posToLight, posToLight); // light distance squared
@@ -98,11 +98,11 @@ void ApplySpotLight(ViewSrg::SpotLight spotLight, float3 dirToCamera, Surface su
lightIntensity *= penumbraMask;
}
diffuseLighting += GetDiffuseLighting(surface, lightIntensity, dirToCamera, dirToLight) * litRatio;
translucentBackLighting += GetBackLighting(surface, lightIntensity, dirToCamera, dirToLight, backShadowRatio);
lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, dirToLight) * litRatio;
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, dirToLight, backShadowRatio);
// Calculate the reflection off the normal from the view lightDirection
float3 reflectionDir = reflect(-dirToCamera, surface.normal);
float3 reflectionDir = reflect(-lightingData.dirToCamera, surface.normal);
float reflectionDotLight = dot(reflectionDir, -spotLight.m_direction);
// Let 'Intersection' denote the point where the reflection ray intersects the diskLight plane
@@ -133,20 +133,20 @@ void ApplySpotLight(ViewSrg::SpotLight spotLight, float3 dirToCamera, Surface su
// Adjust the intensity of the light based on the bulb size to conserve energy
float diskIntensityNormalization = GetIntensityAdjustedByRadiusAndRoughness(surface.roughnessA, spotLight.m_bulbRadius, distanceToLight2);
specularLighting += diskIntensityNormalization * GetSpecularLighting(surface, lightIntensity, dirToCamera, normalize(posToLight)) * litRatio;
lightingData.specularLighting += diskIntensityNormalization * GetSpecularLighting(surface, lightingData, lightIntensity, normalize(posToLight)) * litRatio;
}
}
void ApplySpotLights(inout LightCullingTileIterator tileIterator, float3 dirToCamera, Surface surface, inout float3 diffuseLighting, inout float3 specularLighting, inout float3 translucentBackLighting)
void ApplySpotLights(Surface surface, inout LightingData lightingData)
{
tileIterator.LoadAdvance();
lightingData.tileIterator.LoadAdvance();
while( !tileIterator.IsDone() )
while( !lightingData.tileIterator.IsDone() )
{
uint currLightIndex = tileIterator.GetValue();
tileIterator.LoadAdvance();
uint currLightIndex = lightingData.tileIterator.GetValue();
lightingData.tileIterator.LoadAdvance();
ViewSrg::SpotLight light = ViewSrg::m_spotLights[currLightIndex];
ApplySpotLight(light, dirToCamera, surface, currLightIndex, diffuseLighting, specularLighting, translucentBackLighting);
ApplySpotLight(light, surface, lightingData, currLightIndex);
}
}
@@ -23,8 +23,15 @@
#include "Ggx.azsli"
#include "Fresnel.azsli"
option bool o_applySpecularAA;
option bool o_enableAnisotropy = false;
// ------- Diffuse Lighting -------
//! Simple Lambertian BRDF.
float3 DiffuseLambertian(float3 albedo, float3 normal, float3 dirToLight)
{
float NdotL = saturate(dot(normal, dirToLight));
return albedo * NdotL * INV_PI;
}
// Normalized Disney diffuse function taken from Frostbite's PBR course notes (page 10):
// https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/course-notes-moving-frostbite-to-pbr-v32.pdf
@@ -73,29 +80,11 @@ float3 DiffuseTitanfall(float roughnessA, float3 albedo, float3 normal, float3 d
return albedo * (single + albedo * multi) * NdotL;
}
//! Simple Lambertian BRDF.
float3 DiffuseLambertian(float3 albedo, float3 normal, float3 dirToLight)
{
float NdotL = saturate(dot(normal, dirToLight));
return albedo * NdotL * INV_PI;
}
// Specular Anti-Aliasing technique from this paper:
// http://www.jp.square-enix.com/tech/library/pdf/ImprovedGeometricSpecularAA.pdf
float ApplySpecularAA(float roughnessA2, float3 normal)
{
// Constants for formula below
const float screenVariance = 0.25f;
const float varianceThresh = 0.18f;
// Specular Anti-Aliasing
float3 dndu = ddx_fine( normal );
float3 dndv = ddy_fine( normal );
float variance = screenVariance * (dot( dndu , dndu ) + dot( dndv , dndv ));
float kernelRoughnessA2 = min(2.0 * variance , varianceThresh );
float filteredRoughnessA2 = saturate ( roughnessA2 + kernelRoughnessA2 );
return filteredRoughnessA2;
}
// ------- Specular Lighting -------
//! Computes specular response from surfaces with microgeometry. The common form for microfacet
//! implementations is D * G * F / (4.0 * NdotL * NdotV), with D G F being swappable terms.
@@ -103,53 +92,55 @@ float ApplySpecularAA(float roughnessA2, float3 normal)
//!
//! @param roughnessA2 alpha roughness ^ 2 (alpha roughness is the unpacked form of artist authored linear roughness and is what is used for lighting calculations)
//! @param specularF0 the fresnel f0 spectral value of the surface
float3 SpecularGGX( Surface surface, float3 dirToCamera, float3 dirToLight)
float3 SpecularGGX( float3 dirToCamera, float3 dirToLight, float3 normal, float3 specularF0, float NdotV, float roughnessA2, float3 multiScatterCompensation )
{
float3 halfVector = normalize(dirToLight + dirToCamera);
float NdotH = saturate(dot(surface.normal, halfVector));
float NdotV = saturate(dot(surface.normal, dirToCamera));
float NdotL = saturate(dot(surface.normal, dirToLight));
float NdotH = saturate(dot(normal, halfVector));
float NdotL = saturate(dot(normal, dirToLight));
float HdotV = saturate(dot(halfVector, dirToCamera)); // Note that HdotL = HdotV, so we don't need to calculate both
// Specular Anti-Aliasing correction
float roughnessA2 = surface.roughnessA * surface.roughnessA;
if(o_applySpecularAA)
{
roughnessA2 = ApplySpecularAA(roughnessA2, surface.normal);
}
// D, G and F from the lighting equation
// Note: the division by (4.0 * NdotL * NdotV) is already factored out in the G function as an optimization
float D, G;
if (o_enableAnisotropy)
{
D = NormalDistibution_AnisotropicGGX(
NdotH, halfVector, surface.tangentAniso, surface.bitangentAniso, surface.anisotropyFactors );
G = ShadowingMasking_AnisotropicSmithGGXCorrelated(
surface.tangentAniso, surface.bitangentAniso, dirToCamera, dirToLight, NdotV, NdotL, surface.anisotropyFactors );
}
else
{
D = NormalDistributionGGX(NdotH, roughnessA2);
G = GeometricShadowingMaskingGGXCorrelated(NdotV, NdotL, roughnessA2);
}
float3 F = FresnelSchlick(HdotV, surface.specularF0);
float D = NormalDistributionGGX(NdotH, roughnessA2);
float G = GeometricShadowingMaskingGGXCorrelated(NdotV, NdotL, roughnessA2);
float3 F = FresnelSchlick(HdotV, specularF0);
D = max(0.0, D);
G = max(0.0, G);
// Multiply with multiscattering compensation in order to take account for several specular light bounces.
return surface.multiScatterCompensation * (D * G * F * NdotL);
return multiScatterCompensation * (D * G * F * NdotL);
}
float3 AnisotropicGGX( float3 dirToCamera, float3 dirToLight, float3 normal, float3 tangent, float3 bitangent, float2 anisotropyFactors,
float3 specularF0, float NdotV, float3 multiScatterCompensation )
{
float3 halfVector = normalize(dirToLight + dirToCamera);
float NdotH = saturate(dot(normal, halfVector));
float NdotL = saturate(dot(normal, dirToLight));
float HdotV = saturate(dot(halfVector, dirToCamera)); // Note that HdotL = HdotV, so we don't need to calculate both
// D, G and F from the lighting equation
// Note: the division by (4.0 * NdotL * NdotV) is already factored out in the G function as an optimization
float D = NormalDistibution_AnisotropicGGX( NdotH, halfVector, tangent, bitangent, anisotropyFactors );
float G = ShadowingMasking_AnisotropicSmithGGXCorrelated(tangent, bitangent, dirToCamera, dirToLight, NdotV, NdotL, anisotropyFactors);
float3 F = FresnelSchlick(HdotV, specularF0);
D = max(0.0, D);
G = max(0.0, G);
// Multiply with multiscattering compensation in order to take account for several specular light bounces.
return multiScatterCompensation * (D * G * F * NdotL);
}
float3 ClearCoatGGX(float NdotH, float HdotL, float NdotL, float3 normal, float roughnessA, float3 F)
{
// Specular Anti-Aliasing correction
float roughnessA2 = roughnessA * roughnessA;
if(o_applySpecularAA)
{
roughnessA2 = ApplySpecularAA(roughnessA2, normal);
}
//if(o_applySpecularAA)
//{
// roughnessA2 = ApplySpecularAA(roughnessA2, normal);
//}
float D = NormalDistributionGGX(NdotH, roughnessA2);
// Kelemen geometry term : Kelemen. C. and Szirmay-Kalos. L. 2001
@@ -12,22 +12,23 @@
#pragma once
// ------- Fresnel Functions -------
//! Calculate fresnel reflectance using the Schlick method.
//! Reference Naty Hoffman, "Background: Physics and Math of Shading": https://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf Page 16, (6)
//!
//! @param H half vector (which coincides with the normal vector at microlevel)
//! @param V view vector (or light vector; the dot product is the same for both)
//! @param F0 the characteristic specular reflectance of the material. Also referred to it as specular color.
float3 FresnelSchlick(const float HdotV, const float3 F0)
{
// At angles where (View = Normal) the dot product HdotV is 1.0
return F0 + (float3(1.0, 1.0, 1.0) - F0) * pow(1.0 - HdotV, 5.0);
}
float3 FresnelSchlickF90(const float HdotV, const float3 F0, const float F90)
{
// At angles where (View = Normal) the dot product HdotV is 1.0
return F0 + (F90 - F0) * pow(1.0 - HdotV, 5.0);
return F0 + (F90 - F0) * pow(1.0f - HdotV, 5.0f);
}
float3 FresnelSchlick(const float HdotV, const float3 F0)
{
return FresnelSchlickF90(HdotV, F0, 1.0f);
}
//! Calculate fresnel reflectance using the Schlick method, taking roughness into account.
@@ -42,9 +43,9 @@ float3 FresnelSchlickF90(const float HdotV, const float3 F0, const float F90)
//! @param roughness roughness value used to further attenuate the fresnel response. This is a "fudge factor" and may or may not be perceptually linear.
float3 FresnelSchlickWithRoughness(const float NdotV, const float3 specularF0, const float roughness)
{
float smoothness = 1.0 - roughness;
float smoothness = 1.0f - roughness;
float3 F0 = specularF0;
return F0 + (max(smoothness, F0) - F0) * pow(1.0 - saturate(NdotV), 5.0);
return F0 + (max(smoothness, F0) - F0) * pow(1.0 - saturate(NdotV), 5.0f);
}
@@ -28,7 +28,44 @@
#include <Atom/RPI/Math.azsli>
//! Distribution function (D) for the anisotropic GGX
// ------- GGX -------
//! (D) Normal Distribution function for GGX
//! Provides statistical distribution of microfacet normals M around the macrosurface normal N.
//!
//! @param H potential microfacet normal, usually the half-vector between the light and the camera
//! @param N macrosurface normal
//! @param roughnessA2 alpha roughness ^ 2 (alpha roughness is the unpacked form of artist authored linear roughness and is what is used for lighting calculations)
//! @return factor for how much of the macrosurface has normals in the direction M. The value non-negative, but it's not normalized, so values are not restricted to [0, 1].
float NormalDistributionGGX(const float NdotH, const float roughnessA2)
{
// Walter equation 33, simplified given that the positive characteristic function is handled elsewhere
float b = (NdotH * roughnessA2 - NdotH) * NdotH + 1.0;
return roughnessA2 / (PI * b * b);
}
//! (G) Geometric Shadowing and Masking Distribution function for GGX
//! Variant with height correlated Smith
//!
//! @param N macrosurface normal
//! @param V direction to the camera
//! @param L direction to the light
//! @param roughnessA2 alpha roughness ^ 2 (alpha roughness is the unpacked form of artist authored linear roughness and is what is used for lighting calculations)
//! @return probability that V and L are not masked.
float GeometricShadowingMaskingGGXCorrelated(const float NdotV, const float NdotL, const float roughnessA2)
{
// See Frostbite PBR guide (page 12)
// https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/course-notes-moving-frostbite-to-pbr-v32.pdf
float ggxV = NdotL * sqrt((-NdotV * roughnessA2 + NdotV) * NdotV + roughnessA2);
float ggxL = NdotV * sqrt((-NdotL * roughnessA2 + NdotL) * NdotL + roughnessA2);
return 0.5f / max(ggxV + ggxL, GGX_EPSILON);
}
// ------- Anisotropic GGX -------
//! (D) Normal Distribution function for anisotropic GGX
//!
//! @param h - half vector viewer to the light, used to approximate the reflection probability for a microfacet normal at this angle
//! @param t - rotated surface tangent for calculating anisotropic response
//! @param b - rotated surface bitangent for calculating anisotropic response
@@ -46,7 +83,7 @@ float NormalDistibution_AnisotropicGGX( float NdotH, const float3 h, const float
return a2 * w2 * w2 * (1.0 / PI);
}
//! Geometric shadowing and masking distribution function (G) for the anisotropic GGX
//! (G) Geometric shadowing and masking distribution function for anisotropic GGX
//! @param t - rotated surface tangent for calculating anisotropic response
//! @param b - rotated surface bitangent for calculating anisotropic response
//! @param anisotropyFactors - anisotropic tangent and bitangent factor
@@ -66,35 +103,8 @@ float ShadowingMasking_AnisotropicSmithGGXCorrelated(
return min(v, FLOAT_16_MAX);
}
//! GGX normal distribution function.
//! Provides statistical distribution of microfacet normals M around the macrosurface normal N.
//!
//! @param H potential microfacet normal, usually the half-vector between the light and the camera
//! @param N macrosurface normal
//! @param roughnessA2 alpha roughness ^ 2 (alpha roughness is the unpacked form of artist authored linear roughness and is what is used for lighting calculations)
//! @return factor for how much of the macrosurface has normals in the direction M. The value non-negative, but it's not normalized, so values are not restricted to [0, 1].
float NormalDistributionGGX(const float NdotH, const float roughnessA2)
{
// Walter equation 33, simplified given that the positive characteristic function is handled elsewhere
float b = (NdotH * roughnessA2 - NdotH) * NdotH + 1.0;
return roughnessA2 / (PI * b * b);
}
//! GGX geometry shadowing/masking function, using the height correlated Smith
//!
//! @param N macrosurface normal
//! @param V direction to the camera
//! @param L direction to the light
//! @param roughnessA2 alpha roughness ^ 2 (alpha roughness is the unpacked form of artist authored linear roughness and is what is used for lighting calculations)
//! @return probability that V and L are not masked.
float GeometricShadowingMaskingGGXCorrelated(const float NdotV, const float NdotL, const float roughnessA2)
{
// See Frostbite PBR guide (page 12)
// https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/course-notes-moving-frostbite-to-pbr-v32.pdf
float ggxV = NdotL * sqrt((-NdotV * roughnessA2 + NdotV) * NdotV + roughnessA2);
float ggxL = NdotV * sqrt((-NdotL * roughnessA2 + NdotL) * NdotL + roughnessA2);
return 0.5f / max(ggxV + ggxL, GGX_EPSILON);
}
// ------- Importance Sampling -------
//! Importance-sample a microgeometry normal using GGX distribution.
//!
@@ -12,57 +12,57 @@
#pragma once
//! The surface struct should contain all the info for a pixel that can be
//! passed onto the rendering logic for shading.
//! Note that metallic workflow can be supported by first converting to these physical properties first.
struct Surface
{
float3 position;
float3 normal;
float3 tangentAniso; //! surface space tangent for anisotropic use
float3 bitangentAniso; //! surface space bitangent for anisotropic use
float2 anisotropyFactors; //! anisotory factors along the tangent and the bitangent directions
float3 albedo;
float3 specularF0; //!< actual fresnel f0 spectral value of the surface (as opposed to a "factor")
float3 multiScatterCompensation; //!< the constant scaling term to approximate multiscattering contribution in specular BRDF
float roughnessLinear; //!< perceptually linear roughness value authored by artists. Must be remapped to roughnessA before use
float roughnessA; //!< actual roughness value ( a.k.a. "alpha roughness") to be used in microfacet calculations
float thickness; //!< pre baked local thickness, used for transmission
float4 transmissionParams; //!< parameters: thick mode->(attenuation coefficient, power, distortion, scale), thin mode: (float3 scatter distance, scale)
float clearCoatFactor; //!< clear coat strength factor
float clearCoatRoughness; //!< clear coat linear roughness (not base layer one)
float3 clearCoatNormal; //!< normal used for top layer clear coat
};
//! Calculate and fill the data required for fast directional anisotropty surface response.
//! Assumption: the normal and roughnessA surface properties were filled and are valid
//! Notice that since the newly created surface tangent and bitangent will be rotated
//! according to the anisotropy direction and should not be used for other purposes uness
//! rotated back.
void CalculateSurfaceDirectionalAnisotropicData(
inout Surface surface, float2 anisotropyAngleAndFactor,
float3 vtxTangent, float3 vtxBitangent )
{
const float anisotropyAngle = anisotropyAngleAndFactor.x;
const float anisotropyFactor = anisotropyAngleAndFactor.y;
surface.anisotropyFactors = max( 0.01,
float2( surface.roughnessA * (1.0 + anisotropyFactor),
surface.roughnessA * (1.0 - anisotropyFactor) )
);
if (anisotropyAngle > 0.01)
{
// Base rotation according to anisotropic main direction
float aniSin, aniCos;
sincos(anisotropyAngle, aniSin, aniCos);
// Rotate the vertex tangent to get new aligned to surface normal tangent
vtxTangent = aniCos * vtxTangent - aniSin * vtxBitangent;
}
// Now create the new surface base according to the surface normal
// If rotation was required it was already applied to the tangent, hence to the bitangent
surface.bitangentAniso = normalize(cross(surface.normal, vtxTangent));
surface.tangentAniso = cross(surface.bitangentAniso, surface.normal);
}
// //! The surface struct should contain all the info for a pixel that can be
// //! passed onto the rendering logic for shading.
// //! Note that metallic workflow can be supported by first converting to these physical properties first.
// struct Surface
// {
// float3 position;
// float3 normal;
// float3 tangentAniso; //! surface space tangent for anisotropic use
// float3 bitangentAniso; //! surface space bitangent for anisotropic use
// float2 anisotropyFactors; //! anisotory factors along the tangent and the bitangent directions
// float3 albedo;
// float3 specularF0; //!< actual fresnel f0 spectral value of the surface (as opposed to a "factor")
// float3 multiScatterCompensation; //!< the constant scaling term to approximate multiscattering contribution in specular BRDF
// float roughnessLinear; //!< perceptually linear roughness value authored by artists. Must be remapped to roughnessA before use
// float roughnessA; //!< actual roughness value ( a.k.a. "alpha roughness") to be used in microfacet calculations
// float thickness; //!< pre baked local thickness, used for transmission
// float4 transmissionParams; //!< parameters: thick mode->(attenuation coefficient, power, distortion, scale), thin mode: (float3 scatter distance, scale)
// float clearCoatFactor; //!< clear coat strength factor
// float clearCoatRoughness; //!< clear coat linear roughness (not base layer one)
// float3 clearCoatNormal; //!< normal used for top layer clear coat
// };
//
// //! Calculate and fill the data required for fast directional anisotropty surface response.
// //! Assumption: the normal and roughnessA surface properties were filled and are valid
// //! Notice that since the newly created surface tangent and bitangent will be rotated
// //! according to the anisotropy direction and should not be used for other purposes uness
// //! rotated back.
// void CalculateSurfaceDirectionalAnisotropicData(
// inout Surface surface, float2 anisotropyAngleAndFactor,
// float3 vtxTangent, float3 vtxBitangent )
// {
// const float anisotropyAngle = anisotropyAngleAndFactor.x;
// const float anisotropyFactor = anisotropyAngleAndFactor.y;
//
// surface.anisotropyFactors = max( 0.01,
// float2( surface.roughnessA * (1.0 + anisotropyFactor),
// surface.roughnessA * (1.0 - anisotropyFactor) )
// );
//
// if (anisotropyAngle > 0.01)
// {
// // Base rotation according to anisotropic main direction
// float aniSin, aniCos;
// sincos(anisotropyAngle, aniSin, aniCos);
//
// // Rotate the vertex tangent to get new aligned to surface normal tangent
// vtxTangent = aniCos * vtxTangent - aniSin * vtxBitangent;
// }
//
// // Now create the new surface base according to the surface normal
// // If rotation was required it was already applied to the tangent, hence to the bitangent
// surface.bitangentAniso = normalize(cross(surface.normal, vtxTangent));
// surface.tangentAniso = cross(surface.bitangentAniso, surface.normal);
// }
@@ -0,0 +1,53 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
option bool o_enableAnisotropy = false;
// ------- Surface Data -------
class AnisotropicSurfaceData
{
float3 tangent; //!< surface space tangent for anisotropic use
float3 bitangent; //!< surface space bitangent for anisotropic use
float2 anisotropyFactors; //!< anisotory factors along the tangent and the bitangent directions
void Init(float3 normal, float3 vtxTangent, float3 vtxBitangent, float anisotropyAngle, float anisotropyFactor, float roughnessA);
};
// ------- Functions -------
//! Notice that since the newly created surface tangent and bitangent will be rotated according
//! to the anisotropy direction and should not be used for other purposes unless rotated back.
void AnisotropicSurfaceData::Init(float3 normal, float3 vtxTangent, float3 vtxBitangent, float anisotropyAngle, float anisotropyFactor, float roughnessA)
{
anisotropyFactors = max( 0.01,
float2( roughnessA * (1.0 + anisotropyFactor),
roughnessA * (1.0 - anisotropyFactor) )
);
if (anisotropyAngle > 0.01)
{
// Base rotation according to anisotropic main direction
float aniSin, aniCos;
sincos(anisotropyAngle, aniSin, aniCos);
// Rotate the vertex tangent to get new aligned to surface normal tangent
vtxTangent = aniCos * vtxTangent - aniSin * vtxBitangent;
}
// Now create the new surface base according to the surface normal
// If rotation was required it was already applied to the tangent, hence to the bitangent
bitangent = normalize(cross(normal, vtxTangent));
tangent = cross(bitangent, normal);
}
@@ -0,0 +1,92 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
option bool o_applySpecularAA;
// ------- Constants -------
// For artists workflows, specular F0 for dialectric (non-metal) materials is exposed as a 0-1 scale
// However the specular F0 for dialectrics is much lower than metals, so we expose this scaling factor here
static const float3 MaxDielectricSpecularF0 = 0.08f;
// Make sure roughnessA is above 0 to avoid precision and divide by zero issues.
// 0.0005f is sufficient for directional lights since they tend to be quite bright.
static const float MinRoughnessA = 0.0005f;
// ------- Surface Data -------
class BasePbrSurfaceData
{
float3 position; //!< Position in world-space
float3 normal; //!< Normal in world-space
float3 albedo; //!< Albedo color of the non-metallic material, will be multiplied against the diffuse lighting value
float3 specularF0; //!< Fresnel f0 spectral value of the surface
float roughnessLinear; //!< Perceptually linear roughness value authored by artists. Must be remapped to roughnessA before use
float roughnessA; //!< Actual roughness value ( a.k.a. "alpha roughness") to be used in microfacet calculations
float roughnessA2; //!< Alpha roughness ^ 2 (i.e. roughnessA * roughnessA), used in GGX, cached here for perfromance
//! Applies specular anti-aliasing to roughnessA2
void ApplySpecularAA();
//! Calculates roughnessA and roughnessA2 after roughness has been set
void CalculateRoughnessA();
//! Sets albedo and specularF0 using metallic workflow
void SetAlbedoAndSpecularF0(float3 baseColor, float inSpecularF0, float metallic);
};
// ------- Functions -------
// Specular Anti-Aliasing technique from this paper:
// http://www.jp.square-enix.com/tech/library/pdf/ImprovedGeometricSpecularAA.pdf
void BasePbrSurfaceData::ApplySpecularAA()
{
// Constants for formula below
const float screenVariance = 0.25f;
const float varianceThresh = 0.18f;
// Specular Anti-Aliasing
float3 dndu = ddx_fine( normal );
float3 dndv = ddy_fine( normal );
float variance = screenVariance * (dot( dndu , dndu ) + dot( dndv , dndv ));
float kernelRoughnessA2 = min(2.0 * variance , varianceThresh );
float filteredRoughnessA2 = saturate ( roughnessA2 + kernelRoughnessA2 );
roughnessA2 = filteredRoughnessA2;
}
void BasePbrSurfaceData::CalculateRoughnessA()
{
// The roughness value in microfacet calculations (called "alpha" in the literature) does not give perceptually
// linear results. Disney found that squaring the roughness value before using it in microfacet equations causes
// the user-provided roughness parameter to be more perceptually linear. We keep both values available as some
// equations need roughnessLinear (i.e. IBL sampling) while others need roughnessA (i.e. GGX equations).
// See Burley's Disney PBR: https://pdfs.semanticscholar.org/eeee/3b125c09044d3e2f58ed0e4b1b66a677886d.pdf
roughnessA = max(roughnessLinear * roughnessLinear, MinRoughnessA);
roughnessA2 = roughnessA * roughnessA;
if(o_applySpecularAA)
{
ApplySpecularAA();
}
}
void BasePbrSurfaceData::SetAlbedoAndSpecularF0(float3 baseColor, float inSpecularF0, float metallic)
{
float3 dielectricSpecularF0 = MaxDielectricSpecularF0 * inSpecularF0;
// Compute albedo and specularF0 based on metalness
albedo = lerp(baseColor, float3(0.0f, 0.0f, 0.0f), metallic);
specularF0 = lerp(dielectricSpecularF0, baseColor, metallic);
}
@@ -0,0 +1,20 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
class ClearCoatSurfaceData
{
float factor; //!< clear coat strength factor
float roughness; //!< clear coat linear roughness (not base layer one)
float3 normal; //!< normal used for top layer clear coat
};
@@ -0,0 +1,26 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Atom/Features/PBR/Surfaces/AnisotropicSurfaceData.azsli>
#include <Atom/Features/PBR/Surfaces/BasePbrSurfaceData.azsli>
#include <Atom/Features/PBR/Surfaces/ClearCoatSurfaceData.azsli>
#include <Atom/Features/PBR/Surfaces/TransmissionSurfaceData.azsli>
class Surface
{
BasePbrSurfaceData pbr;
//AnisotropicSurfaceData anisotropy;
TransmissionSurfaceData transmission;
};
@@ -0,0 +1,91 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Atom/Features/PBR/Surfaces/AnisotropicSurfaceData.azsli>
#include <Atom/Features/PBR/Surfaces/BasePbrSurfaceData.azsli>
#include <Atom/Features/PBR/Surfaces/ClearCoatSurfaceData.azsli>
#include <Atom/Features/PBR/Surfaces/TransmissionSurfaceData.azsli>
class Surface //: BasePbrSurfaceData
{
//BasePbrSurfaceData pbr;
AnisotropicSurfaceData anisotropy;
ClearCoatSurfaceData clearCoat;
TransmissionSurfaceData transmission;
// ------- BasePbrSurfaceData -------
float3 position; //!< Position in world-space
float3 normal; //!< Normal in world-space
float3 albedo; //!< Albedo color of the non-metallic material, will be multiplied against the diffuse lighting value
float3 specularF0; //!< Fresnel f0 spectral value of the surface
float roughnessLinear; //!< Perceptually linear roughness value authored by artists. Must be remapped to roughnessA before use
float roughnessA; //!< Actual roughness value ( a.k.a. "alpha roughness") to be used in microfacet calculations
float roughnessA2; //!< Alpha roughness ^ 2 (i.e. roughnessA * roughnessA), used in GGX, cached here for perfromance
//! Applies specular anti-aliasing to roughnessA2
void ApplySpecularAA();
//! Calculates roughnessA and roughnessA2 after roughness has been set
void CalculateRoughnessA();
//! Sets albedo and specularF0 using metallic workflow
void SetAlbedoAndSpecularF0(float3 baseColor, float inSpecularF0, float metallic);
};
// Specular Anti-Aliasing technique from this paper:
// http://www.jp.square-enix.com/tech/library/pdf/ImprovedGeometricSpecularAA.pdf
void Surface::ApplySpecularAA()
{
// Constants for formula below
const float screenVariance = 0.25f;
const float varianceThresh = 0.18f;
// Specular Anti-Aliasing
float3 dndu = ddx_fine( normal );
float3 dndv = ddy_fine( normal );
float variance = screenVariance * (dot( dndu , dndu ) + dot( dndv , dndv ));
float kernelRoughnessA2 = min(2.0 * variance , varianceThresh );
float filteredRoughnessA2 = saturate ( roughnessA2 + kernelRoughnessA2 );
roughnessA2 = filteredRoughnessA2;
}
void Surface::CalculateRoughnessA()
{
// The roughness value in microfacet calculations (called "alpha" in the literature) does not give perceptually
// linear results. Disney found that squaring the roughness value before using it in microfacet equations causes
// the user-provided roughness parameter to be more perceptually linear. We keep both values available as some
// equations need roughnessLinear (i.e. IBL sampling) while others need roughnessA (i.e. GGX equations).
// See Burley's Disney PBR: https://pdfs.semanticscholar.org/eeee/3b125c09044d3e2f58ed0e4b1b66a677886d.pdf
roughnessA = max(roughnessLinear * roughnessLinear, MinRoughnessA);
roughnessA2 = roughnessA * roughnessA;
if(o_applySpecularAA)
{
ApplySpecularAA();
}
}
void Surface::SetAlbedoAndSpecularF0(float3 baseColor, float inSpecularF0, float metallic)
{
float3 dielectricSpecularF0 = MaxDielectricSpecularF0 * inSpecularF0;
// Compute albedo and specularF0 based on metalness
albedo = lerp(baseColor, float3(0.0f, 0.0f, 0.0f), metallic);
specularF0 = lerp(dielectricSpecularF0, baseColor, metallic);
}
@@ -0,0 +1,20 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
class TransmissionSurfaceData
{
float3 tint;
float thickness; //!< pre baked local thickness, used for transmission
float4 transmissionParams; //!< parameters: thick mode->(attenuation coefficient, power, distortion, scale), thin mode: (float3 scatter distance, scale)
};
@@ -152,4 +152,4 @@ float SampleShadowMapBicubic_16Tap(SampleShadowMapBicubicParameters param)
shadow /= 2704;
return shadow * shadow;
}
}
@@ -63,4 +63,4 @@ ShaderResourceGroupSemantic SRG_RayTracingGlobal
ShaderResourceGroupSemantic SRG_RayTracingScene
{
FrequencyId = 1;
};
};
@@ -0,0 +1,57 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
// ------------------------------------------------------------------------------
// NOTE: VSInput, VSOutput, ObjectSrg must be defined before including this file.
// ---------------------------------------------------------------------------------
// Options
#include <Atom/Features/PBR/LightingOptions.azsli>
// Shader Resource Groups
#include <viewsrg.srgi>
#include <scenesrg.srgi>
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <Atom/Features/PBR/ForwardPassSrg.azsli>
// Math
#include <Atom/RPI/Math.azsli>
#include <Atom/RPI/TangentSpace.azsli>
// Shadow Coords
#include <Atom/Features/Shadow/DirectionalLightShadow.azsli>
//! @param skipShadowCoords can be useful for example when PixelDepthOffset is enable, because the pixel shader will have to run before the final world position is known
void VertexHelper(in VSInput IN, inout VSOutput OUT, float3 worldPosition, bool skipShadowCoords = false)
{
OUT.m_worldPosition = worldPosition;
OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPosition, 1.0));
float4x4 objectToWorld = ObjectSrg::GetWorldMatrix();
float3x3 objectToWorldIT = ObjectSrg::GetWorldMatrixInverseTranspose();
ConstructTBN(IN.m_normal, IN.m_tangent, IN.m_bitangent, objectToWorld, objectToWorldIT, OUT.m_normal, OUT.m_tangent, OUT.m_bitangent);
// directional light shadow
const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight;
if (o_enableShadows && !skipShadowCoords && shadowIndex < SceneSrg::m_directionalLightCount)
{
DirectionalLightShadow::GetShadowCoords(
shadowIndex,
worldPosition,
OUT.m_shadowCoords);
}
}
@@ -5,8 +5,6 @@
"Depth" : { "Enable" : false }
},
"CompilerVersion" : "1.0",
"ProgramSettings":
{
"EntryPoints":
@@ -5,8 +5,6 @@
"Depth" : { "Enable" : false }
},
"CompilerVersion" : "1.0",
"ProgramSettings":
{
"EntryPoints":
@@ -102,7 +102,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex)
float NdotV = saturate(dot(normal, dirToCamera));
NdotV = max(NdotV, 0.01f); // [GFX TODO][ATOM-4466] This is a current band-aid for specular noise at grazing angles.
float2 brdf = PassSrg::m_brdfMap.Sample(PassSrg::LinearSampler, GetBRDFTexCoords(roughness, NdotV)).rg;
float3 multiScatterCompensation = GetMultiScatterCompensation(NdotV, specularF0, brdf, multiScatterCompensationEnabled);
float3 multiScatterCompensation = GetMultiScatterCompensation(specularF0, brdf, multiScatterCompensationEnabled);
float3 specular = blendWeight * globalSpecular * multiScatterCompensation * (specularF0 * brdf.x + brdf.y);
float4 encodedClearCoatNormal = PassSrg::m_clearCoatNormal.Load(IN.m_position.xy, sampleIndex);
@@ -123,7 +123,7 @@ PSOutput MainPS(VSOutput IN)
float NdotV = saturate(dot(normal, dirToCamera));
NdotV = max(NdotV, 0.01f); // [GFX TODO][ATOM-4466] This is a current band-aid for specular noise at grazing angles.
float2 brdf = PassSrg::m_brdfMap.Sample(PassSrg::LinearSampler, GetBRDFTexCoords(roughness, NdotV)).rg;
float3 multiScatterCompensation = GetMultiScatterCompensation(NdotV, specularF0, brdf, multiScatterCompensationEnabled);
float3 multiScatterCompensation = GetMultiScatterCompensation(specularF0, brdf, multiScatterCompensationEnabled);
float3 specular = blendWeight * globalSpecular * multiScatterCompensation * (specularF0 * brdf.x + brdf.y);
float4 encodedClearCoatNormal = PassSrg::m_clearCoatNormal.Load(int3(IN.m_position.xy, 0));
@@ -58,7 +58,7 @@ bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float3 aabbMin
float3 probeSpecular = ObjectSrg::m_reflectionCubeMap.SampleLevel(SceneSrg::m_samplerEnv, GetCubemapCoords(localReflectDir), GetRoughnessMip(roughness)).rgb;
// compute final specular amount
float3 multiScatterCompensation = GetMultiScatterCompensation(NdotV, specularF0, brdf, multiScatterCompensationEnabled);
float3 multiScatterCompensation = GetMultiScatterCompensation(specularF0, brdf, multiScatterCompensationEnabled);
specular = probeSpecular * multiScatterCompensation * (specularF0.xyz * brdf.x + brdf.y);
// compute clear coat specular amount
@@ -23,6 +23,11 @@ set(FILES
Materials/Types/EnhancedPBR_ForwardPass_EDS.shader
Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl
Materials/Types/EnhancedPBR_Shadowmap_WithPS.shader
Materials/Types/Skin.azsl
Materials/Types/Skin.materialtype
Materials/Types/Skin.shader
Materials/Types/Skin_Common.azsli
Materials/Types/Skin_WrinkleMaps.lua
Materials/Types/StandardMultilayerPBR.materialtype
Materials/Types/StandardMultilayerPBR_ClearCoatEnableFeature.lua
Materials/Types/StandardMultilayerPBR_Common.azsli
@@ -57,6 +62,7 @@ set(FILES
Materials/Types/MaterialInputs/AlphaInput.azsli
Materials/Types/MaterialInputs/BaseColorInput.azsli
Materials/Types/MaterialInputs/ClearCoatInput.azsli
Materials/Types/MaterialInputs/DetailMapsCommonFunctor.lua
Materials/Types/MaterialInputs/DetailMapsInput.azsli
Materials/Types/MaterialInputs/EmissiveInput.azsli
Materials/Types/MaterialInputs/MetallicInput.azsli
@@ -106,6 +112,7 @@ set(FILES
Passes/DepthUpsample.pass
Passes/DiffuseComposite.pass
Passes/DiffuseGlobalFullscreen.pass
Passes/DiffuseGlobalFullscreen_nomsaa.pass
Passes/DiffuseGlobalIllumination.pass
Passes/DiffuseProbeGridBlendDistance.pass
Passes/DiffuseProbeGridBlendIrradiance.pass
@@ -162,6 +169,7 @@ set(FILES
Passes/ReflectionComposite.pass
Passes/ReflectionCopyFrameBuffer.pass
Passes/ReflectionGlobalFullscreen.pass
Passes/ReflectionGlobalFullscreen_nomsaa.pass
Passes/ReflectionProbeBlendWeight.pass
Passes/ReflectionProbeRenderInner.pass
Passes/ReflectionProbeRenderOuter.pass
@@ -173,6 +181,7 @@ set(FILES
Passes/ReflectionScreenSpaceBlurVertical.pass
Passes/ReflectionScreenSpaceComposite.pass
Passes/ReflectionScreenSpaceTrace.pass
Passes/Reflections_nomsaa.pass
Passes/ShadowParent.pass
Passes/Skinning.pass
Passes/SkyBox.pass
@@ -191,6 +200,8 @@ set(FILES
Passes/TransparentParent.pass
Passes/UI.pass
Passes/UIParent.pass
Scripts/material_property_overrides_demo.lua
ShaderLib/Atom/Features/BlendUtility.azsli
ShaderLib/Atom/Features/IndirectRendering.azsli
ShaderLib/Atom/Features/MatrixUtility.azsli
ShaderLib/Atom/Features/ParallaxMapping.azsli
@@ -212,6 +223,7 @@ set(FILES
ShaderLib/Atom/Features/Math/Filter.azsli
ShaderLib/Atom/Features/Math/FilterPassSrg.azsli
ShaderLib/Atom/Features/Math/IntersectionTests.azsli
ShaderLib/Atom/Features/MorphTargets/MorphTargetCompression.azsli
ShaderLib/Atom/Features/PBR/AlphaUtils.azsli
ShaderLib/Atom/Features/PBR/BackLighting.azsli
ShaderLib/Atom/Features/PBR/Decals.azsli
@@ -220,21 +232,33 @@ set(FILES
ShaderLib/Atom/Features/PBR/ForwardPassSrg.azsli
ShaderLib/Atom/Features/PBR/Hammersley.azsli
ShaderLib/Atom/Features/PBR/LightingModel.azsli
ShaderLib/Atom/Features/PBR/LightingOptions.azsli
ShaderLib/Atom/Features/PBR/LightingUtils.azsli
ShaderLib/Atom/Features/PBR/Surface.azsli
ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli
ShaderLib/Atom/Features/PBR/Lighting/DualSpecularLighting.azsli
ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli
ShaderLib/Atom/Features/PBR/Lighting/StandardLighting.azsli
ShaderLib/Atom/Features/PBR/Lights/CapsuleLight.azsli
ShaderLib/Atom/Features/PBR/Lights/DirectionalLight.azsli
ShaderLib/Atom/Features/PBR/Lights/DiskLight.azsli
ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli
ShaderLib/Atom/Features/PBR/Lights/Lights.azsli
ShaderLib/Atom/Features/PBR/Lights/LightTypesCommon.azsli
ShaderLib/Atom/Features/PBR/Lights/Ltc.azsli
ShaderLib/Atom/Features/PBR/Lights/PointLight.azsli
ShaderLib/Atom/Features/PBR/Lights/PolygonLight.azsli
ShaderLib/Atom/Features/PBR/Lights/QuadLight.azsli
ShaderLib/Atom/Features/PBR/Lights/SpotLight.azsli
ShaderLib/Atom/Features/PBR/Microfacet/Brdf.azsli
ShaderLib/Atom/Features/PBR/Microfacet/Fresnel.azsli
ShaderLib/Atom/Features/PBR/Microfacet/Ggx.azsli
ShaderLib/Atom/Features/PBR/Surfaces/AnisotropicSurfaceData.azsli
ShaderLib/Atom/Features/PBR/Surfaces/BasePbrSurfaceData.azsli
ShaderLib/Atom/Features/PBR/Surfaces/ClearCoatSurfaceData.azsli
ShaderLib/Atom/Features/PBR/Surfaces/DualSpecularSurface.azsli
ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli
ShaderLib/Atom/Features/PBR/Surfaces/TransmissionSurfaceData.azsli
ShaderLib/Atom/Features/PostProcessing/Aces.azsli
ShaderLib/Atom/Features/PostProcessing/AcesColorSpaceConversion.azsli
ShaderLib/Atom/Features/PostProcessing/FullscreenPixelInfo.azsli
@@ -245,6 +269,7 @@ set(FILES
ShaderLib/Atom/Features/PostProcessing/GlyphRender.azsli
ShaderLib/Atom/Features/PostProcessing/PostProcessUtil.azsli
ShaderLib/Atom/Features/ScreenSpace/ScreenSpaceUtil.azsli
ShaderLib/Atom/Features/Shadow/BicubicPcfFilters.azsli
ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli
ShaderLib/Atom/Features/Shadow/JitterTablePcf.azsli
ShaderLib/Atom/Features/Shadow/Shadow.azsli
@@ -279,10 +304,16 @@ set(FILES
Shaders/Depth/DepthPassTransparentMin.shader
Shaders/DiffuseGlobalIllumination/DiffuseComposite.azsl
Shaders/DiffuseGlobalIllumination/DiffuseComposite.shader
Shaders/DiffuseGlobalIllumination/DiffuseComposite_nomsaa.azsl
Shaders/DiffuseGlobalIllumination/DiffuseComposite_nomsaa.shader
Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen.azsl
Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen.shader
Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen_nomsaa.azsl
Shaders/DiffuseGlobalIllumination/DiffuseGlobalFullscreen_nomsaa.shader
Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.azsl
Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample.shader
Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample_nomsaa.azsl
Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample_nomsaa.shader
Shaders/ImGui/ImGui.azsl
Shaders/ImGui/ImGui.shader
Shaders/LightCulling/LightCulling.azsl
@@ -398,8 +429,12 @@ set(FILES
Shaders/Reflections/ReflectionCommon.azsli
Shaders/Reflections/ReflectionComposite.azsl
Shaders/Reflections/ReflectionComposite.shader
Shaders/Reflections/ReflectionComposite_nomsaa.azsl
Shaders/Reflections/ReflectionComposite_nomsaa.shader
Shaders/Reflections/ReflectionGlobalFullscreen.azsl
Shaders/Reflections/ReflectionGlobalFullscreen.shader
Shaders/Reflections/ReflectionGlobalFullscreen_nomsaa.azsl
Shaders/Reflections/ReflectionGlobalFullscreen_nomsaa.shader
Shaders/Reflections/ReflectionProbeBlendWeight.azsl
Shaders/Reflections/ReflectionProbeBlendWeight.shader
Shaders/Reflections/ReflectionProbeRenderCommon.azsli
@@ -106,7 +106,7 @@ PixelOutput MainPS(VertexOutput input)
if (o_useNormalMap)
{
float4 sampledValue = MaterialSrg::m_normalMap.Sample(MaterialSrg::m_sampler, input.m_uv);
normal = GetWorldSpaceNormal(sampledValue, input.m_normal, input.m_tangent, input.m_bitangent);
normal = GetWorldSpaceNormal(sampledValue.xy, input.m_normal, input.m_tangent, input.m_bitangent);
}
else
{
@@ -224,4 +224,4 @@ float NextRandomFloatUniform(inout uint seed)
{
seed = Xorshift(seed);
return (float)seed / 4294967295.0f;
}
}
@@ -138,31 +138,14 @@ namespace AZ
AZStd::array_view<float> ModelKdTree::GetPositionsBuffer(const ModelLodAsset::Mesh& mesh)
{
const BufferAssetView* positionBufferAssetView = mesh.GetSemanticBufferAssetView(AZ::Name{"POSITION"});
if (positionBufferAssetView)
{
const AZStd::array_view<uint8_t> positionRawBuffer = positionBufferAssetView->GetBufferAsset()->GetBuffer();
const auto size = positionBufferAssetView->GetBufferViewDescriptor().m_elementSize;
return {
reinterpret_cast<const float*>(positionRawBuffer.data() + positionBufferAssetView->GetBufferViewDescriptor().m_elementOffset * size),
positionBufferAssetView->GetBufferViewDescriptor().m_elementCount * size / sizeof(float)
};
}
AZ_Warning("ModelKdTree", false, "Could not find position buffers in a mesh");
return {};
AZStd::array_view<float> positionBuffer = mesh.GetSemanticBufferTyped<float>(AZ::Name{"POSITION"});
AZ_Warning("ModelKdTree", !positionBuffer.empty(), "Could not find position buffers in a mesh");
return positionBuffer;
}
AZStd::array_view<ModelKdTree::TriangleIndices> ModelKdTree::GetIndexBuffer(const ModelLodAsset::Mesh& mesh)
{
const BufferAssetView& indexBufferAssetView = mesh.GetIndexBufferAssetView();
const AZStd::array_view<uint8_t> indexRawBuffer = indexBufferAssetView.GetBufferAsset()->GetBuffer();
const auto size = indexBufferAssetView.GetBufferViewDescriptor().m_elementSize;
static_assert(sizeof(TriangleIndices) == 3 * sizeof(uint32_t));
return {
reinterpret_cast<const TriangleIndices*>(indexRawBuffer.data() + indexBufferAssetView.GetBufferViewDescriptor().m_elementOffset * size),
indexBufferAssetView.GetBufferViewDescriptor().m_elementCount * size / sizeof(TriangleIndices)
};
return mesh.GetIndexBufferTyped<ModelKdTree::TriangleIndices>();
}
void ModelKdTree::BuildRecursively(ModelKdTreeNode* pNode, const AZ::Aabb& boundbox, AZStd::vector<ObjectIdTriangleIndices>& indices)
@@ -39,6 +39,7 @@ struct VSOutput
};
#include <Atom/Features/PBR/LightingModel.azsli>
#include <Atom/Features/Vertex/VertexHelper.azsli>
VSOutput AutoBrick_ForwardPassVS(VSInput IN)
{
@@ -48,7 +49,7 @@ VSOutput AutoBrick_ForwardPassVS(VSInput IN)
OUT.m_uv = IN.m_uv;
PbrVsHelper(IN, OUT, worldPosition);
VertexHelper(IN, OUT, worldPosition);
return OUT;
}
@@ -43,6 +43,7 @@ struct VSOutput
};
#include <Atom/Features/PBR/LightingModel.azsli>
#include <Atom/Features/Vertex/VertexHelper.azsli>
VSOutput MinimalPBR_MainPassVS(VSInput IN)
{
@@ -50,7 +51,7 @@ VSOutput MinimalPBR_MainPassVS(VSInput IN)
float3 worldPosition = mul(ObjectSrg::GetWorldMatrix(), float4(IN.m_position, 1.0)).xyz;
PbrVsHelper(IN, OUT, worldPosition);
VertexHelper(IN, OUT, worldPosition);
return OUT;
}
@@ -44,6 +44,7 @@ namespace AtomToolsFramework
//! Requires the Atom RPI to be initialized in order
//! to internally construct an RPI::ViewportContext.
explicit RenderViewportWidget(AzFramework::ViewportId id = AzFramework::InvalidViewportId, QWidget* parent = nullptr);
~RenderViewportWidget();
//! Gets the name associated with this viewport's ViewportContext.
//! This context name can be used to adjust the current Camera
@@ -93,8 +94,8 @@ namespace AtomToolsFramework
// AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Handler ...
void BeginCursorCapture() override;
void EndCursorCapture() override;
QPoint ViewportCursorScreenPosition() override;
AZStd::optional<QPoint> PreviousViewportCursorScreenPosition() override;
AzFramework::ScreenPoint ViewportCursorScreenPosition() override;
AZStd::optional<AzFramework::ScreenPoint> PreviousViewportCursorScreenPosition() override;
// AzFramework::WindowRequestBus::Handler ...
void SetWindowTitle(const AZStd::string& title) override;
@@ -17,6 +17,7 @@
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Viewport/ViewportControllerList.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzToolsFramework/Viewport/ViewportTypes.h>
#include <AzCore/Math/MathUtils.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/Bootstrap/BootstrapRequestBus.h>
@@ -54,12 +55,22 @@ namespace AtomToolsFramework
AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Handler::BusConnect(GetId());
AzFramework::InputChannelEventListener::Connect();
AZ::TickBus::Handler::BusConnect();
AzFramework::WindowRequestBus::Handler::BusConnect(params.windowHandle);
setUpdatesEnabled(false);
setFocusPolicy(Qt::FocusPolicy::WheelFocus);
setMouseTracking(true);
}
RenderViewportWidget::~RenderViewportWidget()
{
AzFramework::WindowRequestBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AzFramework::InputChannelEventListener::Disconnect();
AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Handler::BusDisconnect();
AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler::BusDisconnect();
}
void RenderViewportWidget::LockRenderTargetSize(uint32_t width, uint32_t height)
{
setFixedSize(aznumeric_cast<int>(width), aznumeric_cast<int>(height));
@@ -183,7 +194,8 @@ namespace AtomToolsFramework
return false;
}
return m_controllerList->HandleInputChannelEvent({GetId(), inputChannel});
AzFramework::NativeWindowHandle windowId = reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
return m_controllerList->HandleInputChannelEvent({GetId(), windowId, inputChannel});
}
void RenderViewportWidget::OnTick([[maybe_unused]]float deltaTime, AZ::ScriptTimePoint time)
@@ -234,14 +246,16 @@ namespace AtomToolsFramework
// Now that we've looked a viewport local mouse position,
// we can go ahead and broadcast the system cursor input event to the controllers.
// This allows any controllers not listening to pure mosue deltas to consistently
// This allows any controllers not listening to pure mouse deltas to consistently
// look up the mouse position in viewport screen coordinates.
const AzFramework::InputDevice* mouseInputDevice = nullptr;
if (AzFramework::InputDeviceRequestBus::EventResult(mouseInputDevice, AzFramework::InputDeviceMouse::Id, &AzFramework::InputDeviceRequests::GetInputDevice);
if (AzFramework::InputDeviceRequestBus::EventResult(
mouseInputDevice, AzFramework::InputDeviceMouse::Id, &AzFramework::InputDeviceRequests::GetInputDevice);
mouseInputDevice != nullptr)
{
const AzFramework::NativeWindowHandle windowId = reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
AzFramework::InputChannel syntheticInput(AzFramework::InputDeviceMouse::SystemCursorPosition, *mouseInputDevice);
m_controllerList->HandleInputChannelEvent({GetId(), syntheticInput});
m_controllerList->HandleInputChannelEvent({GetId(), windowId, syntheticInput});
}
if (m_capturingCursor && m_lastCursorPosition.has_value())
@@ -262,7 +276,7 @@ namespace AtomToolsFramework
const qreal deficePixelRatio = devicePixelRatioF();
const QSize windowSize = uiWindowSize * deficePixelRatio;
AzFramework::NativeWindowHandle windowId = reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
const AzFramework::NativeWindowHandle windowId = reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
AzFramework::WindowNotificationBus::Event(windowId, &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height());
m_windowResizedEvent = false;
}
@@ -391,7 +405,8 @@ namespace AtomToolsFramework
return projectedPosition.GetAsVector3() / projectedPosition.GetW();
}
AZStd::optional<AzToolsFramework::ViewportInteraction::ProjectedViewportRay> RenderViewportWidget::ViewportScreenToWorldRay(const QPoint& screenPosition)
AZStd::optional<AzToolsFramework::ViewportInteraction::ProjectedViewportRay> RenderViewportWidget::ViewportScreenToWorldRay(
const QPoint& screenPosition)
{
auto pos0 = ViewportScreenToWorld(screenPosition, 0.f);
auto pos1 = ViewportScreenToWorld(screenPosition, 1.f);
@@ -407,14 +422,16 @@ namespace AtomToolsFramework
return AzToolsFramework::ViewportInteraction::ProjectedViewportRay{rayOrigin, rayDirection};
}
QPoint RenderViewportWidget::ViewportCursorScreenPosition()
AzFramework::ScreenPoint RenderViewportWidget::ViewportCursorScreenPosition()
{
return m_mousePosition.toPoint();
return AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint(m_mousePosition.toPoint());
}
AZStd::optional<QPoint> RenderViewportWidget::PreviousViewportCursorScreenPosition()
AZStd::optional<AzFramework::ScreenPoint> RenderViewportWidget::PreviousViewportCursorScreenPosition()
{
return m_lastCursorPosition.has_value() ? mapFromGlobal(m_lastCursorPosition.value()) : m_lastCursorPosition;
using AzToolsFramework::ViewportInteraction::ScreenPointFromQPoint;
return m_lastCursorPosition.has_value() ? ScreenPointFromQPoint(mapFromGlobal(m_lastCursorPosition.value()))
: AZStd::optional<AzFramework::ScreenPoint>{};
}
void RenderViewportWidget::BeginCursorCapture()
@@ -23,7 +23,7 @@ ly_add_target(
Source
BUILD_DEPENDENCIES
PRIVATE
3rdParty::FreeType2
3rdParty::freetype
AZ::AzCore
AZ::AtomCore
Legacy::CryCommon
@@ -290,7 +290,7 @@ namespace AZ
void MeshComponentController::UnregisterModel()
{
if (m_meshFeatureProcessor)
if (m_meshFeatureProcessor && m_meshHandle.IsValid())
{
MeshComponentNotificationBus::Event(m_entityId, &MeshComponentNotificationBus::Events::OnModelPreDestroy);
m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
@@ -307,9 +307,9 @@ namespace EMotionFX
const float updatedPositionY = m_actorInstance->GetWorldSpaceTransform().mPosition.GetY();
const float actualDeltaX = AZ::GetAbs(updatedPositionX - originalPositionX);
const float actualDeltaY = AZ::GetAbs(updatedPositionY - originalPositionY);
EXPECT_TRUE(AZ::GetAbs(actualDeltaX - expectedDeltaX) < 0.001f)
EXPECT_NEAR(actualDeltaX, expectedDeltaX, 0.001f)
<< "Diagonal Rotation: The absolute difference between actual delta and expected delta of X-axis should be less than 0.001f.";
EXPECT_TRUE(AZ::GetAbs(actualDeltaY - expectedDeltaY) < 0.001f)
EXPECT_NEAR(actualDeltaY, expectedDeltaY, 0.001f)
<< "Diagonal Rotation: The absolute difference between actual delta and expected delta of Y-axis should be less than 0.001f.";
}
}
+1
View File
@@ -64,6 +64,7 @@ ly_add_target(
3rdParty::etc2comp
3rdParty::PVRTexTool
3rdParty::squish-ccr
3rdParty::zlib
3rdParty::tiff
Legacy::CryCommon
AZ::AzCore
@@ -105,7 +105,7 @@ namespace ImageProcessing
{
}
explicit ColorRGBA16(uint64 a_u)
explicit ColorRGBA16(AZ::u64 a_u)
: u(a_u)
{
}
@@ -152,7 +152,7 @@ namespace ImageProcessing
uint16 b;
uint16 a;
};
uint64 u;
AZ::u64 u;
};
};
@@ -219,7 +219,7 @@ namespace ImageProcessing
srcImage->GetImagePointer(mip, srcMem, srcPitch);
const pvrtexture::CPVRTextureHeader srcHeader(
srcPixelType.PixelTypeID, // uint64 u64PixelFormat,
srcPixelType.PixelTypeID, // AZ::u64 u64PixelFormat,
width, // uint32 u32Height=1,
height, // uint32 u32Width=1,
1, // uint32 u32Depth=1,
@@ -310,7 +310,7 @@ namespace ImageProcessing
// Preparing source compressed data
const pvrtexture::CPVRTextureHeader compressedHeader(
FindPvrPixelFormat(fmtSrc), // uint64 u64PixelFormat,
FindPvrPixelFormat(fmtSrc), // AZ::u64 u64PixelFormat,
width, // uint32 u32Height=1,
height, // uint32 u32Width=1,
1, // uint32 u32Depth=1,
@@ -25,9 +25,5 @@ typedef AZ::s32 int32;
typedef AZ::s32 sint32;
typedef AZ::u32 uint32;
typedef AZ::s64 int64;
typedef AZ::s64 sint64;
typedef AZ::u64 uint64;
typedef float f32;
typedef double f64;
@@ -22,7 +22,7 @@
#include <QString>
#include <libtiff/tiffio.h> // TIFF library
#include <tiffio.h> // TIFF library
namespace ImageProcessing
{
+2
View File
@@ -87,6 +87,8 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
Legacy::CryCommon
Gem::LmbrCentral
Gem::TextureAtlas
Gem::AtomToolsFramework.Static
Gem::AtomToolsFramework.Editor
${additional_dependencies}
PUBLIC
Gem::Atom_RPI.Public
+79 -37
View File
@@ -22,6 +22,8 @@
#include <LyShine/Bus/UiEditorCanvasBus.h>
#include "LyShine.h"
#include "UiRenderer.h"
#include "ViewportNudge.h"
#include "ViewportPivot.h"
#include "ViewportSnap.h"
@@ -34,6 +36,9 @@
#include <QGridLayout>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#define UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_ELEMENT_BORDERS_KEY "ViewportWidget::m_drawElementBordersFlags"
#define UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_ELEMENT_BORDERS_DEFAULT ( ViewportWidget::DrawElementBorders_Unselected )
@@ -198,7 +203,7 @@ namespace
} // anonymous namespace.
ViewportWidget::ViewportWidget(EditorWindow* parent)
: QViewport(parent)
: AtomToolsFramework::RenderViewportWidget(AzFramework::InvalidViewportId, parent)
, m_editorWindow(parent)
, m_viewportInteraction(new ViewportInteraction(m_editorWindow))
, m_viewportAnchor(new ViewportAnchor())
@@ -213,30 +218,12 @@ ViewportWidget::ViewportWidget(EditorWindow* parent)
, m_rulersVisible(GetPersistentRulerVisibility())
, m_guidesVisible(GetPersistentGuideVisibility())
{
QObject::connect(this,
SIGNAL(SignalRender(const SRenderContext&)),
SLOT(HandleSignalRender(const SRenderContext&)));
// Turn off all fancy visuals in the viewport.
{
SViewportSettings tweakedSettings = GetSettings();
tweakedSettings.grid.showGrid = false;
tweakedSettings.grid.origin = false;
tweakedSettings.rendering.fps = false;
tweakedSettings.rendering.wireframe = false;
tweakedSettings.lighting.m_brightness = 0.0f;
tweakedSettings.camera.showViewportOrientation = false;
SetSettings(tweakedSettings);
}
setAcceptDrops(true);
SetUseArrowsForNavigation(false);
UpdateViewportBackground();
InitUiRenderer();
SetupShortcuts();
// Setup a timer for the maximum refresh rate we want.
@@ -264,12 +251,32 @@ ViewportWidget::ViewportWidget(EditorWindow* parent)
});
FontNotificationBus::Handler::BusConnect();
AZ::TickBus::Handler::BusConnect();
}
ViewportWidget::~ViewportWidget()
{
AzToolsFramework::EditorPickModeNotificationBus::Handler::BusDisconnect();
FontNotificationBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
m_uiRenderer.reset();
// Notify LyShine that this is no longer a valid UiRenderer.
// Only one viewport/renderer is currently supported in the UI Editor
CLyShine* lyShine = static_cast<CLyShine*>(gEnv->pLyShine);
lyShine->SetUiRendererForEditor(nullptr);
}
void ViewportWidget::InitUiRenderer()
{
m_uiRenderer = AZStd::make_shared<UiRenderer>(GetViewportContext());
// Notify LyShine that this is the UiRenderer to be used for rendering
// UI canvases that are loaded in the UI Editor.
// Only one viewport/renderer is currently supported in the UI Editor
CLyShine* lyShine = static_cast<CLyShine*>(gEnv->pLyShine);
lyShine->SetUiRendererForEditor(m_uiRenderer);
}
ViewportInteraction* ViewportWidget::GetViewportInteraction()
@@ -292,17 +299,15 @@ void ViewportWidget::ToggleDrawElementBorders(uint32 flags)
void ViewportWidget::UpdateViewportBackground()
{
SViewportSettings tweakedSettings = GetSettings();
ColorB backgroundColor(ViewportHelpers::backgroundColorDark.GetR8(),
const QColor backgroundColor(ViewportHelpers::backgroundColorDark.GetR8(),
ViewportHelpers::backgroundColorDark.GetG8(),
ViewportHelpers::backgroundColorDark.GetB8(),
ViewportHelpers::backgroundColorDark.GetA8());
tweakedSettings.background.useGradient = false;
tweakedSettings.background.topColor = backgroundColor;
tweakedSettings.background.bottomColor = backgroundColor;
tweakedSettings.lighting.m_ambientColor = backgroundColor;
SetSettings(tweakedSettings);
QPalette pal(palette());
pal.setColor(QPalette::Window, backgroundColor);
setPalette(pal);
setAutoFillBackground(true);
}
void ViewportWidget::ActiveCanvasChanged()
@@ -344,8 +349,10 @@ void ViewportWidget::ClearUntilSafeToRedraw()
// set flag so that Update will just clear the screen rather than rendering canvas
m_canvasRenderIsEnabled = false;
#ifdef LYSHINE_ATOM_TODO // check if still needed
// Force an update
Update();
#endif
// Schedule a timer to set the m_canvasRenderIsEnabled flag
// using a time of zero just waits until there is nothing on the event queue
@@ -454,9 +461,10 @@ void ViewportWidget::contextMenuEvent(QContextMenuEvent* e)
}
}
QViewport::contextMenuEvent(e);
RenderViewportWidget::contextMenuEvent(e);
}
#ifdef LYSHINE_ATOM_TODO // check if still needed
void ViewportWidget::HandleSignalRender([[maybe_unused]] const SRenderContext& context)
{
// Called from QViewport when redrawing the viewport.
@@ -477,6 +485,7 @@ void ViewportWidget::HandleSignalRender([[maybe_unused]] const SRenderContext& c
}
}
}
#endif
void ViewportWidget::UserSelectionChanged(HierarchyItemRawPtrList* items)
{
@@ -497,8 +506,34 @@ void ViewportWidget::EnableCanvasRender()
RefreshTick();
}
void ViewportWidget::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
if (!m_uiRenderer->IsReady() || !m_canvasRenderIsEnabled)
{
return;
}
#ifdef LYSHINE_ATOM_TODO
gEnv->pRenderer->SetSrgbWrite(true);
#endif
// Set up to render a frame to this viewport's window
GetViewportContext()->RenderTick();
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
if (editorMode == UiEditorMode::Edit)
{
RenderEditMode(deltaTime);
}
else // if (editorMode == UiEditorMode::Preview)
{
RenderPreviewMode(deltaTime);
}
}
void ViewportWidget::RefreshTick()
{
#ifdef LYSHINE_EDITOR_TODO // still need this?
if (m_refreshRequested)
{
if (m_canvasRenderIsEnabled)
@@ -511,6 +546,7 @@ void ViewportWidget::RefreshTick()
// in case we were called manually, reset the timer
m_updateTimer.start();
}
#endif
}
void ViewportWidget::mousePressEvent(QMouseEvent* ev)
@@ -636,7 +672,7 @@ void ViewportWidget::wheelEvent(QWheelEvent* ev)
m_viewportInteraction->MouseWheelEvent(&scaledEvent);
}
QViewport::wheelEvent(ev);
RenderViewportWidget::wheelEvent(ev);
Refresh();
}
@@ -698,7 +734,7 @@ void ViewportWidget::keyPressEvent(QKeyEvent* event)
bool handled = m_viewportInteraction->KeyPressEvent(event);
if (!handled)
{
QViewport::keyPressEvent(event);
RenderViewportWidget::keyPressEvent(event);
}
}
else // if (editorMode == UiEditorMode::Preview)
@@ -739,7 +775,7 @@ void ViewportWidget::keyReleaseEvent(QKeyEvent* event)
bool handled = m_viewportInteraction->KeyReleaseEvent(event);
if (!handled)
{
QViewport::keyReleaseEvent(event);
RenderViewportWidget::keyReleaseEvent(event);
}
}
else if (editorMode == UiEditorMode::Preview)
@@ -790,7 +826,7 @@ void ViewportWidget::resizeEvent(QResizeEvent* ev)
}
}
QViewport::resizeEvent(ev);
RenderViewportWidget::resizeEvent(ev);
}
bool ViewportWidget::AcceptsMimeData(const QMimeData* mimeData)
@@ -863,7 +899,7 @@ QPointF ViewportWidget::WidgetToViewport(const QPointF & point) const
return point * WidgetToViewportFactor();
}
void ViewportWidget::RenderEditMode()
void ViewportWidget::RenderEditMode(float deltaTime)
{
if (m_fontTextureHasChanged)
{
@@ -893,16 +929,18 @@ void ViewportWidget::RenderEditMode()
AZ::Vector2 viewportSize(aznumeric_cast<float>(size().width()), aznumeric_cast<float>(size().height()));
viewportSize *= QHighDpiScaling::factor(windowHandle()->screen());
#ifdef LYSHINE_ATOM_TODO
// clear the stencil buffer before rendering each canvas - required for masking
// NOTE: the FRT_CLEAR_IMMEDIATE is required since we will not be setting the render target
ColorF viewportBackgroundColor(0, 0, 0, 0); // if clearing color we want to set alpha to zero also
gEnv->pRenderer->ClearTargetsImmediately(FRT_CLEAR_STENCIL, viewportBackgroundColor);
#endif
// Set the target size of the canvas
EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetTargetCanvasSize, false, canvasSize);
// Update this canvas (must be done after SetTargetCanvasSize)
EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, UpdateCanvasInEditorViewport, GetLastFrameTime(), false);
EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, UpdateCanvasInEditorViewport, deltaTime, false);
// Render this canvas
EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, RenderCanvasInEditorViewport, false, viewportSize);
@@ -999,7 +1037,7 @@ void ViewportWidget::RenderEditMode()
}
}
void ViewportWidget::RenderPreviewMode()
void ViewportWidget::RenderPreviewMode(float deltaTime)
{
AZ::EntityId canvasEntityId = m_editorWindow->GetPreviewModeCanvas();
@@ -1059,7 +1097,7 @@ void ViewportWidget::RenderPreviewMode()
EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetTargetCanvasSize, true, canvasSize);
// Update this canvas (must be done after SetTargetCanvasSize)
EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, UpdateCanvasInEditorViewport, GetLastFrameTime(), true);
EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, UpdateCanvasInEditorViewport, deltaTime, true);
// Execute events that have been queued during the canvas update
gEnv->pLyShine->ExecuteQueuedEvents();
@@ -1090,12 +1128,15 @@ void ViewportWidget::RenderPreviewMode()
canvasToViewportMatrix.SetTranslation(translation);
EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetCanvasToViewportMatrix, canvasToViewportMatrix);
#ifdef LYSHINE_ATOM_TODO
// clear the stencil buffer before rendering each canvas - required for masking
// NOTE: the FRT_CLEAR_IMMEDIATE is required since we will not be setting the render target
// We also clear the color to a mid grey so that we can see the bounds of the canvas
ColorF viewportBackgroundColor(0.5f, 0.5f, 0.5f, 0); // if clearing color we want to set alpha to zero also
gEnv->pRenderer->ClearTargetsImmediately(FRT_CLEAR, viewportBackgroundColor);
#endif
#ifdef LYSHINE_ATOM_TODO
// Render a black rectangle covering the canvas area. This allows the canvas bounds to be visible when the canvas size is
// not exactly the same as the viewport size
AZ::Vector2 topLeftInViewportSpace = CanvasHelpers::GetViewportPoint(canvasEntityId, AZ::Vector2(0.0f, 0.0f));
@@ -1104,6 +1145,7 @@ void ViewportWidget::RenderPreviewMode()
Draw2dHelper draw2d;
int texId = gEnv->pRenderer->GetBlackTextureId();
draw2d.DrawImage(texId, topLeftInViewportSpace, sizeInViewportSpace);
#endif
// Render this canvas
// NOTE: the displayBounds param is always false. If we wanted a debug option to display the bounds
+13 -5
View File
@@ -15,18 +15,19 @@
#include "EditorCommon.h"
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
#include <IFont.h>
#include <QViewport.h>
#include <QTimer>
#endif
class RulerWidget;
class QMimeData;
class UiRenderer;
class ViewportWidget
: public QViewport
: public AtomToolsFramework::RenderViewportWidget
, private AzToolsFramework::EditorPickModeNotificationBus::Handler
, private FontNotificationBus::Handler
{
@@ -84,13 +85,14 @@ public: // member functions
void ShowGuides(bool show);
bool AreGuidesShown() { return m_guidesVisible; }
void InitUiRenderer();
protected:
void contextMenuEvent(QContextMenuEvent* e) override;
private slots:
void HandleSignalRender(const SRenderContext& context);
void UserSelectionChanged(HierarchyItemRawPtrList* items);
void EnableCanvasRender();
@@ -141,11 +143,15 @@ private: // member functions
void OnFontTextureUpdated(IFFont* font) override;
// ~FontNotifications
// AZ::TickBus::Handler
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
// ~AZ::TickBus::Handler
//! Render the viewport when in edit mode
void RenderEditMode();
void RenderEditMode(float deltaTime);
//! Render the viewport when in preview mode
void RenderPreviewMode();
void RenderPreviewMode(float deltaTime);
//! Create shortcuts for manipulating the viewport
void SetupShortcuts();
@@ -193,4 +199,6 @@ private: // data
bool m_rulersVisible;
bool m_guidesVisible;
bool m_fontTextureHasChanged = false;
AZStd::shared_ptr<UiRenderer> m_uiRenderer;
};
+13 -1
View File
@@ -288,6 +288,18 @@ UiRenderer* CLyShine::GetUiRenderer()
return m_uiRenderer.get();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
UiRenderer* CLyShine::GetUiRendererForEditor()
{
return m_uiRendererForEditor.get();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void CLyShine::SetUiRendererForEditor(AZStd::shared_ptr<UiRenderer> uiRenderer)
{
m_uiRendererForEditor = uiRenderer;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
AZ::EntityId CLyShine::CreateCanvas()
{
@@ -414,7 +426,7 @@ void CLyShine::Render()
{
FRAME_PROFILER(__FUNCTION__, gEnv->pSystem, PROFILE_UI);
// LYSHINE_ATOM_TODO - verify that this is no longer needed and remove
// LYSHINE_ATOM_TODO - convert to use Atom interface to check for null renderer
if (!gEnv || !gEnv->pRenderer || gEnv->pRenderer->GetRenderType() == ERenderType::eRT_Null)
{
// if the renderer is not initialized or it is the null renderer (e.g. running as a server)
+6 -1
View File
@@ -111,9 +111,13 @@ public:
int GetTickOrder() override;
// ~TickEvents
// Get the UIRenderer (which is owned by CLyShine). This is not exposed outside the gem.
// Get the UIRenderer for the game (which is owned by CLyShine). This is not exposed outside the gem.
UiRenderer* GetUiRenderer();
// Get/set the UIRenderer for the Editor (which is owned by CLyShine). This is not exposed outside the gem.
UiRenderer* GetUiRendererForEditor();
void SetUiRendererForEditor(AZStd::shared_ptr<UiRenderer> uiRenderer);
public: // static member functions
#if defined(LYSHINE_INTERNAL_UNIT_TEST)
@@ -138,6 +142,7 @@ private: // data
std::unique_ptr<CDraw2d> m_draw2d; // using a pointer rather than an instance to avoid including Draw2d.h
std::unique_ptr<UiRenderer> m_uiRenderer; // using a pointer rather than an instance to avoid including UiRenderer.h
AZStd::shared_ptr<UiRenderer> m_uiRendererForEditor;
std::unique_ptr<UiCanvasManager> m_uiCanvasManager;
+24 -8
View File
@@ -253,12 +253,18 @@ namespace
}, context);
}
UiRenderer* GetUiRenderer()
UiRenderer* GetUiRendererForGame()
{
CLyShine* lyShine = static_cast<CLyShine*>(gEnv->pLyShine);
return lyShine->GetUiRenderer();
}
UiRenderer* GetUiRendererForEditor()
{
CLyShine* lyShine = static_cast<CLyShine*>(gEnv->pLyShine);
return lyShine->GetUiRendererForEditor();
}
bool IsValidInteractable(const AZ::EntityId& entityId)
{
if (!entityId.IsValid())
@@ -1974,9 +1980,12 @@ void UiCanvasComponent::UpdateCanvasInEditorViewport(float deltaTime, bool isInG
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiCanvasComponent::RenderCanvasInEditorViewport(bool isInGame, AZ::Vector2 viewportSize)
{
GetUiRenderer()->BeginUiFrameRender();
RenderCanvas(isInGame, viewportSize);
GetUiRenderer()->EndUiFrameRender();
// When isInGame is true we're rendering the canvas in UI Editor's Preview Mode
UiRenderer* uiRenderer = GetUiRendererForEditor();
AZ_Assert(uiRenderer, "Trying to render a canvas in the UI Editor before its UIRenderer has been initialized");
uiRenderer->BeginUiFrameRender();
RenderCanvas(isInGame, viewportSize, uiRenderer);
uiRenderer->EndUiFrameRender();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -2019,7 +2028,7 @@ void UiCanvasComponent::UpdateCanvas(float deltaTime, bool isInGame)
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiCanvasComponent::RenderCanvas(bool isInGame, AZ::Vector2 viewportSize)
void UiCanvasComponent::RenderCanvas(bool isInGame, AZ::Vector2 viewportSize, UiRenderer* uiRenderer)
{
// Ignore render ops if we're not enabled
if (!m_enabled)
@@ -2027,6 +2036,11 @@ void UiCanvasComponent::RenderCanvas(bool isInGame, AZ::Vector2 viewportSize)
return;
}
if (!uiRenderer)
{
uiRenderer = GetUiRendererForGame();
}
// It is possible, due to the LoadScreenComponent, for this canvas to have Render called while it is rendering.
// This is rare but can happen because rendering of text can call FontCreateTexture which results in CreateTextureObject
// being called, which has a load scren update in it. Rendering the canvas to the render graph while already in the
@@ -2051,9 +2065,9 @@ void UiCanvasComponent::RenderCanvas(bool isInGame, AZ::Vector2 viewportSize)
if (!m_renderGraph.IsEmpty())
{
GetUiRenderer()->BeginCanvasRender();
m_renderGraph.Render(GetUiRenderer(), viewportSize);
GetUiRenderer()->EndCanvasRender();
uiRenderer->BeginCanvasRender();
m_renderGraph.Render(uiRenderer, viewportSize);
uiRenderer->EndCanvasRender();
}
m_isRendering = false;
@@ -3724,6 +3738,7 @@ void UiCanvasComponent::DestroyRenderTarget()
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiCanvasComponent::RenderCanvasToTexture()
{
#ifdef LYSHINE_ATOM_TODO
if (m_renderTargetHandle <= 0)
{
return;
@@ -3752,6 +3767,7 @@ void UiCanvasComponent::RenderCanvasToTexture()
GetUiRenderer()->EndUiFrameRender();
}
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////

Some files were not shown because too many files have changed in this diff Show More