XNA Creators Club Online
Page 1 of 1 (9 items)
Sort Posts: Previous Next

Terrain texture question

Last post 03/05/2008 17:03 by jwatte. 8 replies.
  • 20/12/2007 6:10

    Terrain texture question

    I have a texture file that is 1024 * 512. The first 512*512 holds some kind of grass texture. The 512*512 next to it has the rock texture.

    Before the texture file only had 1 set of 512*512. When i create the vertices for the terrain i used to do this (credit to Riemer)

    VertexPositionNormalTexture[] terrainVertices = new VertexPositionNormalTexture[WIDTH * HEIGHT];

    for (int x = 0; x < WIDTH; x++)

    for (int y = 0; y < HEIGHT; y++)

    {

    terrainVertices[x + y * WIDTH].Position = new Vector3(x, y, heightData[x, y]);

    terrainVertices[x + y * WIDTH].Normal = new Vector3(0, 0, 1);

    terrainVertices[x + y * WIDTH].TextureCoordinate.X = (float)(x / 30.0f);

    terrainVertices[x + y * WIDTH].TextureCoordinate.Y = (float)(y / 30.0f);

    }

     

    I read somewhere that 0,0 is the top left of the texture and 1,1 is the bottom right. In the code above with a width of 128 this will result in X and Y textcoords bigger then 1 (127 / 30 = 4.23333333). This seems to be no problem, XNA or the graphics card seem to know that they have to use 0.23333333 instead.

     

    Since i duplicated my texture file, if i only need the first texture, textcoord X must be <= 0.5. So that will make sure it only uses that first texture.

    To do this i thought the following code in place of the TextCoord.X line would do the trick :

    float xx = (x / 30.0f);

    if (xx > ((int)xx + 0.5f))

    {

    xx = (xx - 0.5f);

    }

    terrainVertices[x + y * WIDTH].TextureCoordinate.X = (float)(xx);

     

    This makes sure that if the X is on the second half of the texture file it will set it back to the first half. 0.7 will become 0.2 and so on.

    The problem is that this doesn't work. I still see both textures. I checked Riemer's FlightSim tutorial (http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series2/Loading_the_floorplan.php) and there he does the same thing. Difference is that he has a somewhat fixed TextureCoord.

     

    The reason i'm doing this is because i want more than 1 texture on my terrain. I know i can do this with a MultiTexture Shader (will do that as well) but my game also has to run on pc's without PS 2.0 (My own pc for example...)

     

    Any help would be very appreciated!!

  • 20/12/2007 13:44 In reply to

    Re: Terrain texture question

    If you want to 'splat' terrain types onto the same terrain, there are a lot of ways to do it. Basically, there are 2 main ways. Each vertex can have the type of terrain it is using, and then the pixel shader will blend between each vertex when texture mapping. This will give you accuracy down to the vertex. This is sufficient if your terrain density is 1:1 with the pixels in the heightmap (i.e. 1024x1024 heightmap, 1024x1024 terrain). However, the other way is to not store texture information in the vertices whatsoever, and then you have a texture that matches your heightmap that contains texture type information. Let me explain. You have a 1024x1024 heightmap (greyscale), then you have a seperate 1024x1024 image for terrain types. When the shader reads this texture as a sampler, you can use all the Red values in the image for one type, Green for another, Blue for another, and even Alpha for another if you'd like. This allows you to splat and blend up to 4 different textures however you'd like on your terrain.

    This process is very difficult to explain, it is almost worth making a tutorial for, because it would take awhile to go into great detail. If you'd like, I'm using this process with in the engine in my signiture. It's open source so you're free to look at the terrain classes and terrain.fx file. My process stemmed from Riemer's so it might make things slightly easier to understand, however my terrain is in a few classes as it has Quad-tree and different LODs.

    Good luck.

    XNA QuickStart Engine (3D Game Engine for XNA) | My site
    "I'll be whatever I want to do!", Philip J. Fry
  • 21/12/2007 13:46 In reply to

    Re: Terrain texture question

    Thanks for the excellent explanation!

    Riemer's 4th tutorial is a good one for learning the second multitexture technique. I alread has a look at that and your engine to see how you guys did multitexturing.

    Problem i had, was that i couldn't get the shader to work in PS 1.1. That's why i was wondering if i couldn't do it using a texturefile with multiple textures in it. But then setting the right TextCoords became a huge problem (at least for me).

    BUT (after a hell of a day :-) ) i finally have the multitexture shader working in PS 1.1. Problem is that in 1.1 you can't read a TEXCOORD more than once...

    So now i have 1 TextCoord i pass in the shader and there I set it to 3 seperate once, one per texture, so i can read those in the ps main call. Only thing now is that the lightning won't work anymore. That's because I now use 3 TEXCOORDS for the TextureCoords and in 1.1 you can only use 4 TEXCOORDS variables...

     

    For anyone who has an old graphics card just like me...here's the shader for multitexturing.

     

    //------- Technique: MultiTextured --------

    struct MultiTexVertexVS

    {

    float4 Position : POSITION;

    float3 Normal : NORMAL;

    float2 TextureCoords : TEXCOORD0;

    float4 TerrainColorWeight : COLOR0;

    };

    struct MultiTexVertexPS

    {

    float4 Position : POSITION;

    float3 Normal : TEXCOORD0;

    float2 TextureCoords1 : TEXCOORD1;

    float2 TextureCoords2 : TEXCOORD2;

    float2 TextureCoords3 : TEXCOORD3;

    float4 LightDirection : TEXCOORD4;

    float4 TerrainColorWeight : COLOR0;

    };

    MultiTexVertexPS MultiTextured_Pass_0_Vertex_Shader_vs_main( MultiTexVertexVS Input )

    {

    MultiTexVertexPS Output = (MultiTexVertexPS)0;

    float4x4 preViewProjection = mul (xView, xProjection);

    float4x4 preWorldViewProjection = mul (xWorld, preViewProjection);

    Output.Position = mul(Input.Position, preWorldViewProjection);

    Output.Normal = mul(normalize(Input.Normal), xWorld);

    Output.TextureCoords1 = Input.TextureCoords;

    Output.TextureCoords2 = Input.TextureCoords;

    Output.TextureCoords3 = Input.TextureCoords;

    Output.TerrainColorWeight = Input.TerrainColorWeight;

    Output.LightDirection.xyz = -xLightDirection;

    Output.LightDirection.w = 1;

    return Output;

    }

    float4 MultiTextured_Pass_0_Pixel_Shader_ps_main( MultiTexVertexPS Input ) : COLOR0

    {

    float lightingFactor = 1;

    if (xEnableLighting)

    lightingFactor = saturate(saturate(dot(Input.Normal, Input.LightDirection)) + xAmbient);

    float4 Color;

    Color = tex2D(SandTextureSampler, Input.TextureCoords1) * Input.TerrainColorWeight.r;

    Color += tex2D(GrassTextureSampler, Input.TextureCoords2) * Input.TerrainColorWeight.g;

    Color += tex2D(RockTextureSampler, Input.TextureCoords3) * Input.TerrainColorWeight.b;

    //Color.rgb *= lightingFactor;

    return Color;

    }

    //--------------------------------------------------------------//

    // Technique Section for MultiTextured

    //--------------------------------------------------------------//

    technique MultiTextured

    {

    pass Pass_0

    {

    VertexShader = compile vs_1_1 MultiTextured_Pass_0_Vertex_Shader_vs_main();

    PixelShader = compile ps_1_1 MultiTextured_Pass_0_Pixel_Shader_ps_main();

    }

    }

  • 21/12/2007 13:49 In reply to

    Re: Terrain texture question

    Since you need to support ps 1.1, you can just do multi-pass rendering.  Render one pass with the sand texture, then one with the grass texture, then one with the rock texture.  That should free up enough pixel shader instructions to let you layer textures and have basic lighting.
    Microsoft DirectX/XNA MVP
  • 21/12/2007 17:52 In reply to

    Re: Terrain texture question

    Multiple passes will work like Shaw said, but if you're on an old card doing 3 passes, you're pretty much screwed on framerate.

    I wonder if you could fit two textures into one coordinate, one using x, and y, the other using z, and w? Then you could at least do two textures on a PS 1.1, possibly in a single pass.

    I'm not sure why your MultiTexVertexPS has so many texcoords. In my non-normal mapped shader I'm only using 3. And 1 of those is for the splatting, which could be done by vertex in a PS 1.1, you'd lose quality but you'd only need 2 texcoords.

    struct VSBASIC_OUTPUT
    {
    float4 position : POSITION0;
    float2 texCoord : TEXCOORD0;
    float3 Normal   : TEXCOORD2;
    };
    The shader in the engine uses something similar to this, but uses an extra texcoord so that the texture splatting doesn't lose detail on lower LODs
    XNA QuickStart Engine (3D Game Engine for XNA) | My site
    "I'll be whatever I want to do!", Philip J. Fry
  • 02/05/2008 17:43 In reply to

    Re: Terrain texture question

    I too am trying to learn of a way to paint textures onto the terrain but don't want to be limited to 4(since theres only rgba).  Plus, It seems like a hack doing things with those channels that way.  Im not sure how to do this but lets say we have a full blown world editor.  In our editor, we allow the user to pick a texture from a pallette by clicking on it and then "painting" it  onto the terrain.  After the painting is done then we collapse the original texture that is stretched over our terrain with what we painited and make that into the new texture layer.  This way, you only have 2 terrain texture layers "in use" at a given time and only ever have 1 pass to make.  IE: your making a new .png each time you "collapse" the layers into 1 layer. 

    Then when actually playing, the initial pass needed is only 1 to load your png texture.  Does that make sense? 

    Thanks,

    Gib

  • 02/05/2008 19:53 In reply to

    Re: Terrain texture question

    You can get five textures with RGBA, because the fifth would have the weight of (1-sum(RGBA)).

    Anyway, if you want arbitrarily many textures, then you need to multi-pass. You can do that in two ways: either multi-pass with four textures at a time, using an RGBA control for each (and get 5 textures in the first pass, 4 in any additional pass), or multi-pass once per material kind, with an A8 (single channel) format control texture. The naive way of multi-passing once per material over the entire terrain will use a lot of fill rate; if you generate one index buffer per material (so you only draw the triangles that are necessary for each texture), you'll do better.

     

    Jon Watte, Direct3D MVP
    Tweets, occasionally
    kW X-port 3ds Max .X exporter
    kW Animation source code
  • 03/05/2008 14:39 In reply to

    Re: Terrain texture question

    Thanks Jon,

    Is there any sample code showing this out there?  I've seen p, p1, p2 in some folks HLSL files.  Is that the code for pass0 pass1 pass2 for multipassing? 

    Gib 

  • 03/05/2008 17:03 In reply to

    Re: Terrain texture question

    The names of the passes don't matter much (although you can ask for the name of each individual pass in your program, should you choose to). If you call your pass "p0," "pass0," or "fried_tomatoes" doesn't really matter.
    Jon Watte, Direct3D MVP
    Tweets, occasionally
    kW X-port 3ds Max .X exporter
    kW Animation source code
Page 1 of 1 (9 items) Previous Next