- Added steep parallax mapping, activate it by setting SteepParallax attribute to true in lighting material

- Refactored paralax calculation code in Parallax.glsllib
- Added a boolean param to lighting material PackedNormalParallax to specify if the parallax map is stored in the alpha channel of the normal map (it was already implemented in the shader). added a dds file and a material using this
- The parallax height can now be set by users by setting the ParallaxHeight attribute. default is 0.05
- Deleted old normal map for water
- Inverted green channel of the brickwall normal texture to look good with recent change in normal calculation in lighting material
- Created a test case for parallax mapping where you can swich from classic to steep parallax using spacebar, and where you can tweak the parallax heigh by using I and K keys.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8046 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
rem..om 14 years ago
parent b86b658c93
commit 5f5eb708da
  1. 37
      engine/src/core-data/Common/MatDefs/Light/Lighting.frag
  2. 11
      engine/src/core-data/Common/MatDefs/Light/Lighting.j3md
  3. BIN
      engine/src/core-data/Common/MatDefs/Water/Textures/gradient_map.jpg
  4. 78
      engine/src/core-data/Common/ShaderLib/Parallax.glsllib
  5. 8
      engine/src/test-data/Textures/Terrain/BrickWall/BrickWall2.j3m
  6. BIN
      engine/src/test-data/Textures/Terrain/BrickWall/BrickWall_normal.jpg
  7. BIN
      engine/src/test-data/Textures/Terrain/BrickWall/BrickWall_normal_parallax.dds
  8. 166
      engine/src/test/jme3test/material/TestParallax.java

@ -1,3 +1,4 @@
#import "Common/ShaderLib/Parallax.glsllib"
#import "Common/ShaderLib/Optics.glsllib" #import "Common/ShaderLib/Optics.glsllib"
#define ATTENUATION #define ATTENUATION
//#define HQ_ATTENUATION //#define HQ_ATTENUATION
@ -30,7 +31,10 @@ varying vec3 SpecularSum;
#endif #endif
#ifdef PARALLAXMAP #ifdef PARALLAXMAP
uniform sampler2D m_ParallaxMap; uniform sampler2D m_ParallaxMap;
#endif
#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING)
uniform float m_ParallaxHeight;
#endif #endif
#ifdef LIGHTMAP #ifdef LIGHTMAP
@ -38,7 +42,7 @@ varying vec3 SpecularSum;
#endif #endif
#ifdef NORMALMAP #ifdef NORMALMAP
uniform sampler2D m_NormalMap; uniform sampler2D m_NormalMap;
#else #else
varying vec3 vNormal; varying vec3 vNormal;
#endif #endif
@ -132,20 +136,27 @@ vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 w
void main(){ void main(){
vec2 newTexCoord; vec2 newTexCoord;
#if (defined(PARALLAXMAP) || defined(NORMALMAP_PARALLAX)) && !defined(VERTEX_LIGHTING) #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING)
float h;
#ifdef PARALLAXMAP #ifdef STEEP_PARALLAX
h = texture2D(m_ParallaxMap, texCoord).r; #ifdef NORMALMAP_PARALLAX
//parallax map is stored in the alpha channel of the normal map
newTexCoord = steepParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
#else
//parallax map is a texture
newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);
#endif
#else #else
h = texture2D(m_NormalMap, texCoord).a; #ifdef NORMALMAP_PARALLAX
//parallax map is stored in the alpha channel of the normal map
newTexCoord = classicParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
#else
//parallax map is a texture
newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);
#endif
#endif #endif
float heightScale = 0.05;
float heightBias = heightScale * -0.5;
vec3 normView = normalize(vViewDir);
h = (h * heightScale + heightBias) * normView.z;
newTexCoord = texCoord + (h * normView.xy);
#else #else
newTexCoord = texCoord; newTexCoord = texCoord;
#endif #endif
#ifdef DIFFUSEMAP #ifdef DIFFUSEMAP

@ -61,6 +61,15 @@ MaterialDef Phong Lighting {
// Parallax/height map // Parallax/height map
Texture2D ParallaxMap Texture2D ParallaxMap
//Set to true is parallax map is stored in the alpha channel of the normal map
Boolean PackedNormalParallax
//Sets the relief height for parallax mapping
Float ParallaxHeight : 0.05
//Set to true to activate Steep Parallax mapping
Boolean SteepParallax
// Texture that specifies alpha values // Texture that specifies alpha values
Texture2D AlphaMap Texture2D AlphaMap
@ -124,6 +133,8 @@ MaterialDef Phong Lighting {
NORMALMAP : NormalMap NORMALMAP : NormalMap
SPECULARMAP : SpecularMap SPECULARMAP : SpecularMap
PARALLAXMAP : ParallaxMap PARALLAXMAP : ParallaxMap
NORMALMAP_PARALLAX : PackedNormalParallax
STEEP_PARALLAX : SteepParallax
ALPHAMAP : AlphaMap ALPHAMAP : AlphaMap
COLORRAMP : ColorRamp COLORRAMP : ColorRamp
LIGHTMAP : LightMap LIGHTMAP : LightMap

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

@ -0,0 +1,78 @@
#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING)
vec2 steepParallaxOffset(sampler2D parallaxMap, vec3 vViewDir,vec2 texCoord,float parallaxScale){
vec2 vParallaxDirection = normalize( vViewDir.xy );
// The length of this vector determines the furthest amount of displacement: (Ati's comment)
float fLength = length( vViewDir );
float fParallaxLength = sqrt( fLength * fLength - vViewDir.z * vViewDir.z ) / vViewDir.z;
// Compute the actual reverse parallax displacement vector: (Ati's comment)
vec2 vParallaxOffsetTS = vParallaxDirection * fParallaxLength;
// Need to scale the amount of displacement to account for different height ranges
// in height maps. This is controlled by an artist-editable parameter: (Ati's comment)
parallaxScale *=0.3;
vParallaxOffsetTS *= parallaxScale;
vec3 eyeDir = normalize(vViewDir).xyz;
float nMinSamples = 6;
float nMaxSamples = 1000 * parallaxScale;
float nNumSamples = mix( nMinSamples, nMaxSamples, 1.0 - eyeDir.z ); //In reference shader: int nNumSamples = (int)(lerp( nMinSamples, nMaxSamples, dot( eyeDirWS, N ) ));
float fStepSize = 1.0 / nNumSamples;
float fCurrHeight = 0.0;
float fPrevHeight = 1.0;
float fNextHeight = 0.0;
float nStepIndex = 0;
vec2 vTexOffsetPerStep = fStepSize * vParallaxOffsetTS;
vec2 vTexCurrentOffset = texCoord;
float fCurrentBound = 1.0;
float fParallaxAmount = 0.0;
while ( nStepIndex < nNumSamples && fCurrHeight <= fCurrentBound ) {
vTexCurrentOffset -= vTexOffsetPerStep;
fPrevHeight = fCurrHeight;
#ifdef NORMALMAP_PARALLAX
//parallax map is stored in the alpha channel of the normal map
fCurrHeight = texture2DLod( parallaxMap, vTexCurrentOffset,1.0).a;
#else
//parallax map is a texture
fCurrHeight = texture2DLod( parallaxMap, vTexCurrentOffset,1.0).r;
#endif
fCurrentBound -= fStepSize;
nStepIndex+=1.0;
}
vec2 pt1 = vec2( fCurrentBound, fCurrHeight );
vec2 pt2 = vec2( fCurrentBound + fStepSize, fPrevHeight );
float fDelta2 = pt2.x - pt2.y;
float fDelta1 = pt1.x - pt1.y;
float fDenominator = fDelta2 - fDelta1;
fParallaxAmount = (pt1.x * fDelta2 - pt2.x * fDelta1 ) / fDenominator;
vec2 vParallaxOffset = vParallaxOffsetTS * (1.0 - fParallaxAmount );
return texCoord - vParallaxOffset;
}
vec2 classicParallaxOffset(sampler2D parallaxMap, vec3 vViewDir,vec2 texCoord,float parallaxScale){
float h;
h = texture2D(parallaxMap, texCoord).a;
#ifdef NORMALMAP_PARALLAX
//parallax map is stored in the alpha channel of the normal map
h = texture2D(parallaxMap, texCoord).a;
#else
//parallax map is a texture
h = texture2D(parallaxMap, texCoord).r;
#endif
float heightScale = parallaxScale;
float heightBias = heightScale* -0.6;
vec3 normView = normalize(vViewDir);
h = (h * heightScale + heightBias) * normView.z;
return texCoord + (h * normView.xy);
}
#endif

@ -0,0 +1,8 @@
Material Pong Rock : Common/MatDefs/Light/Lighting.j3md {
MaterialParameters {
Shininess: 2.0
DiffuseMap : Textures/Terrain/BrickWall/BrickWall.jpg
NormalMap : Textures/Terrain/BrickWall/BrickWall_normal_parallax.dds
PackedNormalParallax: true
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 70 KiB

@ -0,0 +1,166 @@
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package jme3test.material;
import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.FXAAFilter;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.SkyFactory;
import com.jme3.util.TangentBinormalGenerator;
public class TestParallax extends SimpleApplication {
private Vector3f lightDir = new Vector3f(-1, -1, .5f).normalizeLocal();
public static void main(String[] args) {
TestParallax app = new TestParallax();
app.start();
}
public void setupSkyBox() {
rootNode.attachChild(SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false));
}
DirectionalLight dl;
public void setupLighting() {
dl = new DirectionalLight();
dl.setDirection(lightDir);
dl.setColor(new ColorRGBA(.9f, .9f, .9f, 1));
rootNode.addLight(dl);
}
Material mat;
public void setupFloor() {
mat = assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall2.j3m");
mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat);
mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat);
mat.setFloat("Shininess", 0);
Node floorGeom = (Node) assetManager.loadAsset("Models/WaterTest/WaterTest.mesh.xml");
Geometry g = ((Geometry) floorGeom.getChild(0));
g.getMesh().scaleTextureCoordinates(new Vector2f(10, 10));
TangentBinormalGenerator.generate(floorGeom);
floorGeom.setLocalTranslation(0, 22, 0);
floorGeom.setLocalScale(100);
floorGeom.setMaterial(mat);
rootNode.attachChild(floorGeom);
}
public void setupSignpost() {
Spatial signpost = assetManager.loadModel("Models/Sign Post/Sign Post.mesh.xml");
Material mat = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m");
TangentBinormalGenerator.generate(signpost);
signpost.setMaterial(mat);
signpost.rotate(0, FastMath.HALF_PI, 0);
signpost.setLocalTranslation(12, 23.5f, 30);
signpost.setLocalScale(4);
signpost.setShadowMode(ShadowMode.CastAndReceive);
rootNode.attachChild(signpost);
}
@Override
public void simpleInitApp() {
cam.setLocation(new Vector3f(-15.445636f, 30.162927f, 60.252777f));
cam.setRotation(new Quaternion(0.05173137f, 0.92363626f, -0.13454558f, 0.35513034f));
flyCam.setMoveSpeed(30);
setupLighting();
setupSkyBox();
setupFloor();
setupSignpost();
inputManager.addListener(new AnalogListener() {
public void onAnalog(String name, float value, float tpf) {
if ("heightUP".equals(name)) {
parallaxHeigh += 0.0001;
mat.setFloat("ParallaxHeight", parallaxHeigh);
}
if ("heightDown".equals(name)) {
parallaxHeigh -= 0.0001;
parallaxHeigh = Math.max(parallaxHeigh, 0);
mat.setFloat("ParallaxHeight", parallaxHeigh);
}
}
}, "heightUP", "heightDown");
inputManager.addMapping("heightUP", new KeyTrigger(KeyInput.KEY_I));
inputManager.addMapping("heightDown", new KeyTrigger(KeyInput.KEY_K));
inputManager.addListener(new ActionListener() {
public void onAction(String name, boolean isPressed, float tpf) {
if (isPressed && "toggleSteep".equals(name)) {
steep = !steep;
mat.setBoolean("SteepParallax", steep);
}
}
}, "toggleSteep");
inputManager.addMapping("toggleSteep", new KeyTrigger(KeyInput.KEY_SPACE));
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
FXAAFilter fxaa = new FXAAFilter();
fxaa.setReduceMul(0.08f);
fpp.addFilter(fxaa);
viewPort.addProcessor(fpp);
}
float parallaxHeigh = 0.05f;
float time = 0;
boolean steep = false;
@Override
public void simpleUpdate(float tpf) {
// time+=tpf;
// lightDir.set(FastMath.sin(time), -1, FastMath.cos(time));
// bsr.setDirection(lightDir);
// dl.setDirection(lightDir);
}
}
Loading…
Cancel
Save