3D Hover CanvasItem

This shader will create a 3D hovering effect with mouse interaction on a canvas item like TextureRect. Also, it renders a specular highlight that changes with hover.

Shader Properties:

  • Tilt Scale – Amount of tilt on mouse hover
  • Is Specular Light – Enable/disable specular highlight
  • Specular Light Intensity – specular highlight strength
  • Specular Light Power – controls the spread of specular highlight
  • Mouse Pos – This is the mouse position that will be set via GDScript attached to the canvas item. You can find this script in the demo project.

It is recommended to use a texture with some transparent space at the border so that the image is not clipped when tilted.

Please comment on the features you would like to add. I will update the project accordingly.

Shader code
shader_type canvas_item;

uniform float _tilt_Scale = 0.5;
uniform bool _isSpecularLight = false;
uniform float _speularLightIntensity = 0.5;
uniform float _speularLightPower = 3.0;
uniform vec2 _mousePos;

varying vec2 vTexCoord;
varying float vFragPerspective;
varying vec2 vMouseoffset;

void vertex() {
    // Normalize texture coordinates
    vTexCoord = VERTEX.xy * TEXTURE_PIXEL_SIZE;

    // Center the coordinates around the origin
    vec2 centeredCoord = vTexCoord - vec2(0.5, 0.5);
	vec2 mouse_centered = ((_mousePos + 0.5/TEXTURE_PIXEL_SIZE) * TEXTURE_PIXEL_SIZE) * 2.0 - 1.0;
	
    vMouseoffset = mouse_centered / 2.0; // varying to be passed to fragment

    // Rotation matrices around the X, Y, and Z axes	
	
    //Rotation along X-axis
    float cosX = cos(mouse_centered.y * _tilt_Scale);
    float sinX = sin(mouse_centered.y * _tilt_Scale);
    mat3 rotationX;
    rotationX[0] = vec3(1.0, 0.0, 0.0);
    rotationX[1] = vec3(0.0, cosX, -sinX);
    rotationX[2] = vec3(0.0, sinX, cosX);

    //Rotation along Y-axis
    float cosY = cos(-mouse_centered.x * _tilt_Scale);
    float sinY = sin(-mouse_centered.x * _tilt_Scale);
    mat3 rotationY;
    rotationY[0] = vec3(cosY, 0.0, sinY);
    rotationY[1] = vec3(0.0, 1.0, 0.0);
    rotationY[2] = vec3(-sinY, 0.0, cosY);

    //Rotation along Z-axis
    //no rotation along Z-axis, so the angle is zero
    float cosZ = cos(0.);
    float sinZ = sin(0.);
    mat3 rotationZ;
    rotationZ[0] = vec3(cosZ, -sinZ, 0.0);
    rotationZ[1] = vec3(sinZ, cosZ, 0.0);
    rotationZ[2] = vec3(0.0, 0.0, 1.0);

    // Combine rotations
    mat3 rotation = rotationZ * rotationY * rotationX;

    // Apply the rotation to the vertex position
    vec3 transformedCoord = rotation * vec3(centeredCoord, 0.0);

    // Apply perspective projection
    float perspective = 1.0 / (1.0 - transformedCoord.z * 0.5);
    transformedCoord.xy *= perspective;		
    vTexCoord *= perspective;
    vFragPerspective = perspective;
	
    // Transform back to screen coordinates
    vec2 screenPosition = (transformedCoord.xy + vec2(0.5)) / TEXTURE_PIXEL_SIZE;	
    VERTEX = screenPosition;	    
}

void fragment() {
    //perspective correction of UV
    vec2 finalTexCoord = vTexCoord / vFragPerspective;	
    vec4 texColor = texture(TEXTURE, finalTexCoord);	    
	
    //specular light
    float specularvalue = pow(clamp(1.0 - length(finalTexCoord - 0.5 + vMouseoffset), 0.0, 1.0), _speularLightPower) * _speularLightIntensity;
    vec3 specularCol = vec3(specularvalue);
    
    if(_isSpecularLight)   
    	COLOR = texColor + vec4(specularCol, 0.0);
    else
	COLOR = texColor;
}
Tags
3D Hover, Hover, Hover highlight, Mouse Hover
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from Sivabalan

Stylized 3D Fire and Smoke

Drawing board shader

Related shaders

Balatro Card Hover

Hologram simple canvasItem shader

Configurable CanvasItem Outline Shader

Subscribe
Notify of
guest

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Paz
Paz
6 days ago

I’ve made some changes on the script, it now allows tween on exit area or in my case I changed for android devices on drag detection, but it looks smoother when stop the inspection, it also includes intensity var that can be used with the mouse entered and exited variables, so allowing user to interact with the texture even out of the image area, something similar to “Valorant Weapon Inspection”

extends TextureRect

@export var exit_tween_duration: float = 0.8
@export var intensity: float = 0.22 #

var exit_tween : Tween
var pressing:bool
func _ready() -> void:
set_process_input(true)

func _input(event):
if event is InputEventScreenTouch:
pressing = event.is_pressed()
if !pressing:
setNormal()
if event is InputEventScreenDrag and pressing:
var mouse_position = event.position
var relative_mouse_position = mouse_position – position
var local_mouse_pos = relative_mouse_position / scale – size / 2.0
local_mouse_pos *= intensity
material.set_shader_parameter(“_mousePos”, local_mouse_pos)

func setNormal():
if exit_tween and exit_tween.is_valid():
exit_tween.kill()

exit_tween = get_tree().create_tween()
exit_tween.tween_property(
material, “shader_parameter/_mousePos”, Vector2.ZERO, exit_tween_duration
).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)

Last edited 6 days ago by ImKJ