Shaders Shader help! "Fragment Shader: sh_nebula at line 24"

Ax209

Member
Hi there. I was wondering if any shader experts could help me out with this one.

I found a great shhader from Shadertoy.com. I ran it through the GMS converter at iarri.github.io/Shadertoy2GM, and implemented it as described.

When I try to run my game, I get the error, "Fragment Shader: sh_nebula at line 24". of course sh_nebula is a shader, and is being used in obj_nebula, with a sprite of 512x512.

I'll show you the code:

GML:
uniform float iGlobalTime;
uniform vec3 iResolution;
varying vec2 fragCoord;
//#define GALAXY

struct PointLight {
    vec2 pos;
    vec3 col;
    float intensity;
};

float palette( in float a, in float b, in float c, in float d, in float x ) {
    return a + b * cos(6.28318 * (c * x + d));
}
    
// 2D Noise from IQ
float Noise2D( in vec2 x )
{
    ivec2 p = ivec2(floor(x));
    vec2 f = fract(x);
    f = f*f*(3.0-2.0*f);
    ivec2 uv = p.xy;
    float rgA = texelFetch( gm_BaseTexture, (uv+ivec2(0,0))&255, 0 ).x;
    float rgB = texelFetch( gm_BaseTexture, (uv+ivec2(1,0))&255, 0 ).x;
    float rgC = texelFetch( gm_BaseTexture, (uv+ivec2(0,1))&255, 0 ).x;
    float rgD = texelFetch( gm_BaseTexture, (uv+ivec2(1,1))&255, 0 ).x;
    return mix( mix( rgA, rgB, f.x ),
                mix( rgC, rgD, f.x ), f.y );
}

float ComputeFBM( in vec2 pos )
{
    float amplitude = 0.75;
    float sum = 0.0;
    float maxAmp = 0.0;
    for(int i = 0; i < 6; ++i)
    {
        sum += Noise2D(pos) * amplitude;
        maxAmp += amplitude;
        amplitude *= 0.5;
        pos *= 2.2;
    }
    return sum / maxAmp;
}

// Same function but with a different, constant amount of octaves
float ComputeFBMStars( in vec2 pos )
{
    float amplitude = 0.75;
    float sum = 0.0;
    float maxAmp = 0.0;
    for(int i = 0; i < 5; ++i)
    {
        sum += Noise2D(pos) * amplitude;
        maxAmp += amplitude;
        amplitude *= 0.5;
        pos *= 2.0;
    }
    return sum / maxAmp * 1.15;
}

vec3 BackgroundColor( in vec2 uv ) {
    
    // Sample various noises and multiply them
    float noise1 = ComputeFBMStars(uv * 5.0);
    float noise2 = ComputeFBMStars(uv * vec2(15.125, 25.7));
    float noise3 = ComputeFBMStars((uv + vec2(0.5, 0.1)) * 4.0 + iGlobalTime * 0.35);
    float starShape = noise1 * noise2 * noise3;
    
    // Compute star falloff - not really doing what i hoped it would, i wanted smooth falloff around each star
    float falloffRadius = 0.2;
    float baseThreshold = 0.6; // higher = less stars
    
    starShape = clamp(starShape - baseThreshold + falloffRadius, 0.0, 1.0);
    
    float weight = starShape / (2.0 * falloffRadius);
    return weight * vec3(noise1 * 0.55, noise2 * 0.4, noise3 * 1.0) * 6.0; // artificial scale just makes the stars brighter
}

void main( void )
{
    vec2 uv = fragCoord.xy / iResolution.xy;
    vec2 scrPt = uv * 2.0 - 1.0;
    
    vec4 finalColor;
    
    #ifdef GALAXY
    
    vec2 samplePt = scrPt;
    
    // Warp noise domain
    float swirlStrength = 2.5;
    float dist = length(samplePt);
    float theta = dist * swirlStrength - iGlobalTime * 0.225;
    mat2 rot;
    
    // cache calls to sin/cos
    float cosTheta = cos(theta);
    float sinTheta = sin(theta);
    
    rot[0][0] = cosTheta;
    rot[0][1] = -sinTheta;
    rot[1][0] = sinTheta;
    rot[1][1] = cosTheta;
    
    samplePt *= rot;
    samplePt *= 3.0;
    
    float noiseVal = ComputeFBM(samplePt + sin(iGlobalTime * 0.03125));
    float maxIntensity = 1.65; // kinda is the galaxy radius/size?
    noiseVal *= clamp(pow(abs(maxIntensity - dist), 5.0) * (1.0 / maxIntensity), 0.0, 1.0);
    
    // Lighting
    PointLight l1;
    l1.pos = vec2(0);
    l1.col = mix(vec3(0.75, 0.5, 0.3), vec3(0.55, 0.4, 0.95), clamp(dist * 0.5, 0.0, 1.0) + (sin(iGlobalTime * 0.5) * 0.5 + 0.5) * 0.5);
    l1.intensity = 4.0;
    
    vec3 l1Col = l1.col * l1.intensity * 1.0 / pow(abs(length(l1.pos - samplePt)), 0.5);
    //vec4 finalColor = vec4(BackgroundColor(fragCoord.xy * 0.25), 1.0);
    //vec4 finalColor = vec4(l1Col * noiseVal, 1.0);
    finalColor = vec4(mix(BackgroundColor(fragCoord.xy * 0.125), l1Col * noiseVal, pow(abs(noiseVal), 1.0)), 1.0);
    
    #else // Milky Way   
    
    // Define density for some shape representing the milky way galaxy
    
    float milkywayShape;
    
    // Distort input screen pos slightly so the galaxy isnt perfectly axis aligned
    float galaxyOffset = (cos(scrPt.x * 5.0) * sin(scrPt.x * 2.0) * 0.5 + 0.5) * 0.0;
    
    // Apply a slight rotation to the screen point, similar to the galaxy
    float theta = length(scrPt) * 0.25; // Visualy tweaked until it looked natural
    mat2 rot;
    
    // cache calls to sin/cos(theta)
    float cosTheta = cos(theta);
    float sinTheta = sin(theta);
    
    rot[0][0] = cosTheta;
    rot[0][1] = -sinTheta;
    rot[1][0] = sinTheta;
    rot[1][1] = cosTheta;
    
    vec2 rotatedScrPt = scrPt * rot;
    
    float noiseVal = ComputeFBM(rotatedScrPt * 5.0 + 50.0 + iGlobalTime * 0.015625 * 1.5);
    
    rotatedScrPt += vec2(noiseVal) * 0.3;
    
    float centralFalloff = clamp(1.0 - length(scrPt.y + galaxyOffset), 0.0, 1.0);
    float xDirFalloff = (cos(scrPt.x * 2.0) * 0.5 + 0.5);
    
    float centralFalloff_rot = 1.0 - length(rotatedScrPt.y + galaxyOffset);
    float xDirFalloff_rot = (cos(rotatedScrPt.x * 2.0) * 0.5 + 0.5);
    
    // Falloff in y dir and x-dir fade
    float lowFreqNoiseForFalloff = ComputeFBM(rotatedScrPt * 4.0 - iGlobalTime * 0.015625 * 1.5); // 1/64
    //float lowFreqNoiseForFalloff_offset = ComputeFBM(rotatedScrPt * 1.5 + 0.005 * lowFreqNoiseForFalloff);
    milkywayShape = clamp(pow(abs(centralFalloff_rot), 3.0) - lowFreqNoiseForFalloff * 0.5, 0.0, 1.0) * xDirFalloff_rot;
    
    // Lighting
    vec3 color;
    
    // desired brown color
    //vec3 brown = vec3(0.35, 0.175, 0.15) * 17.0;
    //vec3 mainColor = vec3(0.925, 1.0, 0.8) * 10.0;
    //color = mix(brown, mainColor, pow(abs(milkywayShape), 1.0)) * 2.0 * milkywayShape;
    
    // Cosine-based pallette: http://dev.thi.ng/gradients/
    // there is also a famous IQ article on this and a less famous shader on my profile
    color.r = palette(0.5, -1.081592653589793, 0.798407346410207, 0.0, pow(abs(milkywayShape), 1.0));
    color.g = palette(0.5, 0.658407346410207, 0.908407346410207, 0.268407346410207, pow(abs(milkywayShape), 1.0));
    color.b = palette(0.5, -0.201592653589793, 0.318407346410207, -0.001592653589793, pow(abs(milkywayShape), 1.0));
    
    /* dont do this
    color.r += 0.5 * palette(0.5, -0.481592653589793, 0.798407346410207, 0.0, pow(abs(noiseVal), 1.0));
    color.g += 0.5 * palette(0.5, 0.428407346410207, 0.908407346410207, 0.268407346410207, pow(abs(noiseVal), 0.5));
    color.b += 0.5 * palette(0.5, -0.001592653589793, 0.318407346410207, -0.001592653589793, pow(abs(noiseVal), 1.0));
    */
    
    // Experimented with removing color, worked out decently
    float removeColor = (pow(abs(milkywayShape), 10.0) + lowFreqNoiseForFalloff * 0.1) * 5.0;
    color -= vec3(removeColor);
    
    // Add some blue to the background
    vec3 backgroundCol = BackgroundColor(fragCoord.xy * 0.125) * pow(abs(centralFalloff), 0.5) * pow(abs(xDirFalloff), 0.5);
    vec3 blueish = vec3(0.2, 0.2, 0.4);
    backgroundCol += blueish * (5.0 - milkywayShape) * pow(abs(centralFalloff_rot), 2.0) * lowFreqNoiseForFalloff * pow(abs(xDirFalloff), 0.75);
    
    vec3 whiteish = vec3(0.5, 1.0, 0.85);
    backgroundCol += whiteish * 0.95 * pow(abs(centralFalloff), 1.5) * lowFreqNoiseForFalloff * pow(abs(xDirFalloff), 2.0);
    
    
    finalColor = vec4(mix(backgroundCol, color, milkywayShape), 1);
    
    #endif
    
    gl_FragColor = finalColor;
}
I can't really do anything with that error code as it doesn't tell me much, so I'm kinda stranded now. I've googled other folk having the same issue, and even though this error is linked to line 24, I believe (from other people's experiences) the problem may be on line 23. But I couldn't tell you why, plus both lines are very similar.

If anyone knows where I'm going wrong with this, I'd love some help.

Many thanks :)
 

rIKmAN

Member
Change those ints into floats in the Noise2D function which are around line 24 as the error suggests.

So 0 becomes 0.0 and 1 becomes 1.0.

Also check the rest of the shader for the same issue as I only looked around line 24 as per the error message.
 

Ax209

Member
as the error suggests.
Sorry what? Where did the error suggest that?

Thank you for your help though, gonna have a pop at this now!

UPDATE:

Okay so I made sure the numbers were floats, as follows...

GML:
// 2D Noise from IQ
float Noise2D( in vec2 x )
{
    ivec2 p = ivec2(floor(x));
    vec2 f = fract(x);
    f = f*f*(3.00-2.00*f);
    ivec2 uv = p.xy;
    float rgA = texelFetch( gm_BaseTexture, (uv+ivec2(0.00,0.00))&255.00, 0.00 ).x;
    float rgB = texelFetch( gm_BaseTexture, (uv+ivec2(1.00,0.00))&255.00, 0.00 ).x;
    float rgC = texelFetch( gm_BaseTexture, (uv+ivec2(0.00,1.00))&255.00, 0.00 ).x;
    float rgD = texelFetch( gm_BaseTexture, (uv+ivec2(1.00,1.00))&255.00, 0.00 ).x;
    return mix( mix( rgA, rgB, f.x ),
                mix( rgC, rgD, f.x ), f.y );
}
Unfortunate I still get the very same error code :(
 
Last edited:

rIKmAN

Member
Sorry what? Where did the error suggest that?
You posted the error message Fragment Shader: sh_nebula at line 24 which points you to look on and around line 24...
I'm not a shader expert but the 0 and 1 ints around that line are what stood out when I quickly scrolled through the code in the OP.

However a quick Google found this thread in which it's mentioned that GMS shaders don't support bitwise operators so it's likely that it's the &255 that is causing the error and you'll have to implement a manual workaround of the bitwise operation using regular arithmetic.

The person who posted that in the linked thread (sp202) really knows his stuff so I'm inclined to believe that this is the case.

EDIT:
Another quick Google of TexelFetch shows that it only started being supported between OpenGL v1.30-v1.50 depending on the version of the function used.

The GMS2 Manual has a Shaders section which links to the OpenGL-ES 2.0 Reference Card but it makes a note of saying ...only the last two cards shown are applicable to GameMaker Studio 2 and the last 2 cards are for OpenGL-ES v1.0.

So as I said I'm no shader expert, but following the breadcrumbs above it seems like the TexelFetch function itself might not be supported by GMS2 as it's implied from the manual page I linked above that it uses OpenGL-ES v1.0.

Hopefully someone more knowledgable with shaders can chime in and confirm/deny it, but that's how it looks to me just following the documentation and knowing issues I've had in the past trying to convert ShaderToy shaders to work in GMS2 and running into the same issue where GMS2's ancient OpenGL-ES support made it impossible/difficult.
 
Last edited:

Ax209

Member
You posted the error message Fragment Shader: sh_nebula at line 24 which points you to look on and around line 24...
I'm not a shader expert but the 0 and 1 ints around that line are what stood out when I quickly scrolled through the code in the OP.

However a quick Google found this thread in which it's mentioned that GMS shaders don't support bitwise operators so it's likely that it's the &255 that is causing the error and you'll have to implement a manual workaround of the bitwise operation using regular arithmetic.

The person who posted that in the linked thread (sp202) really knows his stuff so I'm inclined to believe that this is the case.

EDIT:
Another quick Google of TexelFetch shows that it only started being supported between OpenGL v1.30-v1.50 depending on the version of the function used.

The GMS2 Manual has a Shaders section which links to the OpenGL-ES 2.0 Reference Card but it makes a note of saying ...only the last two cards shown are applicable to GameMaker Studio 2 and the last 2 cards are for OpenGL-ES v1.0.

So as I said I'm no shader expert, but following the breadcrumbs above it seems like the TexelFetch function itself might not be supported by GMS2 as it's implied from the manual page I linked above that it uses OpenGL-ES v1.0.

Hopefully someone more knowledgable with shaders can chime in and confirm/deny it, but that's how it looks to me just following the documentation and knowing issues I've had in the past trying to convert ShaderToy shaders to work in GMS2 and running into the same issue where GMS2's ancient OpenGL-ES support made it impossible/difficult.
Well dude, thank you for the help. It's beyond me, and I may have to draw it out manually (Got to suffer for your art, am I right?).

Thanks again for all of your help! All the best!
 
Top