- 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
This commit is contained in:
parent
b86b658c93
commit
5f5eb708da
@ -1,3 +1,4 @@
|
||||
#import "Common/ShaderLib/Parallax.glsllib"
|
||||
#import "Common/ShaderLib/Optics.glsllib"
|
||||
#define ATTENUATION
|
||||
//#define HQ_ATTENUATION
|
||||
@ -30,7 +31,10 @@ varying vec3 SpecularSum;
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#ifdef LIGHTMAP
|
||||
@ -38,7 +42,7 @@ varying vec3 SpecularSum;
|
||||
#endif
|
||||
|
||||
#ifdef NORMALMAP
|
||||
uniform sampler2D m_NormalMap;
|
||||
uniform sampler2D m_NormalMap;
|
||||
#else
|
||||
varying vec3 vNormal;
|
||||
#endif
|
||||
@ -132,20 +136,27 @@ vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 w
|
||||
void main(){
|
||||
vec2 newTexCoord;
|
||||
|
||||
#if (defined(PARALLAXMAP) || defined(NORMALMAP_PARALLAX)) && !defined(VERTEX_LIGHTING)
|
||||
float h;
|
||||
#ifdef PARALLAXMAP
|
||||
h = texture2D(m_ParallaxMap, texCoord).r;
|
||||
#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING)
|
||||
|
||||
#ifdef STEEP_PARALLAX
|
||||
#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
|
||||
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
|
||||
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
|
||||
newTexCoord = texCoord;
|
||||
newTexCoord = texCoord;
|
||||
#endif
|
||||
|
||||
#ifdef DIFFUSEMAP
|
||||
|
@ -61,6 +61,15 @@ MaterialDef Phong Lighting {
|
||||
// Parallax/height map
|
||||
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
|
||||
Texture2D AlphaMap
|
||||
|
||||
@ -124,6 +133,8 @@ MaterialDef Phong Lighting {
|
||||
NORMALMAP : NormalMap
|
||||
SPECULARMAP : SpecularMap
|
||||
PARALLAXMAP : ParallaxMap
|
||||
NORMALMAP_PARALLAX : PackedNormalParallax
|
||||
STEEP_PARALLAX : SteepParallax
|
||||
ALPHAMAP : AlphaMap
|
||||
COLORRAMP : ColorRamp
|
||||
LIGHTMAP : LightMap
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 33 KiB |
78
engine/src/core-data/Common/ShaderLib/Parallax.glsllib
Normal file
78
engine/src/core-data/Common/ShaderLib/Parallax.glsllib
Normal file
@ -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 |
Binary file not shown.
166
engine/src/test/jme3test/material/TestParallax.java
Normal file
166
engine/src/test/jme3test/material/TestParallax.java
Normal file
@ -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…
x
Reference in New Issue
Block a user