317 lines
No EOL
10 KiB
Text
317 lines
No EOL
10 KiB
Text
include "pipelines/common.glsl"
|
|
|
|
texture_slot {
|
|
name = "Heightmap",
|
|
default_texture = "textures/common/white.tga"
|
|
}
|
|
|
|
texture_slot {
|
|
name = "Detail albedo",
|
|
default_texture = "textures/common/white.tga"
|
|
}
|
|
|
|
texture_slot {
|
|
name = "Detail normal",
|
|
default_texture = "textures/common/white.tga"
|
|
}
|
|
|
|
texture_slot {
|
|
name = "Splatmap",
|
|
default_texture = "textures/common/white.tga"
|
|
}
|
|
|
|
texture_slot {
|
|
name = "Satellite",
|
|
default_texture = "textures/common/white.tga"
|
|
}
|
|
|
|
texture_slot {
|
|
name = "Noise",
|
|
default_texture = "textures/common/blue_noise.tga"
|
|
}
|
|
|
|
uniform("Detail distance", "float")
|
|
uniform("Detail scale", "float")
|
|
uniform("Noise UV scale", "float")
|
|
uniform("Detail diffusion", "float")
|
|
uniform("Detail power", "float")
|
|
|
|
common [[
|
|
layout(binding=0) uniform sampler2D u_hm;
|
|
layout(binding=1) uniform sampler2DArray u_albedo;
|
|
layout(binding=2) uniform sampler2DArray u_normal;
|
|
layout(binding=3) uniform sampler2D u_splatmap;
|
|
layout(binding=4) uniform sampler2D u_satellite;
|
|
layout(binding=5) uniform sampler2D u_noise;
|
|
|
|
layout(std140, binding = 4) uniform Drawcall {
|
|
ivec4 u_from_to;
|
|
ivec4 u_from_to_sup;
|
|
vec4 u_position;
|
|
vec4 u_rel_camera_pos;
|
|
vec4 u_terrain_scale;
|
|
vec2 u_hm_size;
|
|
float u_cell_size;
|
|
};
|
|
|
|
mat3 getTBN(vec2 uv)
|
|
{
|
|
float hscale = u_terrain_scale.y / u_terrain_scale.x;
|
|
float s01 = textureLodOffset(u_hm, uv, 0, ivec2(-1, 0)).x;
|
|
float s21 = textureLodOffset(u_hm, uv, 0, ivec2(1, 0)).x;
|
|
float s10 = textureLodOffset(u_hm, uv, 0, ivec2(0, -1)).x;
|
|
float s12 = textureLodOffset(u_hm, uv, 0, ivec2(0, 1)).x;
|
|
vec3 va = normalize(vec3(1.0, (s21-s01) * hscale, 0.0));
|
|
vec3 vb = normalize(vec3(0.0, (s12-s10) * hscale, 1.0));
|
|
vec3 N = normalize(cross(vb,va));
|
|
vec3 T = normalize(cross(N, vb));
|
|
return mat3(
|
|
T,
|
|
N,
|
|
normalize(cross(T, N))
|
|
);
|
|
}
|
|
|
|
struct Detail {
|
|
vec4 albedo;
|
|
vec3 normal;
|
|
};
|
|
|
|
// http://www.iquilezles.org/www/articles/texturerepetition/texturerepetition.htm
|
|
float sum( vec4 v ) { return v.x+v.y+v.z; }
|
|
|
|
Detail textureNoTile2(float k, vec2 x, int layer, float v )
|
|
{
|
|
float l = k*8;
|
|
float f = fract(l);
|
|
|
|
float ia = floor(l);
|
|
float ib = ia + 1.0;
|
|
|
|
vec2 offa = sin(vec2(3.0,7.0)*ia);
|
|
vec2 offb = sin(vec2(3.0,7.0)*ib);
|
|
|
|
vec4 cola = textureLod(u_albedo, vec3(x + v * offa, layer), 0);
|
|
vec4 colb = textureLod(u_albedo, vec3(x + v * offb, layer), 0);
|
|
|
|
vec3 norma = textureLod(u_normal, vec3(x + v * offa, layer), 0).xyz * 2 - 1;
|
|
vec3 normb = textureLod(u_normal, vec3(x + v * offb, layer), 0).xyz * 2 - 1;
|
|
|
|
Detail detail;
|
|
float t = smoothstep(0.2,0.8,f-0.1*sum(cola-colb));
|
|
detail.albedo = mix(cola, colb, t);
|
|
detail.normal = mix(norma, normb, t);
|
|
return detail;
|
|
}
|
|
|
|
Detail getDetail(vec2 uv_global, vec2 uv_detail, float power) {
|
|
// TODO one noise texture
|
|
float r = texture(u_noise, uv_global * 256).x * 2 - 1;
|
|
float r2 = texture(u_noise, uv_global.yx * 256).x * 0.6 - 0.3;
|
|
uv_global += vec2(r, r2) / u_hm_size;
|
|
|
|
vec2 uv = uv_global * u_hm_size + vec2(0.5);
|
|
vec2 xy = floor(uv);
|
|
vec2 uv_ratio = uv - xy;
|
|
uv_ratio = pow(uv_ratio, vec2(1));
|
|
vec2 uv_opposite = 1.0 - uv_ratio;
|
|
|
|
vec4 bicoef = vec4(
|
|
uv_opposite.x * uv_opposite.y,
|
|
uv_opposite.x * uv_ratio.y,
|
|
uv_ratio.x * uv_opposite.y,
|
|
uv_ratio.x * uv_ratio.y
|
|
);
|
|
|
|
vec2 half_texel = vec2(0.5) / u_hm_size;
|
|
|
|
vec4 splat00 = textureLodOffset(u_splatmap, uv_global - half_texel, 0, ivec2(0, 0));
|
|
vec4 splat10 = textureLodOffset(u_splatmap, uv_global - half_texel, 0, ivec2(1, 0));
|
|
vec4 splat01 = textureLodOffset(u_splatmap, uv_global - half_texel, 0, ivec2(0, 1));
|
|
vec4 splat11 = textureLodOffset(u_splatmap, uv_global - half_texel, 0, ivec2(1, 1));
|
|
|
|
float noise = texture(u_noise, 0.1 * uv_global * u_hm_size).x;
|
|
|
|
Detail c00 = textureNoTile2(noise, uv_detail, int(splat00.x * 255.0 + 0.5), 1);
|
|
Detail c01 = textureNoTile2(noise, uv_detail, int(splat01.x * 255.0 + 0.5), 1);
|
|
Detail c10 = textureNoTile2(noise, uv_detail, int(splat10.x * 255.0 + 0.5), 1);
|
|
Detail c11 = textureNoTile2(noise, uv_detail, int(splat11.x * 255.0 + 0.5), 1);
|
|
|
|
Detail res;
|
|
res.normal = normalize(getTBN(uv_global) * (c00.normal * bicoef.x + c01.normal * bicoef.y + c10.normal * bicoef.z + c11.normal * bicoef.w).xzy);
|
|
res.albedo = c00.albedo * bicoef.x + c01.albedo * bicoef.y + c10.albedo * bicoef.z + c11.albedo * bicoef.w;
|
|
return res;
|
|
}
|
|
]]
|
|
|
|
vertex_shader [[
|
|
#ifndef DEPTH
|
|
layout (location = 0) out vec2 v_uv;
|
|
layout (location = 1) out vec2 v_uv_detail;
|
|
layout (location = 2) out float v_dist2;
|
|
#endif
|
|
|
|
void main() {
|
|
ivec2 ij = u_from_to.xy + ivec2((gl_VertexID >> 1), gl_InstanceID + (gl_VertexID & 1));
|
|
|
|
vec3 v = vec3(0);
|
|
v.xz = vec2(ij) * u_cell_size;
|
|
int mask = ~1;
|
|
vec3 npos = vec3(0);
|
|
npos.xz = vec2(ij & mask) * u_cell_size;
|
|
|
|
vec2 size = vec2(u_from_to_sup.zw - u_from_to_sup.xy);
|
|
vec2 rel = (ij - u_from_to_sup.xy) / size;
|
|
|
|
rel = saturate(abs(rel - vec2(0.5)) * 10 - 4);
|
|
v.xz = mix(v.xz, npos.xz, rel.yx);
|
|
v.xz = clamp(v.xz, vec2(0), u_hm_size.xy);
|
|
|
|
#ifndef DEPTH
|
|
v_uv = (v.xz / (u_hm_size + 1)) + 0.5 / (u_hm_size + 1);
|
|
|
|
// because of float precision
|
|
vec2 detail_uv_offset = floor(u_rel_camera_pos.xz);
|
|
v_uv_detail = u_detail_scale * v_uv * u_hm_size - detail_uv_offset;
|
|
float h = texture(u_hm, v_uv).x * u_terrain_scale.y;
|
|
if (u_cell_size < 0.5) {
|
|
Detail d = getDetail(v_uv, v_uv_detail, u_detail_power);
|
|
float dist = max(0, length(vec4(u_position.xyz + v + vec3(0, h, 0), 1)) - 4);
|
|
const float detail_h_scale = 0.3 * saturate(1 - dist / 3);
|
|
h += d.albedo.a * detail_h_scale - detail_h_scale * 0.5;
|
|
}
|
|
#else
|
|
vec2 uv = (v.xz / (u_hm_size + 1)) + 0.5 / (u_hm_size + 1);
|
|
float h = texture(u_hm, uv).x * u_terrain_scale.y;
|
|
#endif
|
|
|
|
vec4 p = u_pass_view * vec4(u_position.xyz + v + vec3(0, h, 0), 1);
|
|
#ifndef DEPTH
|
|
v_dist2 = dot(p.xyz, p.xyz);
|
|
#endif
|
|
gl_Position = u_pass_projection * p;
|
|
}
|
|
]]
|
|
|
|
|
|
fragment_shader [[
|
|
#ifdef DEFERRED
|
|
layout(location = 0) out vec4 o_gbuffer0;
|
|
layout(location = 1) out vec4 o_gbuffer1;
|
|
layout(location = 2) out vec4 o_gbuffer2;
|
|
#elif !defined DEPTH
|
|
layout(location = 0) out vec4 o_color;
|
|
#endif
|
|
|
|
#ifndef DEPTH
|
|
layout (location = 0) in vec2 v_uv;
|
|
layout (location = 1) in vec2 v_uv_detail;
|
|
layout (location = 2) in float v_dist2;
|
|
|
|
Detail textureNoTile(float k, vec2 x, int layer, float v )
|
|
{
|
|
vec2 duvdx = dFdx( x );
|
|
vec2 duvdy = dFdx( x );
|
|
|
|
float l = k*8;
|
|
float f = fract(l);
|
|
|
|
float ia = floor(l);
|
|
float ib = ia + 1.0;
|
|
|
|
vec2 offa = sin(vec2(3.0,7.0)*ia);
|
|
vec2 offb = sin(vec2(3.0,7.0)*ib);
|
|
|
|
vec4 cola = textureGrad(u_albedo, vec3(x + v * offa, layer), duvdx, duvdy);
|
|
vec4 colb = textureGrad(u_albedo, vec3(x + v * offb, layer), duvdx, duvdy);
|
|
|
|
vec3 norma = textureGrad(u_normal, vec3(x + v * offa, layer), duvdx, duvdy).xyz * 2 - 1;
|
|
vec3 normb = textureGrad(u_normal, vec3(x + v * offb, layer), duvdx, duvdy).xyz * 2 - 1;
|
|
|
|
Detail detail;
|
|
float t = smoothstep(0.2,0.8,f-0.1*sum(cola-colb));
|
|
detail.albedo = mix(cola, colb, t);
|
|
detail.normal = mix(norma, normb, t);
|
|
return detail;
|
|
}
|
|
|
|
Surface getSurface()
|
|
{
|
|
Surface surface;
|
|
if(v_dist2 < u_detail_distance * u_detail_distance) {
|
|
vec2 uvx = v_uv;
|
|
|
|
float r = texture(u_noise, uvx * u_noise_uv_scale * u_hm_size / u_terrain_scale.xz).x * u_detail_diffusion * 2 - u_detail_diffusion;
|
|
float r2 = texture(u_noise, uvx.yx * u_noise_uv_scale * u_hm_size / u_terrain_scale.xz).x * u_detail_diffusion * 2 - u_detail_diffusion;
|
|
uvx += vec2(r, r2) / u_hm_size;
|
|
|
|
vec2 uv = uvx * u_hm_size / u_terrain_scale.xz;
|
|
vec2 xy = floor(uv);
|
|
vec2 uv_ratio = uv - xy;
|
|
uv_ratio = pow(uv_ratio, vec2(u_detail_power));
|
|
vec2 uv_opposite = 1.0 - uv_ratio;
|
|
|
|
vec4 bicoef = vec4(
|
|
uv_opposite.x * uv_opposite.y,
|
|
uv_opposite.x * uv_ratio.y,
|
|
uv_ratio.x * uv_opposite.y,
|
|
uv_ratio.x * uv_ratio.y
|
|
);
|
|
|
|
vec2 half_texel = vec2(0.5) / u_hm_size;
|
|
|
|
vec4 splat00 = textureLodOffset(u_splatmap, uvx, 0, ivec2(0, 0));
|
|
vec4 splat10 = textureLodOffset(u_splatmap, uvx, 0, ivec2(1, 0));
|
|
vec4 splat01 = textureLodOffset(u_splatmap, uvx, 0, ivec2(0, 1));
|
|
vec4 splat11 = textureLodOffset(u_splatmap, uvx, 0, ivec2(1, 1));
|
|
|
|
float noise = texture(u_noise, 0.1 * v_uv * u_hm_size).x;
|
|
|
|
Detail c00 = textureNoTile(noise, v_uv_detail, int(splat00.x * 255.0 + 0.5), 1);
|
|
Detail c01 = textureNoTile(noise, v_uv_detail, int(splat01.x * 255.0 + 0.5), 1);
|
|
Detail c10 = textureNoTile(noise, v_uv_detail, int(splat10.x * 255.0 + 0.5), 1);
|
|
Detail c11 = textureNoTile(noise, v_uv_detail, int(splat11.x * 255.0 + 0.5), 1);
|
|
|
|
Detail s00 = textureNoTile(noise, v_uv_detail, int(splat00.y * 255.0 + 0.5), 1);
|
|
Detail s01 = textureNoTile(noise, v_uv_detail, int(splat01.y * 255.0 + 0.5), 1);
|
|
Detail s10 = textureNoTile(noise, v_uv_detail, int(splat10.y * 255.0 + 0.5), 1);
|
|
Detail s11 = textureNoTile(noise, v_uv_detail, int(splat11.y * 255.0 + 0.5), 1);
|
|
|
|
vec4 v4 = c00.albedo * bicoef.x + c01.albedo * bicoef.y + c10.albedo * bicoef.z + c11.albedo * bicoef.w;
|
|
vec4 v4_2 = s00.albedo * bicoef.x + s01.albedo * bicoef.y + s10.albedo * bicoef.z + s11.albedo * bicoef.w;
|
|
float a = splat00.z * bicoef.x + splat01.z * bicoef.y + splat10.z * bicoef.z + splat11.z * bicoef.w;
|
|
a = a * 2 - 1;
|
|
vec3 n0 = (c00.normal * bicoef.x + c01.normal * bicoef.y + c10.normal * bicoef.z + c11.normal * bicoef.w).xzy;
|
|
vec3 n0_2 = (s00.normal * bicoef.x + s01.normal * bicoef.y + s10.normal * bicoef.z + s11.normal * bicoef.w).xzy;
|
|
|
|
vec3 n = v4.w > v4_2.w + a ? n0.rgb : n0_2.rgb;
|
|
|
|
surface.N = normalize(getTBN(v_uv) * n);
|
|
surface.albedo.rgb = v4.w > v4_2.w + a ? v4.rgb : v4_2.rgb;
|
|
surface.alpha = 1;
|
|
}
|
|
else {
|
|
surface.N = getTBN(v_uv)[1];
|
|
surface.albedo = texture(u_satellite, vec3(v_uv, 0).xy).rgb;
|
|
surface.albedo *= saturate(0.5 + texture(u_noise, v_uv * 10, -2).r * texture(u_noise, v_uv*29, -2).r * 0.5);
|
|
surface.alpha = 1;
|
|
}
|
|
surface.wpos = vec3(0);
|
|
surface.roughness = 0.9;
|
|
surface.metallic = 0;
|
|
surface.emission = 0;
|
|
return surface;
|
|
}
|
|
#endif
|
|
|
|
void main()
|
|
{
|
|
#if defined DEFERRED
|
|
Surface surface = getSurface();
|
|
packSurface(surface, o_gbuffer0, o_gbuffer1, o_gbuffer2);
|
|
#elif !defined DEPTH
|
|
o_color.rgb = vec3(1, 0, 1);
|
|
o_color.w = 1;
|
|
#endif
|
|
}
|
|
]] |