• 3D
  • How can I use a Viewport Texture as a Projected Decal

I have a viewport texture displaying correctly on meshes as described here and I have been playing around with the Screen Space Decals plugin here, both of which have been extremely useful.

I am attempting to put them together to use the viewport texture as a decal, but when doing so, only one corner pixel from the viewport texture is stretched out to the entire decal plane, and I am struggling to figure out how to use the entire viewport texture on the decal instead of just a single pixel. It seems like perhaps the decal code is struggling to know how to scale the viewport texture correctly, but I haven't been able to fix the problem so far.

I'd be grateful if anyone has any thoughts on what might be going wrong.

Sounds to me like the issue is with the decal objects UV's but it's hard to analyze something like this without the code.

Yeah, makes sense. I'm starting to wonder if it is a problem with the decal shader trying to scale the viewport texture onto the decal mesh before the texture exists, since it is being generated live.

Here is the shader code from the decal plugin https://github.com/Mr-Slurpy/Screen-Space-Decals

shader_type spatial;
shader_type spatial;
render_mode unshaded, depth_draw_never, cull_front, depth_test_disable;

uniform sampler2D decal : hint_black;
uniform vec2 offset;
uniform vec2 scale;
uniform bool emulate_lighting;
uniform float brightness;

void fragment(){
	//float zdepth = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).r * 2.0 - 1.0;
	vec4 pos = inverse(WORLD_MATRIX) * inverse(INV_CAMERA_MATRIX) * INV_PROJECTION_MATRIX * 	vec4(SCREEN_UV * 2.0 - 1.0, textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).r * 2.0 - 1.0, 1.0);
	
	pos.xyz /= pos.w;

	bool inside = all(greaterThanEqual(pos.xyz, vec3(-1.0))) && all(lessThanEqual(pos.xyz, vec3(1.0)));

if(inside){
	vec4 color = texture(decal, (pos.xy * -0.5 - 0.5) * scale + offset);
	if(emulate_lighting){
		float lum = dot(textureLod(SCREEN_TEXTURE, SCREEN_UV, 0).rgb, vec3(0.2125, 0.7154, 0.0721));
		lum += brightness;
		lum = clamp(lum, 0.0, 1.0);
		ALBEDO = color.rgb * lum;
	}else{
		ALBEDO = color.rgb;
	}
	ALPHA = color.a;
}else{
	discard;
}
}

Also the decal.gd script:

tool
extends MeshInstance	

const SHADER = preload("decal.shader")

export(Texture) var decal setget set_decal
export(Vector2) var uv_offset = Vector2() setget set_uv_offset
export(Vector2) var uv_scale = Vector2(1, 1) setget set_uv_scale
export(bool) var emulate_lighting = true setget set_emulate_lighting
export(float, -100.0, 100.0) var brightness = 0.0 setget set_brightness

func _init():
	mesh = CubeMesh.new()
	mesh.material = ShaderMaterial.new()
	mesh.material.shader = SHADER

func set_decal(new_decal):
	decal = new_decal
	mesh.material.set_shader_param("decal", decal)

func set_uv_offset(new_offset):
	uv_offset = new_offset
	mesh.material.set_shader_param("offset", uv_offset)

func set_uv_scale(new_scale):
	uv_scale = new_scale
	mesh.material.set_shader_param("scale", uv_scale)

func set_emulate_lighting(new_value):
	emulate_lighting = new_value
	mesh.material.set_shader_param("emulate_lighting", emulate_lighting)

func set_brightness(new_brightness):
	brightness = new_brightness
	mesh.material.set_shader_param("brightness", brightness * 0.01)

At a quick glance looks alright, but I have no clue as to what your initial UV Scale variable is . Is the decal mesh(and thus its UV's) created in a separate script or imported from a file?

I believe the decal mesh is just created in line 13 of the decal.gd script posted previously, inside the _init function:

mesh = CubeMesh.new()

The uv_scale variable I leave at the default Vector2(1,1) to stretch the texture to the entire decal mesh, which works fine for normal textures, but not for the viewport texture. From there, I think the shader code controls how the cube mesh is projected down onto the other geometry along the local z axis to give the decal effect, but I'd have to study up on shader scripting to understand the details of how it functions.

I wouln't trust the UV coordinates of the default objects in godot. At least in the past they were very problematic. If they have been updated within a couple of more recent versions I wouldn't be aware since I've avoided using them for a while now.

Yep, that did it! I brought in a cube with custom UVs from Blender and tweaked the shader code to line things up, and all is working. Thanks for your help, @Megalomaniak !