Hey guys n girls.
This submission is more of a merging of existing code than a personal write. Essentially, it is the same HeightBasedTerrain material that already exists in JME, with the added bonus of ambient, directional, point and spot light support. My apologies for my c# style layout - it just strikes me as easier to read than the traditional java brace standard.
Note: For those copy-pasting, you need to put the appropriate path and name of the vertex and fragment shaders in the first technique of the .jm3d file.
I guess screenshots are a little pointless, but anyways…
.j3md material file
[java]
MaterialDef Height Based Terrain Lighting
{
MaterialParameters
{
Texture2D DiffuseMap
Texture2D DiffuseMap_1
Texture2D DiffuseMap_2
Texture2D DiffuseMap_3
Texture2D SlopeDiffuseMap
Float slopeTileFactor
Float terrainSize
Vector3 region1
Vector3 region2
Vector3 region3
Vector3 region4
}
Technique
{
LightMode MultiPass
VertexShader GLSL100: MatDefs/CustomTerrain/CustomTerrain.vert
FragmentShader GLSL100: MatDefs/CustomTerrain/CustomTerrain.frag
WorldParameters
{
WorldViewProjectionMatrix
WorldViewMatrix
WorldMatrix
NormalMatrix
ViewMatrix
}
Defines
{
DIFFUSEMAP : DiffuseMap
}
}
Technique PreShadow {
VertexShader GLSL100 : Common/MatDefs/Shadow/PreShadow.vert
FragmentShader GLSL100 : Common/MatDefs/Shadow/PreShadow.frag
WorldParameters {
WorldViewProjectionMatrix
WorldViewMatrix
}
Defines {
DIFFUSEMAP_ALPHA : DiffuseMap
}
RenderState {
FaceCull Off
DepthTest On
DepthWrite On
PolyOffset 5 0
ColorWrite Off
}
}
Technique PreNormalPass {
VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert
FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag
WorldParameters {
WorldViewProjectionMatrix
WorldViewMatrix
NormalMatrix
}
Defines {
DIFFUSEMAP_ALPHA : DiffuseMap
}
RenderState {
}
}
Technique GBuf {
VertexShader GLSL100: Common/MatDefs/Light/GBuf.vert
FragmentShader GLSL100: Common/MatDefs/Light/GBuf.frag
WorldParameters {
WorldViewProjectionMatrix
WorldMatrix
}
Defines {
VERTEX_COLOR : UseVertexColor
MATERIAL_COLORS : UseMaterialColors
V_TANGENT : VTangent
MINNAERT : Minnaert
WARDISO : WardIso
DIFFUSEMAP : DiffuseMap
NORMALMAP : NormalMap
SPECULARMAP : SpecularMap
PARALLAXMAP : ParallaxMap
}
}
Technique {
LightMode FixedPipeline
}
Technique Glow {
VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert
FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag
WorldParameters {
WorldViewProjectionMatrix
}
Defines {
HAS_GLOWMAP : GlowMap
HAS_GLOWCOLOR : GlowColor
}
}
}
[/java]
.vert shader
[java]
uniform float m_tilingFactor;
uniform float m_terrainSize;
uniform mat4 g_WorldViewProjectionMatrix;
uniform mat4 g_WorldViewMatrix;
uniform mat4 g_WorldMatrix;
uniform mat3 g_NormalMatrix;
uniform mat4 g_ViewMatrix;
uniform vec4 g_LightColor;
uniform vec4 g_LightPosition;
uniform vec4 g_AmbientLightColor;
attribute vec3 inNormal;
attribute vec3 inPosition;
varying vec3 hbNormal;
varying vec4 hbPosition;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec3 vViewDir;
varying vec4 vLightDir;
varying vec3 lightVec;
varying vec4 AmbientSum;
varying vec4 DiffuseSum;
varying vec4 SpecularSum;
// JME3 lights in world space
void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir)
{
float posLight = step(0.5, color.w);
vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
lightVec.xyz = tempVec;
float dist = length(tempVec);
lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
lightDir.xyz = tempVec / vec3(dist);
}
void main()
{
hbNormal = normalize(inNormal);
hbPosition = g_WorldMatrix * vec4(inPosition, 0.0);
vec4 pos = vec4(inPosition, 1.0);
gl_Position = g_WorldViewProjectionMatrix * pos;
vec3 wvPosition = (g_WorldViewMatrix * pos).xyz;
vec3 wvNormal = normalize(g_NormalMatrix * inNormal);
vec3 viewDir = normalize(-wvPosition);
vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition.xyz,clamp(g_LightColor.w,0.0,1.0)));
wvLightPos.w = g_LightPosition.w;
vec4 lightColor = g_LightColor;
vNormal = wvNormal;
vPosition = wvPosition;
vViewDir = viewDir;
lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
AmbientSum = vec4(0.2, 0.2, 0.2, 1.0) * g_AmbientLightColor; // Default: ambient color is dark gray
DiffuseSum = lightColor;
SpecularSum = lightColor;
}
[/java]
.frag shader
[java]
uniform vec4 g_LightDirection;
uniform vec3 m_region1;
uniform vec3 m_region2;
uniform vec3 m_region3;
uniform vec3 m_region4;
uniform sampler2D m_DiffuseMap;
uniform sampler2D m_DiffuseMap_1;
uniform sampler2D m_DiffuseMap_2;
uniform sampler2D m_DiffuseMap_3;
uniform sampler2D m_SlopeDiffuseMap;
uniform float m_slopeTileFactor;
uniform float m_terrainSize;
varying vec3 hbNormal;
varying vec4 hbPosition;
varying vec4 AmbientSum;
varying vec4 DiffuseSum;
varying vec4 SpecularSum;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec3 vViewDir;
varying vec4 vLightDir;
varying vec3 lightVec;
vec4 GenerateTerrainColor()
{
float height = hbPosition.y;
vec4 p = hbPosition / m_terrainSize;
vec3 blend = abs( hbNormal );
blend = (blend -0.2) * 0.7;
blend = normalize(max(blend, 0.00001)); // Force weights to sum to 1.0 (very important!)
float b = (blend.x + blend.y + blend.z);
blend /= vec3(b, b, b);
vec4 terrainColor = vec4(0.0, 0.0, 0.0, 1.0);
float m_regionMin = 0.0;
float m_regionMax = 0.0;
float m_regionRange = 0.0;
float m_regionWeight = 0.0;
vec4 slopeCol1 = texture2D(m_SlopeDiffuseMap, p.yz * m_slopeTileFactor);
vec4 slopeCol2 = texture2D(m_SlopeDiffuseMap, p.xy * m_slopeTileFactor);
// Terrain m_region 1.
m_regionMin = m_region1.x;
m_regionMax = m_region1.y;
m_regionRange = m_regionMax - m_regionMin;
m_regionWeight = (m_regionRange - abs(height - m_regionMax)) / m_regionRange;
m_regionWeight = max(0.0, m_regionWeight);
terrainColor += m_regionWeight * texture2D(m_DiffuseMap, p.xz * m_region1.z);
// Terrain m_region 2.
m_regionMin = m_region2.x;
m_regionMax = m_region2.y;
m_regionRange = m_regionMax - m_regionMin;
m_regionWeight = (m_regionRange - abs(height - m_regionMax)) / m_regionRange;
m_regionWeight = max(0.0, m_regionWeight);
terrainColor += m_regionWeight * (texture2D(m_DiffuseMap_1, p.xz * m_region2.z));
// Terrain m_region 3.
m_regionMin = m_region3.x;
m_regionMax = m_region3.y;
m_regionRange = m_regionMax - m_regionMin;
m_regionWeight = (m_regionRange - abs(height - m_regionMax)) / m_regionRange;
m_regionWeight = max(0.0, m_regionWeight);
terrainColor += m_regionWeight * texture2D(m_DiffuseMap_2, p.xz * m_region3.z);
// Terrain m_region 4.
m_regionMin = m_region4.x;
m_regionMax = m_region4.y;
m_regionRange = m_regionMax - m_regionMin;
m_regionWeight = (m_regionRange - abs(height - m_regionMax)) / m_regionRange;
m_regionWeight = max(0.0, m_regionWeight);
terrainColor += m_regionWeight * texture2D(m_DiffuseMap_3, p.xz * m_region4.z);
return (blend.y * terrainColor + blend.x * slopeCol1 + blend.z * slopeCol2);
}
float tangDot(in vec3 v1, in vec3 v2)
{
float d = dot(v1,v2);
return d;
}
float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir)
{
return max(0.0, dot(norm, lightdir));
}
float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny)
{
if (shiny <= 1.0)
{
return 0.0;
}
vec3 R = reflect(-lightdir, norm);
return pow(max(tangDot(R, viewdir), 0.0), shiny);
}
vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 wvLightDir)
{
float shininess = 0.0;
float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir, wvViewDir);
float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir, shininess);
specularFactor *= step(1.0, shininess);
float att = vLightDir.w;
return vec2(diffuseFactor, specularFactor) * vec2(att);
}
void main()
{
vec4 diffuseColor = GenerateTerrainColor();
float spotFallOff = 1.0;
if(g_LightDirection.w!=0.0)
{
vec3 L = normalize(lightVec.xyz);
vec3 spotdir = normalize(g_LightDirection.xyz);
float curAngleCos = dot(-L, spotdir);
float innerAngleCos = floor(g_LightDirection.w) * 0.001;
float outerAngleCos = fract(g_LightDirection.w);
float innerMinusOuter = innerAngleCos - outerAngleCos;
spotFallOff = (curAngleCos - outerAngleCos) / innerMinusOuter;
if(spotFallOff <= 0.0)
{
gl_FragColor = AmbientSum * diffuseColor;
return;
}
else
{
spotFallOff = clamp(spotFallOff, 0.0, 1.0);
}
}
vec3 normal = vNormal;
vec4 lightDir = vLightDir;
lightDir.xyz = normalize(lightDir.xyz);
vec2 light = computeLighting(vPosition, normal, vViewDir.xyz, lightDir.xyz)*spotFallOff;
vec4 specularColor = vec4(1.0);
gl_FragColor = AmbientSum * diffuseColor +
DiffuseSum * diffuseColor * light.x +
SpecularSum * specularColor * light.y;
}
[/java]
finally, the useage is different only by the name of the textures since lighting now affects the material:
[java]
float scale = 8f;
Material terrainMat = new Material(gameManager.getAssetManager(), “MatDefs/CustomTerrain/CustomTerrain.j3md”);
Texture tex_region1 = gameManager.getAssetManager().loadTexture(“Textures/Terrain/sandstone_top.png”);
tex_region1.setWrap(WrapMode.Repeat);
Texture tex_region2 = gameManager.getAssetManager().loadTexture(“Textures/Terrain/realdirt.png”);
tex_region2.setWrap(WrapMode.Repeat);
Texture tex_region3 = gameManager.getAssetManager().loadTexture(“Textures/Terrain/Grass_1.png”);
tex_region3.setWrap(WrapMode.Repeat);
Texture tex_region4 = gameManager.getAssetManager().loadTexture(“Textures/Terrain/realsnow.png”);
tex_region4.setWrap(WrapMode.Repeat);
terrainMat.setTexture(“DiffuseMap”, tex_region1);
terrainMat.setVector3(“region1”, new Vector3f(0, 88, scale));
terrainMat.setTexture(“DiffuseMap_1”, tex_region2);
terrainMat.setVector3(“region2”, new Vector3f(88, 130, scale));
terrainMat.setTexture(“DiffuseMap_2”, tex_region3);
terrainMat.setVector3(“region3”, new Vector3f(130, 270, scale));
terrainMat.setTexture(“DiffuseMap_3”, tex_region4);
terrainMat.setVector3(“region4”, new Vector3f(270, 384, scale));
// slope
Texture rock = gameManager.getAssetManager().loadTexture(“Textures/Terrain/Tundra.jpg”);
rock.setWrap(WrapMode.Repeat);
terrainMat.setTexture(“SlopeDiffuseMap”, rock);
terrainMat.setFloat(“slopeTileFactor”, 2f);
terrainMat.setFloat(“terrainSize”, (GlobalSettings.BLOCK_SIZE - 1) / 4);
[/java]