Formalize cubemap skybox pipeline across backends

This commit is contained in:
2026-04-06 01:37:04 +08:00
parent 66a6818b89
commit f014ae6e6f
24 changed files with 549 additions and 196 deletions

View File

@@ -1,7 +1,7 @@
// XC_BUILTIN_SKYBOX_OPENGL_PS
#version 430
layout(binding = 0) uniform sampler2D uSkyboxTexture;
layout(binding = 0) uniform samplerCube uSkyboxTexture;
layout(std140, binding = 0) uniform EnvironmentConstants {
vec4 gSkyboxTopColor;
@@ -37,18 +37,14 @@ vec3 EvaluateProceduralSkybox(vec3 viewRay) {
return color;
}
vec2 ComputePanoramicUv(vec3 viewRay) {
vec3 RotateAroundY(vec3 viewRay) {
float rotation = gSkyboxRotationAndMode.x;
float sinTheta = sin(rotation);
float cosTheta = cos(rotation);
vec3 rotatedRay = normalize(vec3(
return 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() {
@@ -62,8 +58,7 @@ void main() {
vec3 color = EvaluateProceduralSkybox(viewRay);
if (gSkyboxRotationAndMode.y > 0.5) {
vec2 uv = ComputePanoramicUv(viewRay);
color = texture(uSkyboxTexture, uv).rgb *
color = texture(uSkyboxTexture, RotateAroundY(viewRay)).rgb *
gSkyboxTintAndExposure.rgb *
gSkyboxTintAndExposure.w;
}

View File

@@ -15,7 +15,7 @@ layout(set = 1, binding = 0, std140) uniform MaterialConstants {
vec4 gSkyboxRotationAndMode;
};
layout(set = 2, binding = 0) uniform texture2D uSkyboxTexture;
layout(set = 2, binding = 0) uniform textureCube uSkyboxTexture;
layout(set = 3, binding = 0) uniform sampler uLinearSampler;
layout(location = 0) in vec2 vNdc;
@@ -38,18 +38,14 @@ vec3 EvaluateProceduralSkybox(vec3 viewRay) {
return color;
}
vec2 ComputePanoramicUv(vec3 viewRay) {
vec3 RotateAroundY(vec3 viewRay) {
float rotation = gSkyboxRotationAndMode.x;
float sinTheta = sin(rotation);
float cosTheta = cos(rotation);
vec3 rotatedRay = normalize(vec3(
return 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() {
@@ -63,8 +59,7 @@ void main() {
vec3 color = EvaluateProceduralSkybox(viewRay);
if (gSkyboxRotationAndMode.y > 0.5) {
vec2 uv = ComputePanoramicUv(viewRay);
color = texture(sampler2D(uSkyboxTexture, uLinearSampler), uv).rgb *
color = texture(samplerCube(uSkyboxTexture, uLinearSampler), RotateAroundY(viewRay)).rgb *
gSkyboxTintAndExposure.rgb *
gSkyboxTintAndExposure.w;
}

View File

@@ -1,5 +1,5 @@
// XC_BUILTIN_SKYBOX_D3D12_PS
Texture2D gSkyboxTexture : register(t0);
TextureCube gSkyboxTexture : register(t0);
SamplerState gLinearSampler : register(s0);
cbuffer EnvironmentConstants : register(b0) {
@@ -37,18 +37,14 @@ float3 EvaluateProceduralSkybox(float3 viewRay) {
return color;
}
float2 ComputePanoramicUv(float3 viewRay) {
float3 RotateAroundY(float3 viewRay) {
const float rotation = gSkyboxRotationAndMode.x;
const float sinTheta = sin(rotation);
const float cosTheta = cos(rotation);
const float3 rotatedRay = normalize(float3(
return 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 {
@@ -62,8 +58,7 @@ float4 MainPS(PSInput input) : SV_Target {
float3 color = EvaluateProceduralSkybox(viewRay);
if (gSkyboxRotationAndMode.y > 0.5f) {
const float2 uv = ComputePanoramicUv(viewRay);
color = gSkyboxTexture.Sample(gLinearSampler, uv).rgb *
color = gSkyboxTexture.Sample(gLinearSampler, RotateAroundY(viewRay)).rgb *
gSkyboxTintAndExposure.rgb *
gSkyboxTintAndExposure.w;
}

View File

@@ -5,7 +5,7 @@ Shader "Builtin Skybox"
_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)]
_Tex ("Cubemap (HDR)", Cube) = "white" [Semantic(SkyboxTexture)]
}
SubShader
{
@@ -17,7 +17,7 @@ Shader "Builtin Skybox"
{
EnvironmentConstants (ConstantBuffer, 0, 0) [Semantic(Environment)]
MaterialConstants (ConstantBuffer, 1, 0) [Semantic(Material)]
SkyboxTexture (Texture2D, 2, 0) [Semantic(SkyboxTexture)]
SkyboxTexture (TextureCube, 2, 0) [Semantic(SkyboxTexture)]
LinearClampSampler (Sampler, 3, 0) [Semantic(LinearClampSampler)]
}
HLSLPROGRAM

View File

@@ -19,9 +19,9 @@ public:
bool Initialize(ID3D12Device* device, const D3D12_RESOURCE_DESC& desc, D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON);
bool InitializeFromExisting(ID3D12Resource* resource, bool ownsResource = false);
bool InitializeFromData(ID3D12Device* device, ID3D12GraphicsCommandList* commandList,
const void* pixelData, uint32_t width, uint32_t height, DXGI_FORMAT format, uint32_t rowPitch = 0,
ComPtr<ID3D12Resource>* uploadBuffer = nullptr);
bool InitializeFromData(ID3D12Device* device, ID3D12GraphicsCommandList* commandList,
const D3D12_RESOURCE_DESC& textureDesc, TextureType textureType, const void* pixelData, size_t pixelDataSize,
uint32_t rowPitch = 0, ComPtr<ID3D12Resource>* uploadBuffer = nullptr);
bool InitializeDepthStencil(ID3D12Device* device, uint32_t width, uint32_t height, DXGI_FORMAT format = DXGI_FORMAT_D24_UNORM_S8_UINT);
void Shutdown() override;

View File

@@ -18,6 +18,7 @@ struct DescriptorBinding {
uint32_t count;
std::vector<uint32_t> textureUnits;
std::vector<uint32_t> textureIds;
std::vector<uint32_t> textureTargets;
std::vector<uint32_t> samplerIds;
};

View File

@@ -147,16 +147,21 @@ inline const Resources::Texture* ResolveSkyboxTexture(const Resources::Material*
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "SkyboxTexture")) {
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
if (textureHandle.Get() != nullptr && textureHandle->IsValid()) {
if (textureHandle.Get() != nullptr &&
textureHandle->IsValid() &&
(textureHandle->GetTextureType() == Resources::TextureType::TextureCube ||
textureHandle->GetTextureType() == Resources::TextureType::TextureCubeArray)) {
return textureHandle.Get();
}
}
static const char* kSkyboxTexturePropertyNames[] = {
"_Tex",
"_MainTex",
"_PanoramicTex",
"_Cube",
"_SkyboxTexture",
"panoramicTexture",
"cubemap",
"skyboxCubemap",
"skyboxTexture",
"texture"
};
@@ -164,7 +169,10 @@ inline const Resources::Texture* ResolveSkyboxTexture(const Resources::Material*
for (const char* propertyName : kSkyboxTexturePropertyNames) {
const Resources::ResourceHandle<Resources::Texture> textureHandle =
material->GetTexture(Containers::String(propertyName));
if (textureHandle.Get() != nullptr && textureHandle->IsValid()) {
if (textureHandle.Get() != nullptr &&
textureHandle->IsValid() &&
(textureHandle->GetTextureType() == Resources::TextureType::TextureCube ||
textureHandle->GetTextureType() == Resources::TextureType::TextureCubeArray)) {
return textureHandle.Get();
}
}

View File

@@ -285,8 +285,10 @@ private:
std::unordered_map<DynamicDescriptorSetKey, CachedDescriptorSet, DynamicDescriptorSetKeyHash> m_dynamicDescriptorSets;
RHI::RHISampler* m_sampler = nullptr;
RHI::RHISampler* m_shadowSampler = nullptr;
RHI::RHITexture* m_fallbackTexture = nullptr;
RHI::RHIResourceView* m_fallbackTextureView = nullptr;
RHI::RHITexture* m_fallbackTexture2D = nullptr;
RHI::RHIResourceView* m_fallbackTexture2DView = nullptr;
RHI::RHITexture* m_fallbackTextureCube = nullptr;
RHI::RHIResourceView* m_fallbackTextureCubeView = nullptr;
RHI::RHIPipelineLayout* m_skyboxPipelineLayout = nullptr;
RHI::RHIPipelineState* m_skyboxPipelineState = nullptr;
OwnedDescriptorSet m_skyboxEnvironmentSet = {};

View File

@@ -53,7 +53,7 @@ public:
bool Create(Core::uint32 width, Core::uint32 height, Core::uint32 depth,
Core::uint32 mipLevels, TextureType type, TextureFormat format,
const void* data, size_t dataSize);
const void* data, size_t dataSize, Core::uint32 arraySize = 1);
bool GenerateMipmaps();
private:

View File

@@ -196,6 +196,46 @@ D3D12_RTV_DIMENSION ResolveRTVDimension(const ResourceViewDesc& desc, TextureTyp
}
}
D3D12_SRV_DIMENSION ResolveSRVDimension(const ResourceViewDesc& desc, TextureType textureType) {
switch (desc.dimension) {
case ResourceViewDimension::Texture1D:
return D3D12_SRV_DIMENSION_TEXTURE1D;
case ResourceViewDimension::Texture1DArray:
return D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
case ResourceViewDimension::Texture2DArray:
return D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
case ResourceViewDimension::Texture3D:
return D3D12_SRV_DIMENSION_TEXTURE3D;
case ResourceViewDimension::TextureCube:
return D3D12_SRV_DIMENSION_TEXTURECUBE;
case ResourceViewDimension::TextureCubeArray:
return D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
case ResourceViewDimension::Texture2DMS:
return D3D12_SRV_DIMENSION_TEXTURE2DMS;
case ResourceViewDimension::Texture2DMSArray:
return D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY;
case ResourceViewDimension::Unknown:
break;
default:
return D3D12_SRV_DIMENSION_TEXTURE2D;
}
switch (textureType) {
case TextureType::Texture1D:
return D3D12_SRV_DIMENSION_TEXTURE1D;
case TextureType::Texture2DArray:
return D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
case TextureType::Texture3D:
return D3D12_SRV_DIMENSION_TEXTURE3D;
case TextureType::TextureCube:
return D3D12_SRV_DIMENSION_TEXTURECUBE;
case TextureType::TextureCubeArray:
return D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
default:
return D3D12_SRV_DIMENSION_TEXTURE2D;
}
}
bool IsDepthFormat(Format format) {
return format == Format::D16_UNorm ||
format == Format::D24_UNorm_S8_UInt ||
@@ -680,10 +720,6 @@ RHITexture* D3D12Device::CreateTexture(const TextureDesc& desc, const void* init
return CreateTexture(desc);
}
if (desc.textureType != static_cast<uint32_t>(TextureType::Texture2D)) {
return nullptr;
}
const Format format = static_cast<Format>(desc.format);
uint32_t resolvedRowPitch = rowPitch;
if (resolvedRowPitch == 0) {
@@ -716,14 +752,27 @@ RHITexture* D3D12Device::CreateTexture(const TextureDesc& desc, const void* init
uploadCommandList.Reset();
auto* texture = new D3D12Texture();
D3D12_RESOURCE_DESC textureDesc = {};
const TextureType textureType = ResolveTextureType(desc.textureType);
textureDesc.Dimension = ToD3D12(textureType);
textureDesc.Alignment = 0;
textureDesc.Width = desc.width;
textureDesc.Height = desc.height;
textureDesc.DepthOrArraySize = ResolveDepthOrArraySize(desc, textureType);
textureDesc.MipLevels = desc.mipLevels > 0 ? static_cast<uint16_t>(desc.mipLevels) : 1;
textureDesc.Format = ResolveD3D12ResourceFormat(format);
textureDesc.SampleDesc.Count = desc.sampleCount > 0 ? desc.sampleCount : 1;
textureDesc.SampleDesc.Quality = desc.sampleQuality;
textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
textureDesc.Flags = static_cast<D3D12_RESOURCE_FLAGS>(desc.flags);
ComPtr<ID3D12Resource> uploadBuffer;
if (!texture->InitializeFromData(
m_device.Get(),
uploadCommandList.GetCommandList(),
textureDesc,
textureType,
initialData,
desc.width,
desc.height,
ToD3D12(format),
initialDataSize,
resolvedRowPitch,
&uploadBuffer)) {
delete texture;
@@ -734,6 +783,8 @@ RHITexture* D3D12Device::CreateTexture(const TextureDesc& desc, const void* init
}
texture->SetState(ResourceStates::PixelShaderResource);
texture->SetFormat(format);
texture->SetTextureType(textureType);
uploadCommandList.Close();
ID3D12CommandList* commandLists[] = { uploadCommandList.GetCommandList() };
@@ -1074,11 +1125,52 @@ RHIResourceView* D3D12Device::CreateShaderResourceView(RHITexture* texture, cons
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
const Format format = desc.format != 0 ? static_cast<Format>(desc.format) : texture->GetFormat();
const TextureType textureType = texture->GetTextureType();
srvDesc.Format = ResolveD3D12ShaderResourceViewFormat(format);
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MostDetailedMip = desc.mipLevel;
srvDesc.Texture2D.MipLevels = 1;
srvDesc.ViewDimension = ResolveSRVDimension(desc, textureType);
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
switch (srvDesc.ViewDimension) {
case D3D12_SRV_DIMENSION_TEXTURE1D:
srvDesc.Texture1D.MostDetailedMip = desc.mipLevel;
srvDesc.Texture1D.MipLevels = 1;
break;
case D3D12_SRV_DIMENSION_TEXTURE1DARRAY:
srvDesc.Texture1DArray.MostDetailedMip = desc.mipLevel;
srvDesc.Texture1DArray.MipLevels = 1;
srvDesc.Texture1DArray.FirstArraySlice = desc.firstArraySlice;
srvDesc.Texture1DArray.ArraySize = desc.arraySize > 0 ? desc.arraySize : 1;
break;
case D3D12_SRV_DIMENSION_TEXTURE2DARRAY:
srvDesc.Texture2DArray.MostDetailedMip = desc.mipLevel;
srvDesc.Texture2DArray.MipLevels = 1;
srvDesc.Texture2DArray.FirstArraySlice = desc.firstArraySlice;
srvDesc.Texture2DArray.ArraySize = desc.arraySize > 0 ? desc.arraySize : 1;
srvDesc.Texture2DArray.PlaneSlice = desc.planeSlice;
break;
case D3D12_SRV_DIMENSION_TEXTURE3D:
srvDesc.Texture3D.MostDetailedMip = desc.mipLevel;
srvDesc.Texture3D.MipLevels = 1;
break;
case D3D12_SRV_DIMENSION_TEXTURECUBE:
srvDesc.TextureCube.MostDetailedMip = desc.mipLevel;
srvDesc.TextureCube.MipLevels = 1;
break;
case D3D12_SRV_DIMENSION_TEXTURECUBEARRAY:
srvDesc.TextureCubeArray.MostDetailedMip = desc.mipLevel;
srvDesc.TextureCubeArray.MipLevels = 1;
srvDesc.TextureCubeArray.First2DArrayFace = desc.firstArraySlice;
srvDesc.TextureCubeArray.NumCubes = desc.arraySize > 0 ? std::max<uint32_t>(desc.arraySize / 6u, 1u) : 1u;
break;
case D3D12_SRV_DIMENSION_TEXTURE2DMS:
case D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY:
break;
case D3D12_SRV_DIMENSION_TEXTURE2D:
default:
srvDesc.Texture2D.MostDetailedMip = desc.mipLevel;
srvDesc.Texture2D.MipLevels = 1;
srvDesc.Texture2D.PlaneSlice = desc.planeSlice;
break;
}
auto heap = std::make_unique<D3D12DescriptorHeap>();
if (!heap->Initialize(m_device.Get(), DescriptorHeapType::CBV_SRV_UAV, 1, false)) {
@@ -1087,6 +1179,9 @@ RHIResourceView* D3D12Device::CreateShaderResourceView(RHITexture* texture, cons
}
view->InitializeAsShaderResource(m_device.Get(), resource, &srvDesc, heap.get(), 0);
if (IsDepthFormat(format)) {
view->SetFormat(format);
}
view->SetOwnedHeap(std::move(heap));
return view;

View File

@@ -1,5 +1,8 @@
#include "XCEngine/RHI/D3D12/D3D12Texture.h"
#include <algorithm>
#include <vector>
namespace XCEngine {
namespace RHI {
@@ -72,22 +75,12 @@ bool D3D12Texture::InitializeFromExisting(ID3D12Resource* resource, bool ownsRes
return true;
}
bool D3D12Texture::InitializeFromData(ID3D12Device* device, ID3D12GraphicsCommandList* commandList,
const void* pixelData, uint32_t width, uint32_t height, DXGI_FORMAT format, uint32_t rowPitch,
ComPtr<ID3D12Resource>* uploadBuffer) {
D3D12_RESOURCE_DESC textureDesc = {};
textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
textureDesc.Alignment = 0;
textureDesc.Width = width;
textureDesc.Height = height;
textureDesc.DepthOrArraySize = 1;
textureDesc.MipLevels = 1;
textureDesc.Format = format;
textureDesc.SampleDesc.Count = 1;
textureDesc.SampleDesc.Quality = 0;
textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
bool D3D12Texture::InitializeFromData(ID3D12Device* device, ID3D12GraphicsCommandList* commandList,
const D3D12_RESOURCE_DESC& textureDesc, TextureType textureType, const void* pixelData, size_t pixelDataSize,
uint32_t rowPitch, ComPtr<ID3D12Resource>* uploadBuffer) {
if (device == nullptr || commandList == nullptr || pixelData == nullptr || pixelDataSize == 0) {
return false;
}
D3D12_HEAP_PROPERTIES heapProperties = {};
heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
@@ -105,16 +98,24 @@ bool D3D12Texture::InitializeFromData(ID3D12Device* device, ID3D12GraphicsComman
return false;
}
m_ownsResource = true;
m_format = FromD3D12(format);
m_textureType = TextureType::Texture2D;
m_format = FromD3D12(textureDesc.Format);
m_textureType = textureType;
textureDesc = m_resource->GetDesc();
const UINT subresourceCount = textureDesc.MipLevels *
(textureDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? 1u : textureDesc.DepthOrArraySize);
std::vector<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> subresourceFootprints(subresourceCount);
std::vector<UINT> subresourceRowCounts(subresourceCount);
std::vector<UINT64> subresourceRowSizes(subresourceCount);
UINT64 memorySizeUsed = 0;
UINT64 rowSizeInBytes = 0;
UINT rowUsed = 0;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT subresourceFootprint;
device->GetCopyableFootprints(&textureDesc, 0, 1, 0,
&subresourceFootprint, &rowUsed, &rowSizeInBytes, &memorySizeUsed);
device->GetCopyableFootprints(
&textureDesc,
0,
subresourceCount,
0,
subresourceFootprints.data(),
subresourceRowCounts.data(),
subresourceRowSizes.data(),
&memorySizeUsed);
ComPtr<ID3D12Resource> tempBufferObject;
D3D12_HEAP_PROPERTIES d3dTempHeapProperties = {};
@@ -146,26 +147,49 @@ bool D3D12Texture::InitializeFromData(ID3D12Device* device, ID3D12GraphicsComman
return false;
}
BYTE* pData;
BYTE* pData = nullptr;
tempBufferObject->Map(0, nullptr, reinterpret_cast<void**>(&pData));
BYTE* pDstTempBuffer = reinterpret_cast<BYTE*>(pData + subresourceFootprint.Offset);
const BYTE* pSrcData = reinterpret_cast<const BYTE*>(pixelData);
const UINT sourceRowPitch = rowPitch > 0 ? rowPitch : static_cast<UINT>(rowSizeInBytes);
for (UINT i = 0; i < rowUsed; i++) {
memcpy(pDstTempBuffer + subresourceFootprint.Footprint.RowPitch * i, pSrcData + sourceRowPitch * i, rowSizeInBytes);
const UINT sourceRowPitch = rowPitch > 0 ? rowPitch : static_cast<UINT>(subresourceRowSizes[0]);
size_t sourceOffset = 0;
for (UINT subresourceIndex = 0; subresourceIndex < subresourceCount; ++subresourceIndex) {
const D3D12_PLACED_SUBRESOURCE_FOOTPRINT& footprint = subresourceFootprints[subresourceIndex];
const UINT rowCount = subresourceRowCounts[subresourceIndex];
const UINT64 rowSizeInBytes = subresourceRowSizes[subresourceIndex];
BYTE* pDstTempBuffer = pData + footprint.Offset;
for (UINT depthSlice = 0; depthSlice < footprint.Footprint.Depth; ++depthSlice) {
for (UINT rowIndex = 0; rowIndex < rowCount; ++rowIndex) {
const size_t srcRowOffset =
sourceOffset +
static_cast<size_t>(depthSlice) * static_cast<size_t>(sourceRowPitch) * rowCount +
static_cast<size_t>(rowIndex) * sourceRowPitch;
memcpy(
pDstTempBuffer +
static_cast<size_t>(depthSlice) * footprint.Footprint.RowPitch * rowCount +
static_cast<size_t>(rowIndex) * footprint.Footprint.RowPitch,
pSrcData + srcRowOffset,
static_cast<size_t>(std::min<UINT64>(rowSizeInBytes, sourceRowPitch)));
}
}
sourceOffset += static_cast<size_t>(sourceRowPitch) * rowCount * footprint.Footprint.Depth;
}
tempBufferObject->Unmap(0, nullptr);
D3D12_TEXTURE_COPY_LOCATION dst = {};
dst.pResource = m_resource.Get();
dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dst.SubresourceIndex = 0;
for (UINT subresourceIndex = 0; subresourceIndex < subresourceCount; ++subresourceIndex) {
D3D12_TEXTURE_COPY_LOCATION dst = {};
dst.pResource = m_resource.Get();
dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dst.SubresourceIndex = subresourceIndex;
D3D12_TEXTURE_COPY_LOCATION src = {};
src.pResource = tempBufferObject.Get();
src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
src.PlacedFootprint = subresourceFootprint;
commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
D3D12_TEXTURE_COPY_LOCATION src = {};
src.pResource = tempBufferObject.Get();
src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
src.PlacedFootprint = subresourceFootprints[subresourceIndex];
commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
}
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;

View File

@@ -2,6 +2,8 @@
#include "XCEngine/RHI/OpenGL/OpenGLPipelineLayout.h"
#include "XCEngine/RHI/OpenGL/OpenGLTextureUnitAllocator.h"
#include "XCEngine/RHI/OpenGL/OpenGLResourceView.h"
#include "XCEngine/RHI/OpenGL/OpenGLEnums.h"
#include "XCEngine/RHI/OpenGL/OpenGLTexture.h"
#include "XCEngine/RHI/OpenGL/OpenGLSampler.h"
#include <glad/glad.h>
@@ -81,6 +83,7 @@ bool OpenGLDescriptorSet::Initialize(OpenGLTextureUnitAllocator* allocator, uint
m_bindings[i].count = layout.bindings[i].count;
m_bindings[i].textureUnits.resize(layout.bindings[i].count);
m_bindings[i].textureIds.resize(layout.bindings[i].count, 0);
m_bindings[i].textureTargets.resize(layout.bindings[i].count, GL_TEXTURE_2D);
m_bindings[i].samplerIds.resize(layout.bindings[i].count, 0);
if (m_bindings[i].type != DescriptorType::SRV &&
@@ -159,6 +162,9 @@ void OpenGLDescriptorSet::Update(uint32_t offset, RHIResourceView* view) {
OpenGLResourceView* glView = static_cast<OpenGLResourceView*>(view);
binding->textureIds[0] = glView->GetTexture();
if (const OpenGLTexture* texture = glView->GetTextureResource()) {
binding->textureTargets[0] = static_cast<uint32_t>(ToOpenGL(texture->GetOpenGLType()));
}
}
void OpenGLDescriptorSet::UpdateSampler(uint32_t offset, RHISampler* sampler) {
@@ -200,7 +206,9 @@ void OpenGLDescriptorSet::Bind() {
if (textureId != 0 && binding.type != DescriptorType::Sampler) {
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, textureId);
glBindTexture(
j < binding.textureTargets.size() ? binding.textureTargets[j] : GL_TEXTURE_2D,
textureId);
}
if (samplerId != 0) {
@@ -246,7 +254,9 @@ void OpenGLDescriptorSet::BindWithPipelineLayout(const OpenGLPipelineLayout* pip
glBindImageTexture(bindingPoint, textureId, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
} else if (binding.type != DescriptorType::Sampler) {
glActiveTexture(GL_TEXTURE0 + bindingPoint);
glBindTexture(GL_TEXTURE_2D, textureId);
glBindTexture(
i < binding.textureTargets.size() ? binding.textureTargets[i] : GL_TEXTURE_2D,
textureId);
}
}
@@ -276,7 +286,9 @@ void OpenGLDescriptorSet::Unbind() {
uint32_t unit = binding.textureUnits[j];
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, 0);
glBindTexture(
j < binding.textureTargets.size() ? binding.textureTargets[j] : GL_TEXTURE_2D,
0);
glBindSampler(unit, 0);
}
}

View File

@@ -7,6 +7,34 @@
namespace XCEngine {
namespace RHI {
namespace {
size_t GetOpenGLFormatPixelSize(OpenGLFormat format) {
switch (format) {
case OpenGLFormat::R8:
return 1;
case OpenGLFormat::RG8:
return 2;
case OpenGLFormat::RGBA8:
case OpenGLFormat::RGBA8_SRGB:
return 4;
case OpenGLFormat::RG32F:
return sizeof(float) * 2;
case OpenGLFormat::RGBA16F:
return sizeof(uint16_t) * 4;
case OpenGLFormat::RGBA32F:
return sizeof(float) * 4;
case OpenGLFormat::Depth24Stencil8:
return 4;
case OpenGLFormat::Depth32F:
return sizeof(float);
default:
return 0;
}
}
} // namespace
OpenGLTexture::OpenGLTexture()
: m_texture(0)
, m_type(OpenGLTextureType::Texture2D)
@@ -37,8 +65,22 @@ bool OpenGLTexture::Initialize(OpenGLTextureType type, int width, int height, in
glBindTexture(target, m_texture);
if (type == OpenGLTextureType::TextureCube) {
const size_t faceSize = static_cast<size_t>(width) * static_cast<size_t>(height) * GetOpenGLFormatPixelSize(format);
const unsigned char* facePixels = reinterpret_cast<const unsigned char*>(data);
for (int i = 0; i < 6; i++) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internalFormat, width, height, 0, glFormat, glType, data);
const void* faceData = (facePixels != nullptr && faceSize > 0)
? facePixels + faceSize * static_cast<size_t>(i)
: nullptr;
glTexImage2D(
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
0,
internalFormat,
width,
height,
0,
glFormat,
glType,
faceData);
}
} else if (type == OpenGLTextureType::Texture1D) {
glTexImage1D(GL_TEXTURE_1D, 0, internalFormat, width, 0, glFormat, glType, data);

View File

@@ -396,7 +396,7 @@ bool BuiltinForwardPipeline::ExecuteForwardSkyboxPass(const RenderPassContext& p
RHI::RHIResourceView* skyboxTextureView = ResolveTextureView(skyboxTexture);
const bool useTexturedSkybox = sceneData.environment.HasMaterialSkybox() && skyboxTextureView != nullptr;
if (skyboxTextureView == nullptr) {
skyboxTextureView = m_fallbackTextureView;
skyboxTextureView = m_fallbackTextureCubeView;
}
const BuiltinSkyboxMaterialData skyboxMaterialData = BuildBuiltinSkyboxMaterialData(skyboxMaterial);
@@ -553,29 +553,72 @@ bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& contex
return false;
}
const unsigned char whitePixel[4] = { 255, 255, 255, 255 };
RHI::TextureDesc textureDesc = {};
textureDesc.width = 1;
textureDesc.height = 1;
textureDesc.depth = 1;
textureDesc.mipLevels = 1;
textureDesc.arraySize = 1;
textureDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
textureDesc.textureType = static_cast<uint32_t>(RHI::TextureType::Texture2D);
textureDesc.sampleCount = 1;
textureDesc.sampleQuality = 0;
textureDesc.flags = 0;
m_fallbackTexture = context.device->CreateTexture(textureDesc, whitePixel, sizeof(whitePixel), 4);
if (m_fallbackTexture == nullptr) {
const unsigned char whitePixels2D[4] = {
255, 255, 255, 255
};
RHI::TextureDesc fallback2DDesc = {};
fallback2DDesc.width = 1;
fallback2DDesc.height = 1;
fallback2DDesc.depth = 1;
fallback2DDesc.mipLevels = 1;
fallback2DDesc.arraySize = 1;
fallback2DDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
fallback2DDesc.textureType = static_cast<uint32_t>(RHI::TextureType::Texture2D);
fallback2DDesc.sampleCount = 1;
fallback2DDesc.sampleQuality = 0;
fallback2DDesc.flags = 0;
m_fallbackTexture2D = context.device->CreateTexture(
fallback2DDesc,
whitePixels2D,
sizeof(whitePixels2D),
sizeof(whitePixels2D));
if (m_fallbackTexture2D == nullptr) {
return false;
}
RHI::ResourceViewDesc textureViewDesc = {};
textureViewDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
textureViewDesc.dimension = RHI::ResourceViewDimension::Texture2D;
textureViewDesc.mipLevel = 0;
m_fallbackTextureView = context.device->CreateShaderResourceView(m_fallbackTexture, textureViewDesc);
if (m_fallbackTextureView == nullptr) {
RHI::ResourceViewDesc fallback2DViewDesc = {};
fallback2DViewDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
fallback2DViewDesc.dimension = RHI::ResourceViewDimension::Texture2D;
fallback2DViewDesc.mipLevel = 0;
m_fallbackTexture2DView = context.device->CreateShaderResourceView(m_fallbackTexture2D, fallback2DViewDesc);
if (m_fallbackTexture2DView == nullptr) {
return false;
}
const unsigned char whitePixelsCube[6 * 4] = {
255, 255, 255, 255,
255, 255, 255, 255,
255, 255, 255, 255,
255, 255, 255, 255,
255, 255, 255, 255,
255, 255, 255, 255
};
RHI::TextureDesc fallbackCubeDesc = {};
fallbackCubeDesc.width = 1;
fallbackCubeDesc.height = 1;
fallbackCubeDesc.depth = 1;
fallbackCubeDesc.mipLevels = 1;
fallbackCubeDesc.arraySize = 6;
fallbackCubeDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
fallbackCubeDesc.textureType = static_cast<uint32_t>(RHI::TextureType::TextureCube);
fallbackCubeDesc.sampleCount = 1;
fallbackCubeDesc.sampleQuality = 0;
fallbackCubeDesc.flags = 0;
m_fallbackTextureCube = context.device->CreateTexture(
fallbackCubeDesc,
whitePixelsCube,
sizeof(whitePixelsCube),
4);
if (m_fallbackTextureCube == nullptr) {
return false;
}
RHI::ResourceViewDesc fallbackCubeViewDesc = {};
fallbackCubeViewDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
fallbackCubeViewDesc.dimension = RHI::ResourceViewDimension::TextureCube;
fallbackCubeViewDesc.mipLevel = 0;
m_fallbackTextureCubeView = context.device->CreateShaderResourceView(m_fallbackTextureCube, fallbackCubeViewDesc);
if (m_fallbackTextureCubeView == nullptr) {
return false;
}
@@ -715,8 +758,8 @@ bool BuiltinForwardPipeline::CreateSkyboxResources(const RenderContext& context)
}
m_skyboxSamplerSet.set->UpdateSampler(0, m_sampler);
m_skyboxTextureSet.set->Update(0, m_fallbackTextureView);
m_skyboxBoundTextureView = m_fallbackTextureView;
m_skyboxTextureSet.set->Update(0, m_fallbackTextureCubeView);
m_skyboxBoundTextureView = m_fallbackTextureCubeView;
m_skyboxPipelineState = m_device->CreatePipelineState(
CreateSkyboxPipelineDesc(
@@ -776,18 +819,30 @@ void BuiltinForwardPipeline::DestroyPipelineResources() {
}
m_passResourceLayouts.clear();
if (m_fallbackTextureView != nullptr) {
m_fallbackTextureView->Shutdown();
delete m_fallbackTextureView;
m_fallbackTextureView = nullptr;
}
DestroySkyboxResources();
if (m_fallbackTexture != nullptr) {
m_fallbackTexture->Shutdown();
delete m_fallbackTexture;
m_fallbackTexture = nullptr;
if (m_fallbackTextureCubeView != nullptr) {
m_fallbackTextureCubeView->Shutdown();
delete m_fallbackTextureCubeView;
m_fallbackTextureCubeView = nullptr;
}
if (m_fallbackTextureCube != nullptr) {
m_fallbackTextureCube->Shutdown();
delete m_fallbackTextureCube;
m_fallbackTextureCube = nullptr;
}
if (m_fallbackTexture2DView != nullptr) {
m_fallbackTexture2DView->Shutdown();
delete m_fallbackTexture2DView;
m_fallbackTexture2DView = nullptr;
}
if (m_fallbackTexture2D != nullptr) {
m_fallbackTexture2D->Shutdown();
delete m_fallbackTexture2D;
m_fallbackTexture2D = nullptr;
}
if (m_sampler != nullptr) {

View File

@@ -516,7 +516,7 @@ RHI::RHIResourceView* BuiltinForwardPipeline::ResolveTextureView(
const Resources::Material* material = ResolveMaterial(visibleItem);
const Resources::Texture* texture = ResolveTexture(material);
RHI::RHIResourceView* textureView = ResolveTextureView(texture);
return textureView != nullptr ? textureView : m_fallbackTextureView;
return textureView != nullptr ? textureView : m_fallbackTexture2DView;
}
BuiltinForwardPipeline::LightingConstants BuiltinForwardPipeline::BuildLightingConstants(
@@ -651,7 +651,7 @@ bool BuiltinForwardPipeline::DrawVisibleItem(
RHI::RHIResourceView* shadowMapTextureView = sceneData.lighting.HasMainDirectionalShadow()
? sceneData.lighting.mainDirectionalShadow.shadowMap
: m_fallbackTextureView;
: m_fallbackTexture2DView;
if (passLayout->shadowMapTexture.IsValid() && shadowMapTextureView == nullptr) {
return false;
}

View File

@@ -14,14 +14,18 @@ void Texture::Release() {
bool Texture::Create(Core::uint32 width, Core::uint32 height, Core::uint32 depth,
Core::uint32 mipLevels, TextureType type, TextureFormat format,
const void* data, size_t dataSize) {
const void* data, size_t dataSize, Core::uint32 arraySize) {
m_width = width;
m_height = height;
m_depth = depth;
m_mipLevels = mipLevels;
m_textureType = type;
m_format = format;
m_arraySize = arraySize > 0
? arraySize
: (type == TextureType::TextureCube || type == TextureType::TextureCubeArray ? 6u : 1u);
m_pixelData.Clear();
if (data && dataSize > 0) {
m_pixelData.Resize(dataSize);
std::memcpy(m_pixelData.Data(), data, dataSize);

View File

@@ -22,9 +22,13 @@ Containers::String GetResourceNameFromPath(const Containers::String& path) {
}
LoadResult CreateTextureResource(const Containers::String& path,
TextureType textureType,
TextureFormat format,
Core::uint32 width,
Core::uint32 height,
Core::uint32 depth,
Core::uint32 mipLevels,
Core::uint32 arraySize,
const void* pixelData,
size_t pixelDataSize) {
auto* texture = new Texture();
@@ -38,12 +42,13 @@ LoadResult CreateTextureResource(const Containers::String& path,
if (!texture->Create(width,
height,
1,
1,
TextureType::Texture2D,
depth,
mipLevels,
textureType,
format,
pixelData,
pixelDataSize)) {
pixelDataSize,
arraySize)) {
delete texture;
return LoadResult(Containers::String("Failed to create texture resource: ") + path);
}
@@ -105,9 +110,18 @@ LoadResult LoadTextureArtifact(const Containers::String& path) {
}
return CreateTextureResource(path,
static_cast<TextureType>(header.textureType),
static_cast<TextureFormat>(header.textureFormat),
header.width,
header.height,
header.depth > 0 ? header.depth : 1u,
header.mipLevels > 0 ? header.mipLevels : 1u,
header.arraySize > 0
? header.arraySize
: (header.textureType == static_cast<Core::uint32>(TextureType::TextureCube) ||
header.textureType == static_cast<Core::uint32>(TextureType::TextureCubeArray)
? 6u
: 1u),
pixelData.Data(),
pixelData.Size());
}
@@ -202,9 +216,13 @@ LoadResult TextureLoader::LoadFromMemory(
4u *
sizeof(float);
LoadResult result = CreateTextureResource(path,
TextureType::Texture2D,
ResolveDecodedTextureFormat(settings, true),
static_cast<Core::uint32>(width),
static_cast<Core::uint32>(height),
1,
1,
1,
pixels,
pixelDataSize);
stbi_image_free(pixels);
@@ -226,9 +244,13 @@ LoadResult TextureLoader::LoadFromMemory(
4u *
sizeof(stbi_uc);
LoadResult result = CreateTextureResource(path,
TextureType::Texture2D,
ResolveDecodedTextureFormat(settings, false),
static_cast<Core::uint32>(width),
static_cast<Core::uint32>(height),
1,
1,
1,
pixels,
pixelDataSize);
stbi_image_free(pixels);

View File

@@ -48,6 +48,9 @@ target_compile_definitions(rendering_integration_skybox_scene PRIVATE
)
add_custom_command(TARGET rendering_integration_skybox_scene POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/Res
$<TARGET_FILE_DIR:rendering_integration_skybox_scene>/Res
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/tests/RHI/integration/compare_ppm.py
$<TARGET_FILE_DIR:rendering_integration_skybox_scene>/

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@@ -0,0 +1,11 @@
Skybox source:
Poly Haven - "farm_field_puresky"
Page: https://polyhaven.com/a/farm_field_puresky
Direct download used for preprocessing:
https://dl.polyhaven.org/file/ph-assets/HDRIs/extra/Tonemapped%20JPG/farm_field_puresky.jpg
License:
CC0 1.0 Universal (public domain), as provided by Poly Haven.
Repository note:
The file committed here is a resized derivative used only for automated rendering tests.

View File

@@ -1,3 +1,6 @@
#define NOMINMAX
#include <windows.h>
#include <gtest/gtest.h>
#include "../RenderingIntegrationMain.h"
@@ -19,12 +22,16 @@
#include <XCEngine/Resources/Mesh/Mesh.h>
#include <XCEngine/Resources/Shader/Shader.h>
#include <XCEngine/Resources/Texture/Texture.h>
#include <XCEngine/Resources/Texture/TextureImportSettings.h>
#include <XCEngine/Resources/Texture/TextureLoader.h>
#include <XCEngine/RHI/RHITexture.h>
#include <XCEngine/Scene/Scene.h>
#include "../../../RHI/integration/fixtures/RHIIntegrationFixture.h"
#include <algorithm>
#include <cmath>
#include <filesystem>
#include <memory>
#include <vector>
@@ -42,78 +49,134 @@ constexpr const char* kOpenGLScreenshot = "skybox_scene_opengl.ppm";
constexpr const char* kVulkanScreenshot = "skybox_scene_vulkan.ppm";
constexpr uint32_t kFrameWidth = 1280;
constexpr uint32_t kFrameHeight = 720;
constexpr uint32_t kSkyboxFaceSize = 256;
constexpr float kPi = 3.14159265358979323846f;
Texture* CreatePanoramicSkyboxTexture() {
constexpr uint32_t kTextureWidth = 256;
constexpr uint32_t kTextureHeight = 128;
std::filesystem::path GetExecutableDirectory() {
char exePath[MAX_PATH] = {};
const DWORD length = GetModuleFileNameA(nullptr, exePath, MAX_PATH);
if (length == 0 || length >= MAX_PATH) {
return std::filesystem::current_path();
}
return std::filesystem::path(exePath).parent_path();
}
std::filesystem::path ResolveRuntimePath(const char* relativePath) {
return GetExecutableDirectory() / relativePath;
}
Vector3 ComputeCubemapDirection(uint32_t faceIndex, float s, float t) {
switch (faceIndex) {
case 0:
return Vector3(1.0f, -t, -s).Normalized();
case 1:
return Vector3(-1.0f, -t, s).Normalized();
case 2:
return Vector3(s, 1.0f, t).Normalized();
case 3:
return Vector3(s, -1.0f, -t).Normalized();
case 4:
return Vector3(s, -t, 1.0f).Normalized();
case 5:
default:
return Vector3(-s, -t, -1.0f).Normalized();
}
}
void SamplePanoramaNearest(
const Texture& panorama,
float u,
float v,
unsigned char outRgba[4]) {
const uint32_t width = panorama.GetWidth();
const uint32_t height = panorama.GetHeight();
const auto* pixels = static_cast<const unsigned char*>(panorama.GetPixelData());
if (pixels == nullptr || width == 0 || height == 0) {
outRgba[0] = 255;
outRgba[1] = 255;
outRgba[2] = 255;
outRgba[3] = 255;
return;
}
u = u - std::floor(u);
v = std::clamp(v, 0.0f, 1.0f);
const uint32_t x = std::min<uint32_t>(
static_cast<uint32_t>(u * static_cast<float>(width - 1) + 0.5f),
width - 1);
const uint32_t y = std::min<uint32_t>(
static_cast<uint32_t>(v * static_cast<float>(height - 1) + 0.5f),
height - 1);
const size_t pixelIndex = (static_cast<size_t>(y) * static_cast<size_t>(width) + x) * 4u;
outRgba[0] = pixels[pixelIndex + 0];
outRgba[1] = pixels[pixelIndex + 1];
outRgba[2] = pixels[pixelIndex + 2];
outRgba[3] = pixels[pixelIndex + 3];
}
Texture* CreateCubemapSkyboxTexture(const std::filesystem::path& panoramaPath) {
TextureLoader loader;
TextureImportSettings settings;
settings.SetSRGB(true);
LoadResult loadResult = loader.Load(panoramaPath.string().c_str(), &settings);
if (!loadResult || loadResult.resource == nullptr) {
return nullptr;
}
std::unique_ptr<Texture> panorama(static_cast<Texture*>(loadResult.resource));
if (!panorama || panorama->GetPixelData() == nullptr || panorama->GetWidth() == 0 || panorama->GetHeight() == 0) {
return nullptr;
}
auto* texture = new Texture();
IResource::ConstructParams params = {};
params.name = "SkyboxPanoramicTexture";
params.path = "Tests/Rendering/SkyboxScene/skybox_panorama.texture";
params.name = "SkyboxCubemapTexture";
params.path = "Tests/Rendering/SkyboxScene/skybox_cubemap.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);
std::vector<unsigned char> pixels(
static_cast<size_t>(kSkyboxFaceSize) * static_cast<size_t>(kSkyboxFaceSize) * 4u * 6u,
255u);
for (uint32_t faceIndex = 0; faceIndex < 6u; ++faceIndex) {
for (uint32_t y = 0; y < kSkyboxFaceSize; ++y) {
for (uint32_t x = 0; x < kSkyboxFaceSize; ++x) {
const float s = (2.0f * (static_cast<float>(x) + 0.5f) / static_cast<float>(kSkyboxFaceSize)) - 1.0f;
const float t = (2.0f * (static_cast<float>(y) + 0.5f) / static_cast<float>(kSkyboxFaceSize)) - 1.0f;
const Vector3 direction = ComputeCubemapDirection(faceIndex, s, t);
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 u = std::atan2(direction.z, direction.x) / (2.0f * kPi) + 0.5f;
const float v = std::acos(std::clamp(direction.y, -1.0f, 1.0f)) / kPi;
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;
unsigned char rgba[4] = {};
SamplePanoramaNearest(*panorama, u, v, rgba);
if (u > 0.62f && u < 0.68f && v > 0.46f && v < 0.82f) {
color = Vector3(0.10f, 0.52f, 0.96f);
const size_t pixelIndex =
((static_cast<size_t>(faceIndex) * static_cast<size_t>(kSkyboxFaceSize) *
static_cast<size_t>(kSkyboxFaceSize)) +
(static_cast<size_t>(y) * static_cast<size_t>(kSkyboxFaceSize) + x)) * 4u;
pixels[pixelIndex + 0] = rgba[0];
pixels[pixelIndex + 1] = rgba[1];
pixels[pixelIndex + 2] = rgba[2];
pixels[pixelIndex + 3] = 255u;
}
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);
const size_t pixelDataSize = pixels.size();
if (!texture->Create(
kTextureWidth,
kTextureHeight,
kSkyboxFaceSize,
kSkyboxFaceSize,
1,
1,
XCEngine::Resources::TextureType::Texture2D,
XCEngine::Resources::TextureFormat::RGBA32_FLOAT,
XCEngine::Resources::TextureType::TextureCube,
panorama->GetFormat(),
pixels.data(),
pixelDataSize)) {
pixelDataSize,
6)) {
delete texture;
return nullptr;
}
@@ -184,14 +247,14 @@ Material* CreateMaterial(
Material* CreateSkyboxMaterial(Texture* texture) {
auto* material = new Material();
IResource::ConstructParams params = {};
params.name = "SkyboxPanoramicMaterial";
params.path = "Tests/Rendering/SkyboxScene/skybox_panorama.mat";
params.name = "SkyboxCubemapMaterial";
params.path = "Tests/Rendering/SkyboxScene/skybox_cubemap.mat";
params.guid = ResourceGUID::Generate(params.path);
material->Initialize(params);
material->SetShader(ResourceManager::Get().Load<Shader>(GetBuiltinSkyboxShaderPath()));
material->SetTexture("_MainTex", ResourceHandle<Texture>(texture));
material->SetTexture("_Tex", ResourceHandle<Texture>(texture));
material->SetFloat4("_Tint", Vector4(1.0f, 1.0f, 1.0f, 1.0f));
material->SetFloat("_Exposure", 1.05f);
material->SetFloat("_Exposure", 1.0f);
material->SetFloat("_Rotation", 0.0f);
return material;
}
@@ -341,7 +404,7 @@ void SkyboxSceneTest::BuildScene() {
camera->SetFarClipPlane(20.0f);
camera->SetClearColor(XCEngine::Math::Color(0.01f, 0.01f, 0.01f, 1.0f));
camera->SetSkyboxEnabled(true);
Texture* skyboxTexture = CreatePanoramicSkyboxTexture();
Texture* skyboxTexture = CreateCubemapSkyboxTexture(ResolveRuntimePath("Res/skyboxes/farm_field_puresky_1024.jpg"));
ASSERT_NE(skyboxTexture, nullptr);
Material* skyboxMaterial = CreateSkyboxMaterial(skyboxTexture);
ASSERT_NE(skyboxMaterial, nullptr);

View File

@@ -173,10 +173,10 @@ TEST(BuiltinForwardPipeline_Test, BuiltinSkyboxShaderDeclaresExplicitEnvironment
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");
const ShaderPropertyDesc* cubemap = shader->FindProperty("_Tex");
ASSERT_NE(cubemap, nullptr);
EXPECT_EQ(cubemap->type, ShaderPropertyType::TextureCube);
EXPECT_EQ(cubemap->semantic, "SkyboxTexture");
EXPECT_EQ(pass->resources[0].semantic, "Environment");
EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer);
@@ -189,7 +189,7 @@ TEST(BuiltinForwardPipeline_Test, BuiltinSkyboxShaderDeclaresExplicitEnvironment
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].type, ShaderResourceType::TextureCube);
EXPECT_EQ(pass->resources[2].set, 2u);
EXPECT_EQ(pass->resources[2].binding, 0u);

View File

@@ -34,4 +34,30 @@ TEST(Texture, IsValid) {
EXPECT_FALSE(texture.IsValid());
}
TEST(Texture, CreatePreservesExplicitTextureCubeArraySize) {
Texture texture;
const unsigned char pixels[6 * 4] = {
255, 255, 255, 255,
255, 255, 255, 255,
255, 255, 255, 255,
255, 255, 255, 255,
255, 255, 255, 255,
255, 255, 255, 255
};
ASSERT_TRUE(texture.Create(
1,
1,
1,
1,
TextureType::TextureCube,
TextureFormat::RGBA8_UNORM,
pixels,
sizeof(pixels),
6));
EXPECT_EQ(texture.GetTextureType(), TextureType::TextureCube);
EXPECT_EQ(texture.GetArraySize(), 6u);
EXPECT_EQ(texture.GetPixelDataSize(), sizeof(pixels));
}
} // namespace