I wrote this shader in 2019 to generate large scale cities inside QBatica. It is expected to be used in pairs with Instancing Shader. It was made to be applied to the terrain plane to tweak the map on which the buildings blocks are arreanged; it is implemented at the fragment and the tessellation level (so it leverages OpenGL 4 Tessellation technology).
Through the TE stage the shader subdivides the plane geometry basing the density on the camera distance.
Then it displaces the tasselleted e newly GPU-created geometry vertices on the Y axis based on the ripidiy map's RGB values.
#version 410 core
//triangles ccw
layout(quads, fractional_odd_spacing, cw) in;
in VertexData {
vec3 worldPosition;
vec3 normal;
vec2 map;
vec3 tangentViewPos;
vec3 tangentFragPos;
vec3 _tangentLightPositions[5];
} TES_IN[];
out VertexData {
vec3 worldPosition;
vec3 normal;
vec2 map;
vec3 tangentViewPos;
vec3 tangentFragPos;
vec3 _tangentLightPositions[5];
} TES_OUT;
uniform mat4 uPMatrix;
uniform mat4 uMVMatrix;
uniform vec3 cameraPosition;
uniform float displacementCoords;
uniform float displacementMultiplier;
uniform sampler2D ripidityMap;
vec2 interpolate2D(vec2 v0, vec2 v1, vec2 v2) {
return vec2(gl_TessCoord.x) * v0 + vec2(gl_TessCoord.y) * v1 + vec2(gl_TessCoord.z) * v2;
}
vec3 interpolate3D(vec3 v0, vec3 v1, vec3 v2) {
return vec3(gl_TessCoord.x) * v0 + vec3(gl_TessCoord.y) * v1 + vec3(gl_TessCoord.z) * v2;
}
mat3 interpolateMat3(mat3 v0, mat3 v1, mat3 v2) {
return gl_TessCoord.x * v0 + gl_TessCoord.y * v1 + gl_TessCoord.z * v2;
}
void main(void) {
vec3 p0 = mix(TES_IN[0].worldPosition, TES_IN[3].worldPosition, gl_TessCoord.x);
vec3 p1 = mix(TES_IN[1].worldPosition, TES_IN[2].worldPosition, gl_TessCoord.x);
vec3 p = mix(p0, p1, gl_TessCoord.y);
TES_OUT.worldPosition = p;
vec3 n0 = mix(TES_IN[0].normal, TES_IN[3].normal, gl_TessCoord.x);
vec3 n1 = mix(TES_IN[1].normal, TES_IN[2].normal, gl_TessCoord.x);
vec3 n = mix(n0, n1, gl_TessCoord.y);
TES_OUT.normal = normalize(n);
vec2 uv0 = mix(TES_IN[0].map, TES_IN[3].map, gl_TessCoord.x);
vec2 uv1 = mix(TES_IN[1].map, TES_IN[2].map, gl_TessCoord.x);
vec2 uv = mix(uv0, uv1, gl_TessCoord.y);
TES_OUT.map = uv;
TES_OUT.tangentFragPos = interpolate3D(TES_IN[0].tangentFragPos, TES_IN[1].tangentFragPos, TES_IN[2].tangentFragPos);
TES_OUT.tangentViewPos = interpolate3D(TES_IN[0].tangentViewPos, TES_IN[1].tangentViewPos, TES_IN[2].tangentViewPos);
TES_OUT._tangentLightPositions = TES_IN[1]._tangentLightPositions;
// __________________________________________
// DISPLACE
// __________________________________________
float displacement = texture(ripidityMap, TES_OUT.map * displacementCoords).b;
TES_OUT.worldPosition += TES_OUT.normal * (displacement * displacementMultiplier);
gl_Position = uPMatrix * uMVMatrix * vec4(TES_OUT.worldPosition, 1.0);
}