传统材质和照明模型使用“环境”、“漫反射”、“镜面反射”和“自发光”颜色,这就是您在 Wavefront OBJ 文件中找到这些颜色的原因。这些通常可以被替换或与纹理颜色相乘。
(现已停产)XNA Game Studio 产品在 BasicEffect“Stock Shaders”中提供简单的“经典”着色器方面做得很好。我在 DirectX Tool Kit 中将它们用于DX11 和DX12。
查看BasicEffect.fx 了解传统材质像素着色器。如果您主要寻找像素着色器处理,那就是“逐像素光照”,而不是“顶点光照”,后者在我们没有强大的 GPU 时更常见。
这是一个“内联”版本,因此您可以在一个地方进行所有操作:
struct VSInputNmTx
{
float4 Position : SV_Position;
float3 Normal : NORMAL;
float2 TexCoord : TEXCOORD0;
};
Texture2D<float4> Texture : register(t0);
sampler Sampler : register(s0);
cbuffer Parameters : register(b0)
{
float4 DiffuseColor : packoffset(c0);
float3 EmissiveColor : packoffset(c1);
float3 SpecularColor : packoffset(c2);
float SpecularPower : packoffset(c2.w);
float3 LightDirection[3] : packoffset(c3);
float3 LightDiffuseColor[3] : packoffset(c6);
float3 LightSpecularColor[3] : packoffset(c9);
float3 EyePosition : packoffset(c12);
float3 FogColor : packoffset(c13);
float4 FogVector : packoffset(c14);
float4x4 World : packoffset(c15);
float3x3 WorldInverseTranspose : packoffset(c19);
float4x4 WorldViewProj : packoffset(c22);
};
struct VSOutputPixelLightingTx
{
float2 TexCoord : TEXCOORD0;
float4 PositionWS : TEXCOORD1;
float3 NormalWS : TEXCOORD2;
float4 Diffuse : COLOR0;
float4 PositionPS : SV_Position;
};
// Vertex shader: pixel lighting + texture.
VSOutputPixelLighting VSBasicPixelLightingTx(VSInputNmTx vin)
{
VSOutputPixelLighting vout;
vout.PositionPS = mul(vin.Position, WorldViewProj);
vout.PositionWS.xyz = mul(vin.Position, World).xyz;
// ComputeFogFactor
vout.PositionWS.w = saturate(dot(vin.Position, FogVector));
vout.NormalWS = normalize(mul(vin.Normal, WorldInverseTranspose));
vout.Diffuse = float4(1, 1, 1, DiffuseColor.a);
vut.TexCoord = vin.TexCoord;
return vout;
}
struct PSInputPixelLightingTx
{
float2 TexCoord : TEXCOORD0;
float4 PositionWS : TEXCOORD1;
float3 NormalWS : TEXCOORD2;
float4 Diffuse : COLOR0;
};
// Pixel shader: pixel lighting + texture.
float4 PSBasicPixelLightingTx(PSInputPixelLightingTx pin) : SV_Target0
{
float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse;
float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz);
float3 worldNormal = normalize(pin.NormalWS);
ColorPair lightResult = ComputeLights(eyeVector, worldNormal, 3);
color.rgb *= lightResult.Diffuse;
// AddSpecular
color.rgb += lightResult.Specular * color.a;
// ApplyFog (we passed fogfactor in via PositionWS.w)
color.rgb = lerp(color.rgb, FogColor * color.a, pin.PositionWS.w);
return color;
}
这里是辅助函数 ComputeLights,它为镜面高光实现了 Blinn-Phong 反射模型。
struct ColorPair
{
float3 Diffuse;
float3 Specular;
};
ColorPair ComputeLights(float3 eyeVector, float3 worldNormal, uniform int numLights)
{
float3x3 lightDirections = 0;
float3x3 lightDiffuse = 0;
float3x3 lightSpecular = 0;
float3x3 halfVectors = 0;
[unroll]
for (int i = 0; i < numLights; i++)
{
lightDirections[i] = LightDirection[i];
lightDiffuse[i] = LightDiffuseColor[i];
lightSpecular[i] = LightSpecularColor[i];
halfVectors[i] = normalize(eyeVector - lightDirections[i]);
}
float3 dotL = mul(-lightDirections, worldNormal);
float3 dotH = mul(halfVectors, worldNormal);
float3 zeroL = step(0, dotL);
float3 diffuse = zeroL * dotL;
float3 specular = pow(max(dotH, 0) * zeroL, SpecularPower) * dotL;
ColorPair result;
result.Diffuse = mul(diffuse, lightDiffuse) * DiffuseColor.rgb + EmissiveColor;
result.Specular = mul(specular, lightSpecular) * SpecularColor;
return result;
}
这些 BasicEffect 着色器不使用环境色,但您可以根据需要对其进行修改。所有环境色所做的只是提供一个独立于动态光的“最小颜色值”。
请注意,某些 Wavefront OBJ 文件中还有一些非官方的基于物理的渲染 (PBR) 材质扩展。见Extending Wavefront MTL for Physically-Based。更现代的几何格式(如glTF)假设 PBR 材料属性,例如反照率纹理、正常纹理、粗糙度/金属度纹理等。