如何使用像素着色器从 Wavefront Obj 文件渲染 material?

How to use pixel shader to render material from a Wavefront Obj file?

一些导出为 Wavefront.obj 格式的 3d 网格通常带有一个 .mtl 文件,该文件包含其使用的纹理及其 materials 的附加数据,当从 Blender 导出时,它们总是会出现将环境光、漫反射、镜面反射和自发光 RGB 数据作为其 material 的一部分,但我不确定如何在像素着色器中使用这些数据并获得正确的颜色输出。

如果有人能向我解释如何使用这些 material,我将不胜感激,欢迎任何代码示例。

传统的 material 和照明模型使用“环境光”、“漫反射”、“镜面反射”和“自发光”颜色,这就是您在 Wavefront OBJ 文件中找到这些颜色的原因。这些通常可以替换或与纹理颜色相乘使用。

(现已停产)XNA Game Studio 产品在 BasicEffect“Stock Shaders”中提供简单的 'classic' 着色器方面做得很好。我在 DirectX 工具包 中使用它们 DX11 and DX12

看看 BasicEffect.fx 的传统 material 像素着色器。如果您主要寻找像素着色器处理,那就是“逐像素照明”,而不是“顶点照明”,后者在我们的 GPU 功能较弱时更为常见。

这是一个 'inlined' 版本,因此您可以在一个地方完成所有操作:

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 着色器不使用环境颜色,但如果需要,您可以对其进行修改。所有环境颜色都提供了一个独立于动态光的 'minimum color value'。

Note that there also some unofficial Physically-Based Rendering (PBR) materials extension in some Wavefront OBJ files. See Extending Wavefront MTL for Physically-Based. More modern geometry formats like glTF assume PBR materials properties which is things like an albedo texture, normal texture, roughness/metalness texture, etc..