Formalize material-driven panoramic skybox path
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
// XC_BUILTIN_SKYBOX_OPENGL_PS
|
||||
#version 430
|
||||
|
||||
layout(binding = 0) uniform sampler2D uSkyboxTexture;
|
||||
|
||||
layout(std140, binding = 0) uniform EnvironmentConstants {
|
||||
vec4 gSkyboxTopColor;
|
||||
vec4 gSkyboxHorizonColor;
|
||||
@@ -10,10 +12,45 @@ layout(std140, binding = 0) uniform EnvironmentConstants {
|
||||
vec4 gCameraForwardAndUnused;
|
||||
};
|
||||
|
||||
layout(std140, binding = 1) uniform MaterialConstants {
|
||||
vec4 gSkyboxTintAndExposure;
|
||||
vec4 gSkyboxRotationAndMode;
|
||||
};
|
||||
|
||||
in vec2 vNdc;
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
const float XC_PI = 3.14159265358979323846;
|
||||
const float XC_INV_PI = 0.31830988618379067154;
|
||||
const float XC_INV_TWO_PI = 0.15915494309189533577;
|
||||
|
||||
vec3 EvaluateProceduralSkybox(vec3 viewRay) {
|
||||
float vertical = clamp(viewRay.y, -1.0, 1.0);
|
||||
vec3 color = gSkyboxHorizonColor.rgb;
|
||||
if (vertical >= 0.0) {
|
||||
color = mix(gSkyboxHorizonColor.rgb, gSkyboxTopColor.rgb, pow(clamp(vertical, 0.0, 1.0), 0.65));
|
||||
} else {
|
||||
color = mix(gSkyboxHorizonColor.rgb, gSkyboxBottomColor.rgb, pow(clamp(-vertical, 0.0, 1.0), 0.55));
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
vec2 ComputePanoramicUv(vec3 viewRay) {
|
||||
float rotation = gSkyboxRotationAndMode.x;
|
||||
float sinTheta = sin(rotation);
|
||||
float cosTheta = cos(rotation);
|
||||
vec3 rotatedRay = normalize(vec3(
|
||||
viewRay.x * cosTheta - viewRay.z * sinTheta,
|
||||
viewRay.y,
|
||||
viewRay.x * sinTheta + viewRay.z * cosTheta));
|
||||
|
||||
float u = fract(atan(rotatedRay.z, rotatedRay.x) * XC_INV_TWO_PI + 0.5);
|
||||
float v = acos(clamp(rotatedRay.y, -1.0, 1.0)) * XC_INV_PI;
|
||||
return vec2(u, clamp(v, 0.0, 1.0));
|
||||
}
|
||||
|
||||
void main() {
|
||||
float tanHalfFov = gCameraRightAndTanHalfFov.w;
|
||||
float aspect = gCameraUpAndAspect.w;
|
||||
@@ -23,12 +60,12 @@ void main() {
|
||||
vNdc.x * aspect * tanHalfFov * gCameraRightAndTanHalfFov.xyz +
|
||||
vNdc.y * tanHalfFov * gCameraUpAndAspect.xyz);
|
||||
|
||||
float vertical = clamp(viewRay.y, -1.0, 1.0);
|
||||
vec3 color = gSkyboxHorizonColor.rgb;
|
||||
if (vertical >= 0.0) {
|
||||
color = mix(gSkyboxHorizonColor.rgb, gSkyboxTopColor.rgb, pow(clamp(vertical, 0.0, 1.0), 0.65));
|
||||
} else {
|
||||
color = mix(gSkyboxHorizonColor.rgb, gSkyboxBottomColor.rgb, pow(clamp(-vertical, 0.0, 1.0), 0.55));
|
||||
vec3 color = EvaluateProceduralSkybox(viewRay);
|
||||
if (gSkyboxRotationAndMode.y > 0.5) {
|
||||
vec2 uv = ComputePanoramicUv(viewRay);
|
||||
color = texture(uSkyboxTexture, uv).rgb *
|
||||
gSkyboxTintAndExposure.rgb *
|
||||
gSkyboxTintAndExposure.w;
|
||||
}
|
||||
|
||||
fragColor = vec4(color, 1.0);
|
||||
|
||||
@@ -10,10 +10,48 @@ layout(set = 0, binding = 0, std140) uniform EnvironmentConstants {
|
||||
vec4 gCameraForwardAndUnused;
|
||||
};
|
||||
|
||||
layout(set = 1, binding = 0, std140) uniform MaterialConstants {
|
||||
vec4 gSkyboxTintAndExposure;
|
||||
vec4 gSkyboxRotationAndMode;
|
||||
};
|
||||
|
||||
layout(set = 2, binding = 0) uniform texture2D uSkyboxTexture;
|
||||
layout(set = 3, binding = 0) uniform sampler uLinearSampler;
|
||||
|
||||
layout(location = 0) in vec2 vNdc;
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
const float XC_PI = 3.14159265358979323846;
|
||||
const float XC_INV_PI = 0.31830988618379067154;
|
||||
const float XC_INV_TWO_PI = 0.15915494309189533577;
|
||||
|
||||
vec3 EvaluateProceduralSkybox(vec3 viewRay) {
|
||||
float vertical = clamp(viewRay.y, -1.0, 1.0);
|
||||
vec3 color = gSkyboxHorizonColor.rgb;
|
||||
if (vertical >= 0.0) {
|
||||
color = mix(gSkyboxHorizonColor.rgb, gSkyboxTopColor.rgb, pow(clamp(vertical, 0.0, 1.0), 0.65));
|
||||
} else {
|
||||
color = mix(gSkyboxHorizonColor.rgb, gSkyboxBottomColor.rgb, pow(clamp(-vertical, 0.0, 1.0), 0.55));
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
vec2 ComputePanoramicUv(vec3 viewRay) {
|
||||
float rotation = gSkyboxRotationAndMode.x;
|
||||
float sinTheta = sin(rotation);
|
||||
float cosTheta = cos(rotation);
|
||||
vec3 rotatedRay = normalize(vec3(
|
||||
viewRay.x * cosTheta - viewRay.z * sinTheta,
|
||||
viewRay.y,
|
||||
viewRay.x * sinTheta + viewRay.z * cosTheta));
|
||||
|
||||
float u = fract(atan(rotatedRay.z, rotatedRay.x) * XC_INV_TWO_PI + 0.5);
|
||||
float v = acos(clamp(rotatedRay.y, -1.0, 1.0)) * XC_INV_PI;
|
||||
return vec2(u, clamp(v, 0.0, 1.0));
|
||||
}
|
||||
|
||||
void main() {
|
||||
float tanHalfFov = gCameraRightAndTanHalfFov.w;
|
||||
float aspect = gCameraUpAndAspect.w;
|
||||
@@ -23,12 +61,12 @@ void main() {
|
||||
vNdc.x * aspect * tanHalfFov * gCameraRightAndTanHalfFov.xyz +
|
||||
vNdc.y * tanHalfFov * gCameraUpAndAspect.xyz);
|
||||
|
||||
float vertical = clamp(viewRay.y, -1.0, 1.0);
|
||||
vec3 color = gSkyboxHorizonColor.rgb;
|
||||
if (vertical >= 0.0) {
|
||||
color = mix(gSkyboxHorizonColor.rgb, gSkyboxTopColor.rgb, pow(clamp(vertical, 0.0, 1.0), 0.65));
|
||||
} else {
|
||||
color = mix(gSkyboxHorizonColor.rgb, gSkyboxBottomColor.rgb, pow(clamp(-vertical, 0.0, 1.0), 0.55));
|
||||
vec3 color = EvaluateProceduralSkybox(viewRay);
|
||||
if (gSkyboxRotationAndMode.y > 0.5) {
|
||||
vec2 uv = ComputePanoramicUv(viewRay);
|
||||
color = texture(sampler2D(uSkyboxTexture, uLinearSampler), uv).rgb *
|
||||
gSkyboxTintAndExposure.rgb *
|
||||
gSkyboxTintAndExposure.w;
|
||||
}
|
||||
|
||||
fragColor = vec4(color, 1.0);
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
// XC_BUILTIN_SKYBOX_D3D12_PS
|
||||
Texture2D gSkyboxTexture : register(t0);
|
||||
SamplerState gLinearSampler : register(s0);
|
||||
|
||||
cbuffer EnvironmentConstants : register(b0) {
|
||||
float4 gSkyboxTopColor;
|
||||
float4 gSkyboxHorizonColor;
|
||||
@@ -8,11 +11,46 @@ cbuffer EnvironmentConstants : register(b0) {
|
||||
float4 gCameraForwardAndUnused;
|
||||
}
|
||||
|
||||
cbuffer MaterialConstants : register(b1) {
|
||||
float4 gSkyboxTintAndExposure;
|
||||
float4 gSkyboxRotationAndMode;
|
||||
}
|
||||
|
||||
struct PSInput {
|
||||
float4 position : SV_POSITION;
|
||||
float2 ndc : TEXCOORD0;
|
||||
};
|
||||
|
||||
static const float XC_PI = 3.14159265358979323846f;
|
||||
static const float XC_INV_PI = 0.31830988618379067154f;
|
||||
static const float XC_INV_TWO_PI = 0.15915494309189533577f;
|
||||
|
||||
float3 EvaluateProceduralSkybox(float3 viewRay) {
|
||||
const float vertical = clamp(viewRay.y, -1.0f, 1.0f);
|
||||
float3 color = gSkyboxHorizonColor.rgb;
|
||||
if (vertical >= 0.0f) {
|
||||
color = lerp(gSkyboxHorizonColor.rgb, gSkyboxTopColor.rgb, pow(saturate(vertical), 0.65f));
|
||||
} else {
|
||||
color = lerp(gSkyboxHorizonColor.rgb, gSkyboxBottomColor.rgb, pow(saturate(-vertical), 0.55f));
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
float2 ComputePanoramicUv(float3 viewRay) {
|
||||
const float rotation = gSkyboxRotationAndMode.x;
|
||||
const float sinTheta = sin(rotation);
|
||||
const float cosTheta = cos(rotation);
|
||||
const float3 rotatedRay = normalize(float3(
|
||||
viewRay.x * cosTheta - viewRay.z * sinTheta,
|
||||
viewRay.y,
|
||||
viewRay.x * sinTheta + viewRay.z * cosTheta));
|
||||
|
||||
const float u = frac(atan2(rotatedRay.z, rotatedRay.x) * XC_INV_TWO_PI + 0.5f);
|
||||
const float v = acos(clamp(rotatedRay.y, -1.0f, 1.0f)) * XC_INV_PI;
|
||||
return float2(u, saturate(v));
|
||||
}
|
||||
|
||||
float4 MainPS(PSInput input) : SV_Target {
|
||||
const float tanHalfFov = gCameraRightAndTanHalfFov.w;
|
||||
const float aspect = gCameraUpAndAspect.w;
|
||||
@@ -22,12 +60,12 @@ float4 MainPS(PSInput input) : SV_Target {
|
||||
input.ndc.x * aspect * tanHalfFov * gCameraRightAndTanHalfFov.xyz +
|
||||
input.ndc.y * tanHalfFov * gCameraUpAndAspect.xyz);
|
||||
|
||||
const float vertical = clamp(viewRay.y, -1.0f, 1.0f);
|
||||
float3 color = gSkyboxHorizonColor.rgb;
|
||||
if (vertical >= 0.0f) {
|
||||
color = lerp(gSkyboxHorizonColor.rgb, gSkyboxTopColor.rgb, pow(saturate(vertical), 0.65f));
|
||||
} else {
|
||||
color = lerp(gSkyboxHorizonColor.rgb, gSkyboxBottomColor.rgb, pow(saturate(-vertical), 0.55f));
|
||||
float3 color = EvaluateProceduralSkybox(viewRay);
|
||||
if (gSkyboxRotationAndMode.y > 0.5f) {
|
||||
const float2 uv = ComputePanoramicUv(viewRay);
|
||||
color = gSkyboxTexture.Sample(gLinearSampler, uv).rgb *
|
||||
gSkyboxTintAndExposure.rgb *
|
||||
gSkyboxTintAndExposure.w;
|
||||
}
|
||||
|
||||
return float4(color, 1.0f);
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
Shader "Builtin Skybox"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_Tint ("Tint", Color) = (1,1,1,1) [Semantic(Tint)]
|
||||
_Exposure ("Exposure", Float) = 1.0 [Semantic(Exposure)]
|
||||
_Rotation ("Rotation", Float) = 0.0 [Semantic(Rotation)]
|
||||
_MainTex ("Panoramic", 2D) = "white" [Semantic(SkyboxTexture)]
|
||||
}
|
||||
SubShader
|
||||
{
|
||||
Pass
|
||||
@@ -9,6 +16,9 @@ Shader "Builtin Skybox"
|
||||
Resources
|
||||
{
|
||||
EnvironmentConstants (ConstantBuffer, 0, 0) [Semantic(Environment)]
|
||||
MaterialConstants (ConstantBuffer, 1, 0) [Semantic(Material)]
|
||||
SkyboxTexture (Texture2D, 2, 0) [Semantic(SkyboxTexture)]
|
||||
LinearClampSampler (Sampler, 3, 0) [Semantic(LinearClampSampler)]
|
||||
}
|
||||
HLSLPROGRAM
|
||||
#pragma vertex MainVS
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Components/Component.h>
|
||||
#include <XCEngine/Core/Asset/AssetRef.h>
|
||||
#include <XCEngine/Core/Asset/ResourceHandle.h>
|
||||
#include <XCEngine/Core/Math/Color.h>
|
||||
#include <XCEngine/Core/Math/Rect.h>
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Components {
|
||||
@@ -67,6 +72,14 @@ public:
|
||||
bool IsSkyboxEnabled() const { return m_skyboxEnabled; }
|
||||
void SetSkyboxEnabled(bool value) { m_skyboxEnabled = value; }
|
||||
|
||||
const Resources::ResourceHandle<Resources::Material>& GetSkyboxMaterialHandle() const { return m_skyboxMaterial; }
|
||||
Resources::Material* GetSkyboxMaterial() const { return m_skyboxMaterial.Get(); }
|
||||
const std::string& GetSkyboxMaterialPath() const { return m_skyboxMaterialPath; }
|
||||
const Resources::AssetRef& GetSkyboxMaterialAssetRef() const { return m_skyboxMaterialRef; }
|
||||
void SetSkyboxMaterialPath(const std::string& materialPath);
|
||||
void SetSkyboxMaterial(const Resources::ResourceHandle<Resources::Material>& material);
|
||||
void SetSkyboxMaterial(Resources::Material* material);
|
||||
|
||||
const Math::Color& GetSkyboxTopColor() const { return m_skyboxTopColor; }
|
||||
void SetSkyboxTopColor(const Math::Color& value) { m_skyboxTopColor = value; }
|
||||
|
||||
@@ -93,6 +106,9 @@ private:
|
||||
Math::Rect m_viewportRect = Math::Rect(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
Math::Color m_clearColor = Math::Color(0.192f, 0.302f, 0.475f, 1.0f);
|
||||
bool m_skyboxEnabled = false;
|
||||
Resources::ResourceHandle<Resources::Material> m_skyboxMaterial;
|
||||
std::string m_skyboxMaterialPath;
|
||||
Resources::AssetRef m_skyboxMaterialRef;
|
||||
Math::Color m_skyboxTopColor = Math::Color(0.18f, 0.36f, 0.74f, 1.0f);
|
||||
Math::Color m_skyboxHorizonColor = Math::Color(0.78f, 0.84f, 0.92f, 1.0f);
|
||||
Math::Color m_skyboxBottomColor = Math::Color(0.92f, 0.93f, 0.95f, 1.0f);
|
||||
|
||||
@@ -4,12 +4,19 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
class Material;
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
enum class RenderEnvironmentMode : uint32_t {
|
||||
None = 0,
|
||||
ProceduralSkybox
|
||||
ProceduralSkybox,
|
||||
MaterialSkybox
|
||||
};
|
||||
|
||||
struct ProceduralSkyboxData {
|
||||
@@ -18,13 +25,30 @@ struct ProceduralSkyboxData {
|
||||
Math::Color bottomColor = Math::Color(0.92f, 0.93f, 0.95f, 1.0f);
|
||||
};
|
||||
|
||||
struct MaterialSkyboxData {
|
||||
const Resources::Material* material = nullptr;
|
||||
|
||||
bool IsValid() const {
|
||||
return material != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderEnvironmentData {
|
||||
RenderEnvironmentMode mode = RenderEnvironmentMode::None;
|
||||
ProceduralSkyboxData skybox = {};
|
||||
MaterialSkyboxData materialSkybox = {};
|
||||
|
||||
bool HasProceduralSkybox() const {
|
||||
return mode == RenderEnvironmentMode::ProceduralSkybox;
|
||||
}
|
||||
|
||||
bool HasMaterialSkybox() const {
|
||||
return mode == RenderEnvironmentMode::MaterialSkybox && materialSkybox.IsValid();
|
||||
}
|
||||
|
||||
bool HasSkybox() const {
|
||||
return HasProceduralSkybox() || HasMaterialSkybox();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
|
||||
@@ -16,6 +16,12 @@ struct BuiltinForwardMaterialData {
|
||||
Math::Vector4 baseColorFactor = Math::Vector4::One();
|
||||
};
|
||||
|
||||
struct BuiltinSkyboxMaterialData {
|
||||
Math::Vector4 tint = Math::Vector4::One();
|
||||
float exposure = 1.0f;
|
||||
float rotationDegrees = 0.0f;
|
||||
};
|
||||
|
||||
struct MaterialConstantLayoutView {
|
||||
const Resources::MaterialConstantFieldDesc* fields = nullptr;
|
||||
size_t count = 0;
|
||||
@@ -134,6 +140,129 @@ inline BuiltinForwardMaterialData BuildBuiltinForwardMaterialData(const Resource
|
||||
return data;
|
||||
}
|
||||
|
||||
inline const Resources::Texture* ResolveSkyboxTexture(const Resources::Material* material) {
|
||||
if (material == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "SkyboxTexture")) {
|
||||
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
|
||||
if (textureHandle.Get() != nullptr && textureHandle->IsValid()) {
|
||||
return textureHandle.Get();
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kSkyboxTexturePropertyNames[] = {
|
||||
"_MainTex",
|
||||
"_PanoramicTex",
|
||||
"_SkyboxTexture",
|
||||
"panoramicTexture",
|
||||
"skyboxTexture",
|
||||
"texture"
|
||||
};
|
||||
|
||||
for (const char* propertyName : kSkyboxTexturePropertyNames) {
|
||||
const Resources::ResourceHandle<Resources::Texture> textureHandle =
|
||||
material->GetTexture(Containers::String(propertyName));
|
||||
if (textureHandle.Get() != nullptr && textureHandle->IsValid()) {
|
||||
return textureHandle.Get();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline Math::Vector4 ResolveSkyboxTint(const Resources::Material* material) {
|
||||
if (material == nullptr) {
|
||||
return Math::Vector4::One();
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "Tint")) {
|
||||
if (material->HasProperty(property->name) &&
|
||||
(property->type == Resources::ShaderPropertyType::Color ||
|
||||
property->type == Resources::ShaderPropertyType::Vector)) {
|
||||
return material->GetFloat4(property->name);
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kTintPropertyNames[] = {
|
||||
"_Tint",
|
||||
"tint",
|
||||
"_Color",
|
||||
"color"
|
||||
};
|
||||
|
||||
for (const char* propertyName : kTintPropertyNames) {
|
||||
if (material->HasProperty(Containers::String(propertyName))) {
|
||||
return material->GetFloat4(Containers::String(propertyName));
|
||||
}
|
||||
}
|
||||
|
||||
return Math::Vector4::One();
|
||||
}
|
||||
|
||||
inline float ResolveSkyboxExposure(const Resources::Material* material) {
|
||||
if (material == nullptr) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "Exposure")) {
|
||||
if (material->HasProperty(property->name) &&
|
||||
(property->type == Resources::ShaderPropertyType::Float ||
|
||||
property->type == Resources::ShaderPropertyType::Range)) {
|
||||
return material->GetFloat(property->name);
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kExposurePropertyNames[] = {
|
||||
"_Exposure",
|
||||
"exposure"
|
||||
};
|
||||
|
||||
for (const char* propertyName : kExposurePropertyNames) {
|
||||
if (material->HasProperty(Containers::String(propertyName))) {
|
||||
return material->GetFloat(Containers::String(propertyName));
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
inline float ResolveSkyboxRotationDegrees(const Resources::Material* material) {
|
||||
if (material == nullptr) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "Rotation")) {
|
||||
if (material->HasProperty(property->name) &&
|
||||
(property->type == Resources::ShaderPropertyType::Float ||
|
||||
property->type == Resources::ShaderPropertyType::Range)) {
|
||||
return material->GetFloat(property->name);
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kRotationPropertyNames[] = {
|
||||
"_Rotation",
|
||||
"rotation"
|
||||
};
|
||||
|
||||
for (const char* propertyName : kRotationPropertyNames) {
|
||||
if (material->HasProperty(Containers::String(propertyName))) {
|
||||
return material->GetFloat(Containers::String(propertyName));
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
inline BuiltinSkyboxMaterialData BuildBuiltinSkyboxMaterialData(const Resources::Material* material) {
|
||||
BuiltinSkyboxMaterialData data = {};
|
||||
data.tint = ResolveSkyboxTint(material);
|
||||
data.exposure = ResolveSkyboxExposure(material);
|
||||
data.rotationDegrees = ResolveSkyboxRotationDegrees(material);
|
||||
return data;
|
||||
}
|
||||
|
||||
inline MaterialConstantPayloadView ResolveSchemaMaterialConstantPayload(const Resources::Material* material) {
|
||||
if (material == nullptr || material->GetShader() == nullptr) {
|
||||
return {};
|
||||
|
||||
@@ -109,6 +109,11 @@ private:
|
||||
Math::Vector4 cameraForwardAndUnused = Math::Vector4::Zero();
|
||||
};
|
||||
|
||||
struct SkyboxMaterialConstants {
|
||||
Math::Vector4 tintAndExposure = Math::Vector4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
Math::Vector4 rotationRadiansAndMode = Math::Vector4::Zero();
|
||||
};
|
||||
|
||||
struct PassLayoutKey {
|
||||
const Resources::Shader* shader = nullptr;
|
||||
Containers::String passName;
|
||||
@@ -243,9 +248,11 @@ private:
|
||||
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
|
||||
|
||||
const Resources::Texture* ResolveTexture(const Resources::Material* material) const;
|
||||
RHI::RHIResourceView* ResolveTextureView(const Resources::Texture* texture);
|
||||
RHI::RHIResourceView* ResolveTextureView(const VisibleRenderItem& visibleItem);
|
||||
static LightingConstants BuildLightingConstants(const RenderLightingData& lightingData);
|
||||
static AdditionalLightConstants BuildAdditionalLightConstants(const RenderAdditionalLightData& lightData);
|
||||
bool HasSkybox(const RenderSceneData& sceneData) const;
|
||||
bool HasProceduralSkybox(const RenderSceneData& sceneData) const;
|
||||
bool BeginForwardScenePass(const RenderPassContext& context);
|
||||
void EndForwardScenePass(const RenderPassContext& context);
|
||||
@@ -282,8 +289,11 @@ private:
|
||||
RHI::RHIResourceView* m_fallbackTextureView = nullptr;
|
||||
RHI::RHIPipelineLayout* m_skyboxPipelineLayout = nullptr;
|
||||
RHI::RHIPipelineState* m_skyboxPipelineState = nullptr;
|
||||
RHI::RHIDescriptorPool* m_skyboxConstantPool = nullptr;
|
||||
RHI::RHIDescriptorSet* m_skyboxConstantSet = nullptr;
|
||||
OwnedDescriptorSet m_skyboxEnvironmentSet = {};
|
||||
OwnedDescriptorSet m_skyboxMaterialSet = {};
|
||||
OwnedDescriptorSet m_skyboxTextureSet = {};
|
||||
OwnedDescriptorSet m_skyboxSamplerSet = {};
|
||||
RHI::RHIResourceView* m_skyboxBoundTextureView = nullptr;
|
||||
RenderPassSequence m_passSequence;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,49 @@
|
||||
#include "Components/CameraComponent.h"
|
||||
|
||||
#include "Core/Asset/ResourceManager.h"
|
||||
#include "Resources/Material/Material.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Components {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string ToStdString(const Containers::String& value) {
|
||||
return std::string(value.CStr());
|
||||
}
|
||||
|
||||
bool HasVirtualPathScheme(const std::string& path) {
|
||||
return path.find("://") != std::string::npos;
|
||||
}
|
||||
|
||||
std::string EncodeAssetRef(const Resources::AssetRef& assetRef) {
|
||||
if (!assetRef.IsValid()) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
return ToStdString(assetRef.assetGuid.ToString()) + "," +
|
||||
std::to_string(assetRef.localID) + "," +
|
||||
std::to_string(static_cast<int>(assetRef.resourceType));
|
||||
}
|
||||
|
||||
bool TryDecodeAssetRef(const std::string& value, Resources::AssetRef& outRef) {
|
||||
const size_t firstComma = value.find(',');
|
||||
const size_t secondComma = firstComma == std::string::npos ? std::string::npos : value.find(',', firstComma + 1);
|
||||
if (firstComma == std::string::npos || secondComma == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outRef.assetGuid = Resources::AssetGUID::ParseOrDefault(Containers::String(value.substr(0, firstComma).c_str()));
|
||||
outRef.localID = static_cast<Resources::LocalID>(std::stoull(value.substr(firstComma + 1, secondComma - firstComma - 1)));
|
||||
outRef.resourceType = static_cast<Resources::ResourceType>(std::stoi(value.substr(secondComma + 1)));
|
||||
return outRef.IsValid();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void CameraComponent::SetFieldOfView(float value) {
|
||||
m_fieldOfView = std::clamp(value, 1.0f, 179.0f);
|
||||
}
|
||||
@@ -35,7 +73,64 @@ void CameraComponent::SetViewportRect(const Math::Rect& value) {
|
||||
m_viewportRect = Math::Rect(x, y, right - x, bottom - y);
|
||||
}
|
||||
|
||||
void CameraComponent::SetSkyboxMaterialPath(const std::string& materialPath) {
|
||||
m_skyboxMaterialPath = materialPath;
|
||||
m_skyboxMaterialRef.Reset();
|
||||
|
||||
if (materialPath.empty()) {
|
||||
m_skyboxMaterial.Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
m_skyboxMaterial = Resources::ResourceManager::Get().Load<Resources::Material>(materialPath.c_str());
|
||||
if (!Resources::ResourceManager::Get().TryGetAssetRef(
|
||||
materialPath.c_str(),
|
||||
Resources::ResourceType::Material,
|
||||
m_skyboxMaterialRef)) {
|
||||
m_skyboxMaterialRef.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void CameraComponent::SetSkyboxMaterial(const Resources::ResourceHandle<Resources::Material>& material) {
|
||||
m_skyboxMaterial = material;
|
||||
m_skyboxMaterialPath =
|
||||
material.Get() != nullptr
|
||||
? ToStdString(material->GetPath())
|
||||
: std::string();
|
||||
|
||||
if (m_skyboxMaterialPath.empty() ||
|
||||
!Resources::ResourceManager::Get().TryGetAssetRef(
|
||||
m_skyboxMaterialPath.c_str(),
|
||||
Resources::ResourceType::Material,
|
||||
m_skyboxMaterialRef)) {
|
||||
m_skyboxMaterialRef.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void CameraComponent::SetSkyboxMaterial(Resources::Material* material) {
|
||||
SetSkyboxMaterial(Resources::ResourceHandle<Resources::Material>(material));
|
||||
}
|
||||
|
||||
void CameraComponent::Serialize(std::ostream& os) const {
|
||||
Resources::AssetRef serializedSkyboxMaterialRef = m_skyboxMaterialRef;
|
||||
std::string serializedSkyboxMaterialPath = m_skyboxMaterialPath;
|
||||
if (serializedSkyboxMaterialPath.empty() && m_skyboxMaterial.Get() != nullptr) {
|
||||
serializedSkyboxMaterialPath = ToStdString(m_skyboxMaterial->GetPath());
|
||||
}
|
||||
|
||||
if (!serializedSkyboxMaterialRef.IsValid() &&
|
||||
!serializedSkyboxMaterialPath.empty() &&
|
||||
!HasVirtualPathScheme(serializedSkyboxMaterialPath) &&
|
||||
Resources::ResourceManager::Get().TryGetAssetRef(
|
||||
serializedSkyboxMaterialPath.c_str(),
|
||||
Resources::ResourceType::Material,
|
||||
serializedSkyboxMaterialRef)) {
|
||||
}
|
||||
|
||||
if (serializedSkyboxMaterialRef.IsValid() || !HasVirtualPathScheme(serializedSkyboxMaterialPath)) {
|
||||
serializedSkyboxMaterialPath.clear();
|
||||
}
|
||||
|
||||
os << "projection=" << static_cast<int>(m_projectionType) << ";";
|
||||
os << "fov=" << m_fieldOfView << ";";
|
||||
os << "orthoSize=" << m_orthographicSize << ";";
|
||||
@@ -49,13 +144,21 @@ void CameraComponent::Serialize(std::ostream& os) const {
|
||||
os << "viewportRect=" << m_viewportRect.x << "," << m_viewportRect.y << "," << m_viewportRect.width << "," << m_viewportRect.height << ";";
|
||||
os << "clearColor=" << m_clearColor.r << "," << m_clearColor.g << "," << m_clearColor.b << "," << m_clearColor.a << ";";
|
||||
os << "skyboxEnabled=" << (m_skyboxEnabled ? 1 : 0) << ";";
|
||||
os << "skyboxMaterialPath=" << serializedSkyboxMaterialPath << ";";
|
||||
os << "skyboxMaterialRef=" << EncodeAssetRef(serializedSkyboxMaterialRef) << ";";
|
||||
os << "skyboxTopColor=" << m_skyboxTopColor.r << "," << m_skyboxTopColor.g << "," << m_skyboxTopColor.b << "," << m_skyboxTopColor.a << ";";
|
||||
os << "skyboxHorizonColor=" << m_skyboxHorizonColor.r << "," << m_skyboxHorizonColor.g << "," << m_skyboxHorizonColor.b << "," << m_skyboxHorizonColor.a << ";";
|
||||
os << "skyboxBottomColor=" << m_skyboxBottomColor.r << "," << m_skyboxBottomColor.g << "," << m_skyboxBottomColor.b << "," << m_skyboxBottomColor.a << ";";
|
||||
}
|
||||
|
||||
void CameraComponent::Deserialize(std::istream& is) {
|
||||
m_skyboxMaterial.Reset();
|
||||
m_skyboxMaterialPath.clear();
|
||||
m_skyboxMaterialRef.Reset();
|
||||
|
||||
std::string token;
|
||||
std::string pendingSkyboxMaterialPath;
|
||||
Resources::AssetRef pendingSkyboxMaterialRef;
|
||||
while (std::getline(is, token, ';')) {
|
||||
if (token.empty()) {
|
||||
continue;
|
||||
@@ -101,6 +204,10 @@ void CameraComponent::Deserialize(std::istream& is) {
|
||||
ss >> m_clearColor.r >> m_clearColor.g >> m_clearColor.b >> m_clearColor.a;
|
||||
} else if (key == "skyboxEnabled") {
|
||||
m_skyboxEnabled = (std::stoi(value) != 0);
|
||||
} else if (key == "skyboxMaterialPath") {
|
||||
pendingSkyboxMaterialPath = value;
|
||||
} else if (key == "skyboxMaterialRef") {
|
||||
TryDecodeAssetRef(value, pendingSkyboxMaterialRef);
|
||||
} else if (key == "skyboxTopColor") {
|
||||
std::replace(value.begin(), value.end(), ',', ' ');
|
||||
std::istringstream ss(value);
|
||||
@@ -115,6 +222,28 @@ void CameraComponent::Deserialize(std::istream& is) {
|
||||
ss >> m_skyboxBottomColor.r >> m_skyboxBottomColor.g >> m_skyboxBottomColor.b >> m_skyboxBottomColor.a;
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingSkyboxMaterialRef.IsValid()) {
|
||||
m_skyboxMaterialRef = pendingSkyboxMaterialRef;
|
||||
m_skyboxMaterial = Resources::ResourceManager::Get().Load<Resources::Material>(pendingSkyboxMaterialRef);
|
||||
if (m_skyboxMaterial.Get() != nullptr) {
|
||||
m_skyboxMaterialPath = ToStdString(m_skyboxMaterial->GetPath());
|
||||
} else {
|
||||
Containers::String resolvedPath;
|
||||
if (Resources::ResourceManager::Get().TryResolveAssetPath(pendingSkyboxMaterialRef, resolvedPath)) {
|
||||
SetSkyboxMaterialPath(ToStdString(resolvedPath));
|
||||
m_skyboxMaterialRef = pendingSkyboxMaterialRef;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_skyboxMaterial.Get() == nullptr && !pendingSkyboxMaterialPath.empty()) {
|
||||
SetSkyboxMaterialPath(pendingSkyboxMaterialPath);
|
||||
}
|
||||
|
||||
if (m_skyboxMaterial.Get() == nullptr && pendingSkyboxMaterialRef.IsValid()) {
|
||||
m_skyboxMaterialRef = pendingSkyboxMaterialRef;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Components
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <XCEngine/RHI/RHIEnums.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
@@ -9,10 +11,22 @@ namespace {
|
||||
|
||||
RHI::Format ToRHITextureFormat(Resources::TextureFormat format) {
|
||||
switch (format) {
|
||||
case Resources::TextureFormat::R8_UNORM:
|
||||
return RHI::Format::R8_UNorm;
|
||||
case Resources::TextureFormat::RG8_UNORM:
|
||||
return RHI::Format::R8G8_UNorm;
|
||||
case Resources::TextureFormat::RGBA8_UNORM:
|
||||
return RHI::Format::R8G8B8A8_UNorm;
|
||||
case Resources::TextureFormat::RGBA8_SRGB:
|
||||
return RHI::Format::R8G8B8A8_SRGB;
|
||||
case Resources::TextureFormat::R16_FLOAT:
|
||||
return RHI::Format::R16_Float;
|
||||
case Resources::TextureFormat::R32_FLOAT:
|
||||
return RHI::Format::R32_Float;
|
||||
case Resources::TextureFormat::RGBA16_FLOAT:
|
||||
return RHI::Format::R16G16B16A16_Float;
|
||||
case Resources::TextureFormat::RGBA32_FLOAT:
|
||||
return RHI::Format::R32G32B32A32_Float;
|
||||
default:
|
||||
return RHI::Format::Unknown;
|
||||
}
|
||||
@@ -34,6 +48,43 @@ RHI::TextureType ToRHITextureType(Resources::TextureType textureType) {
|
||||
}
|
||||
}
|
||||
|
||||
RHI::ResourceViewDimension ToRHITextureViewDimension(Resources::TextureType textureType) {
|
||||
switch (textureType) {
|
||||
case Resources::TextureType::Texture2DArray:
|
||||
return RHI::ResourceViewDimension::Texture2DArray;
|
||||
case Resources::TextureType::Texture3D:
|
||||
return RHI::ResourceViewDimension::Texture3D;
|
||||
case Resources::TextureType::TextureCube:
|
||||
return RHI::ResourceViewDimension::TextureCube;
|
||||
case Resources::TextureType::TextureCubeArray:
|
||||
return RHI::ResourceViewDimension::TextureCubeArray;
|
||||
case Resources::TextureType::Texture2D:
|
||||
default:
|
||||
return RHI::ResourceViewDimension::Texture2D;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GetTextureBytesPerPixel(Resources::TextureFormat format) {
|
||||
switch (format) {
|
||||
case Resources::TextureFormat::R8_UNORM:
|
||||
return 1;
|
||||
case Resources::TextureFormat::RG8_UNORM:
|
||||
return 2;
|
||||
case Resources::TextureFormat::RGBA8_UNORM:
|
||||
case Resources::TextureFormat::RGBA8_SRGB:
|
||||
case Resources::TextureFormat::R32_FLOAT:
|
||||
return 4;
|
||||
case Resources::TextureFormat::R16_FLOAT:
|
||||
return 2;
|
||||
case Resources::TextureFormat::RGBA16_FLOAT:
|
||||
return 8;
|
||||
case Resources::TextureFormat::RGBA32_FLOAT:
|
||||
return 16;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ShutdownMesh(RenderResourceCache::CachedMesh& cachedMesh) {
|
||||
if (cachedMesh.vertexBufferView != nullptr) {
|
||||
cachedMesh.vertexBufferView->Shutdown();
|
||||
@@ -216,14 +267,19 @@ bool RenderResourceCache::UploadTexture(
|
||||
textureDesc.height = texture->GetHeight();
|
||||
textureDesc.depth = texture->GetDepth();
|
||||
textureDesc.mipLevels = texture->GetMipLevels();
|
||||
textureDesc.arraySize = 1;
|
||||
textureDesc.arraySize = std::max<uint32_t>(texture->GetArraySize(), 1u);
|
||||
textureDesc.format = static_cast<uint32_t>(format);
|
||||
textureDesc.textureType = static_cast<uint32_t>(ToRHITextureType(texture->GetTextureType()));
|
||||
textureDesc.sampleCount = 1;
|
||||
textureDesc.sampleQuality = 0;
|
||||
textureDesc.flags = 0;
|
||||
|
||||
const uint32_t rowPitch = texture->GetWidth() * 4;
|
||||
const uint32_t bytesPerPixel = GetTextureBytesPerPixel(texture->GetFormat());
|
||||
if (bytesPerPixel == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t rowPitch = texture->GetWidth() * bytesPerPixel;
|
||||
cachedTexture.texture = device->CreateTexture(
|
||||
textureDesc,
|
||||
texture->GetPixelData(),
|
||||
@@ -235,7 +291,7 @@ bool RenderResourceCache::UploadTexture(
|
||||
|
||||
RHI::ResourceViewDesc textureViewDesc = {};
|
||||
textureViewDesc.format = static_cast<uint32_t>(format);
|
||||
textureViewDesc.dimension = RHI::ResourceViewDimension::Texture2D;
|
||||
textureViewDesc.dimension = ToRHITextureViewDimension(texture->GetTextureType());
|
||||
textureViewDesc.mipLevel = 0;
|
||||
cachedTexture.shaderResourceView = device->CreateShaderResourceView(cachedTexture.texture, textureViewDesc);
|
||||
if (cachedTexture.shaderResourceView == nullptr) {
|
||||
|
||||
@@ -252,6 +252,12 @@ RenderEnvironmentData BuildEnvironmentData(const CameraRenderRequest& request) {
|
||||
return environment;
|
||||
}
|
||||
|
||||
if (const Resources::Material* skyboxMaterial = request.camera->GetSkyboxMaterial()) {
|
||||
environment.mode = RenderEnvironmentMode::MaterialSkybox;
|
||||
environment.materialSkybox.material = skyboxMaterial;
|
||||
return environment;
|
||||
}
|
||||
|
||||
environment.mode = RenderEnvironmentMode::ProceduralSkybox;
|
||||
environment.skybox.topColor = request.camera->GetSkyboxTopColor();
|
||||
environment.skybox.horizonColor = request.camera->GetSkyboxHorizonColor();
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Rendering/RenderSurface.h"
|
||||
#include "Resources/BuiltinResources.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cmath>
|
||||
|
||||
@@ -234,6 +235,11 @@ bool BuiltinForwardPipeline::HasProceduralSkybox(const RenderSceneData& sceneDat
|
||||
sceneData.cameraData.perspectiveProjection;
|
||||
}
|
||||
|
||||
bool BuiltinForwardPipeline::HasSkybox(const RenderSceneData& sceneData) const {
|
||||
return sceneData.environment.HasSkybox() &&
|
||||
sceneData.cameraData.perspectiveProjection;
|
||||
}
|
||||
|
||||
bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& passContext) {
|
||||
const RenderContext& context = passContext.renderContext;
|
||||
const RenderSurface& surface = passContext.surface;
|
||||
@@ -290,7 +296,7 @@ bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& pass
|
||||
: sceneData.cameraData.clearColor;
|
||||
const float clearValues[4] = { clearColor.r, clearColor.g, clearColor.b, clearColor.a };
|
||||
if (HasRenderClearFlag(sceneData.cameraData.clearFlags, RenderClearFlags::Color) &&
|
||||
!HasProceduralSkybox(sceneData)) {
|
||||
!HasSkybox(sceneData)) {
|
||||
for (RHI::RHIResourceView* renderTarget : renderTargets) {
|
||||
if (renderTarget != nullptr) {
|
||||
commandList->ClearRenderTarget(renderTarget, clearValues, 1, clearRects);
|
||||
@@ -351,7 +357,7 @@ bool BuiltinForwardPipeline::ExecuteForwardSkyboxPass(const RenderPassContext& p
|
||||
const RenderSurface& surface = passContext.surface;
|
||||
const RenderSceneData& sceneData = passContext.sceneData;
|
||||
|
||||
if (!HasProceduralSkybox(sceneData)) {
|
||||
if (!HasSkybox(sceneData)) {
|
||||
return true;
|
||||
}
|
||||
if (surface.GetDepthAttachment() == nullptr) {
|
||||
@@ -361,7 +367,10 @@ bool BuiltinForwardPipeline::ExecuteForwardSkyboxPass(const RenderPassContext& p
|
||||
if (!EnsureSkyboxResources(context) ||
|
||||
m_skyboxPipelineLayout == nullptr ||
|
||||
m_skyboxPipelineState == nullptr ||
|
||||
m_skyboxConstantSet == nullptr) {
|
||||
m_skyboxEnvironmentSet.set == nullptr ||
|
||||
m_skyboxMaterialSet.set == nullptr ||
|
||||
m_skyboxTextureSet.set == nullptr ||
|
||||
m_skyboxSamplerSet.set == nullptr) {
|
||||
Debug::Logger::Get().Error(
|
||||
Debug::LogCategory::Rendering,
|
||||
"BuiltinForwardPipeline skybox pass failed to initialize runtime skybox resources");
|
||||
@@ -380,13 +389,46 @@ bool BuiltinForwardPipeline::ExecuteForwardSkyboxPass(const RenderPassContext& p
|
||||
sceneData.cameraData.aspectRatio);
|
||||
constants.cameraForwardAndUnused = Math::Vector4(sceneData.cameraData.worldForward, 0.0f);
|
||||
|
||||
m_skyboxConstantSet->WriteConstant(0, &constants, sizeof(constants));
|
||||
const Resources::Material* skyboxMaterial = sceneData.environment.HasMaterialSkybox()
|
||||
? sceneData.environment.materialSkybox.material
|
||||
: nullptr;
|
||||
const Resources::Texture* skyboxTexture = ResolveSkyboxTexture(skyboxMaterial);
|
||||
RHI::RHIResourceView* skyboxTextureView = ResolveTextureView(skyboxTexture);
|
||||
const bool useTexturedSkybox = sceneData.environment.HasMaterialSkybox() && skyboxTextureView != nullptr;
|
||||
if (skyboxTextureView == nullptr) {
|
||||
skyboxTextureView = m_fallbackTextureView;
|
||||
}
|
||||
|
||||
const BuiltinSkyboxMaterialData skyboxMaterialData = BuildBuiltinSkyboxMaterialData(skyboxMaterial);
|
||||
SkyboxMaterialConstants materialConstants = {};
|
||||
materialConstants.tintAndExposure = Math::Vector4(
|
||||
skyboxMaterialData.tint.x,
|
||||
skyboxMaterialData.tint.y,
|
||||
skyboxMaterialData.tint.z,
|
||||
std::max(0.0f, skyboxMaterialData.exposure));
|
||||
materialConstants.rotationRadiansAndMode = Math::Vector4(
|
||||
Math::Radians(skyboxMaterialData.rotationDegrees),
|
||||
useTexturedSkybox ? 1.0f : 0.0f,
|
||||
0.0f,
|
||||
0.0f);
|
||||
|
||||
m_skyboxEnvironmentSet.set->WriteConstant(0, &constants, sizeof(constants));
|
||||
m_skyboxMaterialSet.set->WriteConstant(0, &materialConstants, sizeof(materialConstants));
|
||||
if (m_skyboxBoundTextureView != skyboxTextureView) {
|
||||
m_skyboxTextureSet.set->Update(0, skyboxTextureView);
|
||||
m_skyboxBoundTextureView = skyboxTextureView;
|
||||
}
|
||||
|
||||
RHI::RHICommandList* commandList = context.commandList;
|
||||
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
|
||||
commandList->SetPipelineState(m_skyboxPipelineState);
|
||||
RHI::RHIDescriptorSet* descriptorSets[] = { m_skyboxConstantSet };
|
||||
commandList->SetGraphicsDescriptorSets(0, 1, descriptorSets, m_skyboxPipelineLayout);
|
||||
RHI::RHIDescriptorSet* descriptorSets[] = {
|
||||
m_skyboxEnvironmentSet.set,
|
||||
m_skyboxMaterialSet.set,
|
||||
m_skyboxTextureSet.set,
|
||||
m_skyboxSamplerSet.set
|
||||
};
|
||||
commandList->SetGraphicsDescriptorSets(0, 4, descriptorSets, m_skyboxPipelineLayout);
|
||||
commandList->Draw(3, 1, 0, 0);
|
||||
return true;
|
||||
}
|
||||
@@ -543,8 +585,10 @@ bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& contex
|
||||
bool BuiltinForwardPipeline::EnsureSkyboxResources(const RenderContext& context) {
|
||||
if (m_skyboxPipelineLayout != nullptr &&
|
||||
m_skyboxPipelineState != nullptr &&
|
||||
m_skyboxConstantPool != nullptr &&
|
||||
m_skyboxConstantSet != nullptr) {
|
||||
m_skyboxEnvironmentSet.set != nullptr &&
|
||||
m_skyboxMaterialSet.set != nullptr &&
|
||||
m_skyboxTextureSet.set != nullptr &&
|
||||
m_skyboxSamplerSet.set != nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -570,19 +614,52 @@ bool BuiltinForwardPipeline::CreateSkyboxResources(const RenderContext& context)
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::DescriptorSetLayoutBinding constantBinding = {};
|
||||
constantBinding.binding = 0;
|
||||
constantBinding.type = static_cast<uint32_t>(RHI::DescriptorType::CBV);
|
||||
constantBinding.count = 1;
|
||||
constantBinding.visibility = static_cast<uint32_t>(RHI::ShaderVisibility::All);
|
||||
RHI::DescriptorSetLayoutBinding environmentBinding = {};
|
||||
environmentBinding.binding = 0;
|
||||
environmentBinding.type = static_cast<uint32_t>(RHI::DescriptorType::CBV);
|
||||
environmentBinding.count = 1;
|
||||
environmentBinding.visibility = static_cast<uint32_t>(RHI::ShaderVisibility::All);
|
||||
|
||||
RHI::DescriptorSetLayoutDesc constantLayout = {};
|
||||
constantLayout.bindings = &constantBinding;
|
||||
constantLayout.bindingCount = 1;
|
||||
RHI::DescriptorSetLayoutBinding materialBinding = environmentBinding;
|
||||
|
||||
RHI::DescriptorSetLayoutBinding textureBinding = {};
|
||||
textureBinding.binding = 0;
|
||||
textureBinding.type = static_cast<uint32_t>(RHI::DescriptorType::SRV);
|
||||
textureBinding.count = 1;
|
||||
textureBinding.visibility = static_cast<uint32_t>(RHI::ShaderVisibility::All);
|
||||
|
||||
RHI::DescriptorSetLayoutBinding samplerBinding = {};
|
||||
samplerBinding.binding = 0;
|
||||
samplerBinding.type = static_cast<uint32_t>(RHI::DescriptorType::Sampler);
|
||||
samplerBinding.count = 1;
|
||||
samplerBinding.visibility = static_cast<uint32_t>(RHI::ShaderVisibility::All);
|
||||
|
||||
RHI::DescriptorSetLayoutDesc environmentLayout = {};
|
||||
environmentLayout.bindings = &environmentBinding;
|
||||
environmentLayout.bindingCount = 1;
|
||||
|
||||
RHI::DescriptorSetLayoutDesc materialLayout = {};
|
||||
materialLayout.bindings = &materialBinding;
|
||||
materialLayout.bindingCount = 1;
|
||||
|
||||
RHI::DescriptorSetLayoutDesc textureLayout = {};
|
||||
textureLayout.bindings = &textureBinding;
|
||||
textureLayout.bindingCount = 1;
|
||||
|
||||
RHI::DescriptorSetLayoutDesc samplerLayout = {};
|
||||
samplerLayout.bindings = &samplerBinding;
|
||||
samplerLayout.bindingCount = 1;
|
||||
|
||||
RHI::DescriptorSetLayoutDesc setLayouts[] = {
|
||||
environmentLayout,
|
||||
materialLayout,
|
||||
textureLayout,
|
||||
samplerLayout
|
||||
};
|
||||
|
||||
RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {};
|
||||
pipelineLayoutDesc.setLayouts = &constantLayout;
|
||||
pipelineLayoutDesc.setLayoutCount = 1;
|
||||
pipelineLayoutDesc.setLayouts = setLayouts;
|
||||
pipelineLayoutDesc.setLayoutCount = 4;
|
||||
m_skyboxPipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc);
|
||||
if (m_skyboxPipelineLayout == nullptr) {
|
||||
Debug::Logger::Get().Error(
|
||||
@@ -592,27 +669,54 @@ bool BuiltinForwardPipeline::CreateSkyboxResources(const RenderContext& context)
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::DescriptorPoolDesc constantPoolDesc = {};
|
||||
constantPoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV;
|
||||
constantPoolDesc.descriptorCount = 1;
|
||||
constantPoolDesc.shaderVisible = false;
|
||||
m_skyboxConstantPool = m_device->CreateDescriptorPool(constantPoolDesc);
|
||||
if (m_skyboxConstantPool == nullptr) {
|
||||
auto createSkyboxDescriptorSet =
|
||||
[this](const RHI::DescriptorSetLayoutDesc& layout,
|
||||
RHI::DescriptorHeapType heapType,
|
||||
bool shaderVisible,
|
||||
OwnedDescriptorSet& ownedSet) -> bool {
|
||||
RHI::DescriptorPoolDesc poolDesc = {};
|
||||
poolDesc.type = heapType;
|
||||
poolDesc.descriptorCount = 1;
|
||||
poolDesc.shaderVisible = shaderVisible;
|
||||
ownedSet.pool = m_device->CreateDescriptorPool(poolDesc);
|
||||
if (ownedSet.pool == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ownedSet.set = ownedSet.pool->AllocateSet(layout);
|
||||
return ownedSet.set != nullptr;
|
||||
};
|
||||
|
||||
if (!createSkyboxDescriptorSet(
|
||||
environmentLayout,
|
||||
RHI::DescriptorHeapType::CBV_SRV_UAV,
|
||||
false,
|
||||
m_skyboxEnvironmentSet) ||
|
||||
!createSkyboxDescriptorSet(
|
||||
materialLayout,
|
||||
RHI::DescriptorHeapType::CBV_SRV_UAV,
|
||||
false,
|
||||
m_skyboxMaterialSet) ||
|
||||
!createSkyboxDescriptorSet(
|
||||
textureLayout,
|
||||
RHI::DescriptorHeapType::CBV_SRV_UAV,
|
||||
true,
|
||||
m_skyboxTextureSet) ||
|
||||
!createSkyboxDescriptorSet(
|
||||
samplerLayout,
|
||||
RHI::DescriptorHeapType::Sampler,
|
||||
true,
|
||||
m_skyboxSamplerSet)) {
|
||||
Debug::Logger::Get().Error(
|
||||
Debug::LogCategory::Rendering,
|
||||
"BuiltinForwardPipeline failed to create skybox descriptor pool");
|
||||
"BuiltinForwardPipeline failed to allocate skybox descriptor sets");
|
||||
DestroySkyboxResources();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_skyboxConstantSet = m_skyboxConstantPool->AllocateSet(constantLayout);
|
||||
if (m_skyboxConstantSet == nullptr) {
|
||||
Debug::Logger::Get().Error(
|
||||
Debug::LogCategory::Rendering,
|
||||
"BuiltinForwardPipeline failed to allocate skybox descriptor set");
|
||||
DestroySkyboxResources();
|
||||
return false;
|
||||
}
|
||||
m_skyboxSamplerSet.set->UpdateSampler(0, m_sampler);
|
||||
m_skyboxTextureSet.set->Update(0, m_fallbackTextureView);
|
||||
m_skyboxBoundTextureView = m_fallbackTextureView;
|
||||
|
||||
m_skyboxPipelineState = m_device->CreatePipelineState(
|
||||
CreateSkyboxPipelineDesc(
|
||||
@@ -638,17 +742,11 @@ void BuiltinForwardPipeline::DestroySkyboxResources() {
|
||||
m_skyboxPipelineState = nullptr;
|
||||
}
|
||||
|
||||
if (m_skyboxConstantSet != nullptr) {
|
||||
m_skyboxConstantSet->Shutdown();
|
||||
delete m_skyboxConstantSet;
|
||||
m_skyboxConstantSet = nullptr;
|
||||
}
|
||||
|
||||
if (m_skyboxConstantPool != nullptr) {
|
||||
m_skyboxConstantPool->Shutdown();
|
||||
delete m_skyboxConstantPool;
|
||||
m_skyboxConstantPool = nullptr;
|
||||
}
|
||||
DestroyOwnedDescriptorSet(m_skyboxSamplerSet);
|
||||
DestroyOwnedDescriptorSet(m_skyboxTextureSet);
|
||||
DestroyOwnedDescriptorSet(m_skyboxMaterialSet);
|
||||
DestroyOwnedDescriptorSet(m_skyboxEnvironmentSet);
|
||||
m_skyboxBoundTextureView = nullptr;
|
||||
|
||||
if (m_skyboxPipelineLayout != nullptr) {
|
||||
m_skyboxPipelineLayout->Shutdown();
|
||||
|
||||
@@ -497,18 +497,26 @@ const Resources::Texture* BuiltinForwardPipeline::ResolveTexture(const Resources
|
||||
return ResolveBuiltinBaseColorTexture(material);
|
||||
}
|
||||
|
||||
RHI::RHIResourceView* BuiltinForwardPipeline::ResolveTextureView(
|
||||
const Resources::Texture* texture) {
|
||||
if (texture == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const RenderResourceCache::CachedTexture* cachedTexture = m_resourceCache.GetOrCreateTexture(m_device, texture);
|
||||
if (cachedTexture != nullptr && cachedTexture->shaderResourceView != nullptr) {
|
||||
return cachedTexture->shaderResourceView;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RHI::RHIResourceView* BuiltinForwardPipeline::ResolveTextureView(
|
||||
const VisibleRenderItem& visibleItem) {
|
||||
const Resources::Material* material = ResolveMaterial(visibleItem);
|
||||
const Resources::Texture* texture = ResolveTexture(material);
|
||||
if (texture != nullptr) {
|
||||
const RenderResourceCache::CachedTexture* cachedTexture = m_resourceCache.GetOrCreateTexture(m_device, texture);
|
||||
if (cachedTexture != nullptr && cachedTexture->shaderResourceView != nullptr) {
|
||||
return cachedTexture->shaderResourceView;
|
||||
}
|
||||
}
|
||||
|
||||
return m_fallbackTextureView;
|
||||
RHI::RHIResourceView* textureView = ResolveTextureView(texture);
|
||||
return textureView != nullptr ? textureView : m_fallbackTextureView;
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::LightingConstants BuiltinForwardPipeline::BuildLightingConstants(
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <XCEngine/Components/CameraComponent.h>
|
||||
#include <XCEngine/Components/LightComponent.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@@ -26,6 +27,8 @@ TEST(CameraComponent_Test, DefaultValues) {
|
||||
EXPECT_FLOAT_EQ(camera.GetViewportRect().width, 1.0f);
|
||||
EXPECT_FLOAT_EQ(camera.GetViewportRect().height, 1.0f);
|
||||
EXPECT_FALSE(camera.IsSkyboxEnabled());
|
||||
EXPECT_TRUE(camera.GetSkyboxMaterialPath().empty());
|
||||
EXPECT_FALSE(camera.GetSkyboxMaterialAssetRef().IsValid());
|
||||
EXPECT_FLOAT_EQ(camera.GetSkyboxTopColor().r, 0.18f);
|
||||
EXPECT_FLOAT_EQ(camera.GetSkyboxHorizonColor().g, 0.84f);
|
||||
EXPECT_FLOAT_EQ(camera.GetSkyboxBottomColor().b, 0.95f);
|
||||
@@ -62,6 +65,7 @@ TEST(CameraComponent_Test, SerializeRoundTripPreservesViewportAndClearState) {
|
||||
source.SetCullingMask(0x0000000Fu);
|
||||
source.SetViewportRect(XCEngine::Math::Rect(0.25f, 0.125f, 0.5f, 0.625f));
|
||||
source.SetSkyboxEnabled(true);
|
||||
source.SetSkyboxMaterialPath(XCEngine::Resources::GetBuiltinDefaultPrimitiveMaterialPath().CStr());
|
||||
source.SetSkyboxTopColor(XCEngine::Math::Color(0.12f, 0.21f, 0.64f, 1.0f));
|
||||
source.SetSkyboxHorizonColor(XCEngine::Math::Color(0.71f, 0.76f, 0.88f, 1.0f));
|
||||
source.SetSkyboxBottomColor(XCEngine::Math::Color(0.92f, 0.82f, 0.58f, 1.0f));
|
||||
@@ -80,6 +84,7 @@ TEST(CameraComponent_Test, SerializeRoundTripPreservesViewportAndClearState) {
|
||||
EXPECT_FLOAT_EQ(target.GetViewportRect().width, 0.5f);
|
||||
EXPECT_FLOAT_EQ(target.GetViewportRect().height, 0.625f);
|
||||
EXPECT_TRUE(target.IsSkyboxEnabled());
|
||||
EXPECT_EQ(target.GetSkyboxMaterialPath(), XCEngine::Resources::GetBuiltinDefaultPrimitiveMaterialPath().CStr());
|
||||
EXPECT_FLOAT_EQ(target.GetSkyboxTopColor().b, 0.64f);
|
||||
EXPECT_FLOAT_EQ(target.GetSkyboxHorizonColor().g, 0.76f);
|
||||
EXPECT_FLOAT_EQ(target.GetSkyboxBottomColor().r, 0.92f);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -17,11 +17,14 @@
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
#include <XCEngine/Resources/Texture/Texture.h>
|
||||
#include <XCEngine/RHI/RHITexture.h>
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
#include "../../../RHI/integration/fixtures/RHIIntegrationFixture.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@@ -40,6 +43,84 @@ constexpr const char* kVulkanScreenshot = "skybox_scene_vulkan.ppm";
|
||||
constexpr uint32_t kFrameWidth = 1280;
|
||||
constexpr uint32_t kFrameHeight = 720;
|
||||
|
||||
Texture* CreatePanoramicSkyboxTexture() {
|
||||
constexpr uint32_t kTextureWidth = 256;
|
||||
constexpr uint32_t kTextureHeight = 128;
|
||||
|
||||
auto* texture = new Texture();
|
||||
IResource::ConstructParams params = {};
|
||||
params.name = "SkyboxPanoramicTexture";
|
||||
params.path = "Tests/Rendering/SkyboxScene/skybox_panorama.texture";
|
||||
params.guid = ResourceGUID::Generate(params.path);
|
||||
texture->Initialize(params);
|
||||
|
||||
std::vector<float> pixels(static_cast<size_t>(kTextureWidth) * static_cast<size_t>(kTextureHeight) * 4u, 1.0f);
|
||||
for (uint32_t y = 0; y < kTextureHeight; ++y) {
|
||||
for (uint32_t x = 0; x < kTextureWidth; ++x) {
|
||||
const float u = static_cast<float>(x) / static_cast<float>(kTextureWidth - 1u);
|
||||
const float v = static_cast<float>(y) / static_cast<float>(kTextureHeight - 1u);
|
||||
|
||||
Vector3 color = Vector3(0.05f, 0.08f, 0.14f);
|
||||
if (v < 0.42f) {
|
||||
const float t = v / 0.42f;
|
||||
color = Vector3(
|
||||
0.08f + 0.42f * (1.0f - t),
|
||||
0.16f + 0.50f * (1.0f - t),
|
||||
0.34f + 0.58f * (1.0f - t));
|
||||
} else if (v < 0.58f) {
|
||||
const float t = (v - 0.42f) / 0.16f;
|
||||
color = Vector3(
|
||||
0.92f - 0.24f * t,
|
||||
0.74f - 0.18f * t,
|
||||
0.36f - 0.12f * t);
|
||||
} else {
|
||||
const float t = (v - 0.58f) / 0.42f;
|
||||
color = Vector3(
|
||||
0.12f - 0.06f * t,
|
||||
0.10f - 0.05f * t,
|
||||
0.09f - 0.04f * t);
|
||||
}
|
||||
|
||||
const float sunDx = u - 0.22f;
|
||||
const float sunDy = v - 0.34f;
|
||||
const float sun = std::exp(-(sunDx * sunDx + sunDy * sunDy) * 1450.0f) * 4.0f;
|
||||
color += Vector3(1.0f, 0.82f, 0.58f) * sun;
|
||||
|
||||
if (u > 0.62f && u < 0.68f && v > 0.46f && v < 0.82f) {
|
||||
color = Vector3(0.10f, 0.52f, 0.96f);
|
||||
}
|
||||
if (u > 0.34f && u < 0.39f && v > 0.50f && v < 0.86f) {
|
||||
color = Vector3(0.88f, 0.24f, 0.19f);
|
||||
}
|
||||
if (u > 0.78f && u < 0.92f && v > 0.60f && v < 0.78f) {
|
||||
color = Vector3(0.22f, 0.70f, 0.28f);
|
||||
}
|
||||
|
||||
const size_t pixelIndex = (static_cast<size_t>(y) * static_cast<size_t>(kTextureWidth) + x) * 4u;
|
||||
pixels[pixelIndex + 0] = color.x;
|
||||
pixels[pixelIndex + 1] = color.y;
|
||||
pixels[pixelIndex + 2] = color.z;
|
||||
pixels[pixelIndex + 3] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t pixelDataSize = pixels.size() * sizeof(float);
|
||||
if (!texture->Create(
|
||||
kTextureWidth,
|
||||
kTextureHeight,
|
||||
1,
|
||||
1,
|
||||
XCEngine::Resources::TextureType::Texture2D,
|
||||
XCEngine::Resources::TextureFormat::RGBA32_FLOAT,
|
||||
pixels.data(),
|
||||
pixelDataSize)) {
|
||||
delete texture;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
Mesh* CreateQuadMesh() {
|
||||
auto* mesh = new Mesh();
|
||||
IResource::ConstructParams params = {};
|
||||
@@ -100,6 +181,21 @@ Material* CreateMaterial(
|
||||
return material;
|
||||
}
|
||||
|
||||
Material* CreateSkyboxMaterial(Texture* texture) {
|
||||
auto* material = new Material();
|
||||
IResource::ConstructParams params = {};
|
||||
params.name = "SkyboxPanoramicMaterial";
|
||||
params.path = "Tests/Rendering/SkyboxScene/skybox_panorama.mat";
|
||||
params.guid = ResourceGUID::Generate(params.path);
|
||||
material->Initialize(params);
|
||||
material->SetShader(ResourceManager::Get().Load<Shader>(GetBuiltinSkyboxShaderPath()));
|
||||
material->SetTexture("_MainTex", ResourceHandle<Texture>(texture));
|
||||
material->SetFloat4("_Tint", Vector4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
material->SetFloat("_Exposure", 1.05f);
|
||||
material->SetFloat("_Rotation", 0.0f);
|
||||
return material;
|
||||
}
|
||||
|
||||
GameObject* CreateRenderableObject(
|
||||
Scene& scene,
|
||||
const char* name,
|
||||
@@ -151,6 +247,7 @@ private:
|
||||
std::unique_ptr<SceneRenderer> mSceneRenderer;
|
||||
std::vector<RHIResourceView*> mBackBufferViews;
|
||||
std::vector<Material*> mMaterials;
|
||||
std::vector<Texture*> mTextures;
|
||||
Mesh* mMesh = nullptr;
|
||||
RHITexture* mDepthTexture = nullptr;
|
||||
RHIResourceView* mDepthView = nullptr;
|
||||
@@ -221,6 +318,11 @@ void SkyboxSceneTest::TearDown() {
|
||||
}
|
||||
mMaterials.clear();
|
||||
|
||||
for (Texture* texture : mTextures) {
|
||||
delete texture;
|
||||
}
|
||||
mTextures.clear();
|
||||
|
||||
delete mMesh;
|
||||
mMesh = nullptr;
|
||||
|
||||
@@ -239,9 +341,11 @@ void SkyboxSceneTest::BuildScene() {
|
||||
camera->SetFarClipPlane(20.0f);
|
||||
camera->SetClearColor(XCEngine::Math::Color(0.01f, 0.01f, 0.01f, 1.0f));
|
||||
camera->SetSkyboxEnabled(true);
|
||||
camera->SetSkyboxTopColor(XCEngine::Math::Color(0.11f, 0.29f, 0.64f, 1.0f));
|
||||
camera->SetSkyboxHorizonColor(XCEngine::Math::Color(0.82f, 0.87f, 0.96f, 1.0f));
|
||||
camera->SetSkyboxBottomColor(XCEngine::Math::Color(0.96f, 0.93f, 0.86f, 1.0f));
|
||||
Texture* skyboxTexture = CreatePanoramicSkyboxTexture();
|
||||
ASSERT_NE(skyboxTexture, nullptr);
|
||||
Material* skyboxMaterial = CreateSkyboxMaterial(skyboxTexture);
|
||||
ASSERT_NE(skyboxMaterial, nullptr);
|
||||
camera->SetSkyboxMaterial(skyboxMaterial);
|
||||
|
||||
MaterialRenderState opaqueState = {};
|
||||
opaqueState.cullMode = MaterialCullMode::Back;
|
||||
@@ -269,7 +373,8 @@ void SkyboxSceneTest::BuildScene() {
|
||||
Vector4(0.12f, 0.88f, 0.74f, 0.56f),
|
||||
MaterialRenderQueue::Transparent,
|
||||
transparentState);
|
||||
mMaterials = { cubeMaterial, transparentMaterial };
|
||||
mMaterials = { skyboxMaterial, cubeMaterial, transparentMaterial };
|
||||
mTextures = { skyboxTexture };
|
||||
|
||||
CreateRenderableObject(
|
||||
*mScene,
|
||||
|
||||
@@ -156,13 +156,48 @@ TEST(BuiltinForwardPipeline_Test, BuiltinSkyboxShaderDeclaresExplicitEnvironment
|
||||
|
||||
const ShaderPass* pass = shader->FindPass("Skybox");
|
||||
ASSERT_NE(pass, nullptr);
|
||||
ASSERT_EQ(pass->resources.Size(), 1u);
|
||||
ASSERT_EQ(pass->resources.Size(), 4u);
|
||||
|
||||
const ShaderPropertyDesc* tint = shader->FindProperty("_Tint");
|
||||
ASSERT_NE(tint, nullptr);
|
||||
EXPECT_EQ(tint->type, ShaderPropertyType::Color);
|
||||
EXPECT_EQ(tint->semantic, "Tint");
|
||||
|
||||
const ShaderPropertyDesc* exposure = shader->FindProperty("_Exposure");
|
||||
ASSERT_NE(exposure, nullptr);
|
||||
EXPECT_EQ(exposure->type, ShaderPropertyType::Float);
|
||||
EXPECT_EQ(exposure->semantic, "Exposure");
|
||||
|
||||
const ShaderPropertyDesc* rotation = shader->FindProperty("_Rotation");
|
||||
ASSERT_NE(rotation, nullptr);
|
||||
EXPECT_EQ(rotation->type, ShaderPropertyType::Float);
|
||||
EXPECT_EQ(rotation->semantic, "Rotation");
|
||||
|
||||
const ShaderPropertyDesc* panoramic = shader->FindProperty("_MainTex");
|
||||
ASSERT_NE(panoramic, nullptr);
|
||||
EXPECT_EQ(panoramic->type, ShaderPropertyType::Texture2D);
|
||||
EXPECT_EQ(panoramic->semantic, "SkyboxTexture");
|
||||
|
||||
EXPECT_EQ(pass->resources[0].semantic, "Environment");
|
||||
EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer);
|
||||
EXPECT_EQ(pass->resources[0].set, 0u);
|
||||
EXPECT_EQ(pass->resources[0].binding, 0u);
|
||||
|
||||
EXPECT_EQ(pass->resources[1].semantic, "Material");
|
||||
EXPECT_EQ(pass->resources[1].type, ShaderResourceType::ConstantBuffer);
|
||||
EXPECT_EQ(pass->resources[1].set, 1u);
|
||||
EXPECT_EQ(pass->resources[1].binding, 0u);
|
||||
|
||||
EXPECT_EQ(pass->resources[2].semantic, "SkyboxTexture");
|
||||
EXPECT_EQ(pass->resources[2].type, ShaderResourceType::Texture2D);
|
||||
EXPECT_EQ(pass->resources[2].set, 2u);
|
||||
EXPECT_EQ(pass->resources[2].binding, 0u);
|
||||
|
||||
EXPECT_EQ(pass->resources[3].semantic, "LinearClampSampler");
|
||||
EXPECT_EQ(pass->resources[3].type, ShaderResourceType::Sampler);
|
||||
EXPECT_EQ(pass->resources[3].set, 3u);
|
||||
EXPECT_EQ(pass->resources[3].binding, 0u);
|
||||
|
||||
delete shader;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <XCEngine/Rendering/RenderPipelineAsset.h>
|
||||
#include <XCEngine/Rendering/RenderSurface.h>
|
||||
#include <XCEngine/Rendering/Execution/SceneRenderer.h>
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
#include <memory>
|
||||
@@ -43,6 +44,9 @@ struct MockPipelineState {
|
||||
XCEngine::RHI::RHIResourceView* lastShadowMap = nullptr;
|
||||
XCEngine::Math::Matrix4x4 lastShadowViewProjection = XCEngine::Math::Matrix4x4::Identity();
|
||||
XCEngine::Math::Vector4 lastShadowParams = XCEngine::Math::Vector4::Zero();
|
||||
RenderEnvironmentMode lastEnvironmentMode = RenderEnvironmentMode::None;
|
||||
bool lastHasSkybox = false;
|
||||
const XCEngine::Resources::Material* lastSkyboxMaterial = nullptr;
|
||||
std::vector<CameraComponent*> renderedCameras;
|
||||
std::vector<RenderClearFlags> renderedClearFlags;
|
||||
std::vector<XCEngine::Math::Color> renderedClearColors;
|
||||
@@ -299,6 +303,9 @@ public:
|
||||
m_state->lastShadowMap = sceneData.lighting.mainDirectionalShadow.shadowMap;
|
||||
m_state->lastShadowViewProjection = sceneData.lighting.mainDirectionalShadow.viewProjection;
|
||||
m_state->lastShadowParams = sceneData.lighting.mainDirectionalShadow.shadowParams;
|
||||
m_state->lastEnvironmentMode = sceneData.environment.mode;
|
||||
m_state->lastHasSkybox = sceneData.environment.HasSkybox();
|
||||
m_state->lastSkyboxMaterial = sceneData.environment.materialSkybox.material;
|
||||
m_state->renderedCameras.push_back(sceneData.camera);
|
||||
m_state->renderedClearFlags.push_back(sceneData.cameraData.clearFlags);
|
||||
m_state->renderedClearColors.push_back(sceneData.cameraData.clearColor);
|
||||
@@ -576,6 +583,33 @@ TEST(CameraRenderer_Test, AppliesRequestClearColorOverrideToSceneData) {
|
||||
EXPECT_FLOAT_EQ(state->lastClearColor.a, 1.0f);
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, PromotesSkyboxMaterialIntoEnvironmentFrameData) {
|
||||
Scene scene("CameraRendererSkyboxMaterialScene");
|
||||
|
||||
GameObject* cameraObject = scene.CreateGameObject("Camera");
|
||||
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
||||
camera->SetPrimary(true);
|
||||
camera->SetSkyboxEnabled(true);
|
||||
|
||||
XCEngine::Resources::Material skyboxMaterial;
|
||||
camera->SetSkyboxMaterial(&skyboxMaterial);
|
||||
|
||||
auto state = std::make_shared<MockPipelineState>();
|
||||
CameraRenderer renderer(std::make_unique<MockPipeline>(state));
|
||||
|
||||
CameraRenderRequest request;
|
||||
request.scene = &scene;
|
||||
request.camera = camera;
|
||||
request.context = CreateValidContext();
|
||||
request.surface = RenderSurface(320, 200);
|
||||
request.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1));
|
||||
|
||||
ASSERT_TRUE(renderer.Render(request));
|
||||
EXPECT_TRUE(state->lastHasSkybox);
|
||||
EXPECT_EQ(state->lastEnvironmentMode, RenderEnvironmentMode::MaterialSkybox);
|
||||
EXPECT_EQ(state->lastSkyboxMaterial, &skyboxMaterial);
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, ExecutesInjectedPreAndPostPassSequencesAroundPipelineRender) {
|
||||
Scene scene("CameraRendererPassScene");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user