webgpu-app/index.wgsl
2026-07-18 14:28:40 -05:00

119 lines
3.3 KiB
WebGPU Shading Language

struct Configuration {
lighting: vec4f,
matrix: mat4x4f,
matrixWorld: mat4x4f,
};
@group(0) @binding(0) var<uniform> config: Configuration;
@group(0) @binding(1) var<storage> state: array<u32>;
struct ModelConfiguration {
size: vec4f,
color: array<vec4f, 256>,
};
@group(1) @binding(0) var<uniform> modelConfiguration: ModelConfiguration;
struct VertexInput {
@location(0) position: vec3f,
@location(1) texture: vec2f,
@location(2) normal: vec3f,
@location(3) tangent: vec4f,
@location(4) instancePosition: vec4<u32>,
@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);
}