前言
本篇将展示如何实现BlinnPhong光照,以及为人物模型贴上纹理
对于理论不清楚的小伙伴可以看这图形学理论 局部光照,[图形学理论 纹理贴图](https://www.cnblogs.com/chenglixue/p/17109214.html)
具体代码看这github.com
材质
由于漫反射率和镜面反射率我们都是从纹理图中提取,因此材质class中的主要内容是specualrShiness用于控制镜面光照的范围、环境光强度ambientAlbedo
struct Material
{
// material name for looking up
std::string name;
// index of constant buffer
UINT materialCBIndex;
// diffuse index of Srv heap
UINT diffuseSrvHeapIndex;
UINT specularSrvHeapIndex;
UINT numFramesDirty;
// specualr equation's pow
int specualrShiness;
// ambient light strength
float ambientAlbedo;
};
材质的构建:
void MyD3D12::BuildMaterial()
{
for (size_t i = 0; i < m_diffuseTextures.size(); ++i)
{
auto pMaterial = std::make_unique<Core::Material>();
std::string temp = m_models[i]->GetTextureName();
pMaterial->name = temp.substr(0, temp.find_last_of('_'));
pMaterial->materialCBIndex = i;
pMaterial->diffuseSrvHeapIndex = 2 * i;
pMaterial->specularSrvHeapIndex = 2 * i + 1;
pMaterial->numFramesDirty = FrameCount;
pMaterial->specualrShiness = 16;
pMaterial->ambientAlbedo = 0.1;
m_materials.push_back(std::move(pMaterial));
}
assert(m_materials.size() != 0);
}
渲染项的改动:
void MyD3D12::BuildRenderer()
{
for (size_t i = 0; i < m_draws.size(); ++i)
{
auto pRenderer = std::make_unique<Core::Renderer>();
XMStoreFloat4x4(&pRenderer->world, DirectX::XMMatrixRotationY(MathHelper::Pi));
pRenderer->objectIndex = i;
pRenderer->numFramesDirty = FrameCount;
pRenderer->pGeometry = m_geometries[0].get();
pRenderer->pMaterail = m_materials[i].get();
pRenderer->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
pRenderer->indexCount = m_draws[i]->indexCount;
pRenderer->startIndex = m_draws[i]->startIndex;
pRenderer->baseVertex = m_draws[i]->baseVertex;
m_allRenderers.push_back(std::move(pRenderer));
}
assert(m_allRenderers.size() != 0);
for (auto& r : m_allRenderers)
{
m_opaqueRenderers.push_back(r.get());
}
assert(m_opaqueRenderers.size() != 0);
}
光照
在实战中,我们会实现点光源,平行光源,聚光灯,因此Light class中包含这三类光源所需参数
- 由于平行光源忽视距离的因素,认定光强是恒定的。因此它只需要一个参数
lightDirection
- 点光源受距离影响。因此他需要三个参数,两个衰减方程的——falloffStart & falloffEnd,以及光源位置lightPosition
- 聚光灯圆锥体范围内所有光线强度不尽相同。因此在点光源参数的基础上,还多出一个spotPower用于控制圆锥体范围内的光线衰减
struct Light
{
// light color
XMFLOAT3 lightColor;
// for falloff function
float falloffStart;
// light direction
XMFLOAT3 lightDirection;
// for falloff function
float falloffEnd;
// light position
XMFLOAT3 lightPosition;
// spot equation's pow
float spotPower;
};
修改FrameResource
由于需要将材质数据和光源数据从CPU传递给shader,因此我们需要在FrameResource中增加新的内容:
struct MaterialConstantBuffer
{
int specualrShiness;
float ambientStrength;
float pad1;
float pad2;
};
struct PassConstantBuffer
{
XMFLOAT4X4 view;
XMFLOAT4X4 inverseView;
XMFLOAT4X4 projection;
XMFLOAT4X4 inverseProjection;
XMFLOAT4X4 viewProjection;
XMFLOAT4X4 inverseViewProjection;
// 用于求光源方向
XMFLOAT3 eyeWorldPosition;
float pad1;
// 存储的所有光源信息
Light lights[MAX_LIGHT_COUNT];
};
更新方式如下:
void XM_CALLCONV FrameResource::UpdatePassConstantBuffers(XMMATRIX& view, XMMATRIX& projection, XMFLOAT3& eyeWorldPosition)
{
// ...
passConstantBuffer.eyeWorldPosition = eyeWorldPosition;
// direction light
passConstantBuffer.lights[0].lightPosition = { 0.f, 0.f, 0.f };
passConstantBuffer.lights[0].lightDirection = { 0.f, 0.f, -1.f };
passConstantBuffer.lights[0].lightColor = { 1.f, 1.f, 1.f };
passConstantBuffer.lights[0].falloffStart = 1.f;
passConstantBuffer.lights[0].falloffEnd = 20.f;
passConstantBuffer.lights[0].spotPower = 2.f;
// point light
passConstantBuffer.lights[1].lightPosition = { 0.f, 0.f, -2.f };
passConstantBuffer.lights[1].lightDirection = { 0.f, 0.f, 1.f };
passConstantBuffer.lights[1].lightColor = { 1.f, 1.f, 1.f };
passConstantBuffer.lights[1].falloffStart = 1.f;
passConstantBuffer.lights[1].falloffEnd = 20.f;
passConstantBuffer.lights[1].spotPower = 2.f;
// spot light
passConstantBuffer.lights[2].lightPosition = { 0.f, 0.f, -2.f };
passConstantBuffer.lights[2].lightDirection = { 0.f, 0.f, 1.f };
passConstantBuffer.lights[2].lightColor = { 1.f, 1.f, 1.f };
passConstantBuffer.lights[2].falloffStart = 1.f;
passConstantBuffer.lights[2].falloffEnd = 20.f;
passConstantBuffer.lights[2].spotPower = 2.f;
// ...
}
void XM_CALLCONV FrameResource::UpdateMaterialConstantBuffers(std::vector<std::unique_ptr<Material>>& pMaterials)
{
auto currMaterialCB = this->materialUploadCB.get();
for (auto& e : pMaterials)
{
Material* pMaterial = e.get();
if (pMaterial->numFramesDirty > 0)
{
MaterialConstantBuffer materialCB;
materialCB.ambientStrength = pMaterial->ambientAlbedo;
materialCB.specualrShiness = pMaterial->specualrShiness;
currMaterialCB->CopyData(pMaterial->materialCBIndex, materialCB);
pMaterial->numFramesDirty--;
}
}
}
提取DDS纹理数据
微软官方提供了用于DX12提取DDS数据的工具包DDSTextureLoader。在此我对该工具函数进行了包装:
struct Texture
{
std::string name; // texture name
std::wstring fileName; // texture file name
ComPtr<ID3D12Resource> defaultTexture; // texture resource for default heap
std::unique_ptr<uint8_t[]> ddsData; // for LoadDDSTextureFromFile()
std::vector<D3D12_SUBRESOURCE_DATA> subResources; // for LoadDDSTextureFromFile()
ComPtr<ID3D12Resource> uploadTexture; // texture resource for upload heap to copy date to default texture
};
// copy dds data from subresource to defualt buffer and upload buffer is a intermediate object
static void CreateD3DResource(
ID3D12Device* device,
ID3D12GraphicsCommandList* cmdList,
std::vector<D3D12_SUBRESOURCE_DATA>& subResources,
ComPtr<ID3D12Resource>& defaultTexture,
ComPtr<ID3D12Resource>& uploadTexture
)
{
const UINT64 textureUploadBufferSize = GetRequiredIntermediateSize(defaultTexture.Get(), 0, static_cast<UINT>(subResources.size()));
CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_UPLOAD);
auto desc = CD3DX12_RESOURCE_DESC::Buffer(textureUploadBufferSize);
ThrowIfFailed(device->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(uploadTexture.GetAddressOf())
));
cmdList->ResourceBarrier(
1,
&CD3DX12_RESOURCE_BARRIER::Transition(defaultTexture.Get(),
D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_COPY_DEST
));
UpdateSubresources(cmdList, defaultTexture.Get(), uploadTexture.Get(), 0, 0, static_cast<UINT>(subResources.size()), subResources.data());
cmdList->ResourceBarrier(
1,
&CD3DX12_RESOURCE_BARRIER::Transition(defaultTexture.Get(),
D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
));
}
具体使用方法如下:
void MyD3D12::LoadTexture()
{
for (size_t i = 0; i < m_models.size(); ++i)
{
auto pTexture = std::make_unique<Core::Texture>();
auto textureName = m_models[i]->GetTextureName();
auto textureFileName = m_models[i]->GetDirectory() + textureName + "_diffuse.dds";
// 用于调试,验证提取的纹理数据是否正确
OutputDebugStringA((textureName + "_diffuse" + '\n').c_str());
OutputDebugStringA((textureFileName + '\n').c_str());
pTexture->name = textureName;
pTexture->fileName = Util::UTF8ToWideString(textureFileName);
ThrowIfFailed(LoadDDSTextureFromFile(
m_device.Get(),
pTexture->fileName.c_str(),
&pTexture->defaultTexture,
pTexture->ddsData,
pTexture->subResources
));
Core::CreateD3DResource(m_device.Get(), m_commandList.Get(), pTexture->subResources, pTexture->defaultTexture, pTexture->uploadTexture);
m_diffuseTextures.push_back(std::move(pTexture));
}
for (size_t i = 0; i < m_models.size(); ++i)
{
auto pTexture = std::make_unique<Core::Texture>();
auto textureName = m_models[i]->GetTextureName();
auto textureFileName = m_models[i]->GetDirectory() + textureName + "_specular.dds";
OutputDebugStringA((textureName + "_specular" + '\n').c_str());
OutputDebugStringA((textureFileName + '\n').c_str());
pTexture->name = textureName;
pTexture->fileName = Util::UTF8ToWideString(textureFileName);
ThrowIfFailed(LoadDDSTextureFromFile(
m_device.Get(),
pTexture->fileName.c_str(),
&pTexture->defaultTexture,
pTexture->ddsData,
pTexture->subResources
));
Core::CreateD3DResource(m_device.Get(), m_commandList.Get(), pTexture->subResources, pTexture->defaultTexture, pTexture->uploadTexture);
m_specularTextures.push_back(std::move(pTexture));
}
assert(m_specularTextures.size() != 0);
}
SRV
创建SRV描述符堆
D3D12_DESCRIPTOR_HEAP_DESC cbvSrvHeapDesc = {};
cbvSrvHeapDesc.NumDescriptors = m_diffuseTextures.size() + m_specularTextures.size();
cbvSrvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
cbvSrvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
ThrowIfFailed(m_device->CreateDescriptorHeap(&cbvSrvHeapDesc, IID_PPV_ARGS(&m_cbvSrvHeap)));
NAME_D3D12_OBJECT(m_cbvSrvHeap);
m_cbvSrvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
创建SRV
// create srv descriptor
{
CD3DX12_CPU_DESCRIPTOR_HANDLE hSRVDescriptor(m_cbvSrvHeap->GetCPUDescriptorHandleForHeapStart());
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc{};
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Texture2D.MostDetailedMip = 0; // the index of the most detailed mipmap level to use
srvDesc.Texture2D.ResourceMinLODClamp = 0.f; // min mipmap level that you can access. 0.0f means access all of mipmap levels
for (auto& pTexture : m_diffuseTextures)
{
auto currTexture = pTexture->defaultTexture;
srvDesc.Format = currTexture->GetDesc().Format;
srvDesc.Texture2D.MipLevels = currTexture->GetDesc().MipLevels; // combine with MostDetailedMip
m_device->CreateShaderResourceView(currTexture.Get(), &srvDesc, hSRVDescriptor);
hSRVDescriptor.Offset(2, m_cbvSrvDescriptorSize);
}
hSRVDescriptor = m_cbvSrvHeap->GetCPUDescriptorHandleForHeapStart();
hSRVDescriptor.Offset(1, m_cbvSrvDescriptorSize);
for (auto& pTexture : m_specularTextures)
{
auto currTexture = pTexture->defaultTexture;
srvDesc.Format = currTexture->GetDesc().Format;
srvDesc.Texture2D.MipLevels = currTexture->GetDesc().MipLevels; // combine with MostDetailedMip
m_device->CreateShaderResourceView(currTexture.Get(), &srvDesc, hSRVDescriptor);
hSRVDescriptor.Offset(2, m_cbvSrvDescriptorSize);
}
}
随后在根签名中进行绑定即可
静态采样器
由于纹理存在过大过小及mipmap的缺陷情况,因此我们创建的静态采样器都需考虑到这三种情况
// static sampler
static std::array<const CD3DX12_STATIC_SAMPLER_DESC, 6> GetStaticSamplers()
{
const CD3DX12_STATIC_SAMPLER_DESC pointWrap(
0, // shader register
D3D12_FILTER_MIN_MAG_MIP_POINT, // filter
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_WRAP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC pointClamp(
1,
D3D12_FILTER_MIN_MAG_MIP_POINT,
D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
const CD3DX12_STATIC_SAMPLER_DESC linearWrap(
2,
D3D12_FILTER_MIN_MAG_MIP_LINEAR,
D3D12_TEXTURE_ADDRESS_MODE_WRAP,
D3D12_TEXTURE_ADDRESS_MODE_WRAP,
D3D12_TEXTURE_ADDRESS_MODE_WRAP);
const CD3DX12_STATIC_SAMPLER_DESC linearClamp(
3,
D3D12_FILTER_MIN_MAG_MIP_LINEAR,
D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
const CD3DX12_STATIC_SAMPLER_DESC anisotropicWrap(
4,
D3D12_FILTER_ANISOTROPIC,
D3D12_TEXTURE_ADDRESS_MODE_WRAP,
D3D12_TEXTURE_ADDRESS_MODE_WRAP,
D3D12_TEXTURE_ADDRESS_MODE_WRAP
);
const CD3DX12_STATIC_SAMPLER_DESC anisotropicClamp(
5,
D3D12_FILTER_ANISOTROPIC,
D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
D3D12_TEXTURE_ADDRESS_MODE_CLAMP
);
return { pointWrap, pointClamp, linearWrap, linearClamp, anisotropicWrap, anisotropicClamp };
}
在根签名中绑定静态采样器
auto staticSamplers = Core::GetStaticSamplers();
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(_countof(rootParameters), rootParameters, staticSamplers.size(), staticSamplers.data(), D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
HLSL
VS:
cbuffer ObjectCB : register(b0)
{
float4x4 world;
}
cbuffer PassCB : register(b1)
{
float4x4 view;
float4x4 inverseView;
float4x4 projection;
float4x4 inverseProjection;
float4x4 viewProjection;
float4x4 inverseViewProjection;
float3 eyeWorldPosition;
float pad;
}
struct VSInput
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD;
//float3 tangent : TANGENT;
//float3 bitNormal : BINORMAL;
};
struct PSInput
{
float3 positionW : POSITION;
float4 positionH : SV_POSITION;
float3 normalW : NORMAL;
float2 uv : TEXCOORD;
};
PSInput main(VSInput input)
{
PSInput result;
float4 positionW = mul(float4(input.position, 1.f), world);
result.positionW = positionW.xyz;
result.positionH = mul(positionW, viewProjection);
// 一般光照的计算在世界空间中进行
result.normalW = mul(input.normal, (float3x3)world);
result.uv = input.uv;
return result;
}
PS:
//-------------------------------------------------------------------------- macro --------------------------------------------------------------------------
#define MAX_LIGHT_COUNT 3
#ifndef DIRECTION_LIGHT_COUNT
#define DIRECTION_LIGHT_COUNT 1
#endif
#ifndef POINT_LIGHT_COUNT
#define POINT_LIGHT_COUNT 1
#endif
#ifndef SPOT_LIGHT_COUNT
#define SPOT_LIGHT_COUNT 1
#endif
//-------------------------------------------------------------------------- declaration --------------------------------------------------------------------------
struct PSInput
{
float3 positionW : POSITION;
float4 positionH : SV_POSITION;
float3 normalW : NORMAL;
float2 uv : TEXCOORD;
};
struct Material
{
float4 diffuseAlbedo;
float4 specularAlbedo;
float ambientAlbedo;
int specualrShiness;
};
struct Light
{
// light color
float3 lightColor;
// for falloff function
float falloffStart;
// light direction
float3 lightDirection;
// for falloff function
float falloffEnd;
// light position
float3 lightPosition;
// spot equation's pow
float spotPower;
};
float3 BlinnPhong(float3 lightColor, float3 toLightDir, float3 normal, float3 toEyeDir, Material material);
float CalcAttenuation(float falloffStart, float falloffEnd, float d);
float3 CalcDiretionLight(Light light, Material material, float3 normal, float3 toEyeDir);
float3 CalcPointLight(Light light, Material material, float3 normal, float3 toEyeDir, float3 objectPos);
float3 CaleSpotLight(Light light, Material material, float3 normal, float3 toEyeDir, float3 objectPos);
float4 CalcLightColor(Light lights[MAX_LIGHT_COUNT], Material material, float3 normal, float3 toEyeDir, float3 objectPos);
//-------------------------------------------------------------------------- binding data --------------------------------------------------------------------------
//-------------------------------------------------------------------------- binding data --------------------------------------------------------------------------
cbuffer MaterialCB : register(b2)
{
int specualrShiness;
float ambientAlbedo;
}
cbuffer PassCB : register(b1)
{
float4x4 view;
float4x4 inverseView;
float4x4 projection;
float4x4 inverseProjection;
float4x4 viewProjection;
float4x4 inverseViewProjection;
float3 eyeWorldPosition;
Light lights[MAX_LIGHT_COUNT];
}
SamplerState g_SamperPointWrap : register(s0);
SamplerState g_SamperPointClamp : register(s1);
SamplerState g_SamperLinerWrap : register(s2);
SamplerState g_SamperLinerClamp : register(s3);
SamplerState g_SamperAnisotropyWrap : register(s4);
SamplerState g_SamperAnisotropyClamp : register(s5);
Texture2D g_diffuseTexture : register(t0);
Texture2D g_specularTexture : register(t1);
//-------------------------------------------------------------------------- main function --------------------------------------------------------------------------
float4 main(PSInput input) : SV_TARGET
{
float4 textureDiffuseAlbedo = g_diffuseTexture.Sample(g_SamperAnisotropyWrap, input.uv);
float4 textureSpecularAlbedo = g_specularTexture.Sample(g_SamperAnisotropyWrap, input.uv);
input.normalW = normalize(input.normalW);
float3 toEyeDirW = normalize(eyeWorldPosition - input.positionW);
Material material = { textureDiffuseAlbedo, textureSpecularAlbedo, ambientAlbedo, specualrShiness };
float4 resultLightColor = CalcLightColor(lights, material, input.normalW, toEyeDirW, input.positionW);
// 一般从漫反射纹理图中提取alpha
resultLightColor.a = textureDiffuseAlbedo.a;
return resultLightColor;
}
//-------------------------------------------------------------------------- function define --------------------------------------------------------------------------
float3 BlinnPhong(float3 lightColor, float3 toLightDir, float3 normal, float3 toEyeDir, Material material)
{
// ambient
float3 ambient = lightColor * material.ambientAlbedo * material.diffuseAlbedo.rgb;
float3 diffuse = material.diffuseAlbedo.rgb * max(0.f, dot(normal, toLightDir)) * lightColor;
// specular reflection
float3 halfVector = normalize(toLightDir + toEyeDir);
float3 specular = pow(max(0, dot(halfVector, normal)), material.specualrShiness) * lightColor * material.specularAlbedo.rgb;
return ambient + diffuse + specular;
}
// calculate falloff of light strength
float CalcAttenuation(float falloffStart, float falloffEnd, float d)
{
return saturate((falloffEnd - d) / (falloffEnd - falloffStart));
}
// calculate direction light
float3 CalcDiretionLight(Light light, Material material, float3 normal, float3 toEyeDir)
{
float3 toLightDir = normalize(-light.lightDirection);
float3 lightColor = max(dot(normal, toLightDir), 0) * light.lightColor;
return BlinnPhong(lightColor, toLightDir, normal, toEyeDir, material);
}
// calculate point light
float3 CalcPointLight(Light light, Material material, float3 normal, float3 toEyeDir, float3 objectPos)
{
float3 toLightDir = light.lightPosition - objectPos;
// d of falloff equation
float d = length(toLightDir);
if (d > light.falloffEnd)
{
return float3(0.f, 0.f, 0.f);
}
// normalize
toLightDir /= d;
// Lambert
float3 lightColor = max(dot(toLightDir, normal), 0.f) * light.lightColor;
// calc attenuation accroding to distance
lightColor *= CalcAttenuation(light.falloffStart, light.falloffEnd, d);
return BlinnPhong(lightColor, toLightDir, normal, toEyeDir, material);
}
// calculate spot light
float3 CaleSpotLight(Light light, Material material, float3 normal, float3 toEyeDir, float3 objectPos)
{
float3 toLightDir = light.lightPosition - objectPos;
// d of falloff equation
float d = length(toLightDir);
if (d > light.falloffEnd)
{
return float3(0.f, 0.f, 0.f);
}
// normalize
toLightDir /= d;
// Lambert
float3 lightColor = max(dot(toLightDir, normal), 0.f) * light.lightColor;
// calc attenuation accroding to distance
lightColor *= CalcAttenuation(light.falloffStart, light.falloffEnd, d);
// scale spot light strength
float spotLightFactor = pow(max(0, dot(toLightDir, normal)), light.spotPower);
lightColor *= spotLightFactor;
return BlinnPhong(lightColor, toLightDir, normal, toEyeDir, material);
}
float4 CalcLightColor(Light lights[MAX_LIGHT_COUNT], Material material, float3 normal, float3 toEyeDir, float3 objectPos)
{
float3 result = 0.f;
int i = 0;
#if (DIRECTION_LIGHT_COUNT > 0)
for (i = 0; i < DIRECTION_LIGHT_COUNT; ++i)
{
result += CalcDiretionLight(lights[i], material, normal, toEyeDir);
}
#endif
#if (POINT_LIGHT_COUNT > 0)
for (i = DIRECTION_LIGHT_COUNT; i < DIRECTION_LIGHT_COUNT + POINT_LIGHT_COUNT; ++i)
{
result += CalcPointLight(lights[i], material, normal, toEyeDir, objectPos);
}
#endif
#if (SPOT_LIGHT_COUNT > 0)
for (i = DIRECTION_LIGHT_COUNT + POINT_LIGHT_COUNT; i < DIRECTION_LIGHT_COUNT + POINT_LIGHT_COUNT + SPOT_LIGHT_COUNT; ++i)
{
result += CaleSpotLight(lights[i], material, normal, toEyeDir, objectPos);
}
#endif
return float4(result, 1.f);
}
输出
other texture format转DDS format
微软公司提供了一个将其他纹理格式转为DDS格式的.exe工具Texconv,该工具可以调整大小、改变格式、生成mipmap层、块压缩(Block Compression)
-
语法
texconv [-r | -r:keep | -r:flatten] [-flist <filename>] [-w number] [-h number] [-m number] [-f format] [-if filter] [-srgb | -srgbi | -srgbo] [-px string] [-sx string] [-o directory] [-l] [-y] [-ft file-type] [-hflip] [-vflip] [-sepalpha] [-keepcoverage number] [-nowic] [-wrap | -mirror] [-pmalpha | -alpha] [-at number] [-fl feature-level] [-pow2] [-nmap flags] [-nmapamp number] [-tu | -tf] [-dword] [-badtails] [-fixbc4x4] [-xlum] [-dx10 | -dx9] [-tga20] [-wicq number] [-wiclossless] [-wicmulti] [-nologo] [-timing] [-singleproc] [-gpu number | -nogpu] [-bc flags] [-aw number] [-c colorkey] [-rotatecolor rot] [-nits number] [-tonemap] [-x2bias] [-inverty] [-reconstructz] [-swizzle rgbamask] <file-name(s)>
常用的参数含义:
- <file-name(s)>:转换的目标文件的格式。支持的格式有dds、tga、hdr、ppm、pfm、bmp、jpg、png、jxr等等
- -r:搜索目标路径的子目录。默认为
-r:flatten
,意味在子目录下输出文件;若指定为-r:keep,意味在子目录下创建一个新的目录,并输出于此 - -o:输出目录
- -I:将输出目录的和文件名都视为小写(windows文件系统不分大小写,但git区分)
- -y:覆盖现有的输出文件
- -w:输出纹理图的宽
- -h:输出纹理图的高
- -hflip:纹理图水平翻转
- -vflip:纹理图垂直翻转
- -m:输出纹理图中生成的mipmap层数。默认为0,意味生成所有mipmap层。该选项仅用于DDS
- -f:输出纹理图的压缩格式
- -ft file-type:输出的纹理图类型。
默认为dds
- -pow2:将纹理的宽高调整为2的整数幂,
尽量不改变宽高比
- -nmap:将高度映射转换到法线映射。必须包含以下一个以上的标志位组合
- r:使用输入的r作为高度
- g:使用输入的g作为高度
- b:使用输入的b作为高度
- a:使用输入的a作为高度
- l:由输入的rgb计算得到的亮度作为高度
- o:计算一个粗略的遮挡值,将其编码至alpha
- -nmapamp:法线贴图的振幅,默认为1(不懂可以看这ComputeNormalMap)
-
示例
loading
下一步实现立方体贴图,加入背景图
reference
Directx12 3D 游戏开发实战
标签:贴图,BlinnPhong,lights,light,float,DX12,D3D12,pTexture,float3 From: https://www.cnblogs.com/chenglixue/p/17360402.html