【问题标题】:How to use pixel shader to render material from a Wavefront Obj file?如何使用像素着色器从 Wavefront Obj 文件中渲染材质?
【发布时间】:2021-12-04 12:00:01
【问题描述】:

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

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

【问题讨论】:

  • 就在最近,我们在将 Wavefront .obj 材质映射到我们自己的场景图中时遇到了困难。 (它是用 OpenGL 实现的。)我们意识到有些功能似乎可以解决光线追踪问题,只是忽略了它们。其余的(特别是 Ambient、Diffuse、Specular 和 Emissive RGB 数据)与我们已经在 fix-function 管道中使用的常用光照计算一起使用。尝试在谷歌上搜索“Direct3d Light Material”。我发现例如Lights and Materials (Direct3D 9).

标签: c++ directx hlsl wavefront


【解决方案1】:

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

(现已停产)XNA Game Studio 产品在 BasicEffect“Stock Shaders”中提供简单的“经典”着色器方面做得很好。我在 DirectX Tool Kit 中将它们用于DX11DX12

查看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 材料属性,例如反照率纹理、正常纹理、粗糙度/金属度纹理等。

【讨论】:

  • 很好的解释,所以基本上我应该把我在光计算后得到的颜色值和它们各自的材料混合,这样我才能得到正确的值?在我看来,材料看起来更像是一个变量,它告诉您该对象的“光量”,正如您所说的“乘法合取”。
  • 在经典照明中,您将“材质颜色”乘以“灯光颜色”以从该灯光中获得有效照明。
猜你喜欢
  • 2019-10-18
  • 1970-01-01
  • 1970-01-01
  • 2017-02-10
  • 2020-08-28
  • 2012-08-20
  • 1970-01-01
  • 2017-09-30
  • 2019-03-01
相关资源
最近更新 更多