shader Terrain GPU4

Aus DGL Wiki
Wechseln zu: Navigation, Suche

Heightmap-Terrain

Zurück zur Shadersammlung

Beschreibung Autor Version
Shader für ein Heightmap-Terrain, benutzt GL_EXT_gpu_shader4 Coolcat 1.0

Bilder

Terrain mit 3 Texturlayern
Terrain aus Heightmap mit 2048x2048 Auflösung und 16bit Graustufen

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