Multiple texture images blended together onto 3D ground

感情迁移 提交于 2019-11-30 07:07:40

I have recently written a small terrain rendering engine in OpenGL that does something similar to what you are talking about. The technique I use is best described as texture splatting.

I use five textures to accomplish this. Four of these textures are detail textures: grass, rock, water, and sand. These textures are smallish, 512x512 textures, and they are tiled across the terrain. The fifth texture is a mixmap. The mixmap is a giant texture that covers the entire terrain, in my case is 4096x4096.

MixMap

This mixmap uses all 4 color channels (r,g,b,a) to describe how much of a detail texture to display at that specific location. I am using the red color channel to determine how opaque the sand is, green is for grass, blue is for water, and alpha is for rock. This mixmap is calculated based off of the heightmap at initialization and I use altitudes to determine these values. For instance, close to the sea level, I mostly want water, so I set a high value in the blue channel and low values in the other channels. As I get higher into the mountains, I set the alpha color channel to a high value since I want a lot of rock texture, but I set all of the other color channels to lower values.

Fragment Shader

This mixmap is then put to use in the fragment shader, where I take these color channels of the mixmap and use them to combine the detail textures. Here is the GLSL code I am using for the fragment shader:

uniform sampler2D alpha;
uniform sampler2D grass;
uniform sampler2D water;
uniform sampler2D rock;
uniform sampler2D sand;
uniform float texscale;

varying vec3 normal, lightDir ;

void main()
{
   // Get the color information
   vec3 alpha    = texture2D( alpha, gl_TexCoord[0].st ).rgb;
   vec3 texSand  = texture2D( sand, gl_TexCoord[0].st * texscale ).rgb;
   vec3 texGrass = texture2D( grass,  gl_TexCoord[0].st * texscale ).rgb;
   vec3 texWater = texture2D( water, gl_TexCoord[0].st * texscale ).rgb;
   vec3 texRock  = texture2D( rock,  gl_TexCoord[0].st * texscale ).rgb;

   // Mix the colors together
   texSand *= mixmap.r;
   texGrass = mix(texSand,  texGrass, mixmap.g);
   texWater = mix(texGrass, texWater, mixmap.b);
   vec3 tx  = mix(texWater, texRock,  mixmap.a);

   // Lighting calculations
   vec3 dl = gl_LightSource[0].diffuse.rgb;   
   vec3 al = gl_LightSource[0].ambient.rgb;
   vec3 n = normalize(normal);
   vec3 d = tx * (dl * max( dot ( n, lightDir), 0.0 ) + al );   

   // Apply the lighting to the final color   
   vec4 finalColor = vec4( min(d, 1.0), 1.0);
   gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor);
}

The uniform texscale is a value that determines how many times the detail textures are tiled across the terrain. Higher values will make the detail textures look more crisp at the risk of making them look more repetitious.

prebaking is most certainly not inefficient. it's the most efficient in fact because your runtime code doesn't have to do any blending or other calculations. it just renders the texture. Of course, the dynamic techniques offer a simplification of the toolchain and more options.

ID seem to have made the single large "megatexture" (we're talking 32K^2-128K^2) work in QuakeWars.

There's an interesting Q&A with Carmack about it at

http://web.archive.org/web/20080121072936/http://www.gamerwithin.com/?view=article&article=1319&cat=2

(original link seems dead currently, but not not making the above a link because SO seems to have problems with Internet Archive links; they look good in the edit preview, then break on the main page).

One of the standard ways, used for instance in the source(hl2) engine, is to have a per vertex alpha to blend between two materials. The materials usually consist of at least an albedo and a normal map. In the source engine only 2 materials per "blend texture" material is is allowed, which is kind of crappy.

If you want more than 2 materials you have to simulate it by texturing different displacements with different textures, making sure that any individual tile only contains 2 materials at most.

Having an alpha per-vertex rather than per pixel gives a pretty bad appearance. As you noted in Oblivion you get cobblestones blending smoothly into sand for instance. There's a fix for this defect; you use another texture which modulates the alpha on a per pixel basis, so that the cracks between the cobble stones fill with sand readily, but the tops of the cobble stones remain at almost 1.0 alpha until the interpolated vertex alpha is almost 0 for the cobble material. This can make the transition look really nice instead of just an ugly smear.

You could also use a low res texture that contains the alphas(but still much higher than 1 alpha per vertex). The tiled terain textures may be 2048x2048, but the texture used to blend them toghether does not need per-texel resolution. A 256x256 8-bit per texel texture is only 96 kB with mip maps.

Generally, transition areas are precreated, although it's possible to do some things with alpha channels and blending of textures.

you cant run away from tiling but maybe if you use a base texture which makes a little noise on ground, it looks like there is no tiling...

use base texture with the same texture coordinates of alpha texture. base texture can be anything.. a sattelite picture, or a grass texture.. it would match perfect if it is related to your ground shape..

and try to use seamless textures for your grass, rock textures... it does not solve but makes it look as there is no problem

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!