How can I update a shader after it's on screen?

For example, I have the rainbow effect added to this card, But I want to make it shimmer (either change colors or slightly bend the shape). I know how to oscillate the frequency to make the rainbow move, but I can’t get it to update after it’s on screen.

I tried using a time variable and a position variable (as I move it) to make it change, but I don’t know how to pass the variable to the shader.

image

You can use vertex or fragment constants in your shader and change the values how you’d like. You can also use go.animate on the sprite component and animate the constant value from its default to a new value. An example of this is shown in the materials manual > Vertex and fragment constants chapter.

1 Like

But I do have it set in the material file.

My issue appears to be I'm not able to set the variable as the go.set command says it doesn't have the property.

local timex = 0

function init(self)
	-- Store references to the materials
	go.property("material1", resource.material("/assets/materials/rainbow.material"))
	go.property("material2", resource.material("/assets/materials/holographic.material"))
	go.property("material3", resource.material("/assets/materials/negative.material"))
	
	self.rainbow = hash("/assets/materials/rainbow.materialc")
	self.holographic = hash("/assets/materials/holographic.materialc")
	self.negative = hash("/assets/materials/negative.materialc")
	self.sprite = hash("/builtins/materials/sprite.materialc")	
	go.set("#sprite", "timex", timex)
end

function on_message(self, message_id, message, sender)
	if message_id == hash("change_shader") then
		if message.shader == 1 then
			go.set(message.sprite_url, "material", self.rainbow)
		elseif message.shader == 2 then
			go.set(message.sprite_url, "material", self.holographic)
		elseif message.shader == 3 then
			go.set(message.sprite_url, "material", self.negative)
		elseif message.shader == 0 then
			go.set(message.sprite_url, "material", self.sprite)
		end
		-- ... handle more shaders as needed
	end
end

function update(self, dt)
	-- Increment the time variable
	timex = time + dt
	
	-- Pass the time value to the shader
	go.set("#sprite", "timex", timex)
end

and here is my fragment program:

uniform lowp sampler2D texture_sampler;

uniform lowp vec4 tint;

uniform mediump float timex; // Pass a time-based variable to the shader

varying mediump vec2 var_texcoord0;

// Function to create a shifting rainbow color
lowp vec4 rainbowColor(mediump vec2 uv, mediump float timex, mediump float frequency) {
    mediump float angle = atan(uv.y - 0.5, uv.x - 0.5) + radians(90.0);
    mediump float distance = length(uv - vec2(0.5, 0.5));
    mediump float phase = timex * 0.7; // Use time to shift the phase of the color

    mediump float red = cos(frequency * distance + phase + 0.0) * 0.5 + 0.5;
    mediump float green = cos(frequency * distance + phase + 2.0) * 0.5 + 0.5;
    mediump float blue = cos(frequency * distance + phase + 4.0) * 0.5 + 0.5;

    return vec4(red, green, blue, 1.0);
}

void main() {
    // Calculate the oscillating frequency
    mediump float oscillatingFrequency = 30.0 * (0.65 + 0.15 * sin(timex)); // 0.65 + 0.15 * sin(time) oscillates between 0.5 and 0.8

    // Get the original sprite color
    lowp vec4 spriteColor = texture2D(texture_sampler, var_texcoord0.xy);

    // Generate the shifting rainbow color with the oscillating frequency
    lowp vec4 rainbow = rainbowColor(var_texcoord0.xy, timex, oscillatingFrequency);

    // Blend the rainbow color with the sprite color using alpha for semi-transparency
    mediump float alpha = 0.5; // Adjust alpha for desired transparency
    gl_FragColor = mix(spriteColor, rainbow, alpha);
}

I always get this type of error:

ERROR:SCRIPT: main/change_shader.script:13: '#sprite' does not have any property called 'timex'
stack traceback:
  [C]:-1: in function set
  main/change_shader.script:13: in function <main/change_shader.script:3>

Make sure you set right material before doing go.set("#sprite", "timex", timex)
Also it seems the timex variable is vector4 not float type.

First there’s a typo in the code you shared. You have timex = time + dt. Shouldn’t it be timex = timex + dt?

Second, like @Chung_Xa mentioned it is a vec4, so you need to set the correct type in your fragment program:

uniform mediump vec4 timex

And set one component of it:

go.set("#sprite", "timex.x", timex)

And use it in your framgent program:

mediump float phase = timex.x * 0.7;
2 Likes

Perfect thanks. I’m trying to get it to work. Still a couple errors with compatibility that I’m working through. I think I have an int somewhere and it want’s a float. Once fixed I’ll update my results.

@britzl @Chung_Xa

Thank you both very much. I got it working!

Final Shader Fragment Program:

uniform lowp sampler2D texture_sampler;

uniform lowp vec4 tint;

uniform mediump vec4 timex; // Pass a time-based variable to the shader

varying mediump vec2 var_texcoord0;

// Function to create a shifting rainbow color
lowp vec4 rainbowColor(mediump vec2 uv, mediump vec4 timex, mediump float frequency) {
    mediump float angle = atan(uv.y - 0.5, uv.x - 0.5) + radians(90.0);
    mediump float distance = length(uv - vec2(0.5, 0.5));
    mediump float phase = timex.x * 0.7; // Use time to shift the phase of the color

    mediump float red = cos(frequency * distance + phase + 0.0) * 0.5 + 0.5;
    mediump float green = cos(frequency * distance + phase + 2.0) * 0.5 + 0.5;
    mediump float blue = cos(frequency * distance + phase + 4.0) * 0.5 + 0.5;

    return vec4(red, green, blue, 1.0);
}

void main() {
    // Calculate the oscillating frequency
    mediump float oscillatingFrequency = 30.0 * (0.85 + 0.15 * sin(timex.x)); // 0.65 + 0.15 * sin(time) oscillates between 0.5 and 0.8

    // Get the original sprite color
    lowp vec4 spriteColor = texture2D(texture_sampler, var_texcoord0.xy);

    // Generate the shifting rainbow color with the oscillating frequency
    lowp vec4 rainbow = rainbowColor(var_texcoord0.xy, timex, oscillatingFrequency);

    // Blend the rainbow color with the sprite color using alpha for semi-transparency
    mediump float alpha = 0.7; // Adjust alpha for desired transparency
    gl_FragColor = mix(spriteColor, rainbow, alpha);
}
1 Like