Terrain Texture Splatting

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

Hi,
I am currently trying to port this method to OpenGL. The Shader is working fine so far, but I have a problem with combining the different render passes.
Using the inbuild Materialtypes I can accomplish Multipasseffects easily:

Code: Select all

    // First Pass
    Node->setMaterialType(video::EMT_SOLID);
    Node->setMaterialTexture(0,driver->getTexture("Data/textures/tileable_concrete_wall.jpg"));
    Node->render();
 
    //Second Pass
    Node->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL);
    Node->setMaterialTexture(0,driver->getTexture("Data/textures/BigSpark.png"));
    Node->setMaterialTexture(1,driver->getTexture("Data/textures/metal_diamond.jpg"));
    Node->render();
Image

However, when using my Shader strange things happen. When using a CubeSceneNode it looks "quite" right:
Image
(Sand, Gras and Stone = 1. Pass | Earth and Snow = 2. Pass) Two Problems occur: The second pass has a black outline and sometimes when the camera gets closer to the cube the first pass is not rendered anymore, just the second.

When I use a mesh (e.g. a terrain) only the second pass is rendered, I never see the first pass:
Image

Shader code:
Vertex

Code: Select all

void main()
{
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = ftransform();
}
 
Pixel

Code: Select all

uniform sampler2D SplatMap;
uniform sampler2D ColRed;
uniform sampler2D ColGreen;
uniform sampler2D ColBlue;
 
void main(){
        vec4 SplatCol=texture2D(SplatMap,gl_TexCoord[0].xy);
        vec4 RedCol=texture2D(ColRed,gl_TexCoord[0].xy*10);
        vec4 GreenCol=texture2D(ColGreen,gl_TexCoord[0].xy*10);
        vec4 BlueCol=texture2D(ColBlue,gl_TexCoord[0].xy*10);
 
    gl_FragColor=(vec4(SplatCol.r*RedCol+SplatCol.g*GreenCol+SplatCol.b*BlueCol))*vec4(1,1,1,SplatCol.a);
}
 
Setting up the Shader:

Code: Select all

//Set up Cube or Mesh
    Node = mgr->addCubeSceneNode();//mgr->addMeshSceneNode(mgr->getMesh("Data/terrain.b3d"));
    Node->setMaterialFlag(video::EMF_LIGHTING,false);
    Node->setMaterialFlag(video::EMF_NORMALIZE_NORMALS,true);
    Node->setScale(core::vector3df(5,5,5));
 
    video::IGPUProgrammingServices* gpu = mgr->getVideoDriver()->getGPUProgrammingServices();
 
    if (gpu)
    {
        ITerrainSplattingShader *callback = new ITerrainSplattingShader();
 
        ShaderMaterial1 = gpu->addHighLevelShaderMaterialFromFiles(
                "Data/shader/terrainsplatting.vert", "vertMain", video::EVST_VS_1_1,
                "Data/shader/terrainsplatting.frag", "pixelMain", video::EPST_PS_1_1,
                callback, video::EMT_TRANSPARENT_ALPHA_CHANNEL);
 
        callback->drop();
        Node->setMaterialType((video::E_MATERIAL_TYPE)ShaderMaterial1);
 
 
    }
Rendering:

Code: Select all

    // First Pass
    Node->setMaterialTexture(0,driver->getTexture("Data/textures/splat_1.tga"));
    Node->setMaterialTexture(1,driver->getTexture("Data/textures/Felsen.jpg"));
    Node->setMaterialTexture(2,driver->getTexture("Data/textures/Gras.jpg"));
    Node->setMaterialTexture(3,driver->getTexture("Data/textures/Sand.jpg"));
    Node->render();
 
    // Second Pass
    Node->setMaterialTexture(0,driver->getTexture("Data/textures/splat_2.tga"));
    Node->setMaterialTexture(1,driver->getTexture("Data/textures/Schnee.jpg"));
    Node->setMaterialTexture(2,driver->getTexture("Data/textures/Steppe.jpg"));
    Node->render();
It might have something to do with these lines (from the original project):

Code: Select all

         DWORD alpha, srcblend, destblend;
         device->GetRenderState(D3DRS_ALPHABLENDENABLE, &alpha);
         device->GetRenderState(D3DRS_SRCBLEND, &srcblend);
         device->GetRenderState(D3DRS_DESTBLEND, &destblend);
         device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
         device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
         device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); 
But since I want to use OpenGL I cannot use them and I don't know the equivalent for OpenGL.

Can anyone make sense out of this?

edit: Final Code here http://irrlicht.sourceforge.net/forum/v ... =9&t=38676

greetings
Last edited by freetimecoder on Sat Jun 16, 2012 9:24 am, edited 1 time in total.
kjkrum
Posts: 26
Joined: Wed Oct 29, 2003 5:34 pm

Post by kjkrum »

@freetimecoder: That first snippet you posted looks like what I want to do. But I don't know where that code would go. Did you create your own scene node type and put that in the render() method?
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

Yes, I basically copied MasterD's CExtendedTerrainSceneNode and changed the render method.

Still, it does not work so well with own shaders and I have not yet found a solution. Inbuild materialtypes work well.

greetings
netpipe
Posts: 670
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada
Contact:

Post by netpipe »

freetimecoder could you post a complete project to compile please. i'de really like to start playing with this. OGL ftw
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

Here you go:
IMultiPassSceneNode.h

Code: Select all

#ifndef IMULTIPASSSCENENODE_H_INCLUDED
#define IMULTIPASSSCENENODE_H_INCLUDED


class IMultiPassSceneNode : public scene::ISceneNode
{
private:
    scene::ISceneNode *Node;

public:
    IMultiPassSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id=-1);
    virtual void OnAnimate(u32 timeMs);
    virtual void OnRegisterSceneNode();
    virtual void render();
    const core::aabbox3d<f32>& getBoundingBox() const;

    void remove();
    void setPosition(const core::vector3df& newpos);
    void setRotation(const core::vector3df& rotation);
    void setScale(const core::vector3df& scale);
    ~IMultiPassSceneNode();
    scene::ESCENE_NODE_TYPE getType() const;
};


#endif // IMULTIPASSSCENENODE_H_INCLUDED
IMultiPassSceneNode.cpp

Code: Select all

#include "IMultiPassSceneNode.h"

IMultiPassSceneNode::IMultiPassSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id): scene::ISceneNode(parent, mgr, id){
    if(!parent)
        parent = mgr->getRootSceneNode();

    //Add whatever node you want
    Node = mgr->addCubeSceneNode();
    Node->setMaterialFlag(video::EMF_NORMALIZE_NORMALS,true);
    Node->setScale(core::vector3df(5,5,5));
    setVisible(true);
}

void IMultiPassSceneNode::OnAnimate(u32 timeMs){
}

void IMultiPassSceneNode::OnRegisterSceneNode()
{
    if (IsVisible)
    {
         SceneManager->registerNodeForRendering(this);
         Node->setVisible(true);
         Node->OnRegisterSceneNode();
         Node->setVisible(false);
    }

}

void IMultiPassSceneNode::render(){
    Node->setVisible(true);

    //Change these settings to fit your needs

    // First Pass
    Node->setMaterialType(video::EMT_SOLID);
    Node->setMaterialTexture(0,driver->getTexture("texture1.jpg"));
    Node->render();

    //Second Pass
    Node->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL);
    Node->setMaterialTexture(0,driver->getTexture("Alphamap.png"));
    Node->setMaterialTexture(1,driver->getTexture("Color.jpg"));
    Node->render();

    Node->setVisible(false);
}

const core::aabbox3d<f32>& IMultiPassSceneNode::getBoundingBox() const{
	if(Node){
        return Node->getBoundingBox();
	}
	return core::aabbox3d<f32>(0,0,0,0,0,0);
}

void IMultiPassSceneNode::remove()
{
    if(Node)
        Node->remove();
    scene::ISceneNode::remove();
}

IMultiPassSceneNode::~IMultiPassSceneNode(){
}

void IMultiPassSceneNode::setPosition(const core::vector3df& newpos){
    RelativeTranslation = newpos;
    if(Node)
        Node->setPosition(newpos);
    updateAbsolutePosition();
}

void IMultiPassSceneNode::setRotation(const core::vector3df& rotation){
    RelativeRotation = rotation;
    if(Node){
        Node->setRotation(rotation);
    }
    updateAbsolutePosition();
}

void IMultiPassSceneNode::setScale(const core::vector3df& scale){
    RelativeScale = scale;
    if(Node)
        Node->setScale(scale);
    updateAbsolutePosition();
}

scene::ESCENE_NODE_TYPE IMultiPassSceneNode::getType() const{
    return scene::ESNT_UNKNOWN;
}
The OnRegisterSceneNode and render methods are the important ones.
I still haven't found a solution for shaders, though.

greetings
grunt
Posts: 96
Joined: Tue Aug 17, 2004 9:14 pm
Contact:

Post by grunt »

When I use jarheads shader and add a dynamic light, the terrain is unlit. If I comment the shader code out it is fine.
grunt
Posts: 96
Joined: Tue Aug 17, 2004 9:14 pm
Contact:

Post by grunt »

any help on getting lighting and fog working again.
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

When using a custom shader, fog and lighting does not work anymore. You need to implement it yourself in the shader code.

greetings
grunt
Posts: 96
Joined: Tue Aug 17, 2004 9:14 pm
Contact:

Post by grunt »

well ive located this.

http://xna-uk.net/blogs/randomchaos/arc ... ffuse.aspx

Would diffuse lighting be what I want for my terrain?
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

I think so. You could also try to use a lightmap.

greetings
grunt
Posts: 96
Joined: Tue Aug 17, 2004 9:14 pm
Contact:

Post by grunt »

can somebody help me add a directional light to it. Tried to merge some of a per pixel shader into this and got this, but really dont know about shader programming.

Code: Select all

float4x4 matViewProjection : ViewProjection; 

float3 LightPos;
float4x4 mWorld;

sampler AlphaMap = sampler_state 
{ 
   ADDRESSU = WRAP; 
   ADDRESSV = WRAP; 
   ADDRESSW = WRAP; 
}; 

sampler TextureOne = sampler_state 
{ 
   MipFilter = LINEAR; 
   MinFilter = LINEAR; 
   MagFilter = LINEAR; 
   ADDRESSU = WRAP; 
   ADDRESSV = WRAP; 
   ADDRESSW = WRAP; 
}; 

sampler TextureTwo = sampler_state 
{ 
   MipFilter = LINEAR; 
   MinFilter = LINEAR; 
   MagFilter = LINEAR; 
   ADDRESSU = WRAP; 
   ADDRESSV = WRAP; 
   ADDRESSW = WRAP; 
}; 

sampler TextureThree = sampler_state 
{ 
   MipFilter = LINEAR; 
   MinFilter = LINEAR; 
   MagFilter = LINEAR; 
   ADDRESSU = WRAP; 
   ADDRESSV = WRAP; 
   ADDRESSW = WRAP; 
}; 

struct VS_INPUT 
{ 
   float4 Position : POSITION0; 
   float2 alphamap : TEXCOORD0; 
   float2 tex : TEXCOORD1; 
}; 

struct VS_OUTPUT 
{ 
   float4 Position : POSITION0; 
   float2 alphamap : TEXCOORD0; 
   float2 tex : TEXCOORD1; 
   float3 oLightDir : TEXCOORD2
   float3 oNormal : TEXCOORD3
}; 

struct PS_OUTPUT 
{ 
   float4 diffuse : COLOR0; 
   float3 LightDir : TEXCOORD2
   float3 Normal : TEXCOORD3
   float4 color : COLOR
}; 

VS_OUTPUT vs_main( VS_INPUT Input ) 
{ 
   VS_OUTPUT Output; 
   Output.Position = mul( Input.Position, matViewProjection ); 
   Output.alphamap = Input.alphamap; 
   Output.tex = Input.tex; 

   Output.oLightDir = normalize(LightPos-posWorld)
   Output.oNormal = normal

   return( Output ); 
} 

PS_OUTPUT ps_main(in VS_OUTPUT input) 
{ 
   float texScale = 1.0; 	
   PS_OUTPUT output = (PS_OUTPUT)0; 

   vector a = tex2D(AlphaMap, input.alphamap); 
   vector i = tex2D(TextureOne, mul(input.tex, texScale));
   vector j = tex2D(TextureTwo, mul(input.tex, texScale)); 
   vector k = tex2D(TextureThree, mul(input.tex, texScale)); 

   float4 oneminusx = 1.0 - a.x; 
   float4 oneminusy = 1.0 - a.y; 
   float4 oneminusz = 1.0 - a.z; 

   vector l = a.x * i + oneminusx * i; 
   vector m = a.y * j + oneminusy * l; 
   vector n = a.z * k + oneminusz * m; 

   oLightDir = normalize(LightPos-

   output.diffuse = n; 
   output.color.xyz *= dot(LightDir, Normal);
   return output; 
} 

technique Default_DirectX_Effect 
{ 
   pass Pass_0 
   { 
      VertexShader = compile vs_2_0 vs_main(); 
      PixelShader = compile ps_2_0 ps_main(); 
   } 
}
grunt
Posts: 96
Joined: Tue Aug 17, 2004 9:14 pm
Contact:

Post by grunt »

MasterD wrote:Are you submitting the textures in the correct order? Irrlicht transfers the textures 1 to 1, meaning TextureLayer[0].Texture will be the AlphaMap.

Additionally, the constant set within the shader (float texScale = 10.0; ) seamed to have no effect, try to put that declaration within the pixel shader function.

Here's the code I use, which has lighting using ambient lighting and one directional light. NOTE: I've changed the order of the textures, TextureLayer[4] is Alpha Map.

Code: Select all

float4x4 matViewProjection;
float3 fSunlightDir;
float3 fAmbientColor;
float3 fSunlightColor;

sampler2D TextureOne;
sampler2D TextureTwo;
sampler2D TextureThree;
sampler2D AlphaMap;

struct VS_INPUT
{
   float4 Position  : POSITION0;
   float3 Normal    : NORMAL0;
   float2 alphamap  : TEXCOORD0;
   float2 tex       : TEXCOORD1;
};

struct VS_OUTPUT
{
   float4 Position  : POSITION0;
   float2 alphamap  : TEXCOORD0;
   float2 tex       : TEXCOORD1;
   float3 Normal    : TEXCOORD2;
};

struct PS_OUTPUT
{
   float4 diffuse : COLOR0;
};

VS_OUTPUT vs_main( VS_INPUT Input )
{
   VS_OUTPUT Output;
   Output.Position = mul( Input.Position, matViewProjection );
   Output.alphamap  = Input.alphamap;
   Output.tex       = Input.tex;
   Output.Normal    = Input.Normal;

   return( Output );
}

PS_OUTPUT ps_main(in VS_OUTPUT input)
{
    float texScale = 40.0;

    float3 N = normalize(input.Normal);

    PS_OUTPUT output;// = (PS_OUTPUT)0;

    float3 a =  tex2D(AlphaMap,       input.alphamap);
    float3 c1 = tex2D(TextureOne,     input.tex);
    float3 c2 = tex2D(TextureTwo,     input.tex);
    float3 c3 = tex2D(TextureThree,   input.tex);

    float3 detail1 = tex2D(TextureOne,   input.tex * texScale);
    float3 detail2 = tex2D(TextureTwo,   input.tex * texScale);
    float3 detail3 = tex2D(TextureThree, input.tex * texScale);

    float d1 = (detail1.r + detail1.g + detail1.b) / 3;
    float d2 = (detail2.r + detail2.g + detail2.b) / 3;
    float d3 = (detail3.r + detail3.g + detail3.b) / 3;

    c1 = saturate(c1 + d1 - 0.5);
    c2 = saturate(c2 + d2 - 0.5);
    c3 = saturate(c3 + d3 - 0.5);

    float3 oneminusx = 1.0 - a.r;
    float3 oneminusy = 1.0 - a.g;
    float3 oneminusz = 1.0 - a.b;

    output.diffuse.rgb = a.r * c1.rgb;
    output.diffuse.rgb = a.g * c2.rgb + oneminusy * output.diffuse.rgb;
    output.diffuse.rgb = a.b * c3.rgb + oneminusz * output.diffuse.rgb;

    // Lighting
    output.diffuse.rgb = output.diffuse.rgb * 
        saturate((fAmbientColor + fSunlightColor*saturate(dot(N, fSunlightDir))));

// this term is only neccessary for multipass rendering, 
// will be ignored with standard rendering
    output.diffuse.a = saturate(a.r + a.g + a.b);


    return output;
}
[/size]

Edit: Just Realized that I'm responding to quite an old thread, sorry for that, would've made a new one, if I realized that earlier :)
I tried this one and all I get is all black terrain. I tried hard coding the values :

Code: Select all

float3 fSunlightDir = float3(0.7, 1, -0.8); 
float3 fAmbientColor = float3(255, 255, 255); 
float3 fSunlightColor = float3(255, 255, 255); 
If I comment out:

Code: Select all

output.diffuse.rgb = output.diffuse.rgb * 
        saturate((fAmbientColor + fSunlightColor*saturate(dot(N, fSunlightDir))));
It works just without the lighting.
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

Image

Yay! :D
OpenGL Multitexture, still have to do this and that, but it works. I will post code soon.

edit: Final Code here http://irrlicht.sourceforge.net/forum/v ... =9&t=38676

greetings
Last edited by freetimecoder on Sat Jun 16, 2012 9:25 am, edited 1 time in total.
kjkrum
Posts: 26
Joined: Wed Oct 29, 2003 5:34 pm

Post by kjkrum »

Looks wonderful! Can't wait to see the code.
Virror
Posts: 191
Joined: Mon May 02, 2011 3:15 pm

Post by Virror »

Sorry to bump an old thread like this, but just have to say that the example project in this thread is very helpful!
Should be put on wiki.
Might help some more ppl : )
Post Reply