You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebGPU 3D渲染器方向着色显示不均匀问题排查

Hey there! Let's figure out why you're getting that blocky 4-section look with Gouraud shading instead of the smooth result you want. The biggest issue is hiding in your OBJ loader's normal handling—let's break this down:

1. You're Missing Vertex Normal Normalization

Right now, your code accumulates face normals into each shared vertex, but it never normalizes the final vertex normals. Here's why that causes blocky shading:

  • When you add multiple face normals to a vertex, the resulting vector's length depends on how many faces share that vertex.
  • Unnormalized normals will give inconsistent dot product results in your vertex shader, leading to abrupt color jumps between vertex groups (hence the 4 distinct blocks).

Fix this by adding a normalization step after processing all faces in your OBJ loader:

// Add this right before creating the normalBuffer
for (let i = 0; i < normals.length; i += 3) {
    const nx = normals[i];
    const ny = normals[i + 1];
    const nz = normals[i + 2];
    const mag = Math.sqrt(nx * nx + ny * ny + nz * nz);
    
    // Avoid division by zero for isolated vertices
    if (mag > 0.0001) {
        normals[i] = nx / mag;
        normals[i + 1] = ny / mag;
        normals[i + 2] = nz / mag;
    }
}

let normalBuffer = self.createGPUBuffer(device, normals, GPUBufferUsage.VERTEX);

This ensures every vertex normal is a unit vector, so the lighting calculations in your vertex shader will be consistent across the mesh, leading to smooth interpolation between vertices.

2. Double-Check Your Normal Matrix Setup

Your shader uses a mat4 for the normal matrix, but normals are direction vectors (they don't need translation) and require the inverse transpose of the model-view matrix to preserve their perpendicularity to surfaces. Here's a quick fix:

  • On the CPU side, compute the normal matrix as the inverse transpose of your model-view matrix, then pass it as a mat3 (more efficient than mat4):
    // Example CPU-side calculation (using gl-matrix)
    const modelView = mat4.multiply(mat4.create(), viewMatrix, modelMatrix);
    const normalMatrix = mat3.fromMat4(mat3.create(), modelView);
    mat3.invert(normalMatrix, normalMatrix);
    mat3.transpose(normalMatrix, normalMatrix);
    
  • Update your shader's uniform to use mat3x3<f32>:
    @group(0) @binding(2) var<uniform> normalMatrix: mat3x3<f32>;
    
    // In vs_main, simplify the normal transform:
    var n:vec3<f32> = normalize(normalMatrix * inNormal);
    

3. Fix Light/View Direction Transforms

Right now, you're using the normal matrix to transform light and view directions—which is incorrect. Normal matrices are for normals only! Direction vectors like light/view should be transformed with the model-view matrix (without inverse transpose):

// In vs_main, replace your lightDir/viewDir calculations with this:
var viewDir:vec3<f32> = normalize(-viewDirection); // Ensure viewDirection is in view space
var lightDir:vec3<f32> = normalize(-lightDirection); // Ensure lightDirection is in view space

If your light/view directions are in world space, transform them to view space on the CPU first (using the view matrix) instead of doing it in the shader with the normal matrix.

Start with the normalization fix first—it's almost certainly the main cause of your blocky shading. Once that's done, tweak the other steps if you still see inconsistencies.

内容的提问来源于stack exchange,提问作者cowcow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 06:42:35