Reputation: 185
I've got this shader that I ported from a shadertoy into a metal shader for iOS. The original one works fine, but I'm getting some strange behavior now that I've moved it to iOS. Basically, for the first few seconds that the shader is running, everything is misaligned. I think that this is because there is mirroring on the X axis, which is correct, but the vertical coordinates have also been somehow flipped on one side. Can anyone tell me how I should go about fixing this?
Original Shadertoy: https://www.shadertoy.com/view/ltl3Dj
My version, converted into metal shading language:
#include <metal_stdlib>
using namespace metal;
////////////////
///CSB CONSTANTS (not required, just make sure it's handled properly at the bottom)
constant float2 resolution = (1, 1);
constant float contrast = 1.0;
constant float saturation = 1.02;
constant float brightness = 1.5;
struct FloweringQuadVertexToFragmentVariables
{
//basic Active Shader Variables
float4 position [[ position ]];
float2 textureCoordinates;
float time;
//Shader specific variables go here (not required)
};
vertex FloweringQuadVertexToFragmentVariables FloweringQuadVertexShader (constant float4 *positions [[ buffer(0) ]],
constant float2 *textureCoordinates [[ buffer(1) ]],
constant float *shaderFloatZero [[buffer(2)]],
uint vertexID [[ vertex_id ]])
{
FloweringQuadVertexToFragmentVariables output;
//basic variables output here
output.position = positions[vertexID];
output.textureCoordinates = textureCoordinates[vertexID];
output.time = *shaderFloatZero;
//additional variables here
//output
return output;
}
// Remember, can do [color(0)] etc. for rendering to attachments other than just [0]
float3 FloweringContrastSaturationBrightness(float3 color, float brt, float sat, float con)
{
// Increase or decrease theese values to adjust r, g and b color channels seperately
const float AvgLumR = 0.4;
const float AvgLumG = 0.4;
const float AvgLumB = 0.4;
const float3 LumCoeff = float3(0.2125, 0.7154, 0.0721); //luminosity coefficient
float3 AvgLumin = float3(AvgLumR, AvgLumG, AvgLumB);
float3 brtColor = color * brt;
float3 intensity = float3(dot(brtColor, LumCoeff));
float3 satColor = mix(intensity, brtColor, sat);
float3 conColor = mix(AvgLumin, satColor, con);
return conColor;
}
float4 hue(float4 color, float shift) {
const float4 kRGBToYPrime = float4 (0.299, 0.587, 0.114, 0.0);
const float4 kRGBToI = float4 (0.596, -0.275, -0.321, 0.0);
const float4 kRGBToQ = float4 (0.212, -0.523, 0.311, 0.0);
const float4 kYIQToR = float4 (1.0, 0.956, 0.621, 0.0);
const float4 kYIQToG = float4 (1.0, -0.272, -0.647, 0.0);
const float4 kYIQToB = float4 (1.0, -1.107, 1.704, 0.0);
// Convert to YIQ
float YPrime = dot (color, kRGBToYPrime);
float I = dot (color, kRGBToI);
float Q = dot (color, kRGBToQ);
// Calculate the hue and chroma
float hue = atan (Q/ I);
float chroma = sqrt (I * I + Q * Q);
// Make the user's adjustments
hue += shift;
// Convert back to YIQ
Q = chroma * sin (hue);
I = chroma * cos (hue);
// Convert back to RGB
float4 yIQ = float4 (YPrime, I, Q, 0.0);
color.r = dot (yIQ, kYIQToR);
color.g = dot (yIQ, kYIQToG);
color.b = dot (yIQ, kYIQToB);
return color;
}
float2 kale(float2 uv, float angle, float base, float spin) {
float a = atan(uv.y/uv.x)+spin;
float d = length(uv);
a = fmod(a,angle*2.0);
a = abs(a-angle);
uv.x = sin(a+base)*d;
uv.y = cos(a+base)*d;
return uv;
}
float2 rotate(float px, float py, float angle){
float2 r = float2(0);
r.x = cos(angle)*px - sin(angle)*py;
r.y = sin(angle)*px + cos(angle)*py;
return r;
}
float floweringlum(float3 c) {
return dot(c, float3(0.3, 0.59, 0.11));
}
float3 floweringclipcolor(float3 c) {
float l = floweringlum(c);
float n = min(min(c.r, c.g), c.b);
float x = max(max(c.r, c.g), c.b);
if (n < 0.0) {
c.r = l + ((c.r - l) * l) / (l - n);
c.g = l + ((c.g - l) * l) / (l - n);
c.b = l + ((c.b - l) * l) / (l - n);
}
if (x > 1.0) {
c.r = l + ((c.r - l) * (1.0 - l)) / (x - l);
c.g = l + ((c.g - l) * (1.0 - l)) / (x - l);
c.b = l + ((c.b - l) * (1.0 - l)) / (x - l);
}
return c;
}
float3 setfloweringlum(float3 c, float l) {
float d = l - floweringlum(c);
c = c + float3(d);
return floweringclipcolor(c);
}
fragment float4 FloweringQuadFragmentShader(FloweringQuadVertexToFragmentVariables input [[ stage_in ]],
texture2d<float> fragmentTexture [[ texture(0) ]],
sampler samplr [[sampler(0) ]])
{ float timeElapsed = input.time;
float4 textureColor = fragmentTexture.sample(samplr, input.textureCoordinates);
///////
float2 iResolution = (1, 1);
float2 texCoords = input.textureCoordinates;
//float2 p = texCoords.xy / iResolution.xy;
////////
float p = 3.14159265359;
float i = timeElapsed*.5;
float2 uv = texCoords.xy / iResolution.xy*5.0-2.5;
uv = kale(uv, p/6.0,i,i*0.2);
float4 c = float4(1.0);
const float2x2 m = float2x2(float2(sin(uv.y*cos(uv.x+i)+i*0.1)*20.0, -6.0),
float2(sin(uv.x+i*1.5)*3.0,-cos(uv.y-i)*2.0));
uv = rotate(uv.x,uv.y,length(uv)+i*.4);
c.rg = cos(sin(uv.xx+uv.yy)*m-i);
c.b = sin(rotate(uv.x,uv.x,length(uv.xx)*3.0+i).x-uv.y+i);
float4 color = float4(1.0-hue(c,i).rgb,1.0);
////////
float4 finalColor;
float4 FloweringColor;
/*FloweringColor.r = (color.r+(textureColor.r*1.3))/2;
FloweringColor.g = (color.g + (textureColor.g*1.3))/2;
FloweringColor.b = (color.b + (textureColor.b*1.3))/2;
FloweringColor.a = 1.0;*/
float4 cam = textureColor;
float4 overlay = color;
FloweringColor = float4(cam.rgb * (1.0 - overlay.a) + setfloweringlum(overlay.rgb, floweringlum(cam.rgb)) * overlay.a, cam.a);
float3 csbcolor = FloweringContrastSaturationBrightness(FloweringColor.rgb, contrast, saturation, brightness);
float alpha = 1.0;
finalColor = float4(csbcolor.r, csbcolor.g, csbcolor.b, alpha);
return finalColor;//float4(textureColor.a, textureColor.a, textureColor.a, 1.0);
}
Upvotes: 3
Views: 1809
Reputation: 31782
This is due to a difference in behavior between GLSL's mod
function and Metal's fmod
function. In Metal, GLSL's mod function would look like this:
float mod(float x, float y) {
return x - y * floor(x / y);
}
while Metal's own fmod
is equivalent to
float fmod(float x, float y) {
return x - y * trunc(x / y);
}
The intermediate operations respectively floor (toward negative infinity) or truncate (toward zero). If you replace your calls to fmod
with calls to the version of mod
above that emulates GLSL, you should observe identical behavior between the two.
You can flip the coordinate system to match GL's by replacing any occurrences of the texture coordinates (u, v) with (u, 1-v). That will make the lobes revolve clockwise rather than counterclockwise as they currently do in your Metal implementation. It's easiest to just do this transformation once in the vertex function.
Upvotes: 13