From 7cccedb5fbdde377e1c0a13757a712699d1e6cd6 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sun, 28 Sep 2025 18:40:28 +0200 Subject: [PATCH] =?UTF-8?q?Extraer=20shaders=20a=20archivos=20.metal=20sep?= =?UTF-8?q?arados=20para=20mejor=20organizaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Crear directorio data/shaders/ para organizar todos los shaders MSL - Extraer shaders embebidos a archivos individuales: * background.metal - Shader de fondo degradado * triangle.metal - Shader de triángulo multicolor * sprite.metal - Shader de sprites con vertex color tinting * crt.metal - Shader CRT post-processing completo - Modificar main.cpp para cargar shaders desde archivos: * Usar stringWithContentsOfFile para leer código fuente * Compilar dinámicamente con newLibraryWithSource * Manejo robusto de errores de lectura y compilación - Eliminar 351 líneas de strings embebidos de main.cpp - Mantener funcionalidad completa: CRT + sprites + fondo + triángulo Beneficios: - Shaders editables sin recompilar ejecutable - Mejor organización y mantenimiento del código - Syntax highlighting completo en editores - Reutilización de shaders en otros proyectos - Desarrollo más ágil de efectos visuales 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- data/shaders/background.metal | 40 ++++ data/shaders/crt.metal | 158 ++++++++++++++ data/shaders/sprite.metal | 29 +++ data/shaders/triangle.metal | 33 +++ source/main.cpp | 400 +++++----------------------------- 5 files changed, 309 insertions(+), 351 deletions(-) create mode 100644 data/shaders/background.metal create mode 100644 data/shaders/crt.metal create mode 100644 data/shaders/sprite.metal create mode 100644 data/shaders/triangle.metal diff --git a/data/shaders/background.metal b/data/shaders/background.metal new file mode 100644 index 0000000..9b94e35 --- /dev/null +++ b/data/shaders/background.metal @@ -0,0 +1,40 @@ +#include +using namespace metal; + +struct VertexOut { + float4 position [[position]]; + float4 color; +}; + +vertex VertexOut background_vertex_main(uint vertexID [[vertex_id]]) { + VertexOut out; + + // Quad de pantalla completa en coordenadas NDC + float2 positions[6] = { + float2(-1.0, -1.0), // Bottom left + float2( 1.0, -1.0), // Bottom right + float2(-1.0, 1.0), // Top left + float2( 1.0, -1.0), // Bottom right + float2( 1.0, 1.0), // Top right + float2(-1.0, 1.0) // Top left + }; + + // Gradiente de púrpura oscuro arriba a azul cyan abajo + float4 colors[6] = { + float4(0.2, 0.6, 0.8, 1.0), // Bottom left - cyan claro + float4(0.2, 0.6, 0.8, 1.0), // Bottom right - cyan claro + float4(0.3, 0.1, 0.5, 1.0), // Top left - púrpura oscuro + float4(0.2, 0.6, 0.8, 1.0), // Bottom right - cyan claro + float4(0.3, 0.1, 0.5, 1.0), // Top right - púrpura oscuro + float4(0.3, 0.1, 0.5, 1.0) // Top left - púrpura oscuro + }; + + out.position = float4(positions[vertexID], 0.0, 1.0); + out.color = colors[vertexID]; + + return out; +} + +fragment float4 background_fragment_main(VertexOut in [[stage_in]]) { + return in.color; +} \ No newline at end of file diff --git a/data/shaders/crt.metal b/data/shaders/crt.metal new file mode 100644 index 0000000..42273cd --- /dev/null +++ b/data/shaders/crt.metal @@ -0,0 +1,158 @@ +#include +using namespace metal; + +// Parámetros del CRT shader +#define CURVATURE_X 0.05 +#define CURVATURE_Y 0.1 +#define MASK_BRIGHTNESS 0.80 +#define SCANLINE_WEIGHT 6.0 +#define SCANLINE_GAP_BRIGHTNESS 0.12 +#define BLOOM_FACTOR 3.5 +#define INPUT_GAMMA 2.4 +#define OUTPUT_GAMMA 2.2 + +// Features habilitadas +#define SCANLINES +#define MULTISAMPLE +#define GAMMA +// #define FAKE_GAMMA +// #define CURVATURE +// #define SHARPER +#define MASK_TYPE 2 + +struct CRTVertexOut { + float4 position [[position]]; + float2 texCoord; +}; + +vertex CRTVertexOut crt_vertex_main(uint vertexID [[vertex_id]]) { + CRTVertexOut out; + + // Fullscreen quad con coordenadas de textura + float2 positions[6] = { + float2(-1.0, -1.0), // Bottom left + float2( 1.0, -1.0), // Bottom right + float2(-1.0, 1.0), // Top left + float2( 1.0, -1.0), // Bottom right + float2( 1.0, 1.0), // Top right + float2(-1.0, 1.0) // Top left + }; + + float2 texCoords[6] = { + float2(0.0, 1.0), // Bottom left + float2(1.0, 1.0), // Bottom right + float2(0.0, 0.0), // Top left + float2(1.0, 1.0), // Bottom right + float2(1.0, 0.0), // Top right + float2(0.0, 0.0) // Top left + }; + + out.position = float4(positions[vertexID], 0.0, 1.0); + out.texCoord = texCoords[vertexID]; + + return out; +} + +float CalcScanLineWeight(float dist) { + return max(1.0 - dist * dist * SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS); +} + +float CalcScanLine(float dy, float filterWidth) { + float scanLineWeight = CalcScanLineWeight(dy); +#ifdef MULTISAMPLE + scanLineWeight += CalcScanLineWeight(dy - filterWidth); + scanLineWeight += CalcScanLineWeight(dy + filterWidth); + scanLineWeight *= 0.3333333; +#endif + return scanLineWeight; +} + +fragment float4 crt_fragment_main(CRTVertexOut in [[stage_in]], + texture2d sceneTexture [[texture(0)]], + sampler sceneSampler [[sampler(0)]]) { + float2 TextureSize = float2(320.0, 240.0); // Resolución virtual del CRT + float filterWidth = (768.0 / 240.0) / 3.0; + + float2 texcoord = in.texCoord; + + // Convertir a píxeles + float2 texcoordInPixels = texcoord * TextureSize; + +#ifdef SHARPER + float2 tempCoord = floor(texcoordInPixels) + 0.5; + float2 coord = tempCoord / TextureSize; + float2 deltas = texcoordInPixels - tempCoord; + float scanLineWeight = CalcScanLine(deltas.y, filterWidth); + float2 signs = sign(deltas); + deltas.x *= 2.0; + deltas = deltas * deltas; + deltas.y = deltas.y * deltas.y; + deltas.x *= 0.5; + deltas.y *= 8.0; + deltas /= TextureSize; + deltas *= signs; + float2 tc = coord + deltas; +#else + float tempY = floor(texcoordInPixels.y) + 0.5; + float yCoord = tempY / TextureSize.y; + float dy = texcoordInPixels.y - tempY; + float scanLineWeight = CalcScanLine(dy, filterWidth); + float signY = sign(dy); + dy = dy * dy; + dy = dy * dy; + dy *= 8.0; + dy /= TextureSize.y; + dy *= signY; + float2 tc = float2(texcoord.x, yCoord + dy); +#endif + + // Samplear la textura de escena + float3 colour = sceneTexture.sample(sceneSampler, tc).rgb; + +#ifdef SCANLINES +#ifdef GAMMA +#ifdef FAKE_GAMMA + colour = colour * colour; +#else + colour = pow(colour, float3(INPUT_GAMMA)); +#endif +#endif + scanLineWeight *= BLOOM_FACTOR; + colour *= scanLineWeight; + +#ifdef GAMMA +#ifdef FAKE_GAMMA + colour = sqrt(colour); +#else + colour = pow(colour, float3(1.0 / OUTPUT_GAMMA)); +#endif +#endif +#endif + + // Shadow mask +#if MASK_TYPE == 0 + return float4(colour, 1.0); +#else +#if MASK_TYPE == 1 + float whichMask = fract((in.position.x * 1.0001) * 0.5); + float3 mask; + if (whichMask < 0.5) { + mask = float3(MASK_BRIGHTNESS, 1.0, MASK_BRIGHTNESS); + } else { + mask = float3(1.0, MASK_BRIGHTNESS, 1.0); + } +#elif MASK_TYPE == 2 + float whichMask = fract((in.position.x * 1.0001) * 0.3333333); + float3 mask = float3(MASK_BRIGHTNESS, MASK_BRIGHTNESS, MASK_BRIGHTNESS); + if (whichMask < 0.3333333) { + mask.x = 1.0; + } else if (whichMask < 0.6666666) { + mask.y = 1.0; + } else { + mask.z = 1.0; + } +#endif + + return float4(colour * mask, 1.0); +#endif +} \ No newline at end of file diff --git a/data/shaders/sprite.metal b/data/shaders/sprite.metal new file mode 100644 index 0000000..09410f4 --- /dev/null +++ b/data/shaders/sprite.metal @@ -0,0 +1,29 @@ +#include +using namespace metal; + +struct SpriteVertexIn { + float2 position [[attribute(0)]]; + float4 color [[attribute(1)]]; + float2 texCoord [[attribute(2)]]; +}; + +struct SpriteVertexOut { + float4 position [[position]]; + float4 color; + float2 texCoord; +}; + +vertex SpriteVertexOut sprite_vertex_main(SpriteVertexIn in [[stage_in]]) { + SpriteVertexOut out; + out.position = float4(in.position, 0.0, 1.0); + out.color = in.color; + out.texCoord = in.texCoord; + return out; +} + +fragment float4 sprite_fragment_main(SpriteVertexOut in [[stage_in]], + texture2d spriteTexture [[texture(0)]], + sampler textureSampler [[sampler(0)]]) { + float4 textureColor = spriteTexture.sample(textureSampler, in.texCoord); + return textureColor * in.color; // Multiplicar textura por color de vértice para tinting +} \ No newline at end of file diff --git a/data/shaders/triangle.metal b/data/shaders/triangle.metal new file mode 100644 index 0000000..2b41924 --- /dev/null +++ b/data/shaders/triangle.metal @@ -0,0 +1,33 @@ +#include +using namespace metal; + +struct VertexOut { + float4 position [[position]]; + float4 color; +}; + +vertex VertexOut triangle_vertex_main(uint vertexID [[vertex_id]]) { + VertexOut out; + + // Triángulo simple en coordenadas normalized device coordinates + float2 positions[3] = { + float2( 0.0, 0.5), // Top + float2(-0.5, -0.5), // Bottom left + float2( 0.5, -0.5) // Bottom right + }; + + float4 colors[3] = { + float4(1, 0, 0, 1), // Red + float4(0, 1, 0, 1), // Green + float4(0, 0, 1, 1) // Blue + }; + + out.position = float4(positions[vertexID], 0.0, 1.0); + out.color = colors[vertexID]; + + return out; +} + +fragment float4 triangle_fragment_main(VertexOut in [[stage_in]]) { + return in.color; +} \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp index 6011a23..f7eb84d 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -104,404 +104,102 @@ int main(int argc, char* argv[]) { return -1; } - // Shaders para fondo degradado - NSString* backgroundVertexShaderSource = @R"( -#include -using namespace metal; -struct VertexOut { - float4 position [[position]]; - float4 color; -}; -vertex VertexOut background_vertex_main(uint vertexID [[vertex_id]]) { - VertexOut out; - // Quad de pantalla completa en coordenadas NDC - float2 positions[6] = { - float2(-1.0, -1.0), // Bottom left - float2( 1.0, -1.0), // Bottom right - float2(-1.0, 1.0), // Top left - float2( 1.0, -1.0), // Bottom right - float2( 1.0, 1.0), // Top right - float2(-1.0, 1.0) // Top left - }; - - // Gradiente de púrpura oscuro arriba a azul cyan abajo - float4 colors[6] = { - float4(0.2, 0.6, 0.8, 1.0), // Bottom left - cyan claro - float4(0.2, 0.6, 0.8, 1.0), // Bottom right - cyan claro - float4(0.3, 0.1, 0.5, 1.0), // Top left - púrpura oscuro - float4(0.2, 0.6, 0.8, 1.0), // Bottom right - cyan claro - float4(0.3, 0.1, 0.5, 1.0), // Top right - púrpura oscuro - float4(0.3, 0.1, 0.5, 1.0) // Top left - púrpura oscuro - }; - - out.position = float4(positions[vertexID], 0.0, 1.0); - out.color = colors[vertexID]; - - return out; -} -)"; - - NSString* backgroundFragmentShaderSource = @R"( -#include -using namespace metal; - -struct VertexOut { - float4 position [[position]]; - float4 color; -}; - -fragment float4 background_fragment_main(VertexOut in [[stage_in]]) { - return in.color; -} -)"; - - // Shaders para triángulo - NSString* triangleVertexShaderSource = @R"( -#include -using namespace metal; - -struct VertexOut { - float4 position [[position]]; - float4 color; -}; - -vertex VertexOut triangle_vertex_main(uint vertexID [[vertex_id]]) { - VertexOut out; - - // Triángulo simple en coordenadas normalized device coordinates - float2 positions[3] = { - float2( 0.0, 0.5), // Top - float2(-0.5, -0.5), // Bottom left - float2( 0.5, -0.5) // Bottom right - }; - - float4 colors[3] = { - float4(1, 0, 0, 1), // Red - float4(0, 1, 0, 1), // Green - float4(0, 0, 1, 1) // Blue - }; - - out.position = float4(positions[vertexID], 0.0, 1.0); - out.color = colors[vertexID]; - - return out; -} -)"; - - NSString* triangleFragmentShaderSource = @R"( -#include -using namespace metal; - -struct VertexOut { - float4 position [[position]]; - float4 color; -}; - -fragment float4 triangle_fragment_main(VertexOut in [[stage_in]]) { - return in.color; -} -)"; - - // Shaders para sprites con textura y color - NSString* spriteVertexShaderSource = @R"( -#include -using namespace metal; - -struct SpriteVertexIn { - float2 position [[attribute(0)]]; - float4 color [[attribute(1)]]; - float2 texCoord [[attribute(2)]]; -}; - -struct SpriteVertexOut { - float4 position [[position]]; - float4 color; - float2 texCoord; -}; - -vertex SpriteVertexOut sprite_vertex_main(SpriteVertexIn in [[stage_in]]) { - SpriteVertexOut out; - out.position = float4(in.position, 0.0, 1.0); - out.color = in.color; - out.texCoord = in.texCoord; - return out; -} -)"; - - NSString* spriteFragmentShaderSource = @R"( -#include -using namespace metal; - -struct SpriteVertexOut { - float4 position [[position]]; - float4 color; - float2 texCoord; -}; - -fragment float4 sprite_fragment_main(SpriteVertexOut in [[stage_in]], - texture2d spriteTexture [[texture(0)]], - sampler textureSampler [[sampler(0)]]) { - float4 textureColor = spriteTexture.sample(textureSampler, in.texCoord); - return textureColor * in.color; // Multiplicar textura por color de vértice para tinting -} -)"; - - // Shaders para CRT post-processing (fullscreen quad) - NSString* crtVertexShaderSource = @R"( -#include -using namespace metal; - -struct CRTVertexOut { - float4 position [[position]]; - float2 texCoord; -}; - -vertex CRTVertexOut crt_vertex_main(uint vertexID [[vertex_id]]) { - CRTVertexOut out; - - // Fullscreen quad con coordenadas de textura - float2 positions[6] = { - float2(-1.0, -1.0), // Bottom left - float2( 1.0, -1.0), // Bottom right - float2(-1.0, 1.0), // Top left - float2( 1.0, -1.0), // Bottom right - float2( 1.0, 1.0), // Top right - float2(-1.0, 1.0) // Top left - }; - - float2 texCoords[6] = { - float2(0.0, 1.0), // Bottom left - float2(1.0, 1.0), // Bottom right - float2(0.0, 0.0), // Top left - float2(1.0, 1.0), // Bottom right - float2(1.0, 0.0), // Top right - float2(0.0, 0.0) // Top left - }; - - out.position = float4(positions[vertexID], 0.0, 1.0); - out.texCoord = texCoords[vertexID]; - - return out; -} -)"; - - // CRT Fragment Shader - Migrado de GLSL a MSL - NSString* crtFragmentShaderSource = @R"( -#include -using namespace metal; - -// Parámetros del CRT shader -#define CURVATURE_X 0.05 -#define CURVATURE_Y 0.1 -#define MASK_BRIGHTNESS 0.80 -#define SCANLINE_WEIGHT 6.0 -#define SCANLINE_GAP_BRIGHTNESS 0.12 -#define BLOOM_FACTOR 3.5 -#define INPUT_GAMMA 2.4 -#define OUTPUT_GAMMA 2.2 - -// Features habilitadas -#define SCANLINES -#define MULTISAMPLE -#define GAMMA -// #define FAKE_GAMMA -// #define CURVATURE -// #define SHARPER -#define MASK_TYPE 2 - -struct CRTVertexOut { - float4 position [[position]]; - float2 texCoord; -}; - -float CalcScanLineWeight(float dist) { - return max(1.0 - dist * dist * SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS); -} - -float CalcScanLine(float dy, float filterWidth) { - float scanLineWeight = CalcScanLineWeight(dy); -#ifdef MULTISAMPLE - scanLineWeight += CalcScanLineWeight(dy - filterWidth); - scanLineWeight += CalcScanLineWeight(dy + filterWidth); - scanLineWeight *= 0.3333333; -#endif - return scanLineWeight; -} - -fragment float4 crt_fragment_main(CRTVertexOut in [[stage_in]], - texture2d sceneTexture [[texture(0)]], - sampler sceneSampler [[sampler(0)]]) { - float2 TextureSize = float2(320.0, 240.0); // Resolución virtual del CRT - float filterWidth = (768.0 / 240.0) / 3.0; - - float2 texcoord = in.texCoord; - - // Convertir a píxeles - float2 texcoordInPixels = texcoord * TextureSize; - -#ifdef SHARPER - float2 tempCoord = floor(texcoordInPixels) + 0.5; - float2 coord = tempCoord / TextureSize; - float2 deltas = texcoordInPixels - tempCoord; - float scanLineWeight = CalcScanLine(deltas.y, filterWidth); - float2 signs = sign(deltas); - deltas.x *= 2.0; - deltas = deltas * deltas; - deltas.y = deltas.y * deltas.y; - deltas.x *= 0.5; - deltas.y *= 8.0; - deltas /= TextureSize; - deltas *= signs; - float2 tc = coord + deltas; -#else - float tempY = floor(texcoordInPixels.y) + 0.5; - float yCoord = tempY / TextureSize.y; - float dy = texcoordInPixels.y - tempY; - float scanLineWeight = CalcScanLine(dy, filterWidth); - float signY = sign(dy); - dy = dy * dy; - dy = dy * dy; - dy *= 8.0; - dy /= TextureSize.y; - dy *= signY; - float2 tc = float2(texcoord.x, yCoord + dy); -#endif - - // Samplear la textura de escena - float3 colour = sceneTexture.sample(sceneSampler, tc).rgb; - -#ifdef SCANLINES -#ifdef GAMMA -#ifdef FAKE_GAMMA - colour = colour * colour; -#else - colour = pow(colour, float3(INPUT_GAMMA)); -#endif -#endif - scanLineWeight *= BLOOM_FACTOR; - colour *= scanLineWeight; - -#ifdef GAMMA -#ifdef FAKE_GAMMA - colour = sqrt(colour); -#else - colour = pow(colour, float3(1.0 / OUTPUT_GAMMA)); -#endif -#endif -#endif - - // Shadow mask -#if MASK_TYPE == 0 - return float4(colour, 1.0); -#else -#if MASK_TYPE == 1 - float whichMask = fract((in.position.x * 1.0001) * 0.5); - float3 mask; - if (whichMask < 0.5) { - mask = float3(MASK_BRIGHTNESS, 1.0, MASK_BRIGHTNESS); - } else { - mask = float3(1.0, MASK_BRIGHTNESS, 1.0); - } -#elif MASK_TYPE == 2 - float whichMask = fract((in.position.x * 1.0001) * 0.3333333); - float3 mask = float3(MASK_BRIGHTNESS, MASK_BRIGHTNESS, MASK_BRIGHTNESS); - if (whichMask < 0.3333333) { - mask.x = 1.0; - } else if (whichMask < 0.6666666) { - mask.y = 1.0; - } else { - mask.z = 1.0; - } -#endif - - return float4(colour * mask, 1.0); -#endif -} -)"; - - // Compilar shaders CRT + // Cargar shaders CRT desde archivo NSError* error = nil; - id crtVertexLibrary = [device newLibraryWithSource:crtVertexShaderSource options:nil error:&error]; - if (!crtVertexLibrary || error) { + + NSString* crtShaderPath = @"data/shaders/crt.metal"; + NSString* crtShaderSource = [NSString stringWithContentsOfFile:crtShaderPath encoding:NSUTF8StringEncoding error:&error]; + if (!crtShaderSource || error) { if (error) { - std::cout << "Failed to compile CRT vertex shader: " << [[error localizedDescription] UTF8String] << std::endl; + std::cout << "Failed to read CRT shader file: " << [[error localizedDescription] UTF8String] << std::endl; + } else { + std::cout << "Failed to read CRT shader file: data/shaders/crt.metal" << std::endl; } return -1; } - id crtFragmentLibrary = [device newLibraryWithSource:crtFragmentShaderSource options:nil error:&error]; - if (!crtFragmentLibrary || error) { + id crtLibrary = [device newLibraryWithSource:crtShaderSource options:nil error:&error]; + if (!crtLibrary || error) { if (error) { - std::cout << "Failed to compile CRT fragment shader: " << [[error localizedDescription] UTF8String] << std::endl; + std::cout << "Failed to compile CRT shader: " << [[error localizedDescription] UTF8String] << std::endl; } return -1; } - id crtVertexFunction = [crtVertexLibrary newFunctionWithName:@"crt_vertex_main"]; - id crtFragmentFunction = [crtFragmentLibrary newFunctionWithName:@"crt_fragment_main"]; + id crtVertexFunction = [crtLibrary newFunctionWithName:@"crt_vertex_main"]; + id crtFragmentFunction = [crtLibrary newFunctionWithName:@"crt_fragment_main"]; - // Compilar shaders de fondo - id backgroundVertexLibrary = [device newLibraryWithSource:backgroundVertexShaderSource options:nil error:&error]; - if (!backgroundVertexLibrary || error) { + // Cargar shaders de fondo desde archivo + NSString* backgroundShaderPath = @"data/shaders/background.metal"; + NSString* backgroundShaderSource = [NSString stringWithContentsOfFile:backgroundShaderPath encoding:NSUTF8StringEncoding error:&error]; + if (!backgroundShaderSource || error) { if (error) { - std::cout << "Failed to compile background vertex shader: " << [[error localizedDescription] UTF8String] << std::endl; + std::cout << "Failed to read background shader file: " << [[error localizedDescription] UTF8String] << std::endl; + } else { + std::cout << "Failed to read background shader file: data/shaders/background.metal" << std::endl; } return -1; } - id backgroundFragmentLibrary = [device newLibraryWithSource:backgroundFragmentShaderSource options:nil error:&error]; - if (!backgroundFragmentLibrary || error) { + id backgroundLibrary = [device newLibraryWithSource:backgroundShaderSource options:nil error:&error]; + if (!backgroundLibrary || error) { if (error) { - std::cout << "Failed to compile background fragment shader: " << [[error localizedDescription] UTF8String] << std::endl; + std::cout << "Failed to compile background shader: " << [[error localizedDescription] UTF8String] << std::endl; } return -1; } - id backgroundVertexFunction = [backgroundVertexLibrary newFunctionWithName:@"background_vertex_main"]; - id backgroundFragmentFunction = [backgroundFragmentLibrary newFunctionWithName:@"background_fragment_main"]; + id backgroundVertexFunction = [backgroundLibrary newFunctionWithName:@"background_vertex_main"]; + id backgroundFragmentFunction = [backgroundLibrary newFunctionWithName:@"background_fragment_main"]; - // Compilar shaders de triángulo - id triangleVertexLibrary = [device newLibraryWithSource:triangleVertexShaderSource options:nil error:&error]; - if (!triangleVertexLibrary || error) { + // Cargar shaders de triángulo desde archivo + NSString* triangleShaderPath = @"data/shaders/triangle.metal"; + NSString* triangleShaderSource = [NSString stringWithContentsOfFile:triangleShaderPath encoding:NSUTF8StringEncoding error:&error]; + if (!triangleShaderSource || error) { if (error) { - std::cout << "Failed to compile triangle vertex shader: " << [[error localizedDescription] UTF8String] << std::endl; + std::cout << "Failed to read triangle shader file: " << [[error localizedDescription] UTF8String] << std::endl; + } else { + std::cout << "Failed to read triangle shader file: data/shaders/triangle.metal" << std::endl; } return -1; } - id triangleFragmentLibrary = [device newLibraryWithSource:triangleFragmentShaderSource options:nil error:&error]; - if (!triangleFragmentLibrary || error) { + id triangleLibrary = [device newLibraryWithSource:triangleShaderSource options:nil error:&error]; + if (!triangleLibrary || error) { if (error) { - std::cout << "Failed to compile triangle fragment shader: " << [[error localizedDescription] UTF8String] << std::endl; + std::cout << "Failed to compile triangle shader: " << [[error localizedDescription] UTF8String] << std::endl; } return -1; } - id triangleVertexFunction = [triangleVertexLibrary newFunctionWithName:@"triangle_vertex_main"]; - id triangleFragmentFunction = [triangleFragmentLibrary newFunctionWithName:@"triangle_fragment_main"]; + id triangleVertexFunction = [triangleLibrary newFunctionWithName:@"triangle_vertex_main"]; + id triangleFragmentFunction = [triangleLibrary newFunctionWithName:@"triangle_fragment_main"]; - // Compilar shaders de sprites - id spriteVertexLibrary = [device newLibraryWithSource:spriteVertexShaderSource options:nil error:&error]; - if (!spriteVertexLibrary || error) { + // Cargar shaders de sprites desde archivo + NSString* spriteShaderPath = @"data/shaders/sprite.metal"; + NSString* spriteShaderSource = [NSString stringWithContentsOfFile:spriteShaderPath encoding:NSUTF8StringEncoding error:&error]; + if (!spriteShaderSource || error) { if (error) { - std::cout << "Failed to compile sprite vertex shader: " << [[error localizedDescription] UTF8String] << std::endl; + std::cout << "Failed to read sprite shader file: " << [[error localizedDescription] UTF8String] << std::endl; + } else { + std::cout << "Failed to read sprite shader file: data/shaders/sprite.metal" << std::endl; } return -1; } - id spriteFragmentLibrary = [device newLibraryWithSource:spriteFragmentShaderSource options:nil error:&error]; - if (!spriteFragmentLibrary || error) { + id spriteLibrary = [device newLibraryWithSource:spriteShaderSource options:nil error:&error]; + if (!spriteLibrary || error) { if (error) { - std::cout << "Failed to compile sprite fragment shader: " << [[error localizedDescription] UTF8String] << std::endl; + std::cout << "Failed to compile sprite shader: " << [[error localizedDescription] UTF8String] << std::endl; } return -1; } - id spriteVertexFunction = [spriteVertexLibrary newFunctionWithName:@"sprite_vertex_main"]; - id spriteFragmentFunction = [spriteFragmentLibrary newFunctionWithName:@"sprite_fragment_main"]; + id spriteVertexFunction = [spriteLibrary newFunctionWithName:@"sprite_vertex_main"]; + id spriteFragmentFunction = [spriteLibrary newFunctionWithName:@"sprite_fragment_main"]; // Crear pipeline de fondo MTLRenderPipelineDescriptor* backgroundPipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];