TheRedDog 2025-08-01 12:20 采纳率: 0%
浏览 5

D3D12方向光计算有误

在使用D3D12计算光照时(现在只写了方向光),渲染了一个球体,有明显问题,着色器核心代码如下:

VSOutput VS(VSInput vIn)
{
    VSOutput output;
    // float4x4 m = mul(world, viewPorj);
    output.vPos = mul(mul(float4(vIn.mPos, 1.0), world), viewPorj);
    output.vPosWorld = mul(float4(vIn.mPos, 1.0), world).xyz;
    output.vNormal = mul(vIn.mNurmal, (float3x3) world);
    output.vNormal = normalize(output.vNormal);
    return output;
}

// 光照辅助函数
float3 CalcAttenuation(float d, float falloffStart, float falloffEnd)
{
    if (d < falloffStart)
        return 1.0f;
    if (d > falloffEnd)
        return 0.0f;
    return saturate((falloffEnd - d) / (falloffEnd - falloffStart));
}

float3 SchlickFrensnel(float cosTheta, float3 R0)
{
    float m = 1.0 - cosTheta;
    return R0 + (1.0f - R0) * (m * m * m * m * m);
}

float3 Phonglight(float3 normal, float3 viewDir, float3 lightVec, float3 lightColor)
{
    float m = roughness * 256.0f;
    float3 halfVector = normalize(viewDir + lightVec);
    float roughnessFactor = max(0.0f, dot(normal, halfVector));
    roughnessFactor = (m + 8.0f) / 8.0f * pow(roughnessFactor, m);
    float3 Frensnel = SchlickFrensnel(dot(viewDir, halfVector), FrensnelR0);
    float3 spec = roughnessFactor * Frensnel;
    spec = saturate(spec);
    return (spec + (float3) diffuseColor) * lightColor;
}

float3 ComputeDirectionLight(float3 normal, float3 pos, Light light)
{
    float3 lightVec = -light.direction;
    lightVec = normalize(lightVec);
    float ndotl = dot(normal, lightVec);
    ndotl = max(ndotl, 0.0f);
    return ndotl * Phonglight(normal, ((float3) cameraPos - pos), lightVec, light.color);
}

float4 PS(VSOutput pIn) : SV_TARGET
{
    float3 normal = normalize(pIn.vNormal);
    float3 color = ambient * (float3) diffuseColor;
    for (int i = 0; i < lightCount;i++)
    {
        if (lights[i].type == 1) // 平行光源
            color += ComputeDirectionLight(normal, pIn.vPosWorld, lights[i]);
    }
    color = saturate(color);
    return float4(color, diffuseColor.a);
}

PIX抓帧得:

img


然后得到一个这样的窗口:

img


明显,底部和中间不对,而左边是对的
帮忙看一下!

  • 写回答

5条回答 默认 最新

  • 檀越@新空间 2025-08-01 12:21
    关注

    中午好🌞🌞🌞
    本答案参考通义千问

    根据你提供的代码和描述,你的 D3D12方向光计算存在明显错误。问题主要集中在以下几点:


    一、核心问题分析

    1. 着色器中未正确处理光照方向与法线的关系

    ComputeDirectionLight 函数中,你使用了 lightVec = -light.direction;,这是正确的(因为方向光的方向是朝向物体的),但后续没有对 lightVec 进行标准化。

    加粗重点:
    如果没有对 lightVec 进行标准化,会导致光照强度计算不准确,导致渲染结果出现明显偏移或错误。


    2. Phong 光照模型中缺少漫反射项

    你的 Phonglight 函数中只计算了 高光反射项(specular)和 Fresnel 反射,但没有加入 漫反射项(diffuse)。这会导致整个光照效果非常暗,甚至完全缺失。

    加粗重点:
    Phong 模型应包含漫反射(diffuse)、镜面反射(specular)和环境光(ambient)三个部分,缺少任何一部分都会导致渲染错误。


    3. world 矩阵可能未正确传递到着色器

    在 VS 中,你使用了 worldviewproj 矩阵,但在实际代码中,这些矩阵是否通过常量缓冲区正确传递?

    加粗重点:
    如果 world 矩阵未正确传入,那么 vPosWorldvNormal 的计算将不准确,导致光照方向错误。


    二、解决方案

    1. 标准化 lightVec

    确保 lightVec 是单位向量。

    float3 lightVec = normalize(-light.direction);
    

    2. 补充漫反射项

    Phonglight 函数中添加漫反射项:

    float3 Phonglight(float3 normal, float3 viewDir, float3 lightVec, float3 lightColor)
    {
        // 计算漫反射
        float ndotl = max(0.0f, dot(normal, lightVec));
        float3 diffuse = ndotl * lightColor;
    
        // 高光反射
        float m = roughness * 256.0f;
        float3 halfVector = normalize(viewDir + lightVec);
        float roughnessFactor = max(0.0f, dot(normal, halfVector));
        roughnessFactor = (m + 8.0f) / 8.0f * pow(roughnessFactor, m);
    
        float3 Frensnel = SchlickFrensnel(dot(viewDir, halfVector), FrensnelR0);
        float3 spec = roughnessFactor * Frensnel;
    
        return (diffuse + spec) * lightColor;
    }
    

    加粗重点:
    漫反射项必须包含在最终光照计算中,否则整个光照会非常暗,甚至没有颜色。


    3. 检查 world 矩阵是否正确传递

    确保你在 CPU 侧将 world 矩阵写入常量缓冲区,并且在着色器中正确声明了该缓冲区。

    // 在 D3D12 中定义一个常量缓冲区
    struct ConstantBuffer {
        XMFLOAT4X4 world;
        XMFLOAT4X4 view;
        XMFLOAT4X4 proj;
    };
    
    // 在 VS 中声明:
    cbuffer cbPerObject : register(b0) {
        matrix world;
        matrix view;
        matrix proj;
    };
    

    4. 检查 vNormal 是否正确归一化

    你已经在 VS 中对 vNormal 进行了 normalize,这是正确的。

    output.vNormal = mul(vIn.mNurmal, (float3x3) world);
    output.vNormal = normalize(output.vNormal);
    

    三、修改后的完整代码片段

    修改后的 ComputeDirectionLight 函数:

    float3 ComputeDirectionLight(float3 normal, float3 pos, Light light)
    {
        float3 lightVec = normalize(-light.direction); // 标准化
        float ndotl = max(0.0f, dot(normal, lightVec));
        float3 diffuse = ndotl * light.color;
    
        float3 viewDir = normalize(cameraPos - pos); // 相机位置
        float3 halfVector = normalize(viewDir + lightVec);
    
        float m = roughness * 256.0f;
        float roughnessFactor = max(0.0f, dot(normal, halfVector));
        roughnessFactor = (m + 8.0f) / 8.0f * pow(roughnessFactor, m);
    
        float3 Frensnel = SchlickFrensnel(dot(viewDir, halfVector), FrensnelR0);
        float3 spec = roughnessFactor * Frensnel;
    
        return (diffuse + spec) * light.color;
    }
    

    四、建议调试方法

    1. 用简单颜色测试:将 color 设置为 float4(1.0f, 0.0f, 0.0f, 1.0f) 或其他颜色,确认光照是否正常影响。
    2. 输出法线:在 PS 中返回 float4(normal, 1.0f),检查法线是否正确。
    3. 输出光照向量:将 lightVec 输出为颜色,看是否指向正确方向。
    4. 使用 PIX 工具:查看纹理、顶点数据、常量缓冲区是否正确传递。

    五、总结

    | 问题 | 原因 | 解决方案 | |------|------|----------| | 光照方向错误 | lightVec 未标准化 | 添加 normalize() | | 缺少漫反射项 | Phonglight 仅计算高光 | 补充 diffuse 项 | | 法线/世界矩阵错误 | world 未正确传递 | 检查常量缓冲区传递 |


    如果你能提供完整的着色器代码和 C++ 代码片段,我可以进一步帮你定位具体问题。

    评论

报告相同问题?

问题事件

  • 创建了问题 8月1日