EG/RenderPipelineFile/effects/terrain-effect.yaml
2026-02-25 14:56:09 +08:00

143 lines
5.0 KiB
YAML

# Terrain effect
# This effect uses prodecural shader splatting, you most likely want to modify
# it with your own texture-map generation code.
vertex:
inout: |
uniform struct {
sampler2D data_texture;
sampler2D heightfield;
int view_index;
int terrain_size;
int chunk_size;
} ShaderTerrainMesh;
out vec2 terrain_uv;
transform: |
// Terrain data has the layout:
// x: x-pos, y: y-pos, z: size, w: clod
vec4 terrain_data = texelFetch(ShaderTerrainMesh.data_texture,
ivec2(gl_InstanceID, ShaderTerrainMesh.view_index), 0);
// Get initial chunk position in the (0, 0, 0), (1, 1, 0) range
vec3 chunk_position = p3d_Vertex.xyz;
// CLOD implementation
float clod_factor = smoothstep(0, 1, terrain_data.w);
chunk_position.xy -= clod_factor * fract(chunk_position.xy * ShaderTerrainMesh.chunk_size / 2.0)
* 2.0 / ShaderTerrainMesh.chunk_size;
// Scale the chunk
chunk_position *= terrain_data.z * float(ShaderTerrainMesh.chunk_size)
/ float(ShaderTerrainMesh.terrain_size);
chunk_position.z *= ShaderTerrainMesh.chunk_size;
// Offset the chunk, it is important that this happens after the scale
chunk_position.xy += terrain_data.xy / float(ShaderTerrainMesh.terrain_size);
// Compute the terrain UV coordinates
terrain_uv = chunk_position.xy;
// Sample the heightfield and offset the terrain - we do not need to multiply
// the height with anything since the terrain transform is included in the
// model view projection matrix.
chunk_position.z += texture(ShaderTerrainMesh.heightfield, terrain_uv).x;
// Lower the terrain on the borders - this ensures the shadow map is generated
// correctly.
if ( min(terrain_uv.x, terrain_uv.y) < 8.0 / ShaderTerrainMesh.terrain_size ||
max(terrain_uv.x, terrain_uv.y) > 1 - 9.0 / ShaderTerrainMesh.terrain_size) {
chunk_position.z = 0;
}
vOutput.position = (p3d_ModelMatrix * vec4(chunk_position, 1)).xyz;
fragment:
defines: |
#define DONT_FETCH_DEFAULT_TEXTURES 1
#define DONT_SET_MATERIAL_PROPERTIES 1
inout: |
layout(location=4) in vec2 terrain_uv;
layout(location=5) uniform struct {
sampler2D data_texture;
sampler2D heightfield;
int view_index;
int terrain_size;
int chunk_size;
} ShaderTerrainMesh;
material: |
// Compute terrain normal
const float terrain_height = 1000.0;
vec3 pixel_size = vec3(1.0, -1.0, 0) / textureSize(ShaderTerrainMesh.heightfield, 0).xxx;
float h_u0 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.yz).x * terrain_height;
float h_u1 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.xz).x * terrain_height;
float h_v0 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.zy).x * terrain_height;
float h_v1 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.zx).x * terrain_height;
vec3 tangent = normalize(vec3(1, 0, h_u1 - h_u0));
vec3 binormal = normalize(vec3(0, 1, h_v1 - h_v0));
vec3 normal = normalize(cross(tangent, binormal));
normal.x *= -1;
// normal.y *= -1;
// Material splatting
float height = (h_u0 + h_u1 + h_v0 + h_v1) / (4.0 * terrain_height); // xxx
float slope = 1.0 - normal.z;
float grass = 0.0;
float rock = 0.0;
float snow = 0.0;
{ // Snow
snow = saturate(4.0 * (height-0.49));
snow *= saturate(pow(saturate(1.0 - slope), 2.0)) * 12.0;
//snow -= 0.6;
//snow *= 0.5;
snow = saturate(snow);
snow = pow(snow, 2.0);
}
{ // Rock
rock = saturate((pow(slope, 1.2) * 12.0 - 0.02) * 4.5);
}
{ // Grass
grass = 1.0 - saturate(rock + snow);
}
// Material definitions
MaterialShaderOutput grass_mat = make_default_material_output();
grass_mat.basecolor = vec3(0.1, 0.2, 0.1);
grass_mat.roughness = 0.8;
MaterialShaderOutput rock_mat = make_default_material_output();
rock_mat.basecolor = vec3(0.13);
rock_mat.roughness = 0.8;
rock_mat.specular_ior = 1.4;
MaterialShaderOutput snow_mat = make_default_material_output();
snow_mat.basecolor = vec3(0.6, 0.6, 0.9);
snow_mat.roughness = 0.5;
snow_mat.specular_ior = 1.7;
m.basecolor = vec3(0);
m.shading_model = SHADING_MODEL_DEFAULT;
m.specular_ior = 0.0;
m.metallic = 0.0;
m.roughness = 0.0;
m.shading_model_param0 = 0.0;
m.normal = vec3(0);
merge_material_output(m, grass_mat, grass);
merge_material_output(m, rock_mat, rock);
merge_material_output(m, snow_mat, snow);
m.normal = normal;