feat: 材质系统 v1——4 纹理槽(albedo/normal/specular/emissive)+ 法线贴图
- Material:单贴图 → 4 槽(binding=Slot 枚举),MakeCacheKey 组合键 保持材质去重(同贴图组合共享描述符集) - Model:读取 aiTextureType DIFFUSE/NORMALS/SPECULAR/EMISSIVE 四类路径; aiProcess_CalcTangentSpace 生成切线(Vertex +tangent 属性,location 3); 每槽默认纹理兜底(白/平法线(128,128,255)/黑/黑,1x1) - 纹理 sRGB 语义按槽位:颜色图 SRGB 变体,数据图(Normal/Specular)线性 UNORM——Texture 路径加载加 srgb 参数;修复 InitFromPixels 图文 format 不一致的 validation 警告 - UBO 尾部追加 cameraPos(Blinn-Phong V 向量) - simple_shader.frag:TBN 法线贴图(无切线/平法线退化回几何法线)+ Blinn-Phong 高光(specular 图强度系数)+ 自发光直加 - SunTemple 实测:148 张 DDS 全槽位加载(96 张线性变体)、1056 mesh、 validation 零警告
This commit is contained in:
@@ -1,12 +1,22 @@
|
||||
//
|
||||
// Created by atdunbg on 2026/9/21.
|
||||
//
|
||||
// Material:着色材质 = 一组纹理槽 + 一个描述符集。
|
||||
//
|
||||
// 槽位(binding = 槽位序号,与 simple_shader.frag 契约一致):
|
||||
// 0 albedo(BaseColor,sRGB) 1 normal(切线空间法线,线性)
|
||||
// 2 specular(高光强度/颜色,线性) 3 emissive(自发光,sRGB)
|
||||
//
|
||||
// 纹理非拥有(Model 的纹理缓存持有);空槽由调用方传默认纹理
|
||||
//(白/平法线/黑/黑,Model 持有),shader 永远可以安全采样。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_MATERIAL_H
|
||||
#define HELLOVULKAN_MATERIAL_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
namespace Engine
|
||||
@@ -18,20 +28,32 @@ namespace Engine
|
||||
class Material
|
||||
{
|
||||
public:
|
||||
// 槽位序号(与 binding 对齐)
|
||||
enum Slot : u32 {
|
||||
Albedo = 0,
|
||||
Normal = 1,
|
||||
Specular = 2,
|
||||
Emissive = 3,
|
||||
kSlotCount = 4,
|
||||
};
|
||||
|
||||
Material(Device& device,
|
||||
DescriptorManager& descriptorManager,
|
||||
vk::DescriptorSetLayout layout,
|
||||
Texture* texture);
|
||||
const std::array<Texture*, kSlotCount>& textures);
|
||||
~Material();
|
||||
|
||||
Material(const Material&) = delete;
|
||||
Material& operator=(const Material&) = delete;
|
||||
|
||||
vk::DescriptorSet GetDescriptorSet() const { return m_DescriptorSet; }
|
||||
Texture* GetTexture() const { return m_Texture; }
|
||||
Texture* GetTexture(Slot slot) const { return m_Textures[slot]; }
|
||||
|
||||
// 材质缓存键:同贴图组合共享一份(Model 层使用)
|
||||
static std::string MakeCacheKey(const std::string paths[kSlotCount]);
|
||||
|
||||
private:
|
||||
Texture* m_Texture = nullptr;
|
||||
std::array<Texture*, kSlotCount> m_Textures{};
|
||||
vk::DescriptorSet m_DescriptorSet;
|
||||
};
|
||||
} // namespace Engine
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/Renderer/Material.h>
|
||||
#include <Engine/Renderer/Mesh.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <array>
|
||||
@@ -59,12 +60,14 @@ namespace Engine
|
||||
void LoadModel(const std::string& path);
|
||||
void ProcessNode(aiNode* node, const aiScene* scene);
|
||||
Scope<Mesh> ProcessMesh(aiMesh* mesh, const aiScene* scene);
|
||||
void InitDefaultTextures();
|
||||
|
||||
Texture* GetOrLoadTexture(const std::string& path);
|
||||
Texture* GetOrLoadTexture(const std::string& path, bool srgb);
|
||||
|
||||
// 材质缓存入口:同贴图路径共享同一 Material(一个描述符集),
|
||||
// 空路径/缺文件折到兜底材质。路径已做反斜杠归一化。
|
||||
Material* GetOrCreateMaterial(const std::string& path);
|
||||
// 材质缓存入口:同贴图组合共享同一 Material(一个描述符集),
|
||||
// 槽位路径为空/文件缺失折到对应默认纹理。路径已做反斜杠归一化。
|
||||
// paths 下标 = Material::Slot(albedo/normal/specular/emissive)
|
||||
Material* GetOrCreateMaterial(const std::string paths[Material::kSlotCount]);
|
||||
|
||||
Device& m_Device;
|
||||
MemoryAllocator& m_Allocator;
|
||||
@@ -75,7 +78,8 @@ namespace Engine
|
||||
// 析构序 = meshes 先于材质/纹理缓存
|
||||
std::unordered_map<std::string, Scope<Material>> m_MaterialCache;
|
||||
std::unordered_map<std::string, Scope<Texture>> m_TextureCache;
|
||||
Scope<Texture> m_DefaultTexture;
|
||||
// 各槽位默认纹理(材质缺图时兜底):白 / 平法线 / 黑高光 / 黑自发光
|
||||
std::array<Scope<Texture>, Material::kSlotCount> m_DefaultTextures;
|
||||
|
||||
std::vector<Scope<Mesh>> m_Meshes;
|
||||
std::string m_ModelPath;
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace Engine {
|
||||
glm::mat4 projNoJitter; // 未抖动投影(velocity 当前帧 clip 用)
|
||||
glm::mat4 prevView; // 上一帧视图(velocity)
|
||||
glm::mat4 prevProj; // 上一帧未抖动投影(velocity)
|
||||
glm::vec4 cameraPos; // 相机世界位置(高光 V 向量)
|
||||
};
|
||||
|
||||
// 每帧从场景收集的渲染数据(SceneRenderer 生产,pass 消费)。
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Engine {
|
||||
glm::vec3 position;
|
||||
glm::vec3 normal;
|
||||
glm::vec2 uv;
|
||||
glm::vec3 tangent; // 切线(法线贴图 TBN 用;assimp CalcTangentSpace)
|
||||
|
||||
static vk::VertexInputBindingDescription2EXT GetBindingDescription() {
|
||||
vk::VertexInputBindingDescription2EXT binding;
|
||||
@@ -24,8 +25,8 @@ namespace Engine {
|
||||
return binding;
|
||||
}
|
||||
|
||||
static std::array<vk::VertexInputAttributeDescription2EXT, 3> GetAttributeDescriptions() {
|
||||
std::array<vk::VertexInputAttributeDescription2EXT, 3> attrs;
|
||||
static std::array<vk::VertexInputAttributeDescription2EXT, 4> GetAttributeDescriptions() {
|
||||
std::array<vk::VertexInputAttributeDescription2EXT, 4> attrs;
|
||||
|
||||
attrs[0].setLocation(0)
|
||||
.setBinding(0)
|
||||
@@ -42,6 +43,11 @@ namespace Engine {
|
||||
.setFormat(vk::Format::eR32G32Sfloat)
|
||||
.setOffset(offsetof(Vertex, uv));
|
||||
|
||||
attrs[3].setLocation(3)
|
||||
.setBinding(0)
|
||||
.setFormat(vk::Format::eR32G32B32Sfloat)
|
||||
.setOffset(offsetof(Vertex, tangent));
|
||||
|
||||
return attrs;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,13 +21,16 @@ namespace Engine {
|
||||
|
||||
class Texture {
|
||||
public:
|
||||
// 从磁盘图片加载(按扩展名分发:.dds → DdsLoader,其它 → stbi)
|
||||
Texture(Device& device, MemoryAllocator& allocator, const std::string& path);
|
||||
// 从磁盘图片加载(按扩展名分发:.dds → DdsLoader,其它 → stbi)。
|
||||
// srgb:颜色贴图语义(BaseColor/Emissive)为 true;数据图
|
||||
//(Normal/Specular 等)为 false —— 决定 vk 格式的 _SRGB 变体
|
||||
Texture(Device& device, MemoryAllocator& allocator, const std::string& path,
|
||||
bool srgb = true);
|
||||
|
||||
// 程序化纹理:纯色(1x1),用于地面/调试等不依赖磁盘资产的场景
|
||||
struct ProceduralTag {};
|
||||
static Scope<Texture> CreateSolidColor(Device& device, MemoryAllocator& allocator,
|
||||
u8 r, u8 g, u8 b);
|
||||
u8 r, u8 g, u8 b, bool srgb = true);
|
||||
|
||||
~Texture();
|
||||
|
||||
@@ -46,13 +49,13 @@ namespace Engine {
|
||||
private:
|
||||
// 两段式构造:先确定像素来源(磁盘 / 程序化),再走公共上传路径
|
||||
Texture(Device& device, MemoryAllocator& allocator, ProceduralTag,
|
||||
const std::array<u8, 4>& rgba);
|
||||
const std::array<u8, 4>& rgba, bool srgb);
|
||||
|
||||
// 公共路径:staging → device local image → view/sampler(RGBA8 单 mip)
|
||||
void InitFromPixels(const void* pixels, u32 width, u32 height);
|
||||
void InitFromPixels(const void* pixels, u32 width, u32 height, bool srgb);
|
||||
|
||||
// DDS 路径:压缩数据直传(BC 硬件采样解码)+ 文件自带 mip 链
|
||||
void InitFromDds(const std::string& path);
|
||||
void InitFromDds(const std::string& path, bool srgb);
|
||||
|
||||
Device& m_Device;
|
||||
vk::Image m_Image;
|
||||
|
||||
Reference in New Issue
Block a user