Godot Version
v4.2.1.stable.official
Question
Apologies in advance for the lack of pictures. I have plenty of great screenshots to demonstrate this bug, but because I’m a new user, I’m limited to one embedded upload per post.
I am following this (dated) tutorial on YouTube, and I’ve encountered some strange behavior regarding a shader and the texture()
function in the GLSL language.
I have a ShaderMaterial
applied to the Material Override of a MeshInstance3D
within my scene. Here is a the shader file code:
shader_type spatial;
uniform float min_height;
uniform float max_height;
uniform sampler2D height_color: source_color;
varying float height;
void vertex() {
height = length(VERTEX);
}
void fragment() {
float t = height / (max_height - min_height) - (min_height / (max_height - min_height));
vec3 color = texture(height_color, vec2(t, 0.0)).rgb;
ALBEDO = color;
}
The uniform sampler2D height_color
variable is passed an instance of GradientTexture1D
with the following metadata:
[gd_resource type="GradientTexture1D" load_steps=2 format=3 uid="uid://dpje5idhfvvy3"]
[sub_resource type="Gradient" id="Gradient_waokt"]
offsets = PackedFloat32Array(0, 0.03, 0.159686, 0.32199, 0.534031, 0.67801, 0.863874)
colors = PackedColorArray(0.0339462, 0.0841454, 0.507958, 1, 0.95, 0.922767, 0.5415, 1, 0.0666667, 0.25098, 0.141176, 1, 0.0666667, 0.25098, 0.141176, 1, 0.388235, 0.376471, 0.360784, 1, 0.388235, 0.376471, 0.360784, 1, 1, 1, 1, 1)
[resource]
gradient = SubResource("Gradient_waokt")
width = 128
Nothing fancy; just a simple gradient that starts at a deep blue (for water), then transitions through several colors before finishing off at white (for snow-capped mountains). The gradient renders just fine, with one problem: the oceans aren’t blue; they’re white!
At first, I thought the issue was with my vertex calculations, but then I discovered what eventually led me to creating this post. I found if I add the following line to my fragment()
function, the oceans turn blue:
void fragment() {
// Note: `t` now has a minimum value of 0.004
float t = max(0.004, height / (max_height - min_height) - (min_height / (max_height - min_height)));
vec3 color = texture(height_color, vec2(t, 0.0)).rgb;
ALBEDO = color;
}
My next thought was, perhaps there is some weird floating-point bugs occurring because I’m subtracting two almost-zero but not quite zero floating-point values and getting a negative result. To test, I changed my fragment()
function again:
void fragment() {
float t = 0.0;
vec3 color = texture(height_color, vec2(t, 0.0)).rgb;
ALBEDO = color;
}
And the result confirmed the critical code was not with my floating-point arithmetic, but with the texture()
function. The whole planet is white! Compare this to if I hardcode float t = 0.004
. The planet becomes all-blue!
Now that I’ve provided (what I hope to be) sufficient background, my question is simple: why does the texture()
function return a white color when evaluated at 0
? One can see in my GradientTexture1D
config file that I am starting with the deep blue color right from the start (offsets = PackedFloat32Array(0, 0.03, ...)
, colors = PackedColorArray(0.0339462, 0.0841454, 0.507958, 1, ...
). Is this a bug in the texture()
function? Or is this expected behavior? Perhaps I’m using the wrong class to define my color gradient to be used in a shader?
Any clarification at all would be greatly appreciated! Thank you, Godot community!