Blender4iOS/Sources/Shaders/Shaders.metal

158 lines
5.7 KiB
Metal
Raw Normal View History

2026-04-10 21:34:23 -07:00
// Shaders.metal
// BlenderiOS viewport shaders — cube, infinite grid, axis lines.
#include <metal_stdlib>
#include "ShaderTypes.h"
using namespace metal;
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// MARK: - Mesh (default cube)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
struct MeshVertexOut {
float4 position [[position]];
float3 worldPos;
float3 worldNormal;
float4 color;
};
vertex MeshVertexOut mesh_vertex(
const device VertexIn *vertices [[buffer(0)]],
constant Uniforms &uniforms [[buffer(1)]],
uint vid [[vertex_id]]
) {
MeshVertexOut out;
float4 worldPos = uniforms.modelMatrix * float4(vertices[vid].position, 1.0);
out.worldPos = worldPos.xyz;
out.position = uniforms.projectionMatrix * uniforms.viewMatrix * worldPos;
out.worldNormal = (uniforms.normalMatrix * float4(vertices[vid].normal, 0.0)).xyz;
out.color = vertices[vid].color;
return out;
}
fragment float4 mesh_fragment(
MeshVertexOut in [[stage_in]],
constant SceneConstants &scene [[buffer(0)]]
) {
float3 N = normalize(in.worldNormal);
float3 L = normalize(-scene.lightDirection);
float ambient = scene.ambientIntensity;
float diffuse = scene.diffuseIntensity * max(dot(N, L), 0.0);
// Rim light for that Blender viewport feel
float3 V = normalize(scene.cameraPosition - in.worldPos);
float rim = pow(1.0 - max(dot(N, V), 0.0), 3.0) * 0.15;
float3 lit = in.color.rgb * (ambient + diffuse) + rim;
return float4(lit, in.color.a);
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// MARK: - Wireframe overlay
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
struct WireVertexOut {
float4 position [[position]];
float4 color;
};
vertex WireVertexOut wire_vertex(
const device VertexIn *vertices [[buffer(0)]],
constant Uniforms &uniforms [[buffer(1)]],
uint vid [[vertex_id]]
) {
WireVertexOut out;
// Slight bias toward camera to sit on top of solid faces
float4 worldPos = uniforms.modelMatrix * float4(vertices[vid].position, 1.0);
float4 clipPos = uniforms.projectionMatrix * uniforms.viewMatrix * worldPos;
clipPos.z -= 0.0001;
out.position = clipPos;
out.color = float4(0.0, 0.0, 0.0, 0.35);
return out;
}
fragment float4 wire_fragment(WireVertexOut in [[stage_in]]) {
return in.color;
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// MARK: - Infinite grid
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
struct GridVertexOut {
float4 position [[position]];
float3 worldPos;
};
vertex GridVertexOut grid_vertex(
uint vid [[vertex_id]],
constant Uniforms &uniforms [[buffer(0)]]
) {
// Fullscreen quad on the XZ plane
float2 corners[6] = {
{-1, -1}, { 1, -1}, { 1, 1},
{-1, -1}, { 1, 1}, {-1, 1}
};
float scale = 50.0;
float3 worldPos = float3(corners[vid].x * scale, 0.0, corners[vid].y * scale);
GridVertexOut out;
out.worldPos = worldPos;
out.position = uniforms.projectionMatrix * uniforms.viewMatrix * float4(worldPos, 1.0);
return out;
}
fragment float4 grid_fragment(
GridVertexOut in [[stage_in]],
constant GridConstants &grid [[buffer(0)]],
constant SceneConstants &scene [[buffer(1)]]
) {
float2 coord = in.worldPos.xz / grid.gridSpacing;
float2 deriv = fwidth(coord);
float2 gridLine = abs(fract(coord - 0.5) - 0.5) / deriv;
float line = min(gridLine.x, gridLine.y);
float alpha = 1.0 - min(line, 1.0);
// Fade with distance from camera
float dist = length(in.worldPos - scene.cameraPosition);
float fade = 1.0 - smoothstep(grid.gridFadeDistance * 0.5,
grid.gridFadeDistance, dist);
alpha *= fade * 0.3;
// Highlight X axis (red) and Z axis (green-ish blue)
float4 color = grid.color;
if (abs(in.worldPos.z) < deriv.y * grid.gridSpacing * 0.5) {
color = float4(0.78, 0.14, 0.22, 1.0); // X axis red
alpha = fade * 0.8;
}
if (abs(in.worldPos.x) < deriv.x * grid.gridSpacing * 0.5) {
color = float4(0.22, 0.45, 0.78, 1.0); // Z axis blue
alpha = fade * 0.8;
}
if (alpha < 0.005) discard_fragment();
return float4(color.rgb, alpha);
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// MARK: - Origin axis gizmo (3 lines)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
vertex WireVertexOut axis_vertex(
const device VertexIn *vertices [[buffer(0)]],
constant Uniforms &uniforms [[buffer(1)]],
uint vid [[vertex_id]]
) {
WireVertexOut out;
float4 worldPos = float4(vertices[vid].position, 1.0);
out.position = uniforms.projectionMatrix * uniforms.viewMatrix * worldPos;
out.color = vertices[vid].color;
return out;
}
fragment float4 axis_fragment(WireVertexOut in [[stage_in]]) {
return in.color;
}