shader Terrain GPU4
Inhaltsverzeichnis
Heightmap-Terrain
Zurück zur Shadersammlung
| Beschreibung | Autor | Version |
|---|---|---|
| Shader für ein Heightmap-Terrain, benutzt GL_EXT_gpu_shader4 | Coolcat | 1.0 |
Bilder
Beschreibung
Ein Terrain-Shader. Das Terrain wird in Blöcken von 64x64 gerendert, da man ja sinnvollerweise sowieso einen Quadtree oder ähnliches einsetzt. Als Geometrie wird nur ein VBO mit 65x65 minimalen 2D-Vertices und ein entsprechender Indexbuffer benötigt. Die 3D-Vertices, Normalen und Texturkoordinaten werden im Vertexshader mit Hilfe der Heightmap-Textur erzeugt. Im Fragmentshader werden drei Texturlayer mit Hilfe einer Alphamap interpoliert.
Ein solcher Shader ist besonders für hochauflösende/große Terrains sinnvoll, da keine redundanten Daten im Speicher liegen müssen.
Besondere Vorraussetzungen
Ich verwende hier einige Features des ShaderModel 4.0 zwecks Performance-Optimierung, es wird also GL_EXT_gpu_shader4 benötigt. Ich verwende Integer-Arithmetik und die Funktion texelFetch2DOffset für beschleunigten Texturzugriff. Es sollte aber möglich sein den Shader auch so umzuschreiben, dass er auch auf älterer Hardware funktioniert.
Code
Vertexshader:
#extension GL_EXT_gpu_shader4 : enable
uniform sampler2D heightmap; // heightmap texture
uniform ivec2 leafmin; // minimum position of current block
const int tmin = 1; // minimum border for clamping
const int tmax = 2047; // maximum border for clamping
const float leafsize = 64.0; // size of terrain block
const float terrainsize = 2048.0; // size of complete terrain (heightmap size)
const float terrainscale = 100.0; // terrain height scale
varying vec3 normal;
void main()
{
float height;
ivec2 coords = leafmin + ivec2(gl_Vertex.xy);
if (coords.x < tmin || coords.y < tmin || coords.x > tmax || coords.y > tmax) {
// clamping borders
height = 0.0;
normal = vec3(0,-1,0);
}
else {
//retrieve height
height = terrainscale * texelFetch2DOffset(heightmap, coords, 0, ivec2(0,0)).a;
//compute normal
vec3 vector[6];
vector[0] = vec3( 0.0, texelFetch2DOffset(heightmap, coords, 0, ivec2( 0, -1)).a, -1.0);
vector[1] = vec3(-1.0, texelFetch2DOffset(heightmap, coords, 0, ivec2(-1, -1)).a, -1.0);
vector[2] = vec3(-1.0, texelFetch2DOffset(heightmap, coords, 0, ivec2(-1, 0)).a, 0.0);
vector[3] = vec3( 0.0, texelFetch2DOffset(heightmap, coords, 0, ivec2( 0, 1)).a, 1.0);
vector[4] = vec3( 1.0, texelFetch2DOffset(heightmap, coords, 0, ivec2( 1, 1)).a, 1.0);
vector[5] = vec3( 1.0, texelFetch2DOffset(heightmap, coords, 0, ivec2( 1, 0)).a, 0.0);
for (int i=0; i<6; ++i) {
vector[i].y = terrainscale * vector[i].y - height;
}
normal = cross(vector[5], vector[0]);
for (int i=1; i<6; ++i) {
normal += cross(vector[i-1], vector[i]);
}
normal = normalize(normal);
}
// transform position
vec4 position = vec4(coords.x, height, coords.y, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * position;
// generate texture coords
gl_TexCoord[0].xy = gl_Vertex.xy / leafsize; // coords for layer texture
gl_TexCoord[1].xy = vec2(coords) / terrainsize; // coords for alphamap
}
Fragmentshader:
uniform sampler2D layer0;
uniform sampler2D layer1;
uniform sampler2D layer2;
uniform sampler2D alpha;
varying vec3 normal;
void main()
{
// compute terrain color
vec3 tex0 = texture2D(layer0, gl_TexCoord[0].xy).rgb;
vec3 tex1 = texture2D(layer1, gl_TexCoord[0].xy).rgb;
vec3 tex2 = texture2D(layer2, gl_TexCoord[0].xy).rgb;
vec3 a = texture2D(alpha, gl_TexCoord[1].xy).rgb;
vec3 color = a.r*tex0 + a.g*tex1 + a.b*tex2;
// compute diffuse lighting (directional lightsource only)
float diffuse = max(dot(gl_LightSource[0].position.xyz, normal), 0.0);
gl_FragColor = vec4(diffuse * color + gl_LightSource[0].ambient.xyz, 1.0);
}