GLSL TerrainShader

Hi @ all,



here the source from the demo in my thread



http://www.jmonkeyengine.com/jmeforum/index.php?topic=3710.msg33286#msg33286



terrain_dot3_vert.glsl

uniform float groundTexMult;
uniform float rockTexMult;

uniform float detailFadeFactor;
uniform float LightScaleFactor;
uniform vec3  LightPosition;
uniform vec3  SkyColor;
uniform vec3  HazeColor;

varying float fade;
varying float vHeight;
varying vec4  viewCoords;
varying vec3  diffuseColor;
varying vec3  lightVec;

const float C1 = 0.429043;
const float C2 = 0.511664;
const float C3 = 0.743125;
const float C4 = 0.886227;
const float C5 = 0.247708;

const vec3 L00  = vec3( 1.0351604,  0.7603549,  0.7074635);
const vec3 L1m1 = vec3( 0.4442150,  0.3430402,  0.3403777);
const vec3 L10  = vec3(-0.2247797, -0.1828517, -0.1705181);
const vec3 L11  = vec3( 0.7110400,  0.5423169,  0.5587956);
const vec3 L2m2 = vec3( 0.6430452,  0.4971454,  0.5156357);
const vec3 L2m1 = vec3(-0.1150112, -0.0936603, -0.0839287);
const vec3 L20  = vec3(-0.3742487, -0.2755962, -0.2875017);
const vec3 L21  = vec3(-0.1694954, -0.1343096, -0.1335315);
const vec3 L22  = vec3( 0.5515260,  0.4222179,  0.4162488);

void main( void )
{

   gl_Position     = ftransform();
   
   vec3 ecPosition = vec3 (gl_ModelViewMatrix * gl_Vertex);
   vec3 tnorm      = normalize(gl_Normal);
   lightVec        = normalize(LightPosition - ecPosition);
   float cosTheta  = dot(tnorm, lightVec);
   float a         = 0.5 + 0.5 * cosTheta;

   viewCoords = gl_ModelViewProjectionMatrix * gl_Vertex;

   diffuseColor = vec3(mix(HazeColor , SkyColor , a));

    diffuseColor   *= C1 * L22 * (tnorm.x * tnorm.x - tnorm.y * tnorm.y) +
                      C3 * L20 * tnorm.z * tnorm.z + C4 * L00 - C5 * L20 +
                      2.0 * C1 * L2m2 * tnorm.x * tnorm.y +
                      2.0 * C1 * L21  * tnorm.x * tnorm.z +
                      2.0 * C1 * L2m1 * tnorm.y * tnorm.z +
                      2.0 * C2 * L11  * tnorm.x +
                      2.0 * C2 * L1m1 * tnorm.y +
                      2.0 * C2 * L10  * tnorm.z;

    diffuseColor   *= LightScaleFactor;
   
    fade = pow(tnorm.y, detailFadeFactor);
    vHeight = gl_Vertex.y;
   
    gl_TexCoord[0]= gl_MultiTexCoord0;
    gl_TexCoord[1]= gl_MultiTexCoord0 * groundTexMult;
    gl_TexCoord[2]= gl_MultiTexCoord0 * rockTexMult;
   
}



terrain_dot3_frag.glsl

uniform sampler2D groundTex;
uniform sampler2D rockTex;
uniform sampler2D splatTex;
uniform sampler2D rockNormalMap;

uniform vec3  fogColor;
uniform float groundTexMult;
uniform float rockTexMult;

uniform float fadeOutEnabled;
uniform float distanceFadeOutFactor;

varying float fade;
varying float vHeight;
varying vec4  viewCoords;
varying vec3  diffuseColor;
varying vec3  lightVec;

void main( void )
{
   // orginal tex-coord from model
   vec2 texCoord0 = vec2(gl_TexCoord[0].st);
   
   // texcoord multiply by groundTexMult
    vec2 texCoord1 = vec2(gl_TexCoord[1].st);
   
    // texcoord multiply by rockTexMult
    vec2 texCoord2 = vec2(gl_TexCoord[2].st);
   
    // get texel
    vec3 ground = texture2D( groundTex, texCoord1 ).rgb;
   vec3 splat  = texture2D( splatTex,  texCoord0 ).rgb;
   vec3 rock   = texture2D( rockTex,   texCoord2 ).rgb;

   // fade between rock and ground texture
   vec3 color = mix(rock, ground*splat ,vec3(fade));
   
    // generate bump from bumpmap
   vec3 norm = texture2D(rockNormalMap, texCoord2).xyz;
   
   // convert values between -1 and 1
   norm = (norm - 0.5) * 2.0;

   // fade between rock bump and plane terrain
    norm = mix(norm, vec3(0.92) ,vec3(fade));
   
   // Calculate the bump value as n dot l (dot product of the normal map and light vec).
      float dp = clamp(dot(norm, lightVec), 0.5, 0.7);

      // Combine the decal with the bump value and diffuse light from vertex-program
    color = color * dp * diffuseColor;
   
    // fog color from distance
    float fogDist = clamp((viewCoords.z-gl_Fog.start)*gl_Fog.scale,0.0,1.0);
   
    // distance fade out
    float fogFade = 1.0;
    if (fadeOutEnabled == 1.0) {
       fogFade = 1.0-clamp(pow((viewCoords.z-gl_Fog.start)*gl_Fog.scale,
          distanceFadeOutFactor),0.0,1.0);
    }   
   
    // final compose
    gl_FragColor = vec4(mix(color, fogColor , fogDist), fogFade);

}



TerrainRenderPass.java


package de.mistudios.enginex;

import java.net.URL;

import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.pass.Pass;
import com.jme.scene.Spatial;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.CullState;
import com.jme.scene.state.GLSLShaderObjectsState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;

/**
 * GLSL TerrainShadingPass
 *
 * @author Lutz Guettler  http://www.mi-studios.de
 * @version 1.00
 * @since 2006-11-16
 */
public class TerrainRenderPass extends Pass {

    /**
     *
     */
    private static final long serialVersionUID = 7022982191313273671L;

    private boolean supported = true;
   
    private DisplaySystem display;
    private GLSLShaderObjectsState shader;
    private CullState cullBackFace;
    private TextureState ts;
    private AlphaState   as;
   
    protected URL     verticsShader;
    protected URL     fragmentShader;
   
    private ColorRGBA   skyColor;
    private ColorRGBA   hazeColor;
    private ColorRGBA   fogColor;
    private String      groundTexture;
    private String      rockTexture;
    private String      splatTexture;
    private String      rockNormalMap;
    private boolean     fadeOutEnabled;
    private float       distanceFadeOut;
    private float       detailFadeFactor;
    private float       groundTexMult;
    private float       rockTexMult;
    private float       lightScaleFactor;
    private Vector3f    lightPosition;
   
    public void resetParameters() {
       
        verticsShader = getClass().getClassLoader().getResource("shader/terrain_dot3_vert.glsl");
        fragmentShader = getClass().getClassLoader().getResource("shader/terrain_dot3_frag.glsl");
       
        fadeOutEnabled = true;
        distanceFadeOut = 95f;
        detailFadeFactor = 25f;
        groundTexMult = 51f;
        rockTexMult = 51f;
        lightScaleFactor = 0.95f;
        lightPosition = new Vector3f(2000.0f , 2000.0f , 1000.0f);
        skyColor = new ColorRGBA(0.9882f , 0.9609f , 0.8164f, 1.0f);
        hazeColor = new ColorRGBA(0.49f , 0.495f , 0.49f, 1.0f);
        fogColor = new ColorRGBA(0.4f, 0.4f, 0.4f, 1.0f);
       
    }
   
    public boolean isSupported() {
        return supported;
    }
   
    public TerrainRenderPass(   String groundTexture,
                                String rockTexture,
                                String rockNormalMap,
                                String splatTexture ) {
       
        resetParameters();
       
        this.groundTexture = groundTexture;
        this.rockTexture = rockTexture;
        this.rockNormalMap = rockNormalMap;
        this.splatTexture = splatTexture;

        display = DisplaySystem.getDisplaySystem();

        shader = display.getRenderer().createGLSLShaderObjectsState();

        if( !shader.isSupported() ) {
            supported = false;
        }
        else {
        }

        cullBackFace = display.getRenderer().createCullState();
        cullBackFace.setEnabled( true );
        cullBackFace.setCullMode( CullState.CS_BACK );
       
        if( isSupported() ) {

            createRenderStates();
           
            shader.load( verticsShader, fragmentShader );
           
            shader.setEnabled( true );
        }
        else {
            supported = false;
        }

        if( !isSupported() ) {
            ts = display.getRenderer().createTextureState();
            ts.setEnabled( true );

            Texture t1 = TextureManager.loadTexture(
                    getClass().getClassLoader().getResource( groundTexture ),
                    Texture.MM_LINEAR_LINEAR,
                    Texture.FM_LINEAR );
            ts.setTexture( t1, 0 );
            t1.setWrap( Texture.WM_WRAP_S_WRAP_T );

        }
    }
   
    protected void createRenderStates() {

        Texture t;
       
        ts = display.getRenderer().createTextureState();
        {
            ts.setEnabled(true);
        }

        if (groundTexture!=null) {
            t = TextureManager.loadTexture(
                    getClass().getClassLoader()
                    .getResource(groundTexture ),
                    Texture.MM_NEAREST_LINEAR, Texture.FM_LINEAR);
            {
                t.setWrap(Texture.WM_WRAP_S_WRAP_T);
                ts.setTexture(t, 0);
            }
        }

        if (rockTexture!=null) {
            t = TextureManager.loadTexture(
                    getClass().getClassLoader()
                    .getResource(rockTexture ),
                    Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
            {
                t.setWrap(Texture.WM_WRAP_S_WRAP_T);
                ts.setTexture(t, 1);
            }
        }
       
        if (splatTexture!=null) {
            t = TextureManager.loadTexture(
                    getClass().getClassLoader()
                    .getResource(splatTexture ),
                    Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
            {
                t.setWrap(Texture.WM_WRAP_S_WRAP_T);
                ts.setTexture(t, 2);
            }
        }
       
        if (rockNormalMap!=null) {
            t = TextureManager.loadTexture(
                    getClass().getClassLoader()
                    .getResource(rockNormalMap ),
                    Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
            {
                t.setWrap(Texture.WM_WRAP_S_WRAP_T);
                ts.setTexture(t, 3);
            }
        }
       
        as = display.getRenderer().createAlphaState();
        as.setBlendEnabled( true );
        as.setSrcFunction( AlphaState.SB_SRC_ALPHA );
        as.setDstFunction( AlphaState.DB_ONE_MINUS_SRC_ALPHA );
        as.setEnabled( true );

    }

    @Override
    protected void doUpdate( float tpf ) {
        super.doUpdate( tpf );
    }

    public void setShadingOnSpatial( Spatial spatial ) {
        spatial.setRenderState( cullBackFace );
        if( isSupported() ) {
            spatial.setRenderState( shader );
            spatial.setRenderState( ts );
            spatial.setRenderQueueMode( Renderer.QUEUE_TRANSPARENT );
            spatial.setLightCombineMode( LightState.OFF );
            spatial.setRenderState( as );
        }
        spatial.updateRenderState();
    }
   
    /* (non-Javadoc)
     * @see com.jme.renderer.pass.Pass#doRender(com.jme.renderer.Renderer)
     */
    @Override
    protected void doRender(Renderer r) {

        if( isSupported() ) {
            shader.clearUniforms();
           
            shader.setUniform("groundTex",     0);
            shader.setUniform("rockTex",       1);
            shader.setUniform("splatTex",      2);
            shader.setUniform("rockNormalMap", 3);
           
            shader.setUniform("LightScaleFactor", lightScaleFactor);
            shader.setUniform("LightPosition",    lightPosition.x,lightPosition.y,lightPosition.z);
            shader.setUniform("SkyColor",         skyColor.r , skyColor.g , skyColor.b);
            shader.setUniform("HazeColor",        hazeColor.r , hazeColor.g , hazeColor.b);
            shader.setUniform("fogColor",         fogColor.r, fogColor.g, fogColor.b);
           
            shader.setUniform("fadeOutEnabled",        fadeOutEnabled?1f:0f);
            shader.setUniform("distanceFadeOutFactor", distanceFadeOut);
            shader.setUniform("detailFadeFactor",      detailFadeFactor);
           
            shader.setUniform("groundTexMult", groundTexMult);
            shader.setUniform("rockTexMult",   rockTexMult);
           
            shader.apply();

        }
       
       
    }

    /**
     * @return the detailFadeFactor
     */
    public float getDetailFadeFactor() {
        return this.detailFadeFactor;
    }

    /**
     * @param detailFadeFactor the detailFadeFactor to set
     */
    public void setDetailFadeFactor(float detailFadeFactor) {
        this.detailFadeFactor = detailFadeFactor;
    }

    /**
     * @return the distanceFadeOut
     */
    public float getDistanceFadeOut() {
        return this.distanceFadeOut;
    }

    /**
     * @param distanceFadeOut the distanceFadeOut to set
     */
    public void setDistanceFadeOut(float distanceFadeOut) {
        this.distanceFadeOut = distanceFadeOut;
    }

    /**
     * @return the fadeOutEnabled
     */
    public boolean isFadeOutEnabled() {
        return this.fadeOutEnabled;
    }

    /**
     * @param fadeOutEnabled the fadeOutEnabled to set
     */
    public void setFadeOutEnabled(boolean fadeOutEnabled) {
        this.fadeOutEnabled = fadeOutEnabled;
    }

    /**
     * @return the fogColor
     */
    public ColorRGBA getFogColor() {
        return this.fogColor;
    }

    /**
     * @param fogColor the fogColor to set
     */
    public void setFogColor(ColorRGBA fogColor) {
        this.fogColor = fogColor;
    }

    /**
     * @return the groundTexMult
     */
    public float getGroundTexMult() {
        return this.groundTexMult;
    }

    /**
     * @param groundTexMult the groundTexMult to set
     */
    public void setGroundTexMult(int groundTexMult) {
        this.groundTexMult = groundTexMult;
    }

    /**
     * @return the groundTexture
     */
    public String getGroundTexture() {
        return this.groundTexture;
    }

    /**
     * @param groundTexture the groundTexture to set
     */
    public void setGroundTexture(String groundTexture) {
        this.groundTexture = groundTexture;
    }

    /**
     * @return the hazeColor
     */
    public ColorRGBA getHazeColor() {
        return this.hazeColor;
    }

    /**
     * @param hazeColor the hazeColor to set
     */
    public void setHazeColor(ColorRGBA hazeColor) {
        this.hazeColor = hazeColor;
    }

    /**
     * @return the lightPosition
     */
    public Vector3f getLightPosition() {
        return this.lightPosition;
    }

    /**
     * @param lightPosition the lightPosition to set
     */
    public void setLightPosition(Vector3f lightPosition) {
        this.lightPosition = lightPosition;
    }

    /**
     * @return the lightScaleFactor
     */
    public float getLightScaleFactor() {
        return this.lightScaleFactor;
    }

    /**
     * @param lightScaleFactor the lightScaleFactor to set
     */
    public void setLightScaleFactor(float lightScaleFactor) {
        this.lightScaleFactor = lightScaleFactor;
    }

    /**
     * @return the rockNormalMap
     */
    public String getRockNormalMap() {
        return this.rockNormalMap;
    }

    /**
     * @param rockNormalMap the rockNormalMap to set
     */
    public void setRockNormalMap(String rockNormalMap) {
        this.rockNormalMap = rockNormalMap;
    }

    /**
     * @return the rockTexMult
     */
    public float getRockTexMult() {
        return this.rockTexMult;
    }

    /**
     * @param rockTexMult the rockTexMult to set
     */
    public void setRockTexMult(int rockTexMult) {
        this.rockTexMult = rockTexMult;
    }

    /**
     * @return the rockTexture
     */
    public String getRockTexture() {
        return this.rockTexture;
    }

    /**
     * @param rockTexture the rockTexture to set
     */
    public void setRockTexture(String rockTexture) {
        this.rockTexture = rockTexture;
    }

    /**
     * @return the skyColor
     */
    public ColorRGBA getSkyColor() {
        return this.skyColor;
    }

    /**
     * @param skyColor the skyColor to set
     */
    public void setSkyColor(ColorRGBA skyColor) {
        this.skyColor = skyColor;
    }

    /**
     * @return the splatTexture
     */
    public String getSplatTexture() {
        return this.splatTexture;
    }

    /**
     * @param splatTexture the splatTexture to set
     */
    public void setSplatTexture(String splatTexture) {
        this.splatTexture = splatTexture;
    }

}



would you mind posting a small test…nothing fancy…helps me learn  ://

A test would be great

thx for the code BlackBluegL!

The screenshot of your demo looks great.



But i have to agree with the prior replies…

A simple demo would be awsome.



greetz

Imm0

looks great, I'm having trouble intergrating it into my source being a total noob

Offtopic:

Im still learning open gl, and i've got the basics covered, but cant seem to find much on shaders… :? if you know any good resources about vertex and pixel shaders, please point me in the right direction…

Thanx! :slight_smile:

If anyone is interested, to check this out add this code:

import [your package].TerrainRenderPass;

in your main class:

    private TerrainRenderPass trp;



to your init method:

trp.setShadingOnSpatial(terrain); // assumes your terrainpage is called terrain

disp = DisplaySystem.getDisplaySystem();

trp.doRender(disp.getRenderer());



in your terrain method:

trp = new TerrainRenderPass("[ground texture]","[rock texture]","[rock texture normal map]","[splash texture]");

trp.resetParameters();

trp.createRenderStates();



TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();

ts.setEnabled(true);



Texture t1 = TextureManager.loadTexture(Game.class.getClassLoader().getResource(trp.getGroundTexture()),  Texture.MM_LINEAR_LINEAR,  Texture.FM_LINEAR);

    t1.setApply(Texture.AM_COMBINE);

    t1.setWrap(Texture.WM_WRAP_S_WRAP_T);

    t1.setCombineFuncRGB(Texture.ACF_MODULATE);

    t1.setCombineSrc0RGB(Texture.ACS_TEXTURE);

    t1.setCombineOp0RGB(Texture.ACO_SRC_COLOR);

    t1.setCombineSrc1RGB(Texture.ACS_PRIMARY_COLOR);

    t1.setCombineOp1RGB(Texture.ACO_SRC_COLOR);

    t1.setCombineScaleRGB(1.0f);

    ts.setTexture(t1, 0);

Texture t2 = TextureManager.loadTexture(Game.class.getClassLoader().getResource(trp.getRockTexture()), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);

    t2.setWrap(Texture.WM_WRAP_S_WRAP_T);

    t2.setApply(Texture.AM_COMBINE);

    t2.setCombineFuncRGB(Texture.ACF_ADD_SIGNED);

    t2.setCombineSrc0RGB(Texture.ACS_TEXTURE);

    t2.setCombineOp0RGB(Texture.ACO_SRC_COLOR);

    t2.setCombineSrc1RGB(Texture.ACS_PREVIOUS);

    t2.setCombineOp1RGB(Texture.ACO_SRC_COLOR);

    t2.setCombineScaleRGB(1.0f);

    ts.setTexture(t2, 1);

    terrain.setRenderState(ts);

add this to your update method:

trp.doUpdate(1);

Black, this shader is fantastic. I wonder if you could explain a little about the lighting constants:


const vec3 L00  = vec3( 1.0351604,  0.7603549,  0.7074635);
const vec3 L1m1 = vec3( 0.4442150,  0.3430402,  0.3403777);
const vec3 L10  = vec3(-0.2247797, -0.1828517, -0.1705181);
const vec3 L11  = vec3( 0.7110400,  0.5423169,  0.5587956);
const vec3 L2m2 = vec3( 0.6430452,  0.4971454,  0.5156357);
const vec3 L2m1 = vec3(-0.1150112, -0.0936603, -0.0839287);
const vec3 L20  = vec3(-0.3742487, -0.2755962, -0.2875017);
const vec3 L21  = vec3(-0.1694954, -0.1343096, -0.1335315);
const vec3 L22  = vec3( 0.5515260,  0.4222179,  0.4162488);



It would be useful to understand what each of the constants represents so that I could modify them to create different ambiences.

Thanks


Mak

constants for lightsources (L0, L1, L2) from orange-book

Nice one !





Mak

I Update the shaders to work with dynamic lights.



Look at YouTube here:

http://www.youtube.com/watch?v=uCWoThn49-I



and here:

http://www.youtube.com/watch?v=_GhCGf8b75k



Screenshots also updated:

http://mi-studios.de/index.php?option=com_zoom&Itemid=50&catid=1&PageNo=2

any chance you'll release the shaders?

an chance i'll be able to simply plug them into my jme default terrain renderung and they will just work without any additional coding?

HamsterofDeath said:

an chance i'll be able to simply plug them into my jme default terrain renderung and they will just work without any additional coding?


yes, i do it in my game with 2 lines of code.

can it handle N textures, or is there a limit?

I know this is kinda an old topic but its very interesting anyway I think - and the pictures that BlackBlue has provided look real nice. I have tried to make it work for myself though but I cant - so I was wondering if anyone could provide a testclass to go with it?  :?



I can see the thing posted above to test it out but I have had no luck with this, only a black screen so im hoping for a test class perhaps using the textures in the /data/textures/terrain folder or so :slight_smile:

Always nice to get complete runnable test code with contributed code, including asset files such as textures.



Any chance of zipping all the required files to make this work? I'm very interested in this.

I made a test, but the rocks and grass don't blend properly…



EDIT: … link is broken

EDIT: link is fixed

http://cosmos.110mb.com/java/TestTerrainRenderPass.zip

Uuuh cool - I'll check it out and let you know if I can make it fully work! Thanks