improve water

This commit is contained in:
Tuomas Katajisto 2025-10-18 20:18:01 +03:00
parent 857338993b
commit 68828cb119
10 changed files with 762 additions and 371 deletions

View File

@ -1,6 +1,6 @@
{
"exposure": 0.057475,
"contrast": 2.06763,
"contrast": 1.075945,
"saturation": 1.202777,
"gamma": 1.019477,
"tonemap": 1

File diff suppressed because one or more lines are too long

BIN
resources/utiltex/water.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -95,6 +95,7 @@ init :: () {
}
init_after_asset_pack :: () {
init_plane_textures();
add_font_from_pack("./resources/DroidSerif-Regular.ttf");
ui_init_font_fields(*state.font_default);

View File

@ -161,6 +161,9 @@ backend_draw_ground :: (wc: *World_Config) {
w, h := get_render_size();
plane_data.screen_w = w;
plane_data.screen_h = h;
plane_data.cameraPosition = camera.position.component;
plane_data.reflectionDistortion = 0.1;
plane_data.shininess = 0.2;
world_config_to_shader_type(wc, *world_conf);

View File

@ -473,6 +473,12 @@ create_plane_pipeline :: () {
mag_filter = .NEAREST,
}));
gPipelines.plane.bind.samplers[3] = sg_make_sampler(*(sg_sampler_desc.{
wrap_u = .REPEAT,
wrap_v = .REPEAT,
min_filter = .LINEAR,
mag_filter = .LINEAR,
}));
ground_img_desc := sg_image_desc.{
width = 1000,
@ -574,3 +580,7 @@ create_postprocess_pipeline :: () {
}));
}
init_plane_textures :: () {
gPipelines.plane.bind.images[3] = create_texture_from_pack("./resources/utiltex/water.png");
}

File diff suppressed because it is too large Load Diff

View File

@ -26,25 +26,12 @@ in vec4 pos;
in flat int idx;
out vec4 frag_color;
// Uniform bindings from the original shader
layout(binding=3) uniform plane_fs_params {
mat4 mvp_shadow;
int is_reflection;
};
uint murmurHash12(uvec2 src) {
const uint M = 0x5bd1e995u;
uint h = 1190494759u;
src *= M; src ^= src>>24u; src *= M;
h *= M; h ^= src.x; h *= M; h ^= src.y;
h ^= h>>13u; h *= M; h ^= h>>15u;
return h;
}
float hash12(vec2 src) {
uint h = murmurHash12(floatBitsToUint(src));
return uintBitsToFloat(h & 0x007fffffu | 0x3f800000u) - 1.0;
}
layout(binding=1) uniform plane_world_config {
vec3 skyBase;
vec3 skyTop;
@ -55,14 +42,11 @@ layout(binding=1) uniform plane_world_config {
vec3 sunPosition;
float sunIntensity;
float skyIntensity;
int hasClouds;
float planeHeight;
int planeType;
vec3 waterColor;
vec3 deepColor;
float time;
};
@ -70,99 +54,64 @@ layout(binding=2) uniform plane_data {
int screen_w;
int screen_h;
int is_reflection_pass;
vec3 cameraPosition;
float shininess; // Controls the size of the sun's glint, e.g., 64.0
float reflectionDistortion; // Controls how much waves distort reflections, e.g., 0.05
};
// Texture bindings
layout(binding = 0) uniform texture2D reftex;
layout(binding = 1) uniform texture2D groundtex;
layout(binding = 2) uniform texture2D shadow;
layout(binding = 3) uniform texture2D normal_map;
// Sampler bindings
layout(binding = 0) uniform sampler refsmp;
layout(binding = 1) uniform sampler groundsmp;
layout(binding = 2) uniform sampler shadowsmp;
layout(binding = 3) uniform sampler normalsmp;
float random (vec2 st) {
return fract(sin(dot(st.xy,
vec2(12.9898,78.233)))*
43758.5453123);
}
// 2D Noise based on Morgan McGuire @morgan3d
// https://www.shadertoy.com/view/4dS3Wd
float noise (vec2 st) {
vec2 i = floor(st); // Integer part of the coordinate
vec2 f = fract(st); // Fractional part of the coordinate
// Four corners in 2D of a tile
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
// Smoothstep for interpolation
vec2 u = f*f*(3.0-2.0*f);
// Mix (interpolate) the corners
return mix(a, b, u.x) +
(c - a)* u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
int sign2(float x) {
if(x < 0) return -1;
return 1;
}
vec3 get_ground_sample(vec4 pos, float dirX, float dirY) {
ivec2 plane_coord = ivec2(floor(pos.x + dirX) + 500, floor(pos.z + dirY) + 500);
vec4 reflection = texelFetch(sampler2D(reftex, refsmp), ivec2(gl_FragCoord.x, screen_h - gl_FragCoord.y), 0);
vec4 groundSample = texelFetch(sampler2D(groundtex, groundsmp), plane_coord, 0);
// Calculate all materials so we can blend them.
vec3 water = reflection.xyz * vec3(0.95, 1.0, 0.95);
vec3 sand = vec3(mix(0.8, 1.0, hash12(pos.xz)) * vec3(0.8, 0.7, 0.5));
vec3 grass = vec3(mix(0.8, 1.0, hash12(pos.xz)) * vec3(0.4, 0.8, 0.3));
if(groundSample.b == 1.0) {
return water;
} else if(groundSample.r == 1.0) {
return sand;
} else {
return grass;
}
vec3 fresnelSchlick(float cosTheta) {
vec3 F0 = vec3(0.02);
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
void main() {
// vec4 npos = floor(pos * 16.0) / 16.0;
// vec2 tileCenter = vec2(floor(npos.x) + 0.5, floor(npos.z) + 0.5);
// vec2 toCenter = npos.xz - tileCenter;
// // Bilinear filtering
// float u = smoothstep(0.2, 0.5, abs(toCenter.x)) * 0.5;
// float v = smoothstep(0.2, 0.5, abs(toCenter.y)) * 0.5;
// // @ToDo: We should implement some sort of fog system and stop doing all this sampling
// // stuff if we are far enough from the camera. Currently ground rendering is taking way
// // too much time each frame.
// vec3 c0 = get_ground_sample(npos, 0.0, 0.0);
// vec3 c1 = get_ground_sample(npos, sign2(toCenter.x), 0.0);
// vec3 c2 = get_ground_sample(npos, 0.0, sign2(toCenter.y));
// vec3 c3 = get_ground_sample(npos, sign2(toCenter.x), sign2(toCenter.y));
if(idx == 1) { // Second instance of the plane is the actual water surface.
vec2 uv1 = pos.xz * 0.1 + time * 0.01;
vec2 uv2 = pos.xz * 0.1 + time * vec2(-0.005, -0.012);
// // @ToDo: Consider using cool Inigo Quilez trick here to make it even smoother.
// vec3 b01 = mix(c0, c1, u);
// vec3 b23 = mix(c2, c3, u);
// vec3 bf = mix(b01, b23, v);
// vec4 light_proj_pos = mvp_shadow * vec4(npos.xyz + vec3(1.0/32.0, 0.0, 1.0/32.0), 1.0);
// vec3 light_pos = light_proj_pos.xyz / light_proj_pos.w;
// light_pos = light_pos * 0.5 + 0.5;
// float bias = 0.0005;
// float shadowp = max(0.7, texture(sampler2DShadow(shadow, shadowsmp), vec3(light_pos.xy, light_pos.z - bias)));
if(idx == 1) {
float f = skyBase.x * float(screen_h) * mvp_shadow[0][0];
vec4 reflection = texelFetch(sampler2D(reftex, refsmp), ivec2(gl_FragCoord.x, screen_h - gl_FragCoord.y), 0);
frag_color = vec4(reflection.xyz * 0.5 + waterColor * 0.5 + 0.00001 * f, 0.3);
} else {
vec3 normal1 = texture(sampler2D(normal_map, normalsmp), uv1).xzy * 2.0 - 1.0;
vec3 normal2 = texture(sampler2D(normal_map, normalsmp), uv2).xzy * 2.0 - 1.0;
vec3 normal = normalize(normal1 + normal2);
vec3 view_dir = normalize(cameraPosition - pos.xyz);
vec3 light_dir = normalize(sunPosition);
vec3 halfway_dir = normalize(light_dir + view_dir);
float shadow_factor = 1.0; // shadowmap to be implemented
vec3 base_water_color = waterColor;
float diffuse = (dot(normal, light_dir)) + 0.000001 * is_reflection * shininess;
float spec = pow(max(0.0, dot(halfway_dir, normal)), 32);
float fresnel = min(1.0, fresnelSchlick(dot(view_dir, vec3(0.0, 1.0, 0.0))).x + 0.3);
vec3 refracted_color = base_water_color * diffuse * sunLightColor * sunIntensity;
vec3 specular_highlight = sunLightColor * sunIntensity * spec;
vec2 screen_uv = gl_FragCoord.xy / vec2(screen_w, screen_h);
vec2 distortion = normal.xz * 0.005;
screen_uv.y = 1.0 - screen_uv.y;
vec3 reflected_color = texture(sampler2D(reftex, refsmp), screen_uv + distortion).rgb;
vec3 surface_color = mix(refracted_color, reflected_color, fresnel);
vec3 final_color = (surface_color + specular_highlight) * shadow_factor;
float refraction_alpha = 0.3; // Base transparency when looking straight down
float reflection_alpha = 0.5; // Surface is opaque where it's most reflective
float alpha = mix(refraction_alpha, reflection_alpha, fresnel);
frag_color = vec4(final_color, alpha);
} else { // Deep water plane (unchanged)
frag_color = vec4(deepColor, 1.0);
}
}

View File

@ -75,11 +75,11 @@ input_code_from_type_and_notes :: (name: string, type: *Type_Info, notes: []stri
if autoconf.kind == .DEFAULT {
print_to_builder(*builder, "{\n");
print_to_builder(*builder, "orig_w := r.w; orig_x := r.x; r.w = r.w / 3;\n");
print_to_builder(*builder, "GR.number_input(r, tprint(\"\%\", value.%.x), *value.%.x, 0, 100, *number_theme);\n", name, name);
print_to_builder(*builder, "GR.number_input(r, tprint(\"\%\", value.%.x), *value.%.x, -100, 100, *number_theme);\n", name, name);
print_to_builder(*builder, "r.x += r.w;\n");
print_to_builder(*builder, "GR.number_input(r, tprint(\"\%\", value.%.y), *value.%.y, 0, 100, *number_theme);\n", name, name);
print_to_builder(*builder, "GR.number_input(r, tprint(\"\%\", value.%.y), *value.%.y, -100, 100, *number_theme);\n", name, name);
print_to_builder(*builder, "r.x += r.w;\n");
print_to_builder(*builder, "GR.number_input(r, tprint(\"\%\", value.%.z), *value.%.z, 0, 100, *number_theme);\n", name, name);
print_to_builder(*builder, "GR.number_input(r, tprint(\"\%\", value.%.z), *value.%.z, -100, 100, *number_theme);\n", name, name);
print_to_builder(*builder, "r.w = orig_w; r.x = orig_x;\n");
print_to_builder(*builder, "}\n");
} else if autoconf.kind == .COLOR {