shader Terrain GPU4: Unterschied zwischen den Versionen
(→Besondere Vorraussetzungen: texelFetch2DOffset kurz erklärt) |
K (→Besondere Voraussetzungen) |
||
Zeile 27: | Zeile 27: | ||
Es sollte aber möglich sein den Shader auch so umzuschreiben, dass er auch auf älterer Hardware funktioniert. | Es sollte aber möglich sein den Shader auch so umzuschreiben, dass er auch auf älterer Hardware funktioniert. | ||
+ | |||
+ | {{Hinweis|Die Aussagen zur Funktionsweise von ''texelFetch2DOffset'' basieren auf Vermutungen. Die Spezifikation von [[GL_EXT_gpu_shader4]] macht leider keine Angaben was intern genau passiert. Die Tatsache das der Offset konstant sein muss und begrenzt ist legt dies aber Nahe.}} | ||
==Code== | ==Code== |
Version vom 22. Februar 2009, 19:55 Uhr
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 daher nur ein VBO mit 65x65 minimalen 2D-Vertices (keine Höhe) 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 der Grafikkarte liegen müssen.
Besondere Voraussetzungen
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. texelFetch2D ermöglicht den Texturzugriff über exakte Integer-Koordinaten, was intern die Multiplikation mit der Texturgröße sowie die Interpolation zwischen verschiedenen Texeln unnötig macht. Durch texelFetch2DOffset kann der Compiler multiple Texturzugriffe auf die selbe Textur mittels Cache beschleunigen, sofern der Offset klein und konstant ist.
Es sollte aber möglich sein den Shader auch so umzuschreiben, dass er auch auf älterer Hardware funktioniert.
Die Aussagen zur Funktionsweise von texelFetch2DOffset basieren auf Vermutungen. Die Spezifikation von GL_EXT_gpu_shader4 macht leider keine Angaben was intern genau passiert. Die Tatsache das der Offset konstant sein muss und begrenzt ist legt dies aber Nahe. |
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); }