Add terrain shader for height, heat, and moisture rendering
This commit is contained in:
221
Shaders/terrain.gdshader
Normal file
221
Shaders/terrain.gdshader
Normal file
@@ -0,0 +1,221 @@
|
||||
shader_type canvas_item;
|
||||
|
||||
// Textures
|
||||
uniform sampler2D height_map : filter_nearest;
|
||||
|
||||
// Thresholds
|
||||
uniform float water_threshold = 0.0;
|
||||
uniform float rock_threshold = 0.05;
|
||||
uniform float snow_height_threshold = 0.8;
|
||||
uniform float desert_temperature_threshold = 0.7;
|
||||
uniform float desert_moisture_threshold = 0.3;
|
||||
|
||||
// Noise parameters
|
||||
uniform float temperature_noise_frequency = 0.01;
|
||||
uniform float temperature_noise_amplitude = 0.1;
|
||||
uniform float moisture_noise_frequency = 0.01;
|
||||
uniform float moisture_noise_amplitude = 0.1;
|
||||
|
||||
// Debug mode
|
||||
uniform int debug_type = 0; // 0=None, 1=Slope, 2=Height, 3=Temperature, 4=Moisture
|
||||
|
||||
// Colors
|
||||
const vec3 COLOR_WATER = vec3(0.263, 0.502, 0.690); // #4380b0
|
||||
const vec3 COLOR_GRASS = vec3(0.745, 0.835, 0.541); // #bed58a
|
||||
const vec3 COLOR_DESERT = vec3(0.929, 0.788, 0.686); // #edc9af
|
||||
const vec3 COLOR_BEACH = vec3(0.502, 0.502, 0.502); // #808080
|
||||
const vec3 COLOR_SNOW = vec3(0.957, 0.957, 0.957); // #f4f4f4
|
||||
const vec3 COLOR_ROCK = vec3(0.894, 0.827, 0.651); // #e4d3a6
|
||||
|
||||
// Better hash function for noise
|
||||
vec2 hash22(vec2 p, float seed) {
|
||||
vec3 p3 = fract(vec3(p.xyx) * vec3(443.897, 441.423, 437.195));
|
||||
p3 += dot(p3, p3.yzx + 19.19 + seed);
|
||||
return fract((p3.xx + p3.yz) * p3.zy);
|
||||
}
|
||||
|
||||
float hash12(vec2 p, float seed) {
|
||||
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
|
||||
p3 += dot(p3, p3.yzx + 33.33 + seed);
|
||||
return fract((p3.x + p3.y) * p3.z);
|
||||
}
|
||||
|
||||
// Perlin noise
|
||||
float perlin_noise(vec2 p, float seed) {
|
||||
vec2 i = floor(p);
|
||||
vec2 f = fract(p);
|
||||
|
||||
// Quintic interpolation
|
||||
vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
|
||||
|
||||
// Get gradients
|
||||
float a = hash12(i + vec2(0.0, 0.0), seed);
|
||||
float b = hash12(i + vec2(1.0, 0.0), seed);
|
||||
float c = hash12(i + vec2(0.0, 1.0), seed);
|
||||
float d = hash12(i + vec2(1.0, 1.0), seed);
|
||||
|
||||
// Mix
|
||||
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y) * 2.0 - 1.0;
|
||||
}
|
||||
|
||||
// FBM with 12 octaves
|
||||
float fbm(vec2 p, float freq, float seed) {
|
||||
float value = 0.0;
|
||||
float amplitude = 0.5;
|
||||
float frequency = freq;
|
||||
|
||||
for(int i = 0; i < 12; i++) {
|
||||
value += amplitude * perlin_noise(p * frequency, seed);
|
||||
frequency *= 2.0;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Get height at UV coordinates
|
||||
float get_height(vec2 uv) {
|
||||
return texture(height_map, uv).r;
|
||||
}
|
||||
|
||||
// Get heat value with latitude falloff
|
||||
float get_heat(vec2 uv) {
|
||||
vec2 heightmap_size = vec2(textureSize(height_map, 0));
|
||||
vec2 heightmap_coords = uv * heightmap_size;
|
||||
|
||||
// Original C# uses 1/frequency for FastNoiseLite, so we invert
|
||||
float actualFreq = 1.0 / temperature_noise_frequency;
|
||||
float heat = fbm(heightmap_coords, actualFreq, 1738.0);
|
||||
heat *= temperature_noise_amplitude;
|
||||
|
||||
// Latitude falloff (sine wave from poles to equator)
|
||||
float latitude_factor = sin(3.14159265 * uv.y);
|
||||
heat = latitude_factor + heat;
|
||||
|
||||
return heat;
|
||||
}
|
||||
|
||||
// Get moisture value with latitude falloff
|
||||
float get_moisture(vec2 uv) {
|
||||
vec2 heightmap_size = vec2(textureSize(height_map, 0));
|
||||
vec2 heightmap_coords = uv * heightmap_size;
|
||||
|
||||
// Original C# uses 1/frequency for FastNoiseLite, so we invert
|
||||
float actualFreq = 1.0 / moisture_noise_frequency;
|
||||
float moisture = fbm(heightmap_coords, actualFreq, 1337.0);
|
||||
moisture *= moisture_noise_amplitude;
|
||||
|
||||
// Latitude falloff (sine wave from poles to equator)
|
||||
float latitude_factor = sin(3.14159265 * uv.y);
|
||||
moisture = latitude_factor + moisture;
|
||||
|
||||
return moisture;
|
||||
}
|
||||
|
||||
// Check if position is sea
|
||||
bool is_sea(vec2 uv) {
|
||||
return get_height(uv) <= water_threshold;
|
||||
}
|
||||
|
||||
// Check if position is desert
|
||||
bool is_desert(vec2 uv, float heat, float moisture) {
|
||||
return moisture < desert_moisture_threshold && heat > desert_temperature_threshold;
|
||||
}
|
||||
|
||||
// Calculate max height difference with neighbors (for slope)
|
||||
float get_max_slope(vec2 uv) {
|
||||
vec2 heightmap_size = vec2(textureSize(height_map, 0));
|
||||
vec2 texel_size = 1.0 / heightmap_size;
|
||||
|
||||
float center_height = get_height(uv);
|
||||
float max_diff = 0.0;
|
||||
|
||||
// Check 8 neighbors
|
||||
for(int x = -1; x <= 1; x++) {
|
||||
for(int y = -1; y <= 1; y++) {
|
||||
if(x == 0 && y == 0) continue;
|
||||
|
||||
vec2 neighbor_uv = uv + vec2(float(x), float(y)) * texel_size;
|
||||
float neighbor_height = get_height(neighbor_uv);
|
||||
float diff = abs(center_height - neighbor_height);
|
||||
max_diff = max(max_diff, diff);
|
||||
}
|
||||
}
|
||||
|
||||
return max_diff;
|
||||
}
|
||||
|
||||
// Check if any neighbor is sea (for beach detection)
|
||||
bool borders_sea(vec2 uv) {
|
||||
vec2 heightmap_size = vec2(textureSize(height_map, 0));
|
||||
vec2 texel_size = 1.0 / heightmap_size;
|
||||
|
||||
for(int x = -1; x <= 1; x++) {
|
||||
for(int y = -1; y <= 1; y++) {
|
||||
if(x == 0 && y == 0) continue;
|
||||
|
||||
vec2 neighbor_uv = uv + vec2(float(x), float(y)) * texel_size;
|
||||
if(is_sea(neighbor_uv)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec2 uv = UV;
|
||||
|
||||
float height_value = get_height(uv);
|
||||
float heat_value = get_heat(uv);
|
||||
float moisture_value = get_moisture(uv);
|
||||
float slope = get_max_slope(uv);
|
||||
|
||||
vec3 color = COLOR_GRASS; // Default
|
||||
|
||||
if(debug_type == 0) {
|
||||
// Normal rendering
|
||||
|
||||
// Water
|
||||
if(is_sea(uv)) {
|
||||
color = COLOR_WATER;
|
||||
}
|
||||
// Desert
|
||||
else if(is_desert(uv, heat_value, moisture_value)) {
|
||||
color = COLOR_DESERT;
|
||||
}
|
||||
// Beach (borders sea)
|
||||
else if(borders_sea(uv)) {
|
||||
color = COLOR_BEACH;
|
||||
}
|
||||
// Snow (high altitude, not desert)
|
||||
else if(height_value > snow_height_threshold && !is_desert(uv, heat_value, moisture_value)) {
|
||||
color = COLOR_SNOW;
|
||||
}
|
||||
|
||||
// Rock (steep areas) - override everything except water
|
||||
if(slope > rock_threshold && !is_sea(uv)) {
|
||||
color = COLOR_ROCK;
|
||||
}
|
||||
}
|
||||
else if(debug_type == 1) {
|
||||
// Slope debug
|
||||
float gray = slope * 5.0;
|
||||
color = vec3(gray);
|
||||
}
|
||||
else if(debug_type == 2) {
|
||||
// Height debug
|
||||
color = vec3(height_value);
|
||||
}
|
||||
else if(debug_type == 3) {
|
||||
// Temperature debug
|
||||
color = vec3(heat_value);
|
||||
}
|
||||
else if(debug_type == 4) {
|
||||
// Moisture debug
|
||||
color = vec3(moisture_value);
|
||||
}
|
||||
|
||||
COLOR = vec4(color, 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user