Extraer shaders a archivos .metal separados para mejor organización
- 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 <noreply@anthropic.com>
This commit is contained in:
40
data/shaders/background.metal
Normal file
40
data/shaders/background.metal
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#include <metal_stdlib>
|
||||||
|
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;
|
||||||
|
}
|
||||||
158
data/shaders/crt.metal
Normal file
158
data/shaders/crt.metal
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
#include <metal_stdlib>
|
||||||
|
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<float> 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
|
||||||
|
}
|
||||||
29
data/shaders/sprite.metal
Normal file
29
data/shaders/sprite.metal
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include <metal_stdlib>
|
||||||
|
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<float> 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
|
||||||
|
}
|
||||||
33
data/shaders/triangle.metal
Normal file
33
data/shaders/triangle.metal
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#include <metal_stdlib>
|
||||||
|
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;
|
||||||
|
}
|
||||||
400
source/main.cpp
400
source/main.cpp
@@ -104,404 +104,102 @@ int main(int argc, char* argv[]) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shaders para fondo degradado
|
|
||||||
NSString* backgroundVertexShaderSource = @R"(
|
|
||||||
#include <metal_stdlib>
|
|
||||||
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
|
// Cargar shaders CRT desde archivo
|
||||||
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 <metal_stdlib>
|
|
||||||
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 <metal_stdlib>
|
|
||||||
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 <metal_stdlib>
|
|
||||||
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 <metal_stdlib>
|
|
||||||
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 <metal_stdlib>
|
|
||||||
using namespace metal;
|
|
||||||
|
|
||||||
struct SpriteVertexOut {
|
|
||||||
float4 position [[position]];
|
|
||||||
float4 color;
|
|
||||||
float2 texCoord;
|
|
||||||
};
|
|
||||||
|
|
||||||
fragment float4 sprite_fragment_main(SpriteVertexOut in [[stage_in]],
|
|
||||||
texture2d<float> 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 <metal_stdlib>
|
|
||||||
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 <metal_stdlib>
|
|
||||||
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<float> 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
|
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
id<MTLLibrary> 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) {
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
id<MTLLibrary> crtFragmentLibrary = [device newLibraryWithSource:crtFragmentShaderSource options:nil error:&error];
|
id<MTLLibrary> crtLibrary = [device newLibraryWithSource:crtShaderSource options:nil error:&error];
|
||||||
if (!crtFragmentLibrary || error) {
|
if (!crtLibrary || error) {
|
||||||
if (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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
id<MTLFunction> crtVertexFunction = [crtVertexLibrary newFunctionWithName:@"crt_vertex_main"];
|
id<MTLFunction> crtVertexFunction = [crtLibrary newFunctionWithName:@"crt_vertex_main"];
|
||||||
id<MTLFunction> crtFragmentFunction = [crtFragmentLibrary newFunctionWithName:@"crt_fragment_main"];
|
id<MTLFunction> crtFragmentFunction = [crtLibrary newFunctionWithName:@"crt_fragment_main"];
|
||||||
|
|
||||||
// Compilar shaders de fondo
|
// Cargar shaders de fondo desde archivo
|
||||||
id<MTLLibrary> backgroundVertexLibrary = [device newLibraryWithSource:backgroundVertexShaderSource options:nil error:&error];
|
NSString* backgroundShaderPath = @"data/shaders/background.metal";
|
||||||
if (!backgroundVertexLibrary || error) {
|
NSString* backgroundShaderSource = [NSString stringWithContentsOfFile:backgroundShaderPath encoding:NSUTF8StringEncoding error:&error];
|
||||||
|
if (!backgroundShaderSource || error) {
|
||||||
if (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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
id<MTLLibrary> backgroundFragmentLibrary = [device newLibraryWithSource:backgroundFragmentShaderSource options:nil error:&error];
|
id<MTLLibrary> backgroundLibrary = [device newLibraryWithSource:backgroundShaderSource options:nil error:&error];
|
||||||
if (!backgroundFragmentLibrary || error) {
|
if (!backgroundLibrary || error) {
|
||||||
if (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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
id<MTLFunction> backgroundVertexFunction = [backgroundVertexLibrary newFunctionWithName:@"background_vertex_main"];
|
id<MTLFunction> backgroundVertexFunction = [backgroundLibrary newFunctionWithName:@"background_vertex_main"];
|
||||||
id<MTLFunction> backgroundFragmentFunction = [backgroundFragmentLibrary newFunctionWithName:@"background_fragment_main"];
|
id<MTLFunction> backgroundFragmentFunction = [backgroundLibrary newFunctionWithName:@"background_fragment_main"];
|
||||||
|
|
||||||
// Compilar shaders de triángulo
|
// Cargar shaders de triángulo desde archivo
|
||||||
id<MTLLibrary> triangleVertexLibrary = [device newLibraryWithSource:triangleVertexShaderSource options:nil error:&error];
|
NSString* triangleShaderPath = @"data/shaders/triangle.metal";
|
||||||
if (!triangleVertexLibrary || error) {
|
NSString* triangleShaderSource = [NSString stringWithContentsOfFile:triangleShaderPath encoding:NSUTF8StringEncoding error:&error];
|
||||||
|
if (!triangleShaderSource || error) {
|
||||||
if (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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
id<MTLLibrary> triangleFragmentLibrary = [device newLibraryWithSource:triangleFragmentShaderSource options:nil error:&error];
|
id<MTLLibrary> triangleLibrary = [device newLibraryWithSource:triangleShaderSource options:nil error:&error];
|
||||||
if (!triangleFragmentLibrary || error) {
|
if (!triangleLibrary || error) {
|
||||||
if (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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
id<MTLFunction> triangleVertexFunction = [triangleVertexLibrary newFunctionWithName:@"triangle_vertex_main"];
|
id<MTLFunction> triangleVertexFunction = [triangleLibrary newFunctionWithName:@"triangle_vertex_main"];
|
||||||
id<MTLFunction> triangleFragmentFunction = [triangleFragmentLibrary newFunctionWithName:@"triangle_fragment_main"];
|
id<MTLFunction> triangleFragmentFunction = [triangleLibrary newFunctionWithName:@"triangle_fragment_main"];
|
||||||
|
|
||||||
// Compilar shaders de sprites
|
// Cargar shaders de sprites desde archivo
|
||||||
id<MTLLibrary> spriteVertexLibrary = [device newLibraryWithSource:spriteVertexShaderSource options:nil error:&error];
|
NSString* spriteShaderPath = @"data/shaders/sprite.metal";
|
||||||
if (!spriteVertexLibrary || error) {
|
NSString* spriteShaderSource = [NSString stringWithContentsOfFile:spriteShaderPath encoding:NSUTF8StringEncoding error:&error];
|
||||||
|
if (!spriteShaderSource || error) {
|
||||||
if (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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
id<MTLLibrary> spriteFragmentLibrary = [device newLibraryWithSource:spriteFragmentShaderSource options:nil error:&error];
|
id<MTLLibrary> spriteLibrary = [device newLibraryWithSource:spriteShaderSource options:nil error:&error];
|
||||||
if (!spriteFragmentLibrary || error) {
|
if (!spriteLibrary || error) {
|
||||||
if (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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
id<MTLFunction> spriteVertexFunction = [spriteVertexLibrary newFunctionWithName:@"sprite_vertex_main"];
|
id<MTLFunction> spriteVertexFunction = [spriteLibrary newFunctionWithName:@"sprite_vertex_main"];
|
||||||
id<MTLFunction> spriteFragmentFunction = [spriteFragmentLibrary newFunctionWithName:@"sprite_fragment_main"];
|
id<MTLFunction> spriteFragmentFunction = [spriteLibrary newFunctionWithName:@"sprite_fragment_main"];
|
||||||
|
|
||||||
// Crear pipeline de fondo
|
// Crear pipeline de fondo
|
||||||
MTLRenderPipelineDescriptor* backgroundPipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
MTLRenderPipelineDescriptor* backgroundPipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||||
|
|||||||
Reference in New Issue
Block a user