Sign in to follow this  
Shael

Questions on Terrain Rendering

Recommended Posts

Heya Is it wise to scale the terrain vertices? or if creating a 256x256 heightmap it should stay that size? I thought maybe scaling it could create the illusion of a larger terrain but I'm thinking perhaps its better to leave the scale 1:1. In regards to texturing how do most people implement splatting? I'm creating a world editor and I want to be able to have different forms of "brushes" that the user can use to paint the terrain with. My initial thoughts was to have a base texture which is stretched over the terrain and an mask texture also stretched over. The user would then paint on this mask using the brushes. There would be a max of 4 splat layers and each layers weighting/influence corresponds to an RGBA channel in the mask. That's all well and good but it looks crap because it is stretched over the whole terrain, and because each layer uses only one channel the layers can't have proper alpha blending, only solid colours to determine where the layer has influence. Whats the general consensus on these topics? Cheers!

Share this post


Link to post
Share on other sites
Quote:
Original post by Shael
Is it wise to scale the terrain vertices? or if creating a 256x256 heightmap it should stay that size? I thought maybe scaling it could create the illusion of a larger terrain but I'm thinking perhaps its better to leave the scale 1:1.
You mean a 256 pixel heightmap should cover 256 units in the game world?

How big is "1 unit" in your game?
In some engines it's an inch, some it's a centimetre, some it's a metre...

Share this post


Link to post
Share on other sites
The size of your heightmap has nothing to do with the 'physical' size of your terrain - except that you usually need a higher resolution heightmap for large terrains. But again, this only applies if you need to view the terrain 'close'.

It's perfectly up to you to decide if one pixel should translate into 1.0u, 0.1u or even 100.0u - in your 3D world.
It makes no difference whether you scale your terrain up, or scale everything else down.
I typically work with 1:1 scale with meters though.

If your textures look bad when stretched over the entire terrain, then you either need to make your texture higher resolution, or use several textures for the terrain.
I don't see why you can't have proper alpha blending though. You should have 8 bits for each channel, which should do just fine. Or am I missing your point?
I assume you're using a pixel shader for this?

Share this post


Link to post
Share on other sites
Yes, it can be 512x512 or higher. I was just using 256 as an example.

I thought that having it 1:1 would mean less stretching on the texture because if the texture was the same size as the terrain it would map exactly pixel to pixel. I'd be working in metres also, so I guess if I were to go 1:1 then 1 pixel would be 1 meter? and I'd just have to scale down all the velocities, etc to match.

I am using a pixel shader for this and I just realised I can simply tile the splats instead of stretching them too which gives more detail for the splats.

There is 8 bits per channel but for some reason if I paint on my mask in photoshop with a red brush with say 50% opacity, then the splat assigned to the red channel shows on the terrain as if i painted with 100% solid colour. It has no fade out or anything, every pixel which had some transparency in photoshop, shows up solid on the terrain.

Cheers!

Share this post


Link to post
Share on other sites
Shael - are you using blending or alpha-testing to display your masked textures.

Alpha testing is simply on/off based on a threshold value whereas blending will give you the gradual 'everything in between' look you are after.

Share this post


Link to post
Share on other sites
Quote:
Original post by Shael
I thought that having it 1:1 would mean less stretching on the texture because if the texture was the same size as the terrain it would map exactly pixel to pixel.
You convert your height-map pixels to vertexes though, and then groups of vertices create lots of pixels on the screen. So if you stretch a 256 texture over a 256 height-map, you've got 1 pixel : 1 vertex ;)
And if your verts are spaced apart by 1 metre, then that's a resolution of 1 pixel per meter.
Quote:
I am using a pixel shader for this and I just realised I can simply tile the splats instead of stretching them too which gives more detail for the splats.
Yeah, you should stretch your "splat selection" texture, then tile the actual textures. 1px/meter should be ok for the selection one, but is bad for the detailed ones.
Quote:
for some reason if I paint on my mask in photoshop with a red brush with say 50% opacity, then the splat assigned to the red channel shows on the terrain as if i painted with 100% solid colour. It has no fade out or anything
We can help you debug your shader if you like.

Share this post


Link to post
Share on other sites
Sounds like a plan :)

This is what it looks like when rendered, as you can see its blocky and not transparent.. the base texture should be fading through:

http://illusionaldesign.net/terrain.jpg

And this is the mask:

http://illusionaldesign.net/mask1.png

Theres some other problems Im seeing too which is the base texture only draws if there is black painted on the mask, and whatever is left transparent in the mask is display as the last splat texture, which in this case is the snow.


shared float4x4 g_mWorldViewProj : WORLD_VIEW_PROJ;

texture BaseTex;
texture MaskTex;
texture Splat1;
texture Splat2;
texture Splat3;
texture Splat4;

sampler BaseSampler = sampler_state
{
Texture = (BaseTex);
MIPFILTER = LINEAR;
MAGFILTER = LINEAR;
MINFILTER = LINEAR;
};

sampler MaskSampler = sampler_state
{
Texture = (MaskTex);
MIPFILTER = LINEAR;
MAGFILTER = LINEAR;
MINFILTER = LINEAR;
};

sampler RSampler = sampler_state
{
Texture = (Splat1);
MIPFILTER = LINEAR;
MAGFILTER = LINEAR;
MINFILTER = LINEAR;
};

sampler GSampler = sampler_state
{
Texture = (Splat2);
MIPFILTER = LINEAR;
MAGFILTER = LINEAR;
MINFILTER = LINEAR;
};

sampler BSampler = sampler_state
{
Texture = (Splat3);
MIPFILTER = LINEAR;
MAGFILTER = LINEAR;
MINFILTER = LINEAR;
};

sampler ASampler = sampler_state
{
Texture = (Splat4);
MIPFILTER = LINEAR;
MAGFILTER = LINEAR;
MINFILTER = LINEAR;
};


struct VS_INPUT
{
float4 Position : POSITION0;
float2 TexCoord0 : TEXCOORD0;
};

struct PS_INPUT
{
float4 Color : COLOR0;
};


VS_INPUT TerrainVS (VS_INPUT In)
{
VS_INPUT Out;

Out.Position = mul(In.Position, g_mWorldViewProj);
Out.TexCoord0 = In.TexCoord0;

return Out;
}

PS_INPUT TerrainSimplePS (VS_INPUT In)
{
PS_INPUT Out;

Out.Color = tex2D(BaseSampler, In.TexCoord0);

return Out;
}

PS_INPUT TerrainSplatPS (VS_INPUT In)
{
PS_INPUT Out;

vector blender = tex2D(MaskSampler, In.TexCoord0);

float RWeight = blender.r;
float GWeight = blender.g;
float BWeight = blender.b;
float AWeight = blender.a;

vector BaseColour = tex2D(BaseSampler, In.TexCoord0);
vector RColour = tex2D(RSampler, In.TexCoord0 * 4);
vector GColour = tex2D(GSampler, In.TexCoord0 * 4);
vector BColour = tex2D(BSampler, In.TexCoord0 * 4);
vector AColour = tex2D(ASampler, In.TexCoord0 * 4);

// combine texel colors
float4 oneminusr = 1.0 - RWeight;
float4 oneminusg = 1.0 - GWeight;
float4 oneminusb = 1.0 - BWeight;

vector l = RWeight * RColour + oneminusr * BaseColour;
vector m = GWeight * GColour + oneminusg * l;
vector n = BWeight * BColour + oneminusb * m;

Out.Color = n;

return Out;
}


//--------------------------------------------------------------------------------------
// Techniques
//--------------------------------------------------------------------------------------
technique TerrainSimple
{
pass pass0
{
VertexShader = compile vs_2_0 TerrainVS();
PixelShader = compile ps_2_0 TerrainSimplePS();
}
}

technique TerrainSplat
{
pass pass0
{
VertexShader = compile vs_2_0 TerrainVS();
PixelShader = compile ps_2_0 TerrainSplatPS();
}
}

Share this post


Link to post
Share on other sites
Try to following in your pixel shader, assuming you want the base texture to fade through everything. (Note: Un-tested, but should be correct)

PS_INPUT Out;
{
float4 texWeights = tex2D(MaskSampler, In.TexCoord0);
float totalWeight = 1 + texWeights.x + texWeights.y + texWeights.z + texWeights.w;
texWeights /= totalWeight;

float4 BaseColour = tex2D(BaseSampler, In.TexCoord0);
float4 RColour = tex2D(RSampler, In.TexCoord0 * 4);
float4 GColour = tex2D(GSampler, In.TexCoord0 * 4);
float4 BColour = tex2D(BSampler, In.TexCoord0 * 4);
float4 AColour = tex2D(ASampler, In.TexCoord0 * 4);

// combine texel colors
BaseColour = BaseColour * (1.0f / totalWeight) + RColour * texWeights.x + GColour * texWeights.y + BColour * texWeights.z + AColour * texWeights.w;

Out.Color = BaseColour;

return Out;
}

Share this post


Link to post
Share on other sites
Alrighty I tried what you suggested and this was the result:

http://illusionaldesign.net/terrain2.jpg

Still doesn't seem quite right and the edges still don't blend, its all blocky no fading.

Hmm.. other ideas?

Share this post


Link to post
Share on other sites
I'm thinking your mask is at fault.

How are you creating it? In Photoshop? Painting on each seperate channel?

Try using a completely red mask (255, 0, 0 or FF0000) and see how it looks (alternatively, paint white directly on the red channel and black on the others), you should get a perfect blend between your background texture and whatever your red texture is (make sure your GBA channels are all 0 in your mask).

Then try drawing a circle with soft edges using a brush with yellow (255, 255, 0 or FFFF00) as the colour on top of this (alternatively paint a nice soft white cirle on the green channel of your existing mask).


Edit: Finally add a green soft cirle to the middle of this (0, 255, 0 or 00FF00), so you have a smooth blend for red into yellow into green on your mask. (You can just erase some of the red channel to do this too)

Screens of the outcomes of all three would help.

Share this post


Link to post
Share on other sites
Ok I think I misunderstood how the channels worked. I was simply just painting in the Layer0 in photoshop, not the separate channels. I did what you said and it appears to be looking a bit better. Heres the results:

http://illusionaldesign.net/t1.jpg
http://illusionaldesign.net/t2.jpg
http://illusionaldesign.net/t3.jpg

http://illusionaldesign.net/mask1.png

EDIT: I just reverted back to my old code with the new mask and its looking good. I the problem all along was my misunderstanding of the mask and how the channels worked. Heres what it looks like now with my previous code:

http://illusionaldesign.net/t4.jpg

Thanks for your help guys! :)

Share this post


Link to post
Share on other sites
Those look more like I'd expect.

You have to remember that the GPU will read each individual channel. (With a result in the range 0 to 1).

So, if you paint the texture fully red, it will come out with weights of (1, 0, 0, 0), meaning the output will be fully the texture defined by the red channel (although in your case it's 50-50 with the base texture).

If you paint it bright yellow (FFFF00), it will read it as (1, 1, 0, 0) you then get a 50-50 split between your red channel and green channel textures (in your case it comes out to a 33-33-33) split.

Hopefully that helps you understand how it should work a bit more, play around with it, if it doesn't give the results you expect. You could try drawing what you would like your outcome to be, and perhaps myself or someone else could come up with some shader code.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this