struct Configuration { lighting: vec4f, matrix: mat4x4f, matrixWorld: mat4x4f, }; @group(0) @binding(0) var config: Configuration; @group(0) @binding(1) var state: array; struct ModelConfiguration { size: vec4f, color: array, }; @group(1) @binding(0) var modelConfiguration: ModelConfiguration; struct VertexInput { @location(0) position: vec3f, @location(1) texture: vec2f, @location(2) normal: vec3f, @location(3) tangent: vec4f, @location(4) instancePosition: vec4, @builtin(instance_index) instance: u32, }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) positionWorld: vec3f, @interpolate(flat) @location(1) colorIndex: i32, @location(2) normal: vec3f, @interpolate(flat) @location(3) lighting: i32, }; struct FragmentInput { @location(0) positionWorld: vec3f, @interpolate(flat) @location(1) colorIndex: i32, @location(2) normal: vec3f, @interpolate(flat) @location(3) lighting: i32, }; @vertex fn vertexMain(input: VertexInput) -> VertexOutput { let position = input.position * 0.5 + 0.5; let instance = f32(input.instance); /* let state = f32(state[input.instance]); let cell = vec2f(instance % config.gridSize.x, floor(instance / config.gridSize.y)); let grid = (position * state + vec3f(cell, 0)) / config.gridSize.xxx; */ let size = max(max(modelConfiguration.size.x, modelConfiguration.size.y), modelConfiguration.size.z); let cell = (position * 1 + vec3f(input.instancePosition.xzy) - (modelConfiguration.size.xzy/2)) / size; var output: VertexOutput; output.position = config.matrix * vec4f(cell, 1); output.positionWorld = (config.matrixWorld * vec4f(cell, 1.0f)).xyz; output.colorIndex = i32(input.instancePosition.w); output.normal = (config.matrixWorld * vec4f(input.normal, 0.0f)).xyz; output.lighting = i32(config.lighting.x != 0.0); return output; } fn schlickFresnel(R0: vec3f, normal: vec3f, lightVector: vec3f) -> vec3f { let incidentAngle = saturate(dot(normal, lightVector)); let f0 = 1.0 - incidentAngle; let reflectPercent = R0 + (1.0f - R0) * (f0 * f0 * f0 * f0 * f0); return reflectPercent; } fn blinnPhong(ndotl: f32, lightVector: vec3f, normal: vec3f, toEye: vec3f) -> vec3f { let roughness = 0.1; let m = roughness * 256.0; let halfVec = normalize(toEye + lightVector); let roughnessFactor = (m + 8.0) * pow(ndotl, m) / 8.0f; let fresnelR0 = vec3f(0.95f, 0.95f, 0.95f); let fresnelFactor = schlickFresnel(fresnelR0, normal, lightVector); let specular = fresnelFactor * roughnessFactor; return specular; } fn lighting(position: vec3f, normal: vec3f) -> vec3f { let d = 0.9; let eyePosition = vec3f(d, d / 2, d); let lightPosition = vec3f(1, 1, 1); let lightVector = normalize(lightPosition - position); let ndotl = max(dot(lightVector, normal), 0.0); let intensity = (ndotl + 0.3) / 1.3; let toEye = normalize(eyePosition - position); return (1.0 + blinnPhong(ndotl, lightVector, normal, toEye)) * intensity; } @fragment fn fragmentMain(input: FragmentInput) -> @location(0) vec4f { let baseColor = modelConfiguration.color[input.colorIndex - 1] / 255.0; var intensity = vec3f(1.0); if (input.lighting == 1) { intensity = lighting(input.positionWorld, normalize(input.normal)); } return vec4(baseColor.xyz * intensity, baseColor.w); }