102 lines
2.7 KiB
Plaintext
102 lines
2.7 KiB
Plaintext
#include <metal_stdlib>
|
|
using namespace metal;
|
|
|
|
struct Uniforms {
|
|
float4x4 mvp;
|
|
float4x4 model;
|
|
float4x4 view;
|
|
float4x4 projection;
|
|
float4 ambient; // rgb, unused
|
|
float4 light_dir; // xyz normalized, unused
|
|
float4 light_color; // rgb, intensity
|
|
float4 fog_params; // near, far, unused, enabled
|
|
float4 fog_color; // rgb, unused
|
|
float4 tint; // rgba
|
|
float4 style_params; // style_id, vertex_snap, affine, dither
|
|
float4 resolution; // w, h, unused, unused
|
|
};
|
|
|
|
struct VertexIn {
|
|
float3 position [[attribute(0)]];
|
|
float3 normal [[attribute(1)]];
|
|
float2 uv [[attribute(2)]];
|
|
float4 color [[attribute(3)]];
|
|
};
|
|
|
|
struct VertexOut {
|
|
float4 position [[position]];
|
|
float3 world_normal;
|
|
float2 uv;
|
|
float4 color;
|
|
float fog_factor;
|
|
float3 noperspective_uv; // For affine texturing (PS1 style)
|
|
};
|
|
|
|
vertex VertexOut vertex_main(
|
|
VertexIn in [[stage_in]],
|
|
constant Uniforms &uniforms [[buffer(1)]]
|
|
) {
|
|
VertexOut out;
|
|
|
|
float4 world_pos = uniforms.model * float4(in.position, 1.0);
|
|
float4 clip_pos = uniforms.mvp * float4(in.position, 1.0);
|
|
|
|
// PS1-style vertex snapping
|
|
float style_id = uniforms.style_params.x;
|
|
float vertex_snap = uniforms.style_params.y;
|
|
|
|
if (vertex_snap > 0.5 && style_id < 0.5) {
|
|
// PS1 style: snap vertices to grid
|
|
float2 res = uniforms.resolution.xy;
|
|
float2 snap_res = res * 0.5;
|
|
|
|
// Snap in clip space after perspective divide
|
|
float4 snapped = clip_pos;
|
|
if (snapped.w > 0.0) {
|
|
float2 ndc = snapped.xy / snapped.w;
|
|
ndc = floor(ndc * snap_res + 0.5) / snap_res;
|
|
snapped.xy = ndc * snapped.w;
|
|
}
|
|
clip_pos = snapped;
|
|
}
|
|
|
|
out.position = clip_pos;
|
|
|
|
// Transform normal to world space
|
|
float3x3 normal_matrix = float3x3(
|
|
uniforms.model[0].xyz,
|
|
uniforms.model[1].xyz,
|
|
uniforms.model[2].xyz
|
|
);
|
|
out.world_normal = normalize(normal_matrix * in.normal);
|
|
|
|
// UV coordinates
|
|
out.uv = in.uv;
|
|
|
|
// For affine texturing (PS1), we pass UV * w to fragment shader
|
|
// and divide by interpolated w there
|
|
float affine = uniforms.style_params.z;
|
|
if (affine > 0.5) {
|
|
out.noperspective_uv = float3(in.uv * clip_pos.w, clip_pos.w);
|
|
} else {
|
|
out.noperspective_uv = float3(in.uv, 1.0);
|
|
}
|
|
|
|
// Vertex color
|
|
out.color = in.color;
|
|
|
|
// Fog calculation (linear fog)
|
|
float fog_enabled = uniforms.fog_params.w;
|
|
if (fog_enabled > 0.5) {
|
|
float4 view_pos = uniforms.view * world_pos;
|
|
float dist = length(view_pos.xyz);
|
|
float fog_near = uniforms.fog_params.x;
|
|
float fog_far = uniforms.fog_params.y;
|
|
out.fog_factor = clamp((fog_far - dist) / (fog_far - fog_near), 0.0, 1.0);
|
|
} else {
|
|
out.fog_factor = 1.0;
|
|
}
|
|
|
|
return out;
|
|
}
|