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); }