@group(0) @binding(0) var viewProjectionMatrix : mat4x4; @group(0) @binding(1) var instancePositions : array>; @group(0) @binding(2) var cameraPosition : vec3; @group(0) @binding(3) var myTexture : texture_2d; @group(0) @binding(4) var mySampler : sampler; @group(0) @binding(5) var normalMapTexture : texture_2d; struct VertexOutput { @builtin(position) position : vec4, @location(0) worldPosition : vec3, @location(1) worldNormal : vec3, @location(2) worldBitangent : vec3, @location(3) uv : vec2, @location(4) meshIndex : f32 }; @vertex fn vertexEntryPoint( @location(0) position : vec3, @location(1) normal : vec3, @location(2) bitangent : vec3, @location(3) uv : vec2, @builtin(instance_index) instanceIndex : u32 ) -> VertexOutput { var output : VertexOutput; let instanceOffset = instancePositions[instanceIndex].xyz; let worldPosition = position + instanceOffset; output.worldPosition = worldPosition; output.worldNormal = normalize(normal); output.worldBitangent = normalize(bitangent); output.position = viewProjectionMatrix * vec4(worldPosition, 1.0); output.uv = uv; output.meshIndex = f32( instanceIndex ); return output; } @fragment fn fragmentEntryPoint( @location(0) worldPosition : vec3, @location(1) worldNormal : vec3, @location(2) worldBitangent : vec3, @location(3) uv : vec2, @location(4) meshIndex : f32 ) -> @location(0) vec4 { // For test: encode UV as color (no texture sampling) //let baseColor = vec3(uv, 0.0); let baseColor = textureSample(myTexture, mySampler, uv).rgb; // Sample normal map and decode let normalMapSample = textureSample(normalMapTexture, mySampler, uv).rgb; let tangentSpaceNormal = normalMapSample * 2.0 - 1.0; // Convert [0,1] to [-1,1] // Construct TBN matrix let n = normalize(worldNormal); let B = normalize(worldBitangent); let T = normalize(cross(B, n)); let TBN = mat3x3(T, B, n); // Transform normal to world space let mappedNormal = normalize(TBN * tangentSpaceNormal); let pi = 3.14159265; let invPi = 0.318309886; let N = normalize(mappedNormal); let V = normalize(cameraPosition - worldPosition); let L = normalize(vec3(0.5, 1.0, 0.3)); let H = normalize(V + L); let roughnessIndex = meshIndex / 30.0 % 1.0; let metallic = 0.2; let roughness = roughnessIndex; let rough2 = roughness * roughness; let lightColor = vec3(1.0); let NdotV = max(dot(N, V), 0.001); let NdotL = max(dot(N, L), 0.001); let NdotH = max(dot(N, H), 0.001); let HdotV = max(dot(H, V), 0.001); let F0 = mix(vec3(0.04), baseColor, metallic); let f = pow(1.0 - HdotV, 5.0); let F = F0 + (1.0 - F0) * f; let a2 = rough2 * rough2; let NdotH2 = NdotH * NdotH; let denom = NdotH2 * (a2 - 1.0) + 1.0; let NDF = a2 / (pi * denom * denom); let k = (roughness + 1.0); let k2 = (k * k) / 8.0; let Gv = NdotV / (NdotV * (1.0 - k2) + k2); let Gl = NdotL / (NdotL * (1.0 - k2) + k2); let G = Gv * Gl; let spec = (NDF * G * F) / (4.0 * NdotV * NdotL + 0.001); let kd = (vec3(1.0) - F) * (1.0 - metallic); let diff = kd * baseColor * invPi; let color = (diff + spec) * lightColor * NdotL + vec3(0.03) * baseColor; return vec4(pow(color, vec3(1.0 / 2.2)), 1.0); }